C# classes as COM objects

Howto convert my code to a usable library

Page 1 of 1

3 Replies - 5871 Views - Last Post: 18 June 2010 - 02:17 AM Rate Topic: -----

#1 Rico Diesel  Icon User is offline

  • D.I.C Head

Reputation: 62
  • View blog
  • Posts: 122
  • Joined: 06-May 10

C# classes as COM objects

Posted 17 June 2010 - 05:11 AM

Hi all,

I'm trying to expose my C# class functionality through COM to my Delphi application. Quite a few articles on the internet address this subject, but so far I'm not having any luck.

I have a C# Library project for the .NET 3.5 framework (Visual Studio 2010 Express Edition).

[Guid("48D921E8-FE9E-45da-9FCC-BFE4FD076EBE")]
public class MyPublicClass
{
  public MyPublicClass()
  { 
    //Default constructor needed for COM
  }
  
  public int Add(int a, int B)/>
  {
    return a + b;
  }
}



When I compile this to a library (DLL) it seems to work fine, I can use it in any other .NET project.

This article describes an implementation for the library. From a different article I used the Guid attribute (Generated with GuidGen.exe).
After compilation I exported the library with TlbExp.exe (Type Library Exporter) with the following parameters
TlbExp.exe MyLib.dll /tlb:MyLib.tlb /verbose



The TlbExp.exe program returns with success and puts a new .tlb file into the folder.
According to the two mentioned articles it should be ready to be used in a different language (my case Delphi 7).
When I import the Type Library and generate a wrapper class around it, this is the result that Delphi gives me:
unit LibGripDPV_TLB

interface

uses Windows, ActiveX, Classes, Graphics, StdVCL, Variants;
  

// *********************************************************************//
// GUIDS declared in the TypeLibrary. Following prefixes are used:        
//   Type Libraries     : LIBID_xxxx                                      
//   CoClasses          : CLASS_xxxx                                      
//   DISPInterfaces     : DIID_xxxx                                       
//   Non-DISP interfaces: IID_xxxx                                        
// *********************************************************************//
const
  // TypeLibrary Major and minor versions
  LibGripDPVMajorVersion = 1;
  LibGripDPVMinorVersion = 0;

  LIBID_LibGripDPV: TGUID = '{7C77462B-D182-4826-AA12-E40F45042BE1}';


implementation

uses ComObj;

end.



As you can see there is no implementation generated and it seems my class is not recognized or maybe not even transported properly into Type Library.

Now then, the question... What am I doing wrong? I'm probably forgetting something, but I can't figure out what.
Is there any way of checking what is inside my Type Library (.tlb)? Is it possible that Visual Studio Express doesn't provide the needed functionality?

Almost forgot, I also used the procedure described here but also no result.

Hope somebody can help me out with this...

Rico

Is This A Good Question/Topic? 0
  • +

Replies To: C# classes as COM objects

#2 JackOfAllTrades  Icon User is offline

  • Saucy!
  • member icon

Reputation: 6039
  • View blog
  • Posts: 23,437
  • Joined: 23-August 08

Re: C# classes as COM objects

Posted 17 June 2010 - 06:04 AM

No expert on this by any means, but did you you set the assembly to be ComVisible?
Was This Post Helpful? 1
  • +
  • -

#3 Rico Diesel  Icon User is offline

  • D.I.C Head

Reputation: 62
  • View blog
  • Posts: 122
  • Joined: 06-May 10

Re: C# classes as COM objects

Posted 17 June 2010 - 06:37 AM

Thank you JackOfAllTrades, very succinct and helpful answer :P (Sorry, couldn't help myself, even checked Dictionary.com for it)

Delphi now generates a complete ActiveX wrapper component which I can even use at design time (maybe overkill for what I need, but very happy with this).

Now only thing to do is figuring out how to use the ActiveX object in Delphi. Seems I need an interface to be able to load my functionality...


Rico

This post has been edited by Rico Diesel: 17 June 2010 - 07:47 AM

Was This Post Helpful? 0
  • +
  • -

#4 Rico Diesel  Icon User is offline

  • D.I.C Head

Reputation: 62
  • View blog
  • Posts: 122
  • Joined: 06-May 10

Re: C# classes as COM objects

Posted 18 June 2010 - 02:17 AM

An update for this topic. I have been doing some more digging and trying and it turned out that my first DLL wasn't working properly (it wouldn't register with Windows).

I found this article about using C# objects in Delphi. You have to download his code to make any sense of his article. Well then, a small (hopefully) readable howto for using .Net DLL's into usable Type Libraries.

1. Create a new library project in Visual Studio
2. Add "using System.Runtime.InteropServices;" to the top of your code file
3. Define a public interface, example:
public interface IAddInterface
{
 int MyProp { get; set; }
 int Add(int a, int B)/>;
}


4. Define a public class which inherites from MarshalByRefObject and your interface, example:
public class TestClass : MarshalByRefObject, IAddInterface
{
  public TestClass()
  { }

  private int prop = 0;
  public int MyProp
  {
    get { return prop; }
    set { prop = value; }
  }

  public int Add(int a, int B)/>
  {
    return a + b;
  }
}


Notice the empty default constructor for this class, this is a necessity. If you don't define this constructor you will not be able to instantiate the object in Delphi. The reason for inheriting from MarshalByRefObject has to do with better support of the object in an unmanaged enviroment.

5. Open Guidgen.exe to generate a valid Guid for the class and interface, you can find this program in your WindowsSDK folder (my case: C:\Program Files\MicrosoftSKDs\Windows\bin\). Copy paste the generated Guid into your public class and interface:
  [Guid("48D921E8-FE9E-45da-9FCC-BFE4FD076EBE"),
    ClassInterface(ClassInterfaceType.None)]
  public class TestClass: MarshalByRefObject
  {
  
  [Guid("3C2E7D52-DEEF-4d3b-9965-AA185D9A0CF2")]
  public interface IAdding
  {


The Guid is an attribute of the public class and ensures that the object is always registered with the same Guid (In case of an update to the library).
Notice the second attribute ClassInterface, this attribute defines how the class is defined in the Type Library, this MSDN article describes the differences between the options. Short & rough version:

ClassInterfaceType.None = Methods & properties are only exposed through the user defined interfaces (recommended by ms)
ClassInterfaceType.AutoDispatch = Methods & properties are only exposed on request (default option)
ClassInterfaceType.AutoDual = Methods & properties are directly accessible

I chose for ClassInterfaceType.None option because it ensures backwards compability. The differences between the different options are of course more detailed, but to keep this howto nice and easy I chose to only refer to the article, not to quote it. Interested readers can always check it out on MSDN.

6. Only thing to do now is to make sure that your library is COM visible, go to your project properties and open the tab Application. You find here a button called 'Assembly Info', check the 'Make COM Visible' checkbox. (Thanks to JackOfAllTrades)

7. Last thing you need to do in Visual Studio is compile the project into a .DLL

Once the Dll is compiled it is still not ready to be used in an unmanaged enviroment, two more steps need to be taken before you can use the code.

8. Convert the DLL to Type Library with the TlbExp.exe tool (also found in your SDK folder)
I used the following parameters:
TlbExp.exe MyCsharpLibrary.dll /verbose /win32


The /win32 parameter ensures that the Type Library is compiled to a win32 enviroment (I'm working on Win XP, so it's not really necessary, but better safe than sorry)

9. Last step, register your library with the system, regasm.exe does this trick for you (can be found under WINDOWS\Microsoft.NET\Framework\Version

Further steps I took was importing my library into Delphi (actually used the provided tools that came with the mentioned article)
Instantiated an object through the interface and used the methods and properties.
//Delphi code
procedure TForm1.Button1Click(Sender: TObject);
var
  TestClass: IAdding;
begin
  TestClass := CoMyPublicClass.Create() as IAdding;

  Label1.Caption := IntToStr(TestClass.Add(1,2));
  TestClass.MyProp := 12 + StrToInt(Label1.Caption);
  Label1.Caption := IntToStr(TestClass.MyProp);

  TestClass._Release; //dispose the object
end;



If somebody has any additions, remarks or corrections please post them.
Hope somebody finds this useful...


Rico
Was This Post Helpful? 2
  • +
  • -

Page 1 of 1