COM with DOTNET

xiaoxiao2021-03-05  21

Reposted: COM component objects and the mutual conversion of .NET class objects

* This article is selected from: COM concentration

COM component objects with the mutual conversion of .NET class objects

Lostall

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 like calling the COM component, make the .NET program like the COM component like the .NET object, MS uses Wrapper technology. This article details two different Wrapper technology and gives a simple code instance.

Second, COM Wrapper Introduction Traditional COM objects are different from .NET frame object models:

(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), which is automatically recycled through the Garbage Collection mechanism.

(2) Customers of the COM object use the QueryInterface query whether the COM object supports an interface and obtains its interface pointer, and the client of the .NET object uses the reflection (System.Reflection. *) To obtain the description of the object function, including method properties Wait.

(3), the customer of the COM object references the COM object through the pointer, and the location in memory is constant, and the .NET object is managed by the .NET Framework execution environment (Execution Environment), the object is The location in memory is variable, for example, for optimizing performance, will 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 package RCW (runtime callable wrapper) and CCW (COM CALLABLE WRAPPER). Whenever one .NET client calls a COM object method, you create an RCW object, and whenever a COM client calls a .NET object method, you created a CCW object.

The specific schematic is shown in Figure 1:

Figure 1 com wrapper overview

Third, .NET calls COM components

1. Introduction to the RUNTIME CALLABLE Wrapper This 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 and COM object calls, the objects of the column include the parameter return value of the method, such as the conversion between String between the String and the 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. The component class named ATLCOMSERVER, the implementation interface named IATLCOMSERVER, the library is called 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 ATLSERVER 1.0 Type library that just created in the COM property page and add it, the system prompts to add a wrapper, select 'Yes', then automatically generate a file in the BIN directory of the C # program Interop.atlserverLib_1_0.dll, this is the RCW of AtlServer. Also use the command line command TLBIMP ATLSERVER.TLB has the same effect.

(3) Add the code that calls AlTserver in the program, as shown below:

Using system;

Using AtlServerLib; // Take the 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. Calls by interface IATLCOMSERVER in traditional COM customers, while just a normal .NET class in .NET. Because actually calling the class in the wrapper, not the 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) 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 registers in the registry, with CLSID and IID, the interface is implemented, and the internal contains 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 objects are assigned to the Garbage-Collected pile, managed by Runtime, enjoy the benefits of 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 the reference to the .NET object it managed, and release its memory space. When the reference count is 0 on the .NET object, it will be recovered by GC.

Controlled types in .NETs such as Class, Interface, Struct, and Enum can be combined with COM types, but to 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 a .NET class does not explicitly implement an interface, COM Interke automatically generates an interface, which contains all public members of this .NET class and its 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 the Dispatch interface in this mode.

// can only be used with COM customers using Script, VB, etc.

[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 compiling, and it is automatically registered. 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 true implementation of the component is done by the call of the .NET 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 you should use raw_interfaces_only because sharpobject defaults from Objec

// If you don't add this option, you have to generate a packaging function for the public function and attribute of Object.

// and Object :: gettype returns the TYPE type, and does not generate a packaging interface for class Type, so it will be wrong when compiling

#import "../csharpserver/bin/debug/csharpserver.tlb" Raw_Interfaces_only no_namespace named_guids

...

{

Coinitialize (NULL);

//method one

// Since Raw_Interfaces_only is used, the package function GetName, PUTNAME is not generated by generating Name.

_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 Two

/ * _SHARPOBJECT * psharpobject = NULL;

HRESULT HR = CocreateInstance (CLSID_SHARPOBJECT,

NULL,

CLSCTX_INPROC_SERVER,

IID__SHARPOBJECT,

(void **) & psharpobject);

En (ac))

{

Psharpobject-> put_name (_BSTR_T ("Chang Ming")); 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, the use method is exactly the same as that call a general COM object.

(5) The disadvantage of using Class Interface is that the change in .NET class will affect COM customers. Specifically, there is no impact on the changes of the Late Binding mode using Script, VB, such as Test.vbs, NET classes. For Early Binding customers, because DISPID is related to its location in the .NET class, 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 Class Interface IID is randomly generated each time! Therefore, MS strongly requires not to use this way, Class Interface can't be a real interface, it is always constant, which 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 compilation, and it will be automatically registered. 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 inherits only from interface isharpobject2

// and isharpobject2 does not have a parent class, so there will be no compilation error like SharpObject.

#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 Two

/ * IsharpObject2 * psharpobject2 = null;

HRESULT HR = CocreateInstance (CLSID_SHARPOBJECT2,

NULL,

CLSCTX_INPROC_SERVER,

IID_ID_IDPOBJECT2,

(void **) & psharpObject2;

En (ac))

{

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.

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

New Post(0)