|
|||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
AbstractIn industrial automation, OPC (OLE for Process Control, see www.opcfoundation.org) is the primary COM component interface used to connect devices from different manufactures. The OPC standard is defined at 'two different layers' of COM/DCOM. First, as a collection of COM custom interfaces, and secondly as COM-automation compliant components/wrappers. For some reasons and applications, it is preferable to use the custom interface directly. We will show you in this article how to access OPC servers with custom interfaces and how to write an OPC client in .NET. The problemThe new Microsoft .NET Framework will provide some interoperability layers and tools to reuse a large part of the existing COM/ActiveX/OCX components, but with some strong limitations. While automation compliant COM objects will be nicely imported (as referenced COM objects inside Visual Studio .NET, or with the TLBIMP tool), pure custom COM interfaces will not work. To understand the issues with COM custom interfaces and the .NET framework, we must first analyze, why automation components can be used immediately. Visual Studio .NET relies on the information found in a type-library for every imported COM component (e.g. the library generated by MIDL-compiler, named *.TLB). The problem is now, type libraries can only contain automation compliant information. So if we compile a custom-interface IDL file, the generated TLB misses very important type descriptions, especially the method call parameter size (e.g. of arrays). At the time of .NET Beta2, there's unfortunately no tool (like 'IDLIMP') to import custom interface IDL files. Example: Since the MIDL compiler does not propagate One first solution would be to edit the assembly produced by TLBIMP (using ILDASM), and replace But if we look at some very custom IDL files, we also find methods with parameters like
The solution, first stepWe have to rewrite our custom IDL file in a managed language code, here C#. Note this can be a very time consuming work! every method of all interfaces have to be coded in C#, and the critical (custom) parameters must be declared completely different. We found there's often no way around the use of the special Let's look at a sample method (from an OPC custom interface IDL): HRESULT AddItems( [in] DWORD dwCount, [in, size_is( dwCount)] OPCITEMDEF * pItemArray, [out, size_is(,dwCount)] OPCITEMRESULT ** ppAddResults, [out, size_is(,dwCount)] HRESULT ** ppErrors ); Redefined in C#, looks now like this: int AddItems(
[In] int dwCount,
[In] IntPtr pItemArray,
[Out] out IntPtr ppAddResults,
[Out] out IntPtr ppErrors );
As you can see, we loose many type information by declaring parameters as Another point is the default exception mapping of .NET: as custom interface methods return To bypass exception mapping, declare the interface with special signature attributes. See at the code below for the head of the final interface declaration: [ComVisible(true), ComImport,
Guid("39c13a54-011e-11d0-9675-0020afd8adb3"),
InterfaceType( ComInterfaceType.InterfaceIsIUnknown )]
internal interface IOPCItemMgt
{
[PreserveSig]
int AddItems(
[In] int dwCount,
[In] IntPtr pItemArray,
[Out] out IntPtr ppAddResults,
[Out] out IntPtr ppErrors );
...
The solution, second stepTo use the interfaces we declared as above, it is recommended to write some wrapper classes. But more important, this wrapper now has to do all the custom marshaling, e.g. for all
To get the idea, see a simplified excerpt for the sample method ...
IntPtr ptrdef = Marshal.AllocCoTaskMem( count * sizedefinition );
int rundef = (int) ptrdef;
for( int i = 0; i < count; i++ )
{
Marshal.StructureToPtr( definitions[i], (IntPtr) rundef, false );
rundef += sizedefinition;
}
int hresult = itemsinterface.AddItems( count, ptrdef, ... );
...
int rundef = (int) ptrdef;
for( int i = 0; i < count; i++ )
{
Marshal.DestroyStructure( (IntPtr) rundef, typedefinition );
rundef += sizedefinition;
}
Marshal.FreeCoTaskMem( ptrdef );
...
DownloadIn the download package, you will find the complete interface declarations and a sample client application showing how to use them. Please note:
Useful linksOPC, the OPC logo, and OPC Foundation are trademarks of the OPC Foundation. .NET, the .NET logo, and Microsoft .NET are trademarks of the Microsoft Corporation. DisclaimerThe information in this article & source code are published in accordance with the Beta2 bits of the .NET Framework SDK).
|
||||||||||||||||||||||||||||||||||||