none
Unable to use COM component written in .NET in .NET client as a pure COM component. RRS feed

  • Question

  • I am using Delphi 5 and VS2008 in these examples. I am in Windows XP.  My references have either been google searches or using the book ".NET and COM The Complete Interoperability Guide"

    We have a product that is written with a combination of C++ and Delphi using COM.  We want to migrate it to C#.  I am looking into converting one component at a time.  As an exercise I did this:
    I created a very simple com library in Delphi with a single com object. It has a few simple methods to it.
    I created a simple Delphi client that uses this component and proved that everything works.
    I created a simple WPF VS2008 .NET 3.5 client that uses the Delphi com dll in the same way proving it works.  Naturally this created Interop.delphicode.dll.
    Then, I created a new .NET class library to port the delphi dll code. Using online help and the book mentioned above, I rewrote the interface and class in C# with the appropriate tags and guid tags.  I compiled this and did regasm on it with /codebase and /tlb.  I opened the delphi written client and ran it and all worked.  I opened the .NET written client and ran it and I get:
    "Unable to cast object of type 'mylib.myobj' to type 'mylib.myobjClass'.

    I have tried a variety of other options, but most had lead to errors in trying register the .NET dll or errors in getting the class factory.

    Finding help on this online has been sparse. I may not be using the right keywords. The only help I've seen on this basicaly says "don't do this, reference the assembly directly", but the problem is, we have 3rd party clients that use our dlls, creating their own "face" to their software.  These new clients could be C++, VB, C#, Delphi or whatever uses com.  If the client code references our stuff as COM, I would think I could rewrite the delphi COM code into C# COM code and have all clients of all types just continue working.  

     Is this just not possible?  If it is, how do I get it to work?  Is there a blog or tutorial online on this that I just missed?  The book I mentioned doesn't specifically talke about .net com components used in .net clients as com components.

    Thank you
    Tuesday, March 16, 2010 2:47 PM

Answers

  • Hi,

    For COM server that exposed by .NET, if the client is .NET application, we have to use late-binding.

    If we try to converts the type definitions found within a COM type library into equivalent definitions in a common language runtime assembly , there will be an exception:

    error TI0000 : System.Runtime.InteropServices.COMException - Type libra

    ry ' mylib ' was exported from a CLR assembly and cannot be re-imported as a CLR asse mbly .


    Sincerely,
    Eric
    MSDN Subscriber Support in Forum
    If you have any feedback of our support, please contact msdnmg@microsoft.com .
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.
    • Marked as answer by worm3rd Wednesday, March 24, 2010 9:37 AM
    Monday, March 22, 2010 2:40 AM

All replies

  • Can you post the code where you are receiving your error?
    Wednesday, March 17, 2010 5:09 PM

  • May be, I know this. But this was no problem, because it was ok for us to get assembly class directly by assembly reference.

    We did it this way:

      [ComImport, Guid("CE69EB70-3993-4EED-8293-E300BCAC2615")]
      public class ProfileInfoWrapperClass
      {

      };

      [Guid("C57FF161-9106-4D1B-8AAA-4B9927EB500F"),
       InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
       public interface IPrinterProfile
      {
      }

    // Casting error:
    IPrinterProfile Profile = new PrinterInfoWrapperClass() as IPrinterProfile;

    Assembly namespace / COM class mismatch ? We saw the assembly namespace of the exported class in the error message

    Regards,
    T01

     

     

    Wednesday, March 17, 2010 6:03 PM
  • Does your class implement the interface?  I believe it would have to before you could what you are trying to do.
    Wednesday, March 17, 2010 7:24 PM
  • First the code (forgive the naming, as this was just a dicovery project)...

    This is Delphi's View/Type Library text view of the type library (idl):


    [
      uuid(D5DB073D-222C-43F3-A6B5-524F3AB04A15),
      version(1.0),
      helpstring("aaa555")
    ]
    library delphidll
    {

      importlib("stdole2.tlb");
      importlib("STDVCL40.DLL");

      [
        uuid(0DCA2390-76B2-44A6-AA91-6357FD54CDCA),
        version(1.0),
        helpstring("aaa5551"),
        dual,
        oleautomation
      ]
       interface IDelphiCOMPrime: IDispatch
      {
        [id(0x00000001)]
        HRESULT _stdcall GetNextPrime([out, retval] long * resultt );
        [id(0x00000002)]
        HRESULT _stdcall Reset( void );
      };

      [
        uuid(B9C568CA-EA1C-4F9E-BCED-9227D753650D),
        version(1.0),
        helpstring("aaa5552")
      ]
      coclass DelphiCOMPrime
      {
        [default] interface IDelphiCOMPrime;
      };

    };




    The delphi tester does this:

    procedure TForm1.Button1Click(Sender: TObject);
      var
        p : IDelphiCOMPrime;
    begin
      ListBox1.Items.Clear();
      p := CoDelphiCOMPrime.Create();
      try
        test(p, 33);
        test(p, 66);
        test(p, 99);
        test(p, 999);
        test(p, 1000);
      finally
        ListBox1.Items.SaveToFile('C:\data\out.txt');
      end;
    end;

    procedure TForm1.test(p: IDelphiCOMPrime; max: integer);
      var
        i : integer;
    begin
      p.Reset();
      for i := 0 to max do
      begin
        try
          ListBox1.items.add(inttostr(p.GetNextPrime()));
        except
           exit;
        end;
      end;
    end;


    (GetNextPrime throws an error when it runs out of known primes)

    Here is the C# tester code, after grabbing a reference to the delphi com object

        private void Button_Click(object sender, RoutedEventArgs e)
        {
          try
          {
            IDelphiCOMPrime p;
            p = new DelphiCOMPrimeClass();     // sometimes ERROR
            trythis(p, "c:\\data\\out5.txt");
            p = new DelphiCOMPrime();            // sometimes ERROR
            trythis(p, "c:\\data\\out4.txt");
          }
          catch (Exception x)
          {
            ll.Text = x.Message;
          }
        }

        private void trythis(IDelphiCOMPrime p, string file)
        {
          List<int> list = new List<int>();
          test(p, 33, list);
          test(p, 66, list);
          test(p, 99, list);
          test(p, 999, list);
          test(p, 1000, list);
          using (StreamWriter w = new StreamWriter(file))
          {
            for (int i = 0; i < list.Count; i++)
            {
              w.WriteLine(list[i]);
            }
          }
        }

        private void test(IDelphiCOMPrime p, int max, List<int> list)
        {
          p.Reset();
          for (int i = 0; i <= max; i++)
          {
            try
            {
              list.Add(p.GetNextPrime());
            }
            catch (Exception)
            {
              return;
            }
          }
        }

    this // sometimes ERROR lines are where it will error if I'm using the C# version of the prime com control (coming up).  It errors on whichever one is first, but when the delphi com control is registered, all works perfectly.   Also, the delphi tester works in both cases.

    The C# version of the com prime code....
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Runtime.InteropServices;


    namespace delphidllx
    {
      [ComImport]
      [ComVisible(true)]
      [Guid("0DCA2390-76B2-44A6-AA91-6357FD54CDCA")]
      [InterfaceType(ComInterfaceType.InterfaceIsDual)]
      public interface IDelphiCOMPrime
      {           
        [DispId(0x00000001)]
        int GetNextPrime();
        [DispId(0x00000002)]
        void Reset();
      }

      [ComVisible(true)]
      [Guid("B9C568CA-EA1C-4F9E-BCED-9227D753650D")]
      [ClassInterface(ClassInterfaceType.None)]
      [ProgId("delphidll.DelphiCOMPrime")]
      [ComDefaultInterface(typeof(IDelphiCOMPrime))]
      [TypeLibType(2)]
      public class DelphiCOMPrimeClass : IDelphiCOMPrime
      {
        public  int GetNextPrime()
        {
      // specifics not important
        }

        public  void Reset()
        {
      // specifics not important
        }
      }
    }


    I have tried several different attributes and values, and it either works with neither tester or works in only the delphi tester.


     

    Wednesday, March 17, 2010 8:03 PM
  • Hi,

     

    From my understanding of your question, you failed to create a COM server via .NET? Am I right?

     

    If so, you may have a read at CSDllCOMServer project of All-In-One code framework , that sample shows how to expos e .NET Framework components to COM, which allows us to write a .NET type and consuming that type from unmanaged code with distinct activities for COM developers.

     

    To consume the COM component from .NET client, we need to l ate-binding to the COM sever through .NET reflection , the late binding part of CSCOMClient project shows more details.

     


    Sincerely,
    Eric
    MSDN Subscriber Support in Forum
    If you have any feedback of our support, please contact msdnmg@microsoft.com .
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.
    Friday, March 19, 2010 10:06 AM
  • Gave it a try and using late binding worked.  The problem is, this is a change in the client.  We were wanting to deploy these rewritten com controls in .NET and allow existing clients, both .NET and non-.NET clients, to continue working unchanged.  I thought this was the point of COM.  I know some of our internal tools in .NET use early binding on these com controls with the corresponding Interop.*.dll.  And I know some 3rd party .NET tools (that we don't have control over) use our controls via COM in the same way.  We don't plan on doing this, but if we were to deploy the rewritten controls one at a time, this would cause all .NET clients to have to be rewritten in the not-as-easy-to-understand late binding code.   It's looking like this simply is not going to happen?

     

     

     

    Friday, March 19, 2010 1:12 PM
  • Hi,

    For COM server that exposed by .NET, if the client is .NET application, we have to use late-binding.

    If we try to converts the type definitions found within a COM type library into equivalent definitions in a common language runtime assembly , there will be an exception:

    error TI0000 : System.Runtime.InteropServices.COMException - Type libra

    ry ' mylib ' was exported from a CLR assembly and cannot be re-imported as a CLR asse mbly .


    Sincerely,
    Eric
    MSDN Subscriber Support in Forum
    If you have any feedback of our support, please contact msdnmg@microsoft.com .
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.
    • Marked as answer by worm3rd Wednesday, March 24, 2010 9:37 AM
    Monday, March 22, 2010 2:40 AM
  • Hi,

    How about the issue status now? please feel free to let us know if you have any concern.


    Sincerely,
    Eric
    MSDN Subscriber Support in Forum
    If you have any feedback of our support, please contact msdnmg@microsoft.com.
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.
    • Marked as answer by worm3rd Wednesday, March 24, 2010 9:35 AM
    • Unmarked as answer by worm3rd Wednesday, March 24, 2010 9:37 AM
    Wednesday, March 24, 2010 4:48 AM
  • The answer I am looking for is apparently, "It can't be done", in terms of early binding.  It's disappointing to hear this, but that is the case, then that's that.

    Wednesday, March 24, 2010 9:37 AM