Compile COM components in assembly language [2]

Source: Internet
Author: User
The component object model com is composed of Execution Code published in the form of Win32 DLL or EXE. Com is composed of some objects and object interfaces. In COM, interfaces provide object operation mechanisms. Interfaces are composed of one or more related methods, attributes, and events. Here we develop a simple but fully functional COM component in-process (that is, it exists in the form of DLL ).

Here we assume that you have learned the basic knowledge of the COM Object Model and what virtual tables are and what virtual function table pointers are. If you are not familiar with this, we recommend that you read the "com Essence Theory" book.
Let's first analyze the composition of the COM Service in the process. Because it is released as a DLL, it includes five important functions. The following four are exported as DLL export functions.

Dllmain: This is the first entry function of the dynamic link library. It is called when the library is loaded. Through this function, you can check the client program.
Dllregisterserver: This function enables self-registration. registration information is stored as resources in the dynamic link library. This function can read resources, write information into the registry, and use
When regsvr32.exe registers the component, it actually calls the function output by the component.

Dllunregisterserver: when a group is no longer used, this group should be capable of self-unloading. regsvr32.exe can call this function to achieve this step.
Dllcanunloadnow: The global variable in the COM Service is used to save its status. The client can call this function periodically to check whether the component server is in use and uninstall it.

Dllgetclassobject: This is the function used to complete component output. This output requires three parameters: the guid of the component to be created, the guid of the component interface to be created, and the pointer to the object after creation. If the component object or interface is not supported, the execution fails.

So far, we should note that, if it is not because of indirect access, com will be nothing. In fact, the object returned by the dllgetclassobject function is not the object we are looking for. It is a class factory object, and a class factory object knows how to instantiate any other class. The indirect access at the first layer allows the details of component creation to be specified. If it simply and directly returns an object pointer we are looking for, it indicates that the object already exists, we will not be able to set and control any parameters about the constructed object.

Dllgetclassobject returns an iclassfactory interface, which is derived from iunknown and has its own two important member functions.
Hresult createinstance (
Iunknown * punkouter, // pointer to outer object when part of
// Aggregate refiid riid,
Refiid riid, // reference to the interface identifier
Oid ** ppvobject); // address of output variable that has es
// The interface pointer requested in riid

Hresult lockserver (bool flock );
// Increments or decrements the lock count

Lockserver is used to control the reference count of the class factory object. The system checks and changes the count to determine whether to uninstall the component, that is, to control the class factory's survival.
Createinstance is the most important component. The only function of a class factory component is to create other components. A class factory component can correspond to multiple common COM components, but the instance of each class factory component can only create one class COM component.
It receives an interface guid and returns the pointer of this interface. It does not accept the CLSID of the component, so a class factory instance can only create a COM component, that is, the component corresponding to the CLSID passed to cogetclassobject.

Interaction process between customers, com libraries, component DLL, class factory, and components:
1. the customer first calls the cocreateinstance function of the COM library to create the COM component.
2. cocreateinstance first calls the cogetclassobject of the COM library to obtain the class factory.
3. This function creates a class factory by calling the dllgetclassobject output by the component DLL.
4. dllgetclassobject generates a cfactory object through the new function, and obtains its interface pointer (generally iclassfactory pointer) through QueryInterface ).
5. Return to the com library's cocreateinstance to call the createinstance function of the interface pointer (iclassfactory) just obtained.
6. The component class specified by the new function. The specified interface is obtained through QueryInterface.
7. cocreateinstanse releases the iclassfactory pointer (via release) and returns the obtained pointer to the client program.
8. You can use the obtained interface in the customer.
In step 2, you can create different components based on different CLSID to implement a class factory for sharing multiple components in the DLL. But only share classes, not instances. Once CLSID is specified by cogetclassobject when creating a class factory, you can only create instances of this COM component.

Here we will go deep into the C ++ object model to see some internal implementation details. Generally, the compiler will handle this. Com designers make full use of this, so we need to understand it.
When we write a regular program with a sink, I rely on the compiler to create code segments and data segments for us. One area in the memory is the code we execute, and the other area stores the data we need.
C ++ dynamically allocates memory during runtime, giving each class instance and each small code segment its own data segment. In other words, a class instance is the data segment, and the data description of each class instance is saved in a dynamic data area.
You may have heard that when C ++ passes the object member function parameters, there is a hidden parameter, that is, the this pointer. When a person writes a lower-level code for an object (the compiler will do this in C ++, you do not need to consider it ),
The first problem you encounter is, "which object am I writing code? "

This pointer is a simple pointer pointing to this class object instance in the dynamic data memory area. When a class object function is called, this pointer is passed quietly. When the private data of this object is accessed, the code area of the class uses the this pointer to find the data of its object instance.

A com interface pointer is similar to a this pointer. In use, COM is an interface specification, so you cannot see its code implementation.

; Declare the classfactory object structure
Classfactoryobject struct
Lpvtbl DWORD 0; function table pointer
Nrefcount DWORD 0; object and reference count
Classfactoryobject ends

; Declare the mycom object structure
Mycomobject struct
Lpvtbl DWORD 0; function table pointer
Nrefcount DWORD 0; reference count
Nvalue DWORD 0; interface private data
Mycomobject ends

The first lpvtbl is a virtual table pointer pointing to a virtual function table. I use it to control the private data of each interface. Just like nrefcount and nvalue here.
The dynamic memory of these structures is allocated and released through the cotaskmemalloc and cotaskmemfree APIs. These two functions are exported by ole32.dll. Ole32.dll also exports many functions, such as comparing the guids value, converting the guids as a string, or converting the string to guids.

To illustrate how the COM interface works, we create a simple interface, imycom (Note: All com interfaces have an "I" prefix. Like other interfaces, It is derived from the iunknown interface, that is, its first three functions are QueryInterface, addref, and release. Next we will add several interface functions. The following shows the C-style function prototype:
 
Hresult setvalue (long * pval );
Hresult getvalue (long newval );
Hresult raisevalue (long newval );

Here, setvalue and getvalue are used to read and set the data members of our interface. Raisevalue is used to increase the value of this data.
The structure in the memory is as follows:

The client only has a distributed structure pointer (BPPV), which is defined in the C ++ format ("pointer to (void ). ") when creating a class instance, the object data block is dynamically allocated and initialized. The virtual function table vtable and server functions are static and they are defined during compilation.

Note that a virtual function table contains function pointers instead of functions. Therefore, we can modify the routines pointed to in the virtual function table to create a simple "Override" derived function.

In this example, both iclassfactory and imycom are derived from the iunknown interface and both inherit the QueryInterface. However, they support different interfaces. They need to point to different routines and return different results.
Therefore, their respective QueryInterface routines (queryinterfacecf and queryinterfacemc) are directed by different virtual function tables.
Similarly, addref and release must be customized by different interfaces that support them.

Type Library:
Every COM interface obtains information from the system registry. The definitions of these interfaces are described by an Interface Definition Language (IDL). On Windows, midl is used for compilation. We can use the VC development environment to create an original interface definition file through the wizard.

Platform on Wintel platforms,
Create an ATL Project, name it mycomapp, select "Insert a new ATL Object", select "Simple Object", and name it mycom. In this way, an empty imycom interface is created. Then, right-click the interface and choose setvalue and getvalue from the shortcut menu. Then, add a raisevalue method. Then we save and exit the project, copy the mycomapp. IDL file to my assembly project directory.
The content of this IDL file is as follows:
// Mycom. IDL: IDL source for mycom. dll
//
// This file will be processed by the midl tool
// Produce the Type Library (mycom. TLB) and define alling code

Import "oaidl. IDL ";
Import "ocidl. IDL ";
[
Object,
UUID (F8CE5E41-1135-11d4-A324-0040F6D487D9 ),
Helpstring ("imycom interface "),
Pointer_default (unique)
]
Interface imycom: iunknown
{
[Propget, helpstring ("property value")]
Hresult value ([out, retval] Long * pval );
[Propput, helpstring ("property value")]
Hresult value ([in] Long newval );
[Helpstring ("method raise")]
Hresult raise (long value );
};
[
UUID (F8CE5E42-1135-11d4-A324-0040F6D487D9 ),
Version (1.0 ),
Helpstring ("mycomapp 1.0 Type Library ")
]
Library mycomlib
{
Importlib ("stdole32.tlb ");
Importlib ("stdole2.tlb ");
[
UUID (F8CE5E43-1135-11d4-A324-0040F6D487D9 ),
Helpstring ("mycom class ")
]
Coclass mycom
{
[Default] interface imycom;
};
};

This file can be used as a prototype for further interface definition. Note that there are three guids, one for the interface, the other for the coclass, and the other for the Type Library. For new applications, their values must be different.
With this defined file structure, we can easily understand its content.

[Propget, helpstring ("property value")] hresult value ([out, retval] Long * pval); [propput, helpstring ("property value")] hresult value ([in] Long newval); [helpstring ("method raise")] hresult raise (long value );

The definitions of these interfaces in masm32 are as follows:

Getvalue proto: DWORD,: DWORD
Setvalue proto: DWORD,: DWORD
Raisevalue proto: DWORD,: DWORD

They are quite different, but the reason is simple. The interfaces in the Type Library are generic and can be directly used by clients like VB.
To create a type library, you can use the midl command line to compile the IDL file:
Midl mycom. IDL
Several files generated by compilation can be ignored except mycom. TLB. Next we need to add the Type Library to the DLL resource file. For example:
1 typelib mycom. TLB

It is very important to make him the first element in the resource file. In the future, we will use the loadtypelib API function to use this library, and this function also hopes to find this library in the first place.

Register components:

Dllregisterserver and dllunregisterserver register and cancel components for us. The content is as follows:
 
Hkey_classes_root/cmycom
(Default) "cmycom simple client"
Hkey_classes_root/cmycom/CLSID
(Default) "{A21A8C43-1266-11D4-A324-0040F6D487D9 }"

Hkey_classes_root/CLSID/{A21A8C43-1266-11D4-A324-0040F6D487D9}
(Default) "cmycom simple client"
Hkey_classes_root/CLSID/{A21A8C43-1266-11D4-A324-0040F6D487D9}/cmycom
(Default) "cmycom"
Hkey_classes_root/CLSID/{A21A8C43-1266-11D4-A324-0040F6D487D9}/inprocserver32
(Default) "C:/masm32/mycom. dll"
Threadingmodel "single"

Hkey_classes_root/typelib/{A21A8C42-1266-11D4-A324-0040F6D487D9}
(Default) (value not set)
Hkey_classes_root/typelib/{A21A8C42-1266-11D4-A324-0040F6D487D9}/1.0
(Default) "mycom 1.0 Type Library"
Hkey_classes_root/typelib/{A21A8C42-1266-11D4-A324-0040F6D487D9}/1.0/0
(Default) (value not set)
Hkey_classes_root/typelib/{A21A8C42-1266-11D4-A324-0040F6D487D9}/1.0/0/Win32
(Default) "C:/masm32/COM/mycom. dll"
Hkey_classes_root/typelib/{A21A8C42-1266-11D4-A324-0040F6D487D9}/1.0/flags
(Default) "O"
Hkey_classes_root/typelib/{A21A8C42-1266-11D4-A324-0040F6D487D9}/1.0/helpdir
(Default) "C:/masm32/COM/mycom"

A key value changes. It is the path and file name of the Service DLL. On my system, I place it in "C: /masm32/COM/mycom. DLL ", this can be detected when I register the component. By calling getmodulefilename, The dllregisterserver can find the storage location of the DLL itself.

There is a lot of information about this COM Service, but we just need to pass the {A21A8C43-1266-11D4-A324-0040F6D487D9} This ID and a valid Interface ID to the cocreateinstance function to instantiate our com service. This function tracks registry settings and uses CLSID to find what is required to create a component. Once it creates a component, it loads the Type Library to obtain more information.

Fortunately, the last five registration entry items can be completed through the registertypelib function. In dllregisterserver, we use registry functions of some columns to set the first five key values. Then, call registertypelib. The dllunregisterserver function deletes the registry entry in the dllregisterserver and calls unregistertypelib. Do not delete hkey_classes_root/CLSID/

Implement unknown

Addref_mc proc this _: DWORD
MoV eax, this _
INC (mycomobject PTR [eax]). nrefcount
MoV eax, (mycomobject PTR [eax]). nrefcount
RET; note we return the object count
Addref_mc endp

Release_mc proc this _: DWORD
MoV eax, this _
Dec (mycomobject PTR [eax]). nrefcount
MoV eax, (mycomobject PTR [eax]). nrefcount
. If (eax = 0)
; The reference count has dropped to zero
; No one holds reference to the object
; So let's delete it
Invoke cotaskmemfree, this _
Dec mycfobject. nrefcount
XOR eax, eax; clear eax (COUNT = 0)
. Endif
RET; note we return the object count
Release_mc endp

Implementation of mycom members:
Getvalue proc this _: DWORD, pval: DWORD
MoV eax, this _
MoV eax, (mycomobject PTR [eax]). nvalue
MoV edX, pval
MoV [edX], eax
XOR eax, eax; return s_ OK
RET
Getvalue endp

Setvalue proc this _: DWORD, VAL: DWORD
MoV eax, this _
MoV edX, Val
MoV (mycomobject PTR [eax]). nvalue, EDX
XOR eax, eax; return s_ OK
RET
Setvalue endp

Raisevalue proc this _: DWORD, VAL: DWORD
MoV eax, this _
MoV edX, Val
Add (mycomobject PTR [eax]). nvalue, EDX
XOR eax, eax; return s_ OK
RET
Raisevalue endp

Mycom. dll. This COM Service project requires the following five files for compilation:

Mycom. ASM assembler source code

Mycom. IDL file, used to compile and generate mycom. TLB

Mycom. TLB Type Library, requires an RC resource file

Rsrc. RC resource file to obtain the Type Library Information

Mycom. Def standard DLL output file

After compilation, the code will not do anything until we register it. We can use the command line:
Regsvr32 mycom. dll registration.

 

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.