Last Updated: 6/2003
Win32API Interoperability (DllImport) | ||||||||||||
Inside the class declare the function using the [DllImport("dllFilename")]
prototype syntax. To use the DllImport attribute you must first include
a reference to System.Runtime.InteropServices. The imported function needs to
be declared both static and
extern.
|
||||||||||||
Win32API Interoperability (Callbacks) | ||||
To use a Win32API function that requires a callback (such as
EnumDesktopWindows) declare a delegate for the callback, then reference
that delegate in the DllImport marked prototype.
|
||||
Instantiating COM objects (CoCreateInstance) |
COM Wrapper classes are used instead of calling CoCreateClass directly. For each
COM class you wish to import, a wrapper is produced then instead of calling
CoCreateClass we simply 'new' the COM Wrapper class and let .NET handle the
rest. A COM Wrapper class consists of a non-inherited, interface-free class marked with the appropriate attributes:
Example:
All this presumes you don't have a TypeLib for the COM object, else you would just let VS import it and use the resulting COM Wrappers in the InterOp.ImportedDll directly. Note: DEFINE_GUID(CLSID_AutoComplete, 0x00BB2763L, 0x6A77,
0x11D0, this format needs to be converted to the single string format shown above. This is simply a case of merging the number into blocks of the correct length (not forgetting to remove the 0x hex prefix from all numbers and the L long postfix from the first number. |
COM Interfaces on COM
Wrappers ('QueryInterface') |
See above section for details on the [ComImport] and [Guid] attributes. These are used to tell the compiler these are COM interfaces and what IID is associated with the interface. The [InterfaceType] attribute tells the compiler whether it should use IDispatch or IUnknown, or if it can use both (Dual). Since IDispatch is really late binding and requires additional support, I tend to specify IUnknown. The default is dual. Again, interfaces can be automatically created from the TypeLib if one is available.
Obtaining an Interface is then simple: simply case the COM Wrapper to the interface! IAutoComplete2 ac = (IAutoComplete2) new CLSID_AutoComplete(); Unfortunately, it is not currently possible to inherit COM interfaces. If interface 2 includes all the items in interface 1, those items have to be repeated manually in interface 2: you can't just 'public class interface2 : interface1' like you can with normal C# interfaces. |
[PerserveSig] |
In COM, a function typically returns a HRESULT indicating success / failure, with the actual return value from the function being returned in a by-reference parameter. For example:
While in C# we would typically use the result of the function as the result and throw an exception to indicate failure. For example:
.NET does the conversion automatically between the two formats (which is why all the functions in the IAutoComplete2 interface example above return void). Placing [PreserveSig] on the function prototype will prevent this conversion. This attribute can also be used inside the DLLImport attribute, but since the sorts of functions imported by DDLImport rarely use COM, the default is here is not to perform the conversion. |
Auto Complete Example |
// Casting the C# class is the
equivalent of calling IUnknown->QueryInterface on that class IAutoComplete2 ac = (IAutoComplete2) new CLSID_AutoComplete(); ac.SetOptions((int)(ACO.AUTOSUGGEST | ACO.ACF_UPDOWNKEYDROPSLIST)); // ac.Init(this.textBox1.Handle, new CLSID_ACListISF(), null, null); ac.Init(this.textBox1.Handle, new myEnum(), null, null);
|
The Manifest |
The Manifest (see earlier notes) contains:
Not all data types are supported by all .NET languages. For example, if you create a unsigned long public property in C# that class cannot be used by VB.NET as it has no equivalent. |