Operating environment: Visual Studio.Net Beta2, VC7, C #
Reference: MSDN
Level: entry level
I. Introduction
The COM component object is completely different from the .NET class object, but in order to call the COM client image to call the COM component, make the .NET program
Like the .NET object, use COM components, MS uses Wrapper technology. This article details two different Wrapper technology and gives it.
Simple code instance.
Second, COM Wrapper
Traditional COM objects are different from the .NET frame object model:
(1) The customer of the COM object must manage the survival of the COM object, and the living life of the .NET object is managed by the CLR (Common Language Runtime).
It is automatically recovered by the GARBAGE Collection mechanism.
(2), customers of the COM object use the QueryInterface querying whether the COM object supports an interface and gets its interface pointer, and the guest object of the .NET object
Use the reflection (System.Reflection. *) To obtain a description of the object function, including method properties, etc.
(3), the customer of the COM object references to the COM object through the pointer, the location in the memory is constant, and the .NET object resides in memory by the .NET box
EXECUTION Environment is managed, and the location of the object is variable in memory, such as consideration of optimization performance.
Generally update all references to objects. This is also premised in the CLR.
In order to achieve the mutual call between traditional COM programs and .NET programs, .NET provides packaging RCW (Runtime Callable Wrapper) and
CCW (COM CALLABLE WRAPPER). Whenever a .NET client calls a COM object method, create an RCW object, whenever one
The COM client calls a CCW object when calling a .NET object.
The specific schematic is shown in Figure 1:
Figure 1 com wrapper overview
Third, .NET calls COM components
1. Introduction to RUNTIME CALLABLE WRAPPER
The schematic is shown in Figure 2:
Figure 2 Accessing COM Objects Through the runtime callable wrapper
The main functions of RCW:
(1) The RCW is actually a .NET class generated by Runtime, which is packaged in the COM component, and the internal implementation of the COM component is implemented.
(2) Marshal .NET Customer between the Column, the call, the object of the column includes the parameter returns of the method, such as the String in C # and
The conversion between BSTR in COM.
(3) CLR Create an RCW for each COM object, has nothing to do with the reference number on the object, that is, each COM object is there and only one RCW object.
(4) The RCW contains the interface pointer of the COM object and manages the reference count of the COM object. The release of RCW itself is managed by GC mechanism.
2, instance demo
(1) Create a simplest COM object using VC7 / ATL. Component class named ATLCOMSERVER, the interface implemented named IATLCOMSERVER, the library name
ATLSERVER. Add an attribute Name and implement the GET / SET function. Its IDL is as follows:
Import "OAIDL.IDL";
Import "OCIDL.IDL";
[
Object,
UUID (77506E08-D9FB-4F45-85E0-376F5187AF21),
Dual,
Nonextensible,
Helpstring ("IATLCOMSERVER Interface", Pointer_Default (unique)
]
Interface IATLCOMSERVER: Idispatch {
[PropGet, ID (1), Helpstring ("Property Name")] HRESULT NAME ([OUT, RETVAL] BSTR * PVAL);
[PropPut, ID (1), Helpstring ("Property Name")] HRESULT NAME ([In] BSTR newVal);
}
[
UUID (9136EEE6-ECEE-4237-90B6-C38275EF2D82),
Version (1.0),
Helpstring ("ATLSERVER 1.0 Type Library")
]
Library ATLSERVERLIB
{
Importlib ("stdole2.tlb");
[
UUID (0e733e15-2349-4868-8f86-a2b7ff509493),
Helpstring ("ATLCOMSERVER CLASS")
]
CoClass AtlcomServer
{
[default] interface IATLCOMSERVER;
}
}
(2) Create a simplest C # Console program. Execute the menu Project / Add Reference command, select the original created in the COM property page
ATLSERVER 1.0 Type Library and add, the system will prompt to add a Wrapper, select 'Yes', then automatically in the C # program
A file interop.atlserverlib_1_0.dll is generated in the bin directory. This is the RCW of ATLServer. Down to use command line commands
Tlbimp atlserver.tlb has the same effect.
(3) Add the code that calls AlTserver in the program, as shown below:
Using system;
USING ATLSERVERLIB;
// Reference library through NameSpace, define in wrapper (ie interop.atlserverlib_1_0.dll)
Namespace CsharpClient
{
Class class1
{
Static void main (string [] args)
{
ATLCOMSERVER Server = New AtlcomServer ();
Server.name = "chang ming";
Console.writeline ("Hello, My Names IS" Server.Name);
}
}
}
As can be seen from above, ATLSERVERLIB.ATLCOMSERVER represents the COM component ATLCOMSERVER. In the traditional COM customer
IATLCOMSERVER is called, and it is just a normal .NET class in .NET. Because actually calling the class in Wrapper,
Not a real COM object.
Download sample code for RCW (23KB)
Fourth, call the .NET object in the COM program
1. Introduction to CCW (COM CALLABLE WRAPPER)
Its schematic is shown in Figure 3:
Figure 3 Accessing .NET Objects Through COM CALLABLE WRAPPER
The main function of CCW:
(1) CCW is actually a COM component generated by Runtime. It is registered in the registry, CLSID and IID, implements the interface, including pairs
The call to the .NET object.
(2) Marshal .NET object between COM customers. (3) Each .NET object has only one CCW, multiple COM customers call the same CCW.
(4) COM customer calls CCW in a pointer, so CCW is assigned on a non-collection pile, which is not managed by Runtime. And .NET object assignment
On the Garbage-Collected pile, it is managed by Runtime and enjoys the benefits of the CLR.
(5) The CCW is actually a COM component, so it follows the reference count rules. When its reference count is 0, it will release it to manage it .NET objects.
Quote and release your memory space. When the reference count is 0 on the .NET object, it will be recovered by GC.
Controlled types in .NET, such as Class, Interface, Struct, and Enum can be combined with COM types, but
Follow the following rules:
(1) The type of controlled control must be a PUBLIC type. Only the PUBLIC type type will be output to the type library.
(2) Only public type Methods, Properties, Fields, and Events will be output to the type library will be seen by the COM customer.
(3) The controlled type must have a public default constructor. This is because the COM component requires a default constructor.
(4) It is highly recommended to implement the interface in the .NET class. If one .NET class does not explicitly implement an interface, COM Interke will automatically produce
As an interface, the interface contains all public members of this .NET class and their parent class. This automatically generated interface is called "Class Interface".
However, MS strongly recommends using explicit interface definitions, which are explained below.
2, instance demonstration one (no definition interface)
(1) Create a simplest C # Console project, which is as follows:
Using system;
Using system.Runtime.InteropServices;
Namespace CSHARPSERVER
{
/ / The default is ClassInterFaceType.autodispatch, which only generates Dispatch interface // can only be used using a COM customer using late binding methods such as Script, VB.
[ClassInterFacettribute (ClassInterFactype.Autodu)]
Public Class Sharpobject
{
PRIVATE STRING M_STRNAME;
Public Sharpobject () {}
Public String Name
// Property: Name, Get / Set
{
Get {return m_strname;}
Set {m_strname = value;
}
}
}
(2) Set the register for COM Interop in the properties of the project to TRUE. This will generate a CSHARPSERVER.TLB file after compilation.
Automatically register it. Command line command REGASM has the same effect. The registry content is as follows:
[Hkey_classes_root / clsid / {88994E22-E99F-320B-908C-96E32B7BFE56}]
@ = "Csharpserver.sharpobject"
[/ InprocServer32]
@ =
"C: //winnt//system32//mscoree.dll"
"Threadingmodel" = "Both"
"Class" = "csharpserver.sharpobject"
"Assembly" = "csharpserver, version = 1.0.583.39183, culture = neutral, publickeyToken = null" Runtimeversion "=" v1.0.2914 "
"CodeBase" = "File: /// E: /cm/net/c#/exer/csharpserver/bin/debug/csharpserver.dll"
[/ ProgID]
@ = "Csharpserver.sharpobject"
The CSHARPSERVER.TLB file contains the type library information of the component, including CLSID, IID, interface definition, etc. The real realization of the components, to .NET
The call of the object is done by the General Language Runtime Library Mscoree.dll. It can be said that mscoree.dll and csharpserver.tlb are added to the CCW generated by Runtime as CSHARPSERVER .NET class.
(3) Write a simple VBScript program Test.vbs as follows:
DIM OBJ
Set obj = creteObject ("csharpserver.sharpobject")
Obj.name = "chang ming"
Msgbox "My Name IS" & obj.name
Double-click the file and run successfully.
(4) Create a simplest MFC dialog project, add the following code:
/ / Here, Raw_Interfaces_only should be used, because SharPObject default from Object // If this option is not added, you have to generate a package function for the Object's public function and attribute, // and Object :: gettype returns a TYPE type, not for Class TYPE generates a packaging interface, so it will be wrong when compiling
#import "../csharpserver/bin/debug/csharpserver.tlb" Raw_Interfaces_only no_namespace named_guids
...
{
Coinitialize (NULL);
// Method One // Because of the use of Raw_Interfaces_only, the package function getname, PUTNAME is not generated
_SHARPOBJECTPTR PSHARPOBJECT (__ uuidof (sharpObject));
Psharpobject-> put_name (_BSTR_T ("Chang Ming"));
BSTR STRNAME;
Psharpobject-> get_name (& strname);
AFXMessageBox ("My Name IS" _BSTR_T (STRNAME));
Method II // / * _SharpObject * pSharpObject = NULL; HRESULT hr = CoCreateInstance (CLSID_SharpObject, NULL, CLSCTX_INPROC_SERVER, IID__SharpObject, (void **) & pSharpObject); if (SUCCEEDED (hr)) {pSharpObject-> put_Name (_bstr_t ( "Chang ")); Bstr strname; psharpobject-> get_name (& strname); afxMessageBox (" my name is " _BSTR_T (STRNAME)); psharpobject-> release ();} else {afxMessageBox (" error ");} * / Couninitialize ();
}
In the automatically generated Class Interface, the interface name is '_' class name, ie _sharpobject. In addition, in use mode and calling general COM pairs
The like is exactly the same.
(5) The disadvantage of using Class Interface is that the change in .NET class will affect COM customers. Specifically, for Late Binding, using Script, VB, etc.
The language such as Test.vbs, the change of NET classes has no effect. For Early Binding customers, because Dispid is in the .NET class
The location is related, so the change of the .NET class is likely to change the member's DISPID, which will affect the client program, and the client needs to recompile.
For C clients that are directly called through the pointer, each .NET recompile will cause it to recompile because the IID of Class Interface
Every time it is randomly generated! Therefore, MS strongly requires not to use this way, Class Interface can't be a real interface, it is always
Continuous changes, this violates the spirit of the interface and violates the spirit of COM.
3. Example Demo 2 (Display Definition Interface)
(1) Create a simplest C # Console project, which is as follows:
Using system;
Using system.Runtime.InteropServices;
Namespace CSHARPSERVER2
{
/ / If you do not specify a GUID, you will randomly generate the IID each time.
[GUID ("539448DE-9F3B-4781-A1F6-F3C852091FC9")]]
Public Interface IsharPObject2
{
String name
// Property: Name, Get / Set
{
Get;
SET;
}
Void test ();
}
// If you do not specify a GUID, you will randomly generate CLSID each time.
[GUID ("F5A31AAB-FAA9-47CC-9A73-E35606114CE8")]]
Public class sharpObject2: isharpobject2
{
PRIVATE STRING M_STRNAME;
Public SharpObject2 () {}
Public String Name // Property: Name, Get / Set
{
Get {return m_strname;}
Set {m_strname = value;
}
Public void test () {}
}
}
(2) Set the register for COM Interop in the properties of the project to TRUE. This will generate a CSHARPSERVER2.TLB file after compiling, and
And automatically register it. The registry content is as follows:
[HKEY_CLASS_ROOT / CLSID / {F5A31AAB-FAA9-47CC-9A73-E35606114CE8}]
@ = "Csharpserver2.sharpobject2"
[/ InprocServer32]
@ =
"C: //winnt//system32//mscoree.dll"
"Threadingmodel" = "Both"
"Class" = "csharpserver2.sharpobject2"
"Assembly" = "csharpserver2, version = 1.0.583.38696, culture = neutral, publickeytoken = null"
"Runtimeversion" = "v1.0.2914"
"CodeBase" = "File: /// E: /cm/net/c#/exer/csharpserver2/bin/debug/csharpserver2.dll"
[/ ProgID]
@ = "Csharpserver2.sharpobject2"
(3) Create a simplest MFC dialog project, add the following code:
/ / Don't use rAW_INTERFACES_ONLY, because Sharpobject2 is only inherited from the interface isharpobject2 // // isharpobject2 does not have a parent class, so there is no SharPObject to compile errors.
#import "../csharpserver2/bin/debug/csharpserver2.tlb" NO_NAMESPACE NAMED_GUIDS
...
{
Coinitialize (NULL);
//method one
Isharpobject2ptr psharpobject2 (__ uuidof (sharpObject2));
Psharpobject2-> Putname ("chang ming");
AfxMessageBox ("My Name IS" Psharpobject2-> getName ());
Method II // / * ISharpObject2 * pSharpObject2 = NULL; HRESULT hr = CoCreateInstance (CLSID_SharpObject2, NULL, CLSCTX_INPROC_SERVER, IID_ISharpObject2, (void **) & pSharpObject2); if (SUCCEEDED (hr)) {pSharpObject2-> PutName ( "Chang Ming" AfxMessageBox ("My Name IS" psharpobject2-> getname ()); psharpobject2-> release ();} else {afxMessageBox ("error");} * / couninitialize ();
}
Only when the interface isharpobject2 remains the same, it will not affect the COM client.
Download sample code for CCW (50KB)