Prior knowledge of:
Camer2
This document is written using ISO C and .NET V1.1 framework (VS 2003), but its principle is suitable for all support .NET framework
First, preface is due to the traditional COM technology uses static unmanaged programming, and .NET uses dynamic hosted programming, this topic is essentially a special case in the interoperability between managed and non-hosting code. The current .NET V2.0 provides the following three interoperabilities:
Module Level P / Invoke Method This method of operation is suitable for calling the non-hosting function implemented in the Dynamic Link Library (DLL) in the Win32 API. It will be discussed in the next section; the COM Interop method of the component level is the highlight of this article, which is divided into two parts: 1, the method of exporting type library: the protagonist of this article; 2. Method for using package classes: This method is CLI C Unique, automatically generated by the framework (in the COM client is a non-host file code), the principle is the same as the third interoperability; the code level C Interop method is unique to CLI C , It can only be used in VS 2005 and above. It is very simple, and all the packages of the external DLL are generated by the wizard without writing a line of code, so this article is no longer described. (Another reason is that I have no conditions for the time being using vs 2005 ^ _ ^!)
Second, the type conversion and sealing processing is since two platforms, interoperability between the two worlds, the information transfer between them must be converted to the type of the other party can understand, this conversion is called "Marshal", or called "Feng". Data's encapsulation is an abnormal complex process, and the reader of interest can search for documents "Marshaling Details" describing their principles in the MSDN. The following figure is the principle of enctence of CoM Interop. It is good to help us with some simple data packages in the .NET platform, hide the complex Marshal detail. This makes it easy to pass some simple data when calling the COM server in the .NET client, but if the transmitted data is customized, you still need to manually Marshal. The principle of this mechanism describes "COM packaging" in MSDN. The figure below is taken from the MSDN 2003 version, which describes what types of .NET V1.1 will convert our data from one platform type to another platform, and any types without explicit identity will be converted to int32. System type. Well ... I seem to be a lot, but don't be too early. Only in the actual use of intimacy is only a simple type of simple type to guarantee a variety of flexible transmission methods without problems ... @ _ * . It should be noted that the COM party's Variant type (this is also a recommended type when writing COM) will be converted to a specific type and packaged according to the value of its VT, so the COM is set to set the VT, don't make ! In addition, it is best not to use or use or serve as a custom type. For a detailed description of the data type conversion, see "COM Data Type" in MSDN, and the description document for custom type is "Custom Standard Pack". If you want the .NET component to implement a callback interface or connection point like a COM component, you need to manage communication between the unmanaged implementation of the interface and the interface. At this time, you need to customize the Marshal blocking process, and the document that implements this function is "Custom Sealing Processing".
Third, the error handling .NET client does not have a hRESULT type, and if you define a method function in the .NET server, you can also return a value, so how to handle errors? In fact, this work is also made by the .NET platform to help us do. 1, COM server ->. NET client clients can be used directly with TRY () ... catch () to get translated HRESULT information, the system-defined error message is generally defined in the .NET class library. It is very convenient to handle it. 2, .NET Server -> COM client client can get error messages like the HRESULT HR = ...; When the server side does not support enrich error message interface IerrorInfo, the error message will be two-way translations in the following table. When the server is enriched in the error message interface IerrorInfo, the error message will be two-way translations as shown in the table below, and only part of the table is listed in the table. This part of the content and the description document in the MSDN are "HRESULT" and exceptions. Refer to the MSDN document "Processing and Export". IV. Summary The first article mainly introduces some concepts, give you a thinking of thinking about the problem, the specific details do not make a detailed discussion, but give the location in MSDN, you need to refer to the reference, it is recommended to use at least MSDN2003 or higher Chinese version (translated is not bad ^ _ ^). These knowledge are hidden in practice, so it is not big, so it is not enough to understand. This article has no sample code. Next article we have to start active ^ _ ^ COM server P / Invoke method download source code
I. COM client -> COM client This is a traditional COM knowledge. If you don't know this part, you can go to see Yang's personal column, there is a great tutorial, I am not talking about it ^ _ ^ I don't say that it doesn't mean that this is not important. It is just the contrary. If the reader is familiar with this part, it will find that the following content is almost implicitly imitating traditional COM calls.
Second, COM server -> .NET client, this is the focus. The figure below is the principle of this section. Each COM object will have only one run library to call the package (RCW) agent, regardless of how many references it.
In the case where there is no public interface (or there is no)
The operation used in this situation is P / Invoke. We must at least know the following two contents:
The name of the function of the DLL file will be called the name or serial number; then you need to do the following two steps:
Identify it in the .NET program, must be static, external C is like this: EXTERN "C"; call it like calling a normal function; pay attention to parameters:
If it is a structure or class, pay attention to the internal member must be defined as public to disclose some properties to implement "personalization", see "Personalized" attribute code below; if there is a lot of functions to call, or want this Functions become a member of the hosted class, you can use the packaging class:
Declaring a DLL function directly within an existing class; make functions, easy to find, can create a class for each DLL function; to form a logical packet and reduce system overhead, you can write a set of related DLL functions to a class ;
Let's look at the sample code, first come to a simple call step demo, call the MessageBox provided by Win32API ():
// 1. Write a namespace to be used first
Using Namespace System :: Runtime :: InteropServices;
Typedef intptr hwnd; // is a non-managed type void *, Win32 platform is 4 bytes, so INT // 2 can also be written. Use the DLLIMPORT attribute (ie DLLIMPORTATTRIBUTE attribute class) "# import" import DLL file, and Identification call function
[DLLIMPORT ("User32", entrypoint = "messageboxa")]
// 3. Create a prototype, please pay attention to the change of data type
EXTERN "C" int msgbox (hwnd hwnd, string * ptext, string * pcaption, unsigned int utYPE);
// 4. Call
MsgBox (this-> handle, "hello", "hi", 0);
// 5. Packaging class is written, very simple, do not write into the sample code for providing downloads ^ _ ^
PUBLIC __GC Class SDKMSGBOX: {
PUBLIC:
[DLLIMPORT ("User32", entrypoint = "messageboxa")]
EXTERN "C" int msgbox (hwnd hwnd, string * ptext, string * pcaption, unsigned int utYPE);
.......
}
If the value passed is an array, structure or class, it is not so simple, you need to customize the package (ie Marshal, custom type conversion)
/ / For arrays, simply define a method of encircling the method, do not write an example code for downloading.
EXTERN "C" void sendarray
[Marshalas (unmanagedtype :: lparay)] Array
INT Length
);
/ *
For structures, such as this function in user32.dll
Bool PtinRect (Const Rect * LPRC, POINT PT);
RECT and POINT are two structures
Note that the Point below is declared as __value instead of __gc, because the problem of .NET V1.1 will have problems (maybe the pen error,
It may also be .NET V1.1 BUG), can not automatically copy the content points to the hosted pointer to the non-hosting heap (not the __box package
Functional), so use the hosting pointer as the parameter (String *) in the actual use.
* /
// structlayout is the StructLayOutAttribute class, which is used to define the memory layout of the object.
// sequential indicates the memory layout in the order in which the object is defined.
[StructLayout (Layoutkind :: Sequential]
Public __value struct point {
PUBLIC:
INT X;
Int Y;
}
// Explicit Represents a member of the object to perform a memory layout according to the location specified by the fieldoffset (ie, the FieldoffSetAttribute property class)
[Structlayout (layoutkind :: explicit)]
PUBLIC __GC STRUCT RECT {
PUBLIC:
[Fieldoffset (0)] int LEFT; // FieldOffset () number is a memory layout,
[Fieldoffset (4)] int top; // must not be written, here is int, so each time 4
[Fieldoffset (8)] int Right;
[Fieldoffset (12)] int.
}
[DLLIMPORT ("User32.dll")] Extern "C" Bool PtinRect (Const Rect & r, Point P); // The first parameter defines the intersection of the intersection, specifying 1 indirect addressing to use reference
/ *
If there is a char * text in the parameter of the COM server, it is best to define the properties below.
[StructLayout (Layoutkind :: Sequential, Charset = Charset :: ANSI)]
... (..., string * text);
Other types are in this class
* /
}
There is no good way to pass the way the class, which is naturally the same as the conversion method of the structure. However, it is important to note that at least 1 indirect addressing is usually available when passing the class, that is, the pointer (like RECT in the above example).
/ *
For example, this function in kernel32.dll
Void getSystemTime (SystemTime * SystemTime);
Watch SystemTime is classified, structure and classes are "same root" ^ _ ^
* /
[StructLayout (Layoutkind :: Sequential]
PUBLIC __GC Class MySystemTime {
PUBLIC:
UNSIGNED SHORT WYEAR;
Unsigned short wmonth;
UNSIGNED SHORT WDAYOFWEEK;
UNSIGNED SHORT WDAY;
UNSIGNED SHORT WHO
UNSIGNED SHORT WMINUTE;
UNSIGNED SHORT WSECOND;
UNSIGNED SHORT WMILLISECONDS;
}
[DLLIMPORT ("kernel32.dll")]]]]
EXTERN "C" void getSystemTime (MySystemTime & ST);
If you want to use a callback function, it is more troublesome, you need to use the delegate / event mechanism to receive the message.
Using Namespace System :: Runtime :: InteropServices;
/ / Define a commission
__delegate Bool Callback (int hwnd, int lparam);
[DLLIMPORT ("User32")]]
Extern "C" int enjoywindows (Callback * x, int y); // Parameter Callback becomes delegate from the function pointer, in fact, they are similar
// Tune function, explicit window handle in debug window
Bool Report (int hwnd, int lparam) {
System :: Diagnostics :: TRACE :: WriteLine (HWND.TOSTRING (), "Window Handle IS:");
Return True;
}
//use
/ / Instantiate a commission of MyCallback
Callback * mycallback = new callback (this, & enormReport :: report);
Enumwindows (MyCallback, 0); // Transfer the function pointer (instantiated delegation) to the COM server, COM server automatically calls it
If you want to use a callback interface or connection point, see the title of the Collection, there is no interface, how to do it? Ha ha. Ok, P / Invoke is almost the same, I will listen to some useful forms below. Several commonly used Win32 API DLL
Available attributes, typically use DLLIMPORTATTRIBUTE ([DLLIMPORT (...)])