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

zhaozj2021-02-16  52

COM in-depth understanding (below) - Method parameter type is CRUNTIMECLASS *, VOID *, etc. The following article has explained that the class object is actually a structural instance, and in order to implement the process between the process, it is necessary to achieve the purpose of the reference. The class specifically prepares an agent class and exemplifies (ie, instantiates) an object to implement the agent object. And this agent class must be divided into two parts, the number of member functions specifically in the customer process, and the other is specifically transmitted to the component object in the component process to implement data delivery between the process, and transmit the customer's call command. To achieve customer operating components objects. The above approach is actually writing something that should be done when a custom collection operation is written, but you still need to do a few additional provisions of COM, if you must implement the Imarshal interface. This article explains how to transfer a standard proxy / placeholder component for such types to deliver a pointer to a class object across processes (using MIDL to complete). In order to generate a proxy object on the client, some information must be passed, and then build a proxy object based on the passing information. In the type definition of the IDL language, there is no class, so it is impossible to make the interface method to a pointer to a custom class. However, there is indeed, you can only transfer class object pointers to a type identified in a certain IDL. The best candidate is Void *, and then the information generated by the MIDL will pass the information of the agent object. Void * does not have any semantics, which only represents an address, so passing VOID * is incorrect in the IDL because MIDL cannot determine how it should collect it in memory according to the semantic determination of the VOID *. But MIDL still provides a variety of ways to solve this problem, and only two of them are used up to: [call_as ()] attributes and [Wire_Marshal ()] properties. [local] and [CALL_AS ()] [local] interface or interface method can be added to [local] attribute to indicate this method or the method in this interface does not need to generate a collection code, and then avoid the above due to void * without There is any semantics and cannot bring it to the content of the content, because it is not necessary to generate a collection code, and the parameters of the method thereof can be VOID *. The method or interface modified by this property is referred to as a local method or a local interface because these methods do not have a collection code and cannot be remotely called. This is very wide in the standard interface of COM. If you look at the IDL code of IUNKNOWN, it is a local interface.

Another example IClassFactory See IDL interface definitions, as follows: [object, uuid (00000001-0000-0000-C000-000000000046), pointer_default (unique)] interface IClassFactory: IUnknown {typedef [unique] IClassFactory * LPCLASSFACTORY; [local] HRESULT CreateInstance ([In, unique] iUnknown * punkouter, [in] refiid riid, [out, IID_ID)] void ** ppvobject); [Call_as (CreateInstance)] HRESULT RemoteCreateInstance ([In] Refiid RIID, [OUT, IID_IS riid)] IUnknown ** ppvObject); [local] HRESULT LockServer ([in] BOOL fLock); [call_as (LockServer)] HRESULT __stdcall RemoteLockServer ([in] BOOL fLock);} where is the local function CreateInstance and LockServer, MIDL will not generate code for the collection of these two functions, i.e. agent / mass codes, its performance is similar to the following two prototype code: HRESULT STDMETHODCALLTYPE IClassFactory_LockServer_Proxy (IClassFactory * this, BOOL fLock); HRESULT STDMETHODCALLTYPE IClassFactory_LockServer_Stub (IClassFactory * This, Bool flock; that is, when detected a definition of an interface method in .IDL file, MIDL will generate two additional functions for this method. The names are _Proxy and , respectively. _ _stub to be used as a proxy and occupied code. As described above, IclassFactory_RemotecreateInstance_Proxy and iClassFactory_RemotecreateInstance_stub will be generated and defined for declarations and definitions of such two functions. However, when the method is modified by [local] attribute, the declarations and definitions of the above two functions are not generated because they are assumed to be used to call directly, and there is no collection of collection, so there is no collection code, and For the local method. But they still have been added to this function pointer array, that is, the declaration of this type of method can still be seen in the generated interface (but not in the type library, this can be considered a bug of MIDL, but it can be Bypass). [CALL_AS ()] The interface method can be modified with the [CALL_AS ()] attribute to specify the alternative to which this method will be called by the local method specified in parentheses, that is, what is called.

It is not like [local] attribute modified method, which still generates a collection code, but does not appear in the interface, that is, the declaration of this type of method (but it can be seen in the type library " See, this is a bug, which can be wound by predefined macros). This is called method alias because it associates two methods, one ([Local] modified) is another alias of another ([Call_as] modified), is actually used. As mentioned in the previous RemoteLockServer, with the property [Call_as (LOCKSERVER)] to indicate that this function is called when the customer calls LockServer and needs to be delivered. The method of [local] modification is called local version, [Call_as ()] is called a remote version, and it can be considered that the remote version function solves the problem that the local version of the function does not generate a collection code, because local version of the function may have some special Requirements (such as parameter type void *) and cannot generate a collection code. Since [Call_AS ()] produces a function alias, it is associated with two functions, so there must be such a mechanism to achieve this association. MIDL is to achieve this relationship by requesting developers to write a collection code of local version of the local version. For the above LockServer, MIDL generated for two prototype will be as follows: HRESULT STDMETHODCALLTYPE IClassFactory_LockServer_Proxy (IClassFactory * This, BOOL fLock); HRESULT __stdcall IClassFactory_LockServer_Stub (IClassFactory * This, BOOL fLock); but only a prototype declaration i.e., Not defined. Therefore developers need to write the definition of the above two functions. Note: Although the name is iClassFactory_lockServer_stub, its prototype is just with RemoteLockServer, to implement the parameters that pass the remote version of the function to convert to the parameters of the local version. So the associated process is: Customer calls iClassFactory_lockServer_Proxy, then develop this function and converts the passing MIDL cannot or does not want to be processed into parameter forms of iClassFactory_remoteLockServer_Proxy, and calls to deliver parameters. In the component, COM running period library calling developer IClassFactory_lockServer_stub (Note: This function is not LockServer, but RemoteLockServer) to replace the parameters passing through the network to the original MIDL operating or undesired parameter form And call the LockServer method of the passed iClassFactory * parameter to implement the method of calling the component object, and then return.

The following is a special example: there is a custom class CA, as follows: Class Ca {long m_a, m_b; public: long geta (); void seta (long a);}; to deliver its object pointer in the following interface: //Abc.idl/import "oaidl.idl"; import "ocidl.idl"; [Object, UUID (1A201ABC-A669-4AC7-9E02-2DA772E927FC), Pointer_Default (unique) Interface IABC: iUnknown {[local] HRESULT GETA ([OUT] Void * Pa); [Call_as (Geta)] HRESULT RemoteGeta ([OUT] long * pa, [out] long * pb);}; new DLL project, turn off "Pre-translated" Compiling the switch, add the generated ABC_I.C, ABC_P.C, DLLDATA.C and ABC.H to the project, and create an ABC.DEF file to import several necessary functions for registration, as follows : ;;;;;;;;;;;;;;; abc.def ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; LIBRARY "abc" EXPORTS DllCanUnloadNow PRIVATE DllGetClassObject PRIVATE DllRegisterServer PRIVATE DllUnregisterServer PRIVATE abc.cpp and a newly added file, as follows: ///abc.cpp/#include "abc.h" #include class CA {public : Long M_a, M_B; Long Geta (); Void seta (long a);}; hresult stdmethodcalltype}; hResult stdmethodCallType IABC_GETA_PROXY (IABC * this, void * PA) {if (! Pa) Return E_INVALIDARG; ca * PAA = ReinterPret_cast (PA); // Call the remote version of the proxy function to transfer parameters, generate returnix IABC_RemoteGeta_PROXY (this, & paa-> m_a, & paa-> m_b) by MIDL;} HRESULT STDMETHODCALLTYPE IABC_GETA_STUB (IABC * THIS, LONG * PA, LONG * PB) {Void * p = cotaskmemalloc (SizeOf (CA)); if (! p) return e_fail; ca * paa = new (p) CA; // Generates a class object // call the local method of the object HRESULT HR = this-> Geta ( PAA); if (succeededed (hr)) {* PA = PAA-> m_a; * Pb = paa-> m_b;} // Release resource PAA-> ~ CA (); cotaskmemfree (p); return hr;} final Add a predefined macro register_proxy_dll and _win32_winnt = 0x500, connect the RPCRT4.lib library file,

Make sure there is no open / tc or / tp compilation switch to ensure C compilation on the above ABC.cpp, and C-compilation for MIDL generated .c source files. As follows: IABC * Pa; // Assume that CA A; PA-> Geta (ReinterPret_cast (& a)); the code implemented by the component is as follows: stdmethodimp catc :: Geta (void * pa) {IF (! Pa) Return E_INVALIDARG; * Reinterpret_cast (pa) = m_a; returnide S_OK;} To implement the object of the class CA, the transmitted value is implemented, but is not an in-range operation. As mentioned earlier, the latter must be written, and the corresponding proxy class must be written. First use the above method to deliver the necessary information, then initialize the proxy object of class CA according to the transmitted information to establish a connection. Generally, it is best not to write a proxy object, and by converting the class into an interface form, it is achieved by the MIDL auxiliary generation agent / placeholder assembly. The following describes the use [Wire_Marshal ()] property for transmission value. [WIRE_MARSHAL ()] The previous method alias mechanism implements the transfer custom data type, but it is based on the method. When using a data type multiple times, if the previous CA *, if each Both the method used to CA *, the above operation, significantly low efficiency, provides the [Wire_Marshal () attribute (of course not stop such an attribute) for this MIDL. [WIRE_MARSHAL ()] The property can only be used for type definition, namely Typedef, using the syntax as follows: type-specifier userm-type; it will be directly handled by a line type (Wire-Type, MIDL can be processed directly Type) and a description type (Type-Specifier, that is, it cannot or not intended to be identified by the Special Data Type of MIDL Processing) and uses a UserM-Type ID. It is similar to the [Transmit_as ()] attribute, which is associated with two types, just as the two methods are associated with [local] and [CALL_AS ()], but [Wire_Marshal ()] is directly Description Type Press the column format of IDL (Network Data Description NDR - Network Data REPRESET) column to the specified buffer, and [transmit_as ()] also needs to collect code in the middle, then [Wire_Marshal ()] The efficiency should be higher, but since the column set code is required, you need to understand the NDR format, process data alignment, so you seem to have trouble and complicated.

The most common application is the handles are defined as follows: typedef union _RemotableHandle switch (long fContext) u {case WDT_INPROC_CALL: long hInproc; case WDT_REMOTE_CALL: long hRemote;} RemotableHandle; typedef [unique] RemotableHandle * wireHWND; #define DECLARE_WIREM_HANDLE (name) / Typedef [wire_marshal] void * namedeclare_wirem_handle (hwnd); that is, our common HWnd type is: typedef [wire_marshal (WirehWnd) Void * hwnd; 即 其 customer or component The user of the agent / occupancy is the VOID * type. When it is required, it is actually an instance of the transmission structure RemotableHandle, and this structure is a combination of fcontext as an identifier, actually 8-byte length.

In order to implement the association of the VOID * and RemotableHandle * mentioned above, the developer must provide the definition of the following four functions: unsigned long __rpc_user _usersize (// Return to request buffer size unsigned long __rpc_far * pflags, // A flag parameter, the post sode / / give the currently requested buffer size, the return size should be used as the starting point unsigned long startingsize, __rpc_far * puber_typeObject); // Description type to pass examples unsigned char __RPC_FAR * __RPC_USER _UserMarshal (// valid pointer marshaling buffer unsigned long __RPC_FAR * pFlags, // flag parameter unsigned char __RPC_FAR * buffer, // assigned __RPC_FAR * pUser_typeObject ); // Example of the type of description type unsigned char __rpc_far * __rpc_user _Userunmarshal (// distributed unsigned long __rpc_far * pflags, // flag parameter unsigned char __rpc_far * buffer, // column set data Buffer pointer // Description type instance pointer, after the description type is taught from the column data, place the __rpc_far * Puser_TypeObject); void __rpc_user _Userfree (// Release the memory unsigned long __rpc_far * pflags, // flag parameter // user_typeObject parameters in Userunmarshal, pointer __rpc_f Ar * puser_typeObject); For the previous hWnd, the developer must provide the definition of the following four functions (of course Microsoft is already provided): unsigned long __rpc_user hwnd_usersize (unsigned long *, unsigned long, hwnd *); unsigned char * __RPC_USER HWND_UserMarshal (unsigned long *, unsigned char *, HWND *); unsigned char * __RPC_USER HWND_UserUnmarshal (unsigned long *, unsigned char *, HWND *); void __RPC_USER HWND_UserFree (unsigned long *, HWND *); in the MIDL-generated collection In the code, when the method parameter type is hWnd, the following is the following: 1. Call the HWND_USERSIZE and pass the application (customer or component, the HWND is the IN parameter or the OUT parameter), the example of the HWnd passed by the HWnd is obtained to get this instance. The required buffer size 2. Assign the corresponding memory block on the RPC channel. Call hwnd_usermarshal,

Still transmitting the HWnd instances in front and the assigned buffer pointers to set this HWnd instance column to the buffer 4. Transfer the buffer content to the other process space through the RPC channel 5. Call hwnd_usemarshal and pass the RPC channel The resulting column data buffer pointer and the generated one temporary HWnd instance of the pointer to record the astoped HWnd instance 6. Method for calling the application with the returned HWnd instance 7. Call hwnd_userfree, passing the front due to call hwnd_usemarshal The generated temporary record is a pointer to the HWnd instance of the HWnd instance, which is therefore allocated, and the [Wire_Marshal ()] property is the binding of the type and the type of description type. But it is missing, it is the use of flag parameters PFLAGS. This flag parameter is a 4-byte number, which is some coding rules about the NDR format so that the NDR engine (the completed buffer content is arranged in the NDR format rule to transmit the program). Can make the correct data conversion. It is a MSHCTX enumeration value that specificallys the call environment. It is a process within the process or a cross-process. It is a remote or local (please refer to the MSDN), so it can be made according to this value in the above four functions. optimization. The following is the above CA * implementation [Wire_Marshal ()] attribute. As far as, CA * should be delivered using VOID * due to the corresponding type in the IDL, adding the following code in abc.idl: typedef struct _sa {long A, b;} * psa; typedef [Wire_Marshal PSA)] Void * Pa; adds a method for interface IABC: HRESULT SETA ([In] PA A); then adding the following code in abc.cpp: unsigned long __rpc_user pa_usersize (unsigned long * / * pflags * /, unsigned LONG Startingsize, PA * / * PPA * /) {// The reason why this parameter may not be the first column set parameter, // such as HRESULT SETA ([in] long tem1, [in] CHAR TEM2, [IN] Pa A); // At this time, STARTINGSIZE is the reason why it will pass it in order to align it to alignment. Processing, because the structure _SA is only two simple // structures of the unsigned long, no need to be aligned again. Return Startingsize SizeOf (_SA);} unsigned char * __rpc_user pa_usermarshal (unsigned long * pflags, unsigned char * buffer, pa * ppa) {// Point line type (ie, structure _SA) definition fill buffer, pay attention Fill in NDR transmission format //, here is simply replicated due to _sa, so it is simply replicated, there is no problem with one / / toggle data.

For details on the NDR transmission format, please refer to // http://www.opengroup.org/onlinepubs/9629399/chap14.htm if (* pflags & mshctx_inproc) {// is in the process, directly transfer CA * , Not copy * reinterpret_cast (buffer) = * ppa;} else {ca * pa = reinterpret_cast (* ppa); PSA PSA = ReinterPret_cast (Buffer); PSA-> A = PA-> m_a; PSA-> B = PA-> m_b;} // Return the effective position of the buffer, the sizeof (_sa) by the current location Return buffer sizeof (_SA);} unsigned char * __Rpc_user pa_userunMarshal (unsigned char * buffer, pA * ppa) {if (* pflags & mshctx_inproc) {// is in the process, transfer CA *, without copy * ppa = * Reinterpret_cast < Void **> (buffer);} else {void * p = cotaskmemalloc (sizeof (ca)); if (! p) return buffer sizeof (_SA); ca * paa = new (p) ca; // generates one Class object PSA PSA = Reinterpret_cast (Buffer); PAA-> M_A = PSA-> A; PAA-> M_B = PSA-> B; * PPA = P;} // Returns the effective position of the buffer, the sizeof (_SA) by the current location Return buffer sizeof (_SA);} void __rpc_user pa_userfree (unsigned Long * pflags, pa * ppa) {if (! (* pflags & mshctx_inproc) {// is not a collection, allocated memory, release resource ca * paa = reinterpret_cast (* ppa); PAA-> ~ CA (); CotaskMemFree (PAA);}}, then: IABC * Pa; // Assume that CA A; A.Seta (654); PA PAA = & A; PA-> Seta (PAA); / / Or directly PA-> seta (& a); PA-> Geta (& A);

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

New Post(0)