Com in-depth understanding (I) -- The method parameter type is cruntimeclass *, void *, etc.

Source: Internet
Author: User
Tags export class

In-depth understanding of COM (I)

-- The method parameter types are cruntimeclass * and void *.

I often see questions on the Forum about how to pass void * or a custom class, such as class Ca and Ca. This article describes how to pass a pointer to a custom Class Object and clarify the role of midl, so that it is not necessary. Note that the transfer of custom types mentioned in this article is not the same concept as the transfer of custom structures in automation technology (based on idispatch), and there is no relationship between them.

COM Service Provision Method

The essence of the mathematical model "com" is a service provision method. Like DLL (dynamic connection library technology), it provides external services. In a sense, this is also called code reuse, but the difference between them should be distinguished-code reuse is only a way to provide services, which is the purpose of DLL.
DLL defines a section (section, is called an export section. This Section records the relative virtual address (relative virtual address -- RVA) of each exported function entry code in the current DLL file, which is equivalent to the function address. When the DLL file is mapped to the virtual memory space of the loading process, the thread can jump to the DLL export function entry to execute the export function code according to the relative virtual address in the export section. In this case, the exported function code is reused.
In the above implementation method, the DLL function export method is static Export (even if the function address may be changed due to relocation requirements during file ing, but the function address remains unchanged throughout the loading process ). Com is completely different from this. It is dynamically exported, that is, a level-1 pointer is added. What the customer obtains is not a function pointer, but a function pointer array, although the DLL Export Section is also equivalent to a function pointer array, com adds a level-1 package here to obtain a function pointer array dynamically built through the memory allocated on the heap or stack, instead of the read-only type such as the DLL Export Section (at most copy-on-write can be used), the memory (although not the focus) has only one.
The previously mentioned function pointer array represents frequently heard interfaces in the implementation of COM. For a function that obtains an array of dynamic function pointers, the com Runtime Library provides a function to implement -- cogetclassobject, which returns the first function pointer array (Interface) used by the customer ), its function is very similar to getprocaddress, except that the latter returns only one function pointer, while the former returns an array of function pointers. Since the method of returning the array of function pointers is to pass a pointer (that is, to pass the pointer of the function pointer, rather than calling any API), this method can be freely applied anywhere, including the functions exported by the interface. The iunknown: QueryInterface in COM is used for this service.
Because the virtual function implementation mechanism provided by C ++ also has a function pointer array, when C ++ is used to compile COM components, the interface is generally represented by a pure virtual base class. In C, because there is no virtual function, we have to use the structure to define a function pointer array.

Implementation of C ++ classes

Binary code, that is, there is no concept of class in machine code. However, COM is a service provided based on binary, so the following briefly describes the implementation of classes in C ++.
Class is actually a continuous memory and some functions that execute operations on this memory block, and this continuous memory block is a structured instance, in fact, a member function only adds a common C function with the parameter "this" as the type. For example, the following class definition:
Class ca
{
Long;
Public:
CA (): A (0 ){}
Long geta ()
{
Return;
}
};
Ca aa;
Long A = AA. Geta ();
Actually:
Struct s_ca
{
Long;
};
Long s_ca_geta (s_ca * This)
{
Return this->;
}
Void s_ca_ca (s_ca * This)
{
This-> A = 0; // if no optimization switch is enabled
}
S_ca s_aa;
S_ca_ca (& s_aa );
Long A = s_ca_geta (& s_aa );
Therefore, a class is actually a structure and some related functions. Its purpose is to facilitate code writing and introduce semantics to provide support for Object-Oriented Programming ideas. When a class has a virtual function, it is the same as above, except that a member in the structure specifically records an array of function pointers, representing all such virtual functions, this is exactly the implementation form of the COM interface. As follows:
Class ca
{
Long;
Public:
CA (): A (0 ){}
Virtual long geta ()
{
Return;
}
Virtual void Seta (long var)
{
A = var;
}
};
Ca aa;
Long A = AA. Geta ();
AA. Seta (34 );
Changed:
Struct s_ca_d;
Struct s_ca_f // equivalent to a function pointer array with only two elements
{
Long (* geta) (s_ca_d *);
Void (* Seta) (s_ca_d *, long );
};
Struct s_ca_d
{
S_ca_f * PF;
Long;
};
Long s_ca_geta (s_ca_d * This)
{
Return this->;
}
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; // sets the virtual function pointer.
This-> A = 0; // if no optimization switch is enabled
}
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 );

Parameter transfer not supported by midl

After learning about the implementation method of classes in C ++, we need to pass a class pointer, which is actually a structure pointer, the problem in the Forum is that errors such as "undefined type" are always said during midl compilation. The following describes the role of midl.
Midl is only a compiler provided by Microsoft to compile the code written by IDL (Interface Definition Language) or odl (Object Definition Language. This compiler compiles. IDL or. after the odl file, generate the Type Library (if necessary) and proxy/placeholder components (for details about the proxy/placeholder components, refer to another article I wrote "com thread model")..
When midl generates a proxy/placeholder component for com (or RPC), the generated project files include xxx_ I .c, xxx_p.c, and dlldata. c. xxx. H and A make file xxxps. MK (VC self-generated Tool nmake program can use this file) to help generate proxy/placeholder components (where compiled. the IDL file name is XXX. IDL ).
It should be noted that this proxy/placeholder component is not mandatory and only works when necessary (for example, the interface pointer needs to perform collection operations, and the component request Call environment, in addition, it does not work at any time. Therefore, if you can ensure that the generated components are not called across suites or require com to provide additional functions (such as calling cogetcallcontext), you can not generate a proxy/placeholder component, the consequence is that the application scope of this COM component is severely limited (equivalent to a DLL, but it still has the function of dynamically providing services). If no defense code is compiled when collection is required, the program may crash. The following example shows how to compile a COM component equivalent to a DLL without the. IDL file.
The following are the interfaces to be implemented:
Interface imodule: iunknown
{
Hresult getviewruntimeclass ([out] cruntimeclass ** Pclass );
};
Since there is no midl nagging, it can also be as follows:
Interface imodule: iunknown
{
Hresult getviewruntimeclass ([out] cruntimeclass ** Pclass );
Void getmodulename ([out] cstring * pname );
};
Manually generate a. h file as follows:
/// // Moduleinterface. h ///////////////////////////
# Pragma once
# Include <objbase. h>

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 # include "moduleinterface. H" to stdafx. h In the project to import the imodule to the project. Then, you can write methods (MFC or ATL or other component implementation methods) in any source file (. cpp) in the project to implement this interface. The following uses ATL to compile a component that implements the imodule interface.
/// // Popedommodule. h /////////////////////////////
# Pragma once

Class atl_no_vtable cpopedommodule:
Public ccomobjectrootex <ccomsinglethreadmodel>,
Public ccomcoclass <cpopedommodule, & clsid_popedommodule>,
Public imodule
{
Public:

Declare_registry_resourceid (idr_popedommodule)
Begin_com_map (cpopedommodule)
Com_interface_entry (imodule)
End_com_map ()
Declare_protect_final_construct ()

// Interface implementation
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 "permission ";
}
//////////////////////////////////////// /////////////////////////
The above practice is not advocate, but can run, as long as the collection operation (such as cross-Suite access) does not occur, there will be no problem. The specific type of cruntimeclass is used here. Therefore, ensure that the client also loads the MFC library file, and the parameters used are specific types of MFC. Obviously, they cannot be used together with other languages. In addition, due to the export class, the component and the client must be compiled using the same compiler, because different compilers modify the name of member functions differently (for example, I will modify it to s_ca_geta, but others may be modified to ca_geta ). This is why it is equivalent to an advanced DLL, and the advanced level is also semantic, and still has the meaning of interfaces in the COM programming model (about the COM programming model, refer to another article I wrote, "com example (2)".

Proxy object

The above practice is actually to pass a four-byte number (assuming it is developed by a 32-bit system), so it is called an advanced DLL. However, when the user-defined class ca object pointer in this process is passed to another process, the above solution is invalid because the pointer refers to memory related to the process, it cannot be used across processes.
Because the pointer to a Class Object is actually a pointer to a structure instance, you only need to copy the structure instance, you can pass it through any cross-process method (static member variables are not considered here ). This is a value transfer operation, but the pointer of the Class Object already indicates that it is an address transfer operation. The so-called address transfer operation means reference, that is, it is not concerned with the content of class objects, but the control of class objects. However, because the location of the class object in the memory cannot be accessed by any client (another process), this is the same as the Kernel Handle proposed by windows, the core code can only access the memory where the Kernel Handle is located. Therefore, someone must access the memory on behalf of the customer to show that the customer can control the class object, and this person is a famous "proxy object ".
Therefore, to pass the pointer of a custom Class Object for reference purposes (instead of using a pointer for efficiency), you must provide a proxy class for that class, the customer indirectly accesses the real class object through the proxy class object. That is to say, the existence of the proxy object is to allow the customer to operate on the real class object. If only the status of the Class Object is required, the proxy object is not necessary, you only need to perform a value transfer operation.
To show that the customer is operating on the real object, the work of the proxy object is to pass all the commands of the customer to the process where the real object is located and act as the customer to operate on the class object. Therefore, the proxy object is divided into two parts: one part operates in the customer process, and the other part acts as the proxy object. The other part operates in the process where the class object is located and acts as the customer, it is called a placeholder (stub ).
If you convert the pointer of the class object to an interface, you can use midl to generate the code of the proxy class (that is, the proxy/placeholder component) instead of writing it yourself, but the class to be passed cannot be changed. For example, a class in a class library, only the code of the proxy class can be compiled by yourself (because the IDL Language does not have the class concept ).

For space and time considerations, how to use midl to correctly pass a pointer to a custom class object, that is, how to use midl to assist in writing proxy/placeholder components to support passing pointers to custom class objects, instead of the above extreme practices, which will be further explained in "com deep understanding (below.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.