For some code example see this post
Here you can find a series of notes I took as reminders to develpod a COM Class in C#.
I will put it here just as a reference, I hope I will have more time in the future to show you a full example.
You can also find here a C# template you can use to start develop C# COM exposed class. You can delete all the comments out of it. I just add them there for my reference.
To install the template in your VS2008 you need first to find out where they are stored.
To do this, go to File, Export Template. After a few click you should find out where your exported templates
are stored
On my PC for example, they are store here
C:\Users\PP\Documents\Visual Studio 2008\My Exported Templates\WFA01.zip
Once you know this path, just copy the CSharp_Com_Class.zip in the following directory.
You must copy the .zip file. Do not unzip them.
C:\Users\PP\Documents\Visual Studio 2008\Templates\ItemTemplates\Visual C#.
If things go well (and it took me sometime to figure out how to do it) you should have a new template in your
Add New Item, Visual C# Item
C# COM Rules:
To expose properties and methods to COM, you must declare them on the class
interface and mark them with a DispId attribute, and implement them in the class.
The order in which the members are declared in the interface is the
order used for the COM vtable.
ex:
[DispId(1)] void Init(string userid , string password);
[DispId(2)] bool ExecuteSelectCommand(string selCommand);
1)The Class must be public
2) Properties, methods and events that need to be exposed:
a) must be public
b) Properties and methods must be declared in the class interface. The class must implement this interface
c) Event must be declared on the Event Interface. The class should not implement this interface
3) Other Class member that are not declared in the class interface, are not visible to COM but are
visible to .NET classes
4) The class must have a default parametereless constructor. Always write is down even if is empty.
The class can have its constructors and methods overloaded.
5) COM does not support inheritance, only interface implementation
So you can't do Class Employees : List
This will not be exposed to COM
6) C# can pass to COM zero based array using "ref" in the method signature.
Without ref, the method will not work!
ex: double[] Compute(ref double[] a, double b)
in VB 6.0 this will be like Dim a() as double
7) Variant can be passed as type "object"
8) Enum can be exposed. Remeber you need to generate a unique Guid using
C:\Programmi\Microsoft Visual Studio 9.0\Common7\Tools\guidgen.exe Registry format
[Guid("DE23AB62-269E-4418-BCBD-193BE024E21C"),
ComVisible(true)]
public enum MyEnum{
[DispId(1)] A = 0,
[DispId(1)] B = 1,
[DispId(1)] C = 2
Please not that "public enum MyEnum : long" will compile but will cause method that have MyEnum
in their signature not to work at all
9) Collections.
Collections can be implemented using encapsulation and delegation.
We can encapsulate a SortedList and delegate to it the implementation of
Count, Item, Remove, Add methods defined in the COM Interface.
Ex:
-----------------------------------------------------------------------------------------
[Guid("d345c3dc-825e-4be7-b129-2b3d00a7a2a7"),
ComVisible(true)]
public interface INetSortedList : IEnumerable
{
[DispId(-4)] new IEnumerator GetEnumerator(); //Iterator
[DispId(1)] void Add(object key, object value);
[DispId(2)] int Count{ get; }
[DispId(3)] void Remove(object key);
[DispId(0)] object this[object key] {get ; set; } //Default Property
//Events Interface
[Guid("4b1f6f84-c971-410a-8667-f5611f632b33"),
InterfaceType(ComInterfaceType.InterfaceIsIDispatch),
ComVisible(true)]
public interface INetSortedListEvents
{
}
//Class Implement the Class Interface
[Guid("1ca3e210-b5e3-458a-9175-002b7ff3274c"),
ClassInterface(ClassInterfaceType.None),
ComSourceInterfaces(typeof(INetSortedListEvents)),
ComVisible(true)]
public class NetSortedList : INetSortedList
{
private SortedList _sortedList;
public NetSortedList() { _sortedList = new SortedList(); }
public IEnumerator GetEnumerator() { return _sortedList.GetEnumerator(); }
public void Add(object key, object value) { _sortedList.Add(key, value);}
public int Count { get { return _sortedList.Count; } }
public void Remove(object key) { _sortedList.Remove(key); }
public object this[object key] {
get { return _sortedList[key]; }
set { _sortedList[key] = value; }
}
}
----------------------------------------------------------------------------------------------------------------
Please note that
a default Item property so that you can do: list(1) or list.Item(1).
Its DispId must be set to = [DispId(0)] to work as default Property
b) System.Collections.IEnumerator GetEnumerator(); allows for the "for each" loop in VB 6.0. It
You cannot use in the interface a Generic enumerator like "List
The IEnumerator GetEnumerator() return in COM a IEnumVariant so it can be passed to NewEnum in
NewEnum = obj.GetEnumerator()
End Function
otherwise the For Each Loop will work but you will not be able to access the Item of the collection. For
ICollection keys = _sortedList.Keys;
return (IEnumerator)keys.GetEnumerator();
This way the "Current" property will return the Keys in the collection and not the DictionaryEntry Object.
Another way around is to write your own Enumerator.
c) The collection is 0 based.
d) You could have the NetSortedList to implement System.Collections.Generic.ICollection
Project/Properties/Build tick Register for COM interop. (Reccomended Choice)
Otherwise you need to use REGASM. (asembly registration tool). The Assembly Registration tool reads
the metadata within an assembly and adds the necessary entries to the registry, which allows COM clients to create .NET Framework classes transparently. This utility is necessary when you need to expose to COM e .exe (winform application) .net assembly. You can create a type libray in this way. MyAssembly.dll can be also MyAssembly.exe
REGASM
To unregister just do
REGASM /u MyAssembly.dll /tlb:MyAssembly.tlb
As a note. When you add a COM dll to a project, C# calls REGASM for you, create a tlb file out of the
COM dll and put it in your project folder
For more details see the session DEPLOYMENT
Make assembly COM-Visible. This option will set
[assembly: ConVisible(true)]
in the AssemblyInfo.cs file
Which will make all the Class in the Project COM visible. This in practice will make C# to generate
new Guids for each class on which we did not specify a Guid attribute each time we recompile, causing a registry bloat.
It is much better to set
[assembly: ConVisible(false)], and use ComVisible(true) at class level to specify which class should be
visible for COM interop
12) The assembly should be given a strong name.
Go to Properties/Signing. Tick Sign the assembly check box.
Then go to Choose a stroing name key file
Choose a name ex "MyLibrary_COM_Key"
This will create a MyLibray_COM_Key.snk file in the project folder.
The assembly is now signed with a strong name.
-----------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------
DEPLOYMENT
To deploy the Dll we need to register the .net assembly of a computer for COM interop.
To register it manually we have two ways
01) The dll assembly will reside on a specific folder decided at the moment of the assembly registration
You need to type the following commands:
regasm /u "FullPath\MyLibray.dll" /tlb
regasm /codebase "FullPath\MyLibray.dll" /tlb
ragasm is located in c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\regasm.exe
The first line unregister MyLibrary.dll and its type library
The second line register MyLibrary.dll create the type libary and register them in the COM registry
the /codebase option saves in the regestry the path of the dll
This is exactly what the option "Register for COM interop" Does
If we do not put the /codebase option, VB6.0 will complain that it cannot find the dll file
02) The dll assembly will reside in the GAC c:\WINDOWS\assembly. The assembly MUST be strongly named if you want to install it in the GAC.
You need to type the following commands
regasm "FullPath\MyLibray.dll" /tlb
gacutil /if "FullPath\MyLibray.dll"
The first line register the assembly for COM interop. Please note that we must leave out /codebase, because
we will put the dll in the GAC.
The second line, registers MyLibray in the GAC.
This mean that all the application will look at the GAC when we run the assembly
To unregister we do
regasm /u "FullPath\MyLibrary.dll" /tlb
gacutil /uf MyLibrary
To Remove from the GAC a specif version
gacutil /uf MyLibrary , Version=1.1.0.0
Please note the we do not use neither the FullPath nor the .dll/.exe to remove an assembly from the GAC
in addition each .NET Framework has its own gacutil.exe. The one for .NET 3.5 can be found here
"C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\gacutil.exe"
Installing the assembly in the GAC ensures that multiple versions of the component can exist side-by-side.
An additional hard requirement for COM is that you change the GUIDs of the public interfaces and classes
when you change their definitions. Failing to do so will wipe out the registry info for the old component
and will make old client programs that have not been recompiled with the new component crash and burn.
A problem better known as DLL Hëll.
if We need to change the interace of our COM component we need to
1) Change the version number of the assembly. (Do not change the strong key name of file)
2) Change the GUIDs for both the Class and interface of the class that has been changed
Changing them all could be better to avoid trouble when they depend on each other.
No comments:
Post a Comment