COM in-depth understanding (on) - method parameter type is cruntimeclass *, void *, etc.

zhaozj2021-02-16  47

COM in-depth understanding (on) - Method parameter type is cruntimeclass *, void *, etc. often see how to send VOID * or a custom class, such as Class Ca; Ca *, etc. This article describes how to pass a custom class object on COM, and clarify the role of MIDL to illustrate it is not necessary. Note that the customized type of customized types in this article is not the same concept without any relationship with the customized type of automation technology (iDispatch). COM service provides the essence of the mathematical model is a service provider. Like DLL (Dynamic Library Technology), it is an external service. In a sense, this is also called code reuse, but it should be cleared The difference between code - code reuse is just a way of providing services, which is also the purpose of the DLL. DLL defines a section in this format in its PE format (Portable Executable Format, a file format, EXE, DLL, OBJ, etc.) (section, a continuous storage space), called the Export Section . In this section, the relative virtual address of the entry code of each export function in the current DLL file is recorded, which is equivalent to a function address. When this DLL file is mapped to the virtual memory space of the load process, the thread can jump to the DLL's export function entry to the DLL export function entry according to the relative virtual address in the Export section. The code that exported the function is reused at this time. In the above implementation, the DLL functional export method is static export (even if the function address is changed due to the requirements of the file mapping, but the function address is changed during its entire load period). COM is completely different, which is dynamically exported, that is, the first-level pointer, the customer is not a function pointer, but the function pointer array, although the DLL's export section is also equivalent to the function pointer array, but COM here again Add a first-level packaging, get dynamic through a function pointer argument by a function pointer built on a stack or stack, not a read-only type (top multi-on-write) memory like the DLL export section (although not The focus is only one. The previously mentioned function pointer array represents an interface that often hearses in the implementation of COM. And the function of the dynamic function pointer, the COM running period provides a function to implement - CogetClassObject, which returns the first function pointer array (interface) used by the customer, which is very similar to getProcAddress, but after Only returns a function pointer, and the former returns the function pointer array. Since the return function pointer array is to pass a pointer (ie, the pointer to the function pointer is transferred, not what API is called), so this method can be applied to anywhere, including a function of the interface export. IUNKNOWN :: QueryInterface in COM is specifically used for this service. Since the virtual function implementation mechanism provided by C also has a function pointer array, when writing COM components with C , the interface is generally represented by a pure virtual basis. In C, since there is no concept of virtual functions, the structure is used to define a function pointer array. The implementation of the C class is binary code, that is, the machine code is a concept of category.

However, COM is a service based on binary basis, so the implementation of classes in C will be described below. The class is actually a function of continuous memory and some functions executing on this memory block, and this consecutive memory block is an instance of a structure, and the member function is actually only one of the names of this structure as THIS. C function. As the following class definition: Class Ca {long a; public: ca (): a (0) {} long geta () {RETURN A;}}; CA AA; long a = aa.geta (); It actually: Struct s_ca {long a;}; long s_ca_geta (s_ca * this) {Return this-> a;} void s_ca_ca (s_ca * this) {this-> a = 0; // Assumeout no optimization switch} s_ca s_aa; S_CA_CA (& S_AA); Long A = S_CA_GETA (& S_AA); therefore, the class is actually a structure and some related functions, the purpose of its existence is to facilitate the writing of the code and introduce semantics to provide support for programming ideas. When the class has a virtual function, like the above, only one member is more than one member specifically records a function pointer array, representing all the virtual functions of this class, which is also the implementation form of the COM interface. As follows: Class Ca {Long a; public: CA (): a (0) {} Virtual long geta () {RETURN A;} Virtual void seta (long var) {a = var;}}; ca Aa; long A = aa.geta (); aa.seta (34); becomes: struct s_ca_d; struct s_ca_f // is equivalent to a function pointer of only two elements {long (* Geta) (S_CA_D *); Void (* seta (S_ca_d *, long);}; struct s_ca_d {s_ca_f * pf; long a;}; long s_ca_geta (s_ca_d * this) {Return this-> a;} void s_ca_seta (s_ca_d * this, long var) {this- > a = var;} S_CA_F G_S_CA_F = {S_CA_GETA, S_CA_SETA}; Void S_CA_CA (S_CA_D * this) {this-> PF = & g_s_ca_f; // Set virtual function pointer this-> a = 0; // assume no optimization Switch} S_CA_D S_AA; S_CA_CA (& S_AA); long a = (s_aa.pf-> geta) (& S_AA); (S_AA.PF-> Seta) (& S_AA, 34); non-MIDL supported parameters to pass the C Class After the implementation method, it is necessary to pass a class pointer. It is actually transmitted a struct pointer, and the problem in the forum is always the error compiled when MIDL compiles. The "Type undefined" is always said. Let's understand the MIDL's role.

MIDL is just a compiler with a code provided by Microsoft's IDL (Interface Definition Language, Interface Definition Language) or ODL (Object Definition Language, Object Definition Language). After this compiler compiles. IDL or .odl file, generate a type library (if necessary) and proxy / placeholder components (about the agent / placeholder component can refer to another article "COM Thread Model") file. When MIDL generates a proxy / placeholder component for COM (or RPC), its generated engineering file includes: xxx_i.c, xxx_p.c, dlldata.c, xxx.h and an make file xxxps.mk (VC The generation tool NMAKE program can use this file) to help generate proxy / placeholder components (which are compiled .idl file names xxx.idl). It should be noted that this agent / placeholder component does not have to be required, and it only occurs when it is required (such as the interface pointer to make a collection operation, the component request call environment, etc.), in addition to any time there is no action. Therefore, if you can guarantee that the generated component will not be called between the spans or does not require COM to provide additional functions (such as calling CogetherCallContext), the consequence of the agent / placeholder component is the application of this COM component. The range is severely restricted (which is equivalent to a DLL, but still has a dynamic providing service), when needed, if the defensive code is not written, the program is likely to crash. The following example shows how to write a COM component equivalent to the DLL and does not require .idl file. The following interfaces are to be achieved: interface IModule: IUnknown {HRESULT GetViewRuntimeClass ([out] CRuntimeClass ** pClass);}; because they have no MIDL nagging, so also follows: interface IModule: IUnknown {HRESULT GetViewRuntimeClass ([out] CruntimeClass ** PCLASS; void getModulen ([out] cstring * pname;}; manual generation one .h file, as follows: /ModuleInterface.h /// # pragma overce # include class __declspec (UUID) "0E0042F0-0000-0360-3400-0000EA0030AB")) IModule: public IUnknown {public: virtual HRESULT STDMETHODCALLTYPE GetViewRuntimeClass (CruntimeClass ** pClass) = 0; virtual void STDMETHODCALLTYPE getModuleName (CString * pName) = 0;}; / then Add a #include "moduleinterface.h" in the project in the project to import iModule into the project. Then you can prepare the method (MFC or ATL or other component implementation method) to be written in any source file (.cpp) that wants to implement this interface. The following is used to write a component that implements the ImoDule interface.

/PopedomModule.h/#pragma onceclass ATL_NO_VTABLE CPopedomModule: public CComObjectRootEx , public CComCoClass , public IModule {public: DECLARE_REGISTRY_RESOURCEID (IDR_POPEDOMMODULE) BEGIN_COM_MAP (CPopedomModule) COM_INTERFACE_ENTRY (IModule) END_COM_MAP () DECLARE_PROTECT_FINAL_CONSTRUCT () // interface public: // IModule STDMETHOD (GetViewRuntimeClass) (CRuntimeClass ** pClass); void STDMETHODCALLTYPE getModuleName (CString * pName);}; OBJECT_ENTRY_AUTO (__uuidof (PopedomModule), CPopedomModule) // PopedomModule.cpp /// # include "stdafx .h "#include" PopedomModule.h "STDMETHODIMP CPopedomModule :: GetViewRuntimeClass (CRuntimeClass ** pClass) {if (! pClass) return E_INVALIDARG; * pClass = RUNTIME_CLASS (CPopedomView); return S_OK;} void STDMETHODCALLTYPE CPopedomModule :: getModuleName (CString * PNAME) {if (pname) * PNAME = L "Permissions";} / The above practice is not advocated, but it can be run, as long as it does not have a collection operation (such as cross-sleeve access), etc., there will be no problem. . Here, the MFC-specific type of CRUNTIMECLASS is used, so you need to ensure that the client also loads the MFC library file, and the parameters used are MFC-specific types, which is obvious, and cannot be public with other languages. And it must ensure that components and customers are compiled using the same compiler because different compilers are modified to the name of the member function (as I tried to triggered S_CA_GETA above, but others may be modified as ca_geta). This is why it is equivalent to a senior DLL, and the advanced is also a semantics, still has the meaning of the interface in the COM programming model (About the COM programming model, you can refer to I wrote another article "COM sample (II ) "). The practice above the agent is said that it is actually transmitting a four-byte number (assuming to 32-bit system development), so it is called the advanced DLL. However, when the object pointer within this process is passed to another process, the above solution is invalid because the memory refers to the memory is related to the process, and cannot be used across the process. Since the pointer to the class object is actually a pointer to a structural instance, it is only necessary to copy one of the structural instances, which can be passed through any cross-process (there is no consideration of static member variables). This is a transmission value, but this pointer to the class object has indicated that it is an inventive operation.

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

New Post(0)