Interface programming in non-COM environment - Question, Skills, Applications (1)

zhaozj2021-02-16  51

Interface programming in non-COM environment ------ Questions, Tips, Applications

First knowledge: Delphi / interface / DLL / OOP

Difficulty: ★★★ ☆☆

Primer:

The concept of the interface has long been long before coming (COM should be around 95 years) The concept of interface is already in the object-oriented development, and the famous "design model" (94-year publication) also pointed out " Interface programming rather than implementation programming. " Using an interface can reduce the counbies of different modules in a software system, facilitating update and maintenance of software systems. The advantages of the interface are definitely not just in COM, in fact, the interfaces in most programming tasks are a good choice. (Developed by Delphi, knowing that Delphi is also using the interface to describe Web Methord, so the concept of interface is never outdated in the object-oriented field) This article is not an article discussing COM, but wants to pass an example. Note the actual function of the interface in Delphi, as well as the problems that may encounter in development and the required skills.

example:

※ First impression:

People who are familiar with the Windows programming should have already used the DLL in the system they develop. If we want to put an object into the DLL maintenance (not just some functions and procedures)? It is easier to think that the answer is to use COM. What other ways do you have this? Some other methods (such as memory copies) can be solved using Delphi's dynamic bag BPL or some other ways. But now we want to create a standard DLL file, we can operate the objects in which you can use COM, but you don't need to register like the COM component, it should be as long as the ordinary DLL file Can work normally. This advantage is that we are needing a program that allows a plug-in to be extended as most of the drawing software, then in addition to the standard COM technology, we can put an agreement of the agreement interface (that is, the contract of the plugin) is placed in one. In the standard DLL library, in the primary application, according to a different plug-in name and the path in the file configured by the user, so our plug-in is downloaded to the customer's computer, no registration installation process And just just configure it in the main program. This process looks like this:

For i: = 0 to plugincount-1 do

// PluginCount is the number of plugins that have been "installed" from the configuration file

Begin

...

DLLHND [I]: = LoadLibrary (PLUGPATH);

// plugPath is the path to each DLL to get from the previous program from the cultivation document

@GetPlugintf: = getProcaddress (DLLHND [I], 'getPlugintf');

Plugintf [i]: = getPlugintf; // getPluginTh can return an interface of iUnknown

...

END;

Now we get the interface of each plugin loaded and can operate. From the above code, you can see some of the DLL we need to manage objects: This DLL has only one unique export function to get the interface in which the maintained object (GetPlugintf, there may be other export functions, but this is Must be), this function can return an object implemented interface or directly return to the iUnknown interface (so that you can manage all plug-in interfaces with an array, but also to use the loop structure to implement the program, just like it is as seen above), the main program Conversion when needed. In addition, our main program requires a document (contract) that describes the interface with the DLL. The function returned to the interface exported looks like this: VAR

Ourobject: TINTFOBJECT;

...

Function GetfooObjectIntf: IUNKNOWN; STDCALL;

Begin

IF not assigned (otobject) THEN

Begin

Ourobject: = TINTFOBJECT.CREATE;

...

END;

RESULT: = Ourobject as iunknown;

END;

With the above description, you can see that you want to maintain the object in a normal DLL and the interface to publish the object like COM is also a very simple thing. Nothing special, but the above discussion has a big problem: if Our DLL has only one export function, which means that it can only export an interface of an object, just like it is above, but if we want to maintain multiple objects in this DLL (especially some according to the inheritance relationship Objects, or objects with common features)?

※ Using factory model:

The best way to solve the above problem is to use the factory model in the DLL design to manage multiple objects of these maintenance, which can not only maintain different objects, but also maintain a plurality of instances of a class. Then we use the interface to export this factory object in that unique export function, and other object interfaces can be obtained through this interface. For example, like this:

Function Tfoomanager.createafoo: Ifoo;

Begin

Inc (FOONUM);

if Length (Flist)

SETLENGTH (Flist, Foonum * 2);

FLIST [foonum-1]: = tfoo.create;

...

Result: = flist [fumbum-1] as ifoo;

END;

For example, the TFOOManager is a factory class, which is responsible for managing the life cycle of the specific object in the DLL. Createafoo creates a DLL's object named foo and saves it to its own private field, a dynamic array:

Private

Flist: array of tfoo;

Note that the above code uses a small skill. Whenever the space is not large enough, we use double allocation strategies, which are both two times the space we needed to flist. When creating the object request is frequent, this is undoubtedly a policy that effectively improve the efficiency of execution efficiency because the setLEngth function is a time-consuming process when reassigning the space and moves already existing elements. This strategy is also an algorithm for typical spatial transfer times.

Now there is a factory mode, we have to manage multiple objects in the DLL or different objects. We can even let Createafoo accept a parameter to determine what type of object creation, and add an object every other We add an object's private field (dynamic array of dynamic arrays, of course, according to your needs, using other data structures). For other methods of this factory class, please refer to the list of code after text. Questions and Solution Tips:

※ Reference counting problem (when we need to manually manage the life cycle of the object):

Since we define a private field in the factory class to store many object instances in the DLL, a very obvious point of view from here is that we need to manually manage the life cycle of the object, we may also need to add it in the factory class. A method for releasing the managed object:

Procedure tfoomanager.delafoo (ID: integer);

VAR

i: integer;

Begin

IF foonum> 0 THEN

Begin

FLIST [ID] .free;

For i: = id to foonum-2 do

Begin

FLIST [I]: = flist [i 1]; // Move the remaining elements to keep the object in a continuous storage in the FLIST.

END;

FLIST [FOONUM-1]: = NIL;

Dec (fumbum);

END;

END;

This method releases the specified object based on the incoming ID value (when managing a variety of different objects, you need to receive a parameter that represents the required release of the object type to determine which type of object). Ok, everything looks normal, but when we create an object from the DLL to create an object:

Procedure CallCreatefoo;

VAR

Tempfoo: ifoo; // Interface IFOO to create the foo object

Begin

Tempfoo: = foom.createafoo; // foom is the interface of the factory class object

END;

When we need to use the object you just created outside of this process (for example, one method we can use by factory class TFOOManager.getfoobyid (ID: integer) Find the object specified in FLIST according to the incoming ID, if we created through factory methods The object), there will be a memory access error. After careful observation, it will find that the object we just created is not in memory! why? Since the interfaces in Delphi are inherited from Iinterface, this is the same as IUNKNOWN, which means that our objects must implement the three methods in iUnknown, and further we don't need to write these codes in our class. Inherited from the default to implement the Iinterface class TINTERFACEDOBJECT. However, the wrong root is in this place, inheriting the object from TinterFaceDObject, does not allow us to manually manage its lifecycle, because IUNKNOWN's implementation class maintains the life cycle of the object according to the reference count in the object, and the top of Tempfoo is a The partial variable of the process, when it leaves the scope, it will automatically call Tempfoo._ Release (this is a method of IUNKNOWN) when it leaves the scope. This method will automatically release the object when the reference count is 0! And when we call the Tfoomanager.createafoo method, we have made only AS operations RESULT: = flist [foonum-1] as ifoo; (Delphi will automatically add an object's reference count), so the reference count is 1 in _ Release has changed to 0, so this object does not exist before we want to access the objects we created. Ok, the problem of the problem is very clear, it is not difficult to solve, we only use it before returning the request interface _addref operation to increase the reference count value, unless we manually release the object, otherwise the reference count will not 0, the following improvements: function tfoomanager.createafoo: IFOO

Begin

...

FLIST [foonum-1]: = tfoo.create;

(FLIST [FOONUM-1] as iUnknown ._ AddRef;

Result: = flist [fumbum-1] as ifoo;

END;

Ok, it seems that we have overcome all the difficulties, is this? No, troublesome immediately appear! When we call the factory class's delafoo method, we will throw more exceptions! . When we release the object, you will call the TinterfaceObject's Free method. In calling this method, the compiler will automatically call the TinterFaceDObject.beforeDestruction method (in fact this is a method that inherits from TOBJECT, but there is no implementation in TOBJECT This method code is as follows:

Procedure TinterFaceDObject.beforceStruction;

Begin

IF Refcount <> 0 THEN

Error (reinvalidptr);

END;

Seeing the problem here, because we manually increase the reference count value, the value will not be 0 before the object is released, and the above code will throw an exception when the reference value is 0. The way to solve this problem is simple, we only need to define our TinterFaceDObject class TOURINTERFACEDOBJECT, replica in this class (Override, because this is a virtual function) BeforeDestruction, let it code partial blank: procedure TinterfaceDObject.beforceStruction;

Begin

END;

Then all classes in our DLL are only inherited from TourinterFaceDObject. ※ Is there any problem when we do this time we will test it? If it is only the same as the code is really no problem, the problem is that we may need to use the objects we manage anywhere, and the Delphi compiler will automatically call when the interface variable leaves the scope or being manually set to nil _intfclear To decide whether to release the implementation of the interface, if we have called, if we have called such a method such as delafoo, it has released our object, then an access exception appears when calling _intfclear, because the object has not existed at all. ! ! It is time to handle the object life cycle to us management rather than handing over the interface management, because the inside of the object still has a reference count value, we just have used a skill confused It is the management of the object life cycle), it seems that we have to return to the beginning, we have to consider not TinterFaceDObject but write a class that implements Iinterface, thoroughly discard the reference count, and omitted to the like BeforeDestruction. Unnecessary method: TMYINTERFACEDOBJECT = Class (Tobject, IINTERFACE) protected

Function Queryinterface (Const IID: Tguid; Out Obj): hResult; stdcall; function _addref: integer; stdcall; function _release: integer;

Function TMYINTERFACEDOBJECT._ADDREF: Integer; Begin

Result: = 1; // We have no need to quote the count;

Function TMYINTERFACEDOBJECT._RELEASE: INTEGER; Begin

RESULT: = 1;

Function TMYINTERFACEDOBJECT.QUERYINTERFACE (Const IID: Tguid; Out Obj): HRESULT; Begin

IF GetInterface (IID, OBJ) THEN Result: = 0 else

Result: = E_NOINTERFACE; END;

I am very happy to tell you that we have solved all the problems in the life cycle management of the object, and the object we manage can work normally! And we can also remove the skills of the above, from this TMYINTERFACedObject inheritance is enough to solve all the problems. Some people may see that the above code has greatly destroyed the COM specification, but this is the purpose of this article (j), through our improvement, our manual management object is working very well, and this is exactly what we are Is it not required?

转载请注明原文地址:https://www.9cbs.com/read-27341.html

New Post(0)