COM component model

zhaozj2021-02-16  58

I. Dynamic Link Library: Dynamic Link Library is a load object of most COM components (don't care about OCX, it is also a DLL, but only change the suffix). Of course, EXE is also possible (TEXTOSPEECH object in TTS is an example), but it is much less in fact.

In the early days of Windows, the appearance of dynamic link libraries was a revolution. It changed the life of Windows, and also lay a solid cornerstone for the dominant position of today's Windows operating system. (About Windows's historical problem, I have never been clear. Please write the historians of vckbase to write an article as soon as possible ^ _ ^).

Microsoft is explained this to dynamic link library:

Dynamic Link Library (DLL) is an executable as a shared function library. Dynamic links provide a method that allows the process to call a function that does not belong to its executable code. The executable code of the function is located in one DLL, which contains one or more functions that have been compiled, link and separately stored with their processes. The DLL also helps share data and resources. Multiple applications can access the contents of a single DLL copy in the memory.

Well, it's very clear. The dynamic link library is first a executable file (Microsoft explained that EXE is called a direct executable file), which contains a set of functions that need to be shared. When used, a dynamic link library (and Windows system) provides a method to make our application to call the functions. In addition, the dynamic link library also contains some resources (such as icons, dialog templates, etc.). In MFC, Microsoft applies some techniques to provide some additional functions on the basis of existing dynamic link libraries, such as the export of the MFC class.

The link mode of dynamic link library is roughly divided into two categories: static links and dynamic links.

Static links are also implicit links. This link method allows us to indicate in the system without the statement, which is the dynamic link library to load. Its static link declares is placed in the engineering attribute (or uses #pragma comment (lib, "xxx.lib"), which can be placed with #include). At designation, you only need to enter the corresponding import library file (.lib) of its dynamic link library. You can then call the function in the dynamic link library anywhere in the program (of course, you need to include its corresponding header file. In general, the header file will give a piece of the lib file. ). The program generated by this method is initialized (when the specifically is not very clear. But I can definitely be before the Winmain function is ^ _ ^), will automatically load the dynamic link library in the system environment and will It is mapped to our application process. When we call a function that is not defined by our process, the VC runtime will find the function of the corresponding dynamic link library by finding the relevant information of the lib file and calls it. At the end of the process, the system will load the dynamic link library.

Dynamic links are also explicit links, as the name referway, we must explicitly load the dynamic link library by calling the API by calling the API. All COM component models all use this way to load the component module (already DLL). (I think Microsoft's professional term is a chaos.). This approach has many benefits, it can decide which link library to load at runtime, which function is to call ... This is called dynamic link.

It is not difficult to use the dynamic link library, first to call LoadLibrary, whose prototype is as follows:

HMODULE LOADLIBRARY (LPCTSTSTR LPFILENAME / / File Name of Module); Parameter LPFileName is the file name of the dynamic link library to load. If the load is successful, it returns its handle. Otherwise, return NULL.

The prototype is as follows:

Bool Freelibrary (HModule HModule // Handle to DLL Module); I don't have to say more.

When the dynamic link library is loaded by LoadLibrary, the C run library completes the initialization of the dynamic link library through _dllmainCrtStartup, such as the global object (variable), the generation of static member variables, and the initial value. The most important thing is that it also calls the DLLMAIN function. Each dynamic link library must have this function, just like the application must have Main or WinMain. Its prototype is: BOOL WinApi Dllmain (Hinstance Hinstdll, // Handle To The DLL Module Dword FDWREASON,

// Reason for calling function lpvoid lpvreserved /// reserved); You can complete the environmental initialization and destructive operations in your dynamic link library through the Dllmain function. Ah, things are like this:

Dllmain has four situations, these four situations can come from FDWREASON parameters:

they are, respectively

1. DLL_PROCESS_ATTACH, when the dynamic link library is loaded into the process, call DLLMAIN.

2. DLL_THREAD_ATTACH, when the process creates a new thread, the process will call the DLLMAIN that has been loaded with the dynamic link library.

3. DLL_THREAD_DETACH, when a thread ends, the process will call the DLLMAIN that has been loaded with the dynamic link library.

4. DLL_THREAD_DETACH, when the dynamic link library is loaded or the process is completed, call DLLMAIN.

In this way, the life cycle of a dynamic link library can be reacted by the DLLMAIN function.

When we succeed, we will get an HMODULE handle. The use of this handle is very similar to the handle of the Hinstance application instance (Tracing definition, HModule is hinstance). We can use some of the following API functions to use the HMODULE handle:

Loadbitmap, LoadCon, LoadString, ..., getProcaddress, etc.

Among them, the most important thing is GetProcAddress. It is a function pointer for returning a function in the link library, and then we can call this link library function through this function pointer. (If you are not familiar with the function pointer, it is best to look at the C / C syntax. I think the declaration method of the function pointer is very weird) The prototype is as follows:

FarProc getProcaddress (HModule Hmodule, // Handle to DLL Module LPCSTR LPPROCNAME

// function name);, hmodule, I don't say it. The LPPROCNAME parameter is a string that writes the function name of the function we have to find. If you find it, return the pointer to this function, otherwise returns NULL.

for example:

For example, there is a link library function is "INT PLUS (Int Naugend, Int Nadde", I want to call it.

HModule hmathlib = loadingLibrary ("math.dll");

INT (* myproc) (int, int) = null;

INT x = 1, y = 1; MyProc = (int, int)) GetProcaddress (HMATHLIB, "Plus");

IF (MyProc! = NULL) {Printf ("% D", (* myproc) (x, y));} freelibrary; if I have no problem with this link library, I want to output the result should be 2.

I still think that the declaration of the function pointer is very weird, readability is not high, so I usually change a way of writing.

#define defmathproc (name) int (, int, int) #define function (* name) DEFMATHPROC (MyProc) = NULL;

MyProc = (DEFMATHPROC ()) GetProcaddress (Hmathlib, "Plus");

NResult = function (x, y); although a warning is a warning, I think this will feel comfortable.

Well, the situation of dynamic links is basically so. The specific dynamic link library is written and a copy of the COM component is talking in subsequent chapters.

Second, object-oriented component model - Com

The Windows system dominant status will not be shaken in three or four years. Therefore, there is a multi-multi-Windows development platform appeared in front of us. n A variety of development languages ​​are all flowers. So, in the Bible, we have a different language and cannot communicate with each other. In order to change this reality, cute Bill stands out, "I have to change the world!" Microsoft has developed a binary universal interface specification -Component Object Model (Component Object Model). However, the solution to the COM is not intended for a general interface, but is applied to the implementation of the composite document (OLE). And now due to language irrelevant, process transparency, reusability, confidentiality (unless the master master is high, anyone can see the technology from the assembly code), and write is not difficult, so development has become an application A wide range of technologies.

1) Component objects and interfaces

Component objects, the interface is the foundation of COM.

Below, please allow me to make a class ratio with the C object.

The meaning of the component object and the C object is basically the same. It is a function, attribute and logic. It is an entity object that can be used with the features it provides by operating it.

The interface is equivalent to the public member in the C object. It is exposed to the external user, and the user is only allowed to call these features that are exposed outside to use objects. Unlike public members, the interface is not a variable is not a function, but it should be a set of functions. Logically, this group function should be functional. A component object can have many interfaces.

I only know the COM implementation method of C , as for dephi, I don't know anything.

The C implementation method is to complete the implementation of the component object by the C class object, which is represented by the C pure virtual class. C class objects have multiple interfaces by multiplex multiple interfaces.

Below, I will give an example to explain the relationship between the component objects and the interface in C (the example below is not a COM implementation, just to indicate the relationship between the component object and the interface)

If I want to do a person's component object, I should first define some interfaces to represent people's external performance behavior.

Class Physiology {public: Virtual Void Eat (Food In) = 0; Virtual Void Drink (Liquid IN) = 0;

Virtual somethings toilet () = 0;

Class Psychics {public: Virtual Sound laugh () = 0;

Virtual Sound Cry () = 0; Virtual Sound Angry () = 0;};

Class Dynamics {public: Virtual Speed ​​Run () = 0;

Virtual speted walk () = 0; Virtual interval jump () = 0;}; I divide people's behavior into three types of physiology, psychology and dynamics, allowing them to express different behaviors. So, such a related function is three interfaces. The implementation of the C component object is to derive more from these interfaces and achieve them. In this way, we get a component object (declaration, this example is just a concept, the real COM component object also needs to add some Dongdong). Class Human: Public Psychiology, Public Psychics, Public Dynamics {

PUBLIC: Void Eat (Food in) {cout << "Good! Very Delicious!";

Void Drink (Liquid IN) {cout << "NO! I am Not DRUNK!";

Something toilet () {cout << "hum ..."; return devun dejecta ()

Sound laugh () {Return Sound ("Ha ... Ha ...";}

Sound Cry () {Return Sound ("DAD! DON't Beat My BUNS.");} S

Ound angry () {RETURN SOUND ("Where Did You Go Last Night? Darling.");}

Speed ​​Run () {cout << "Run, Police Come!"; Return 20km / h;

Speed ​​Walk () {cout << "OUT. YEGG, I am NO ... NOT AFRAID O .... OF You."; Return 1M / S;}

Interval jump () {cout << "yeah ...."; Return 4m;}};

This way, one component object is defined. When using component objects, a pointer gives you. It is a virtual class pointer implemented by a component object. We can use it to call the functionality implemented by the component object (of course, we have the right to choose any virtual category; as long as the component object supports) .

In summary, an external feature of a component object is composed of different interfaces, which is the function provided by the user to display the components.

Note: If your C virtual function is not well learned, please find a C grammar to see it. Or please see the VCKBase 12th

"Analysis Dynamic Connection".

2) Identifier (GUID)

Above, I said that the COM component is based on binary. Then we want to use the signature (such as a class name, interface name) to specify a component obviously unmembacted (at least in terms of identification). So, since it is the most convenient to use the binary system, it is of course used to use the number identity. So Microsoft defines such a structural standard:

Typedef struct _guid {dowrd data1; word data2; word data4 [8];} GUID; structure is used to store some digital information to identify a COM object, interface, and other COM elements. This structure is called the identifier.

A identifier in C is said to:

Extern "C" const guid clisid_myspellchecker = {0x54bf6567, 0x1007, 0x11d1,

{0xB0, 0xAA, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}} The same identifier is said in other non-C environments: {54BF6567-1007-11D1-B0AA-444553540000} This identifier represents one COM object, this is because the identifier of a COM object is prefixed in CLISID_. The name of the interface is prefixed in IID_. Don't ask me, the identifier definition is related to the specific relationship. I do not know. They have nothing to do at all. When a COM object is written, we use a random way to determine its identifier (this work can be helped by the VC.). Once the COM object gets an identifier and release it, then it cannot be changed. In addition, don't worry that GUID will conflict. If your high school mathematics has already, please count how much the probability of repetition is counted in 128-bit binary. If you really find the guid conflict (you have to ensure that this is not a person), I suggest you come to buy a lottery. You are not far from 5 million.

3) IUNKNOWN interface

The COM mode All interfaces must be complied with certain specifications, which is the source of the IunkNown interface. Each interface must be inherited from this interface. In C , Microsoft has defined iUnknown for us:

TypedEf Guid Iid; Class iUnknown {public: Virtual HRESULT _STDCALL

QueryInterface (const IID & IID, void ** ppv) = 0; Virtual Ulong _stdcall addRef () = 0;

Virtual ulong _stdcall release () = 0;

Note: void * can point to any object. I didn't understand the void * when I started. The reason here used is uncertain from the passage and incoming pointer type.

The QueryInterface function function is to help when we get an interface pointer and we want to get another interface pointer. We will pass the identifier of the interface we want to give to the IID, and will make a pointer to the PPV. If QueryInterface is successful, it will return S_OK. Our pointer will point to the interface we want.

AddRef, Release is used to implement reference counting mechanisms.

In a binary system, component objects have a clear survival in the C environment. This situation may occur, two (or more) places (possibly between different programs, may also be between the simultaneous use of a component object, if one of the places delete off components Object. It is impossible else to know that when they try to call this image, it will cause serious injuries, and the weight will lead to death. This is not what we want to see. Thus, the COM model sets a reference counting mechanism.

When a place starts using an object, it must call addRef () once. When we use QueryInterface, QueryInterface must call us atten (). AddRef () will increase the reference count of the component object. When this place no longer uses an object, it must call Release () once. Release () will reduce the reference number of the component object by 1. When the reference count of the component object becomes 0, it indicates that no one will use the component object again. At this time, the component object should end your life. This ensures that the security of other programs during the survival of the component object is guaranteed.

Of course, you can use your own reference mechanism, as long as your behavior supports AddRef and Release. For example, the reference count of the object is not set, but set a reference count for each interface. When all the interface reference counts are 0, the delete object.

Ok, in the previous example, I didn't abide by the Iuknown specification. Here I have to obey it. I used the same thing in the same time ... I omitted. // {6AAF876E-FCED-4EE0-B5D3-63CD6E2242F5} static const guid iid_iphysiology = {

0x6aaf876e, 0xfced, 0x4ee0, {0xB5, 0x6E, 0x63, 0xCD, 0x6e, 0x222, 0x42, 0xf5}}

Class iPhysiology: public iunknown {public: ......};

// {183FC7A1-4C27-4C38-B72D-D1326E2E8A7C} static const guid iid_ipsychics = {0x183FC7A1,

0x4c27, 0x4c38, {0xB7, 0x2D, ​​0xD1, 0x32, 0x6e, 0x2e, 0x8a, 0x7c}};

Class Ipsychics: public iunknown {public: ......

// {5F144D5C-A20C-42E7-8F91-4D5CAE430B29} static const guid iid_idynamics = {0x5f144d5c,

0xA20C, 0x42E7, {0x8F, 0x91, 0x4D, 0x5c, 0xAe, 0x43, 0xb, 0x29}}

Class idynamics: public iunknown {public: ......

// {abfa7022-7e2f-4d0e-8a4f-f58bbcebb2da} static const guid clisid_human = {0xAbfa7022,

0x7E2F, 0x4D0E, {0x8a, 0x4f, 0xF5, 0x8b, 0xbc, 0xeb, 0xb2, 0xda}}

Class Human: Public iPhysiology, Public Ipsychics, Public iDynamics {

Public: ... human () {m_ulref = 0;} HRESULT Queryinterface (const Iid & Iid, void ** ppv)

{IF (IID == IID_IUNKNOWN || IID == IID_IPHYSIOLOGY) {* ppv = static_cast

(this);

(IPhysiology *) (* this)) -> addRef ();

ELSE IF (IID == iid_ipsychics) {* ppv = static_cast

(this);

(Ipsychics *) (* this)) -> addRef ();}

Else if (IID == iid_idynamics) {* ppv = static_cast

(this);

(IDynamics *) (* this)) -> addRef ();} else {* ppv = null; return e_notinterface;}

Return S_OK;} ulong addref () {return m_ulref;} ulong release () {m_ulref -;

IF (m_ulref <= 0) {m_ulref = 0; delete this;} return m_ulref;} ulong m_ulref;}; so our component object is defined.

Let's give the IDL description and graphical description of our component object

#include "olectl.h" Import "OAIDL.IDL"; import "ocidl.idl"; [Object, UUID (6AAF876E-FCED-4EE0-B5D3-63CD6E2242F5), Nonextensible, Helpstring (IPHYSIology Interface "),

Pointer_Default (unique)] Interface iPhysiology: iunknown {void Eat (Food in);

Void Drink (Liquid IN); Somethings toilet ();

[Object, UUID (5F144D5C-A20C-42E7-8F91-4D5CAE430B29), NONEXTENSIBLE,

Helpstring ("Ipsychics Interface"), Pointer_Default (unique)]

Interface ipsychics: iunknown {sound laugh (); Sound Cry ();

Sound angry ();

[Object, UUID (5F144D5C-A20C-42E7-8F91-4D5CAE430B29), NONEXTENSIBLE,

Helpstring ("iDynamics interface"), Pointer_Default (unique)]

Interface idynamics: IUNKNOWN {speed Run () = 0;

Speed ​​Walk () = 0; interval jump () = 0;};

[UUID (6CC7B329-B92F-4A8F-9CDD-1AB6D7E4CF4D), Version (1.0),

Helpstring ("Oleobject 1.0 Type Library"]

Library oleobjectLib {importlib ("stdole2.tlb");

[UUID (62FD0E39-DA84-4B19-BAB0-960A27AC2B71), Helpstring ("Olepaint Class")]]]]

CoClass Olepaint {[Default] Interface iPhysiology, Interface Ipsychics,

Interface idynamics}};

Please detail, observe the above description IDL code and graphics. Not too hard.

4) Interface principles of COM objects In order to standardize the interface mechanism of COM, Microsoft has released the interface principles of COM objects to COM developers. (1) IUNKNOWN Interface Equity When we have to wait until two interface pointers, how do I determine that they are from one object. The principle of COM interface stipulates that the IUNKNOWN of QueryInterface in the same object should be equal. That is, the iUnknown refer to each object is unique. We can determine whether they point to the same object by judging if the IUNKNOWN pointer is equal.

Iunknown * punknown1 = null, * punknown2 = null; POBJECTA-> Queryinterface (IID_IUNKNOWN,

(void **) & punknown1);

POBJECTB-> queryInterface (IID_IUNKNOWN, (Void **) & punknown2);

IF (punknown1 == punknown2) {cout << "I am Sure Objecta is Objectb."

Else {cout << "I am Sure Objecta is not objectb.";} The same object can be different from the query value of the non-iUnknown interface. (2) The interface is self-translocation, for an interface, query itself should be allowed.

Setting PPSYCHICS is an interface that has been assigned Ipsychics.

Then ppsychics-> queryInterface (IID_IPSYCHICS, (Void **) & xxx; should succeed.

(3) Interface symmetry, when we query another interface from an interface, then we can also query the original interface from the results interface.

E.g:

Ipsychics * psrcpsychics = ... Something, * ptarget = null; idynamics * pdynamics = null; if psrcpsychics-> queryinterface (iid_idynamics, (void **) & pdynamics; success.

Then pDynamics-> queryinterface (IID_IPSYchics, (void **) & ptarget; quite successful.

(4) Interface transmissionability. If we queried from the first interface, we queried from the second interface to the third interface. Then we should be able to query the first interface from the third interface. Others are so pushing.

(5) Interface query time independence. When we queried to an interface at some time, then this interface should also be queried at any time.

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

New Post(0)