4

We have a COM interface declared and implemented in unmanaged C++. We no longer wish to maintain this assembly, but do need to make some changes, and still be backward compatible.

What I want to do, is to write all the new code in C#, and make it ComVisible. From this thread I see a reference to TreatAs to replace the old interface.

I was thinking along the same path as Jonathan Peppers regarding ProgId, Guid, etc. I will implement the exact interface as the old version as a wrapper to the new implementation. I am also thinking about adding MarshalAs attributes exactly as in the generated interop wrapper to be sure the data types will be the same, if possible.

Are there anything else I should consider while working with this? Anyone with experience doing this conversion?

EDIT: I do have the IDL file for the server. I am not sure if there is a way I can auto generate the code based on this. COM is not something I'm very familiar with.

EDIT Q: How should I deal with HRESULT used by existing clients?

ADDED: Figured I should point other readers to a different fix, which is not available for my scenario as I can't recompile all the .NET applications using the existing com:

Bjørnar Sundsbø

Community
  • 1
  • 1
bigfoot
  • 455
  • 5
  • 12
  • It is simple when you have an .idl file or the type library of the original server. It won't be if you don't or when the COM interface(s) are not automation compatible. Update your question with this essential info. – Hans Passant Aug 16 '11 at 14:06
  • Is there a way I can generate server side code from the type library or idl instead of a client wrapper? – bigfoot Aug 16 '11 at 15:20
  • A type library only contains declarations, no code. You'll have to rewrite the code, that was your intention afaict. – Hans Passant Aug 16 '11 at 15:27

2 Answers2

2

One quick way to get started:

  • Import your existing COM object into C# with tlbimp (or Visual Studio)
  • Load up .Net Reflector and get the generated interface to put in your new project
  • Implement the generated interface in C#
  • Switch any import declarations to export instead

This should get all your types and method signatures correct from the start. It is a great starting point for porting an existing COM interface to C#.

jonathanpeppers
  • 26,115
  • 21
  • 99
  • 182
  • Or use [dotPeek](http://www.jetbrains.com/decompiler/) instead of Reflector since the latter now costs money. – siride Aug 16 '11 at 14:27
  • Do you mean replace ComImport with ComVisible? I have now used .NET Reflector and the FileDisassembler to generate my code. This code does include things like MethodIml(MethodImplOptions and method code type. Should I keep these? – bigfoot Aug 16 '11 at 14:39
  • Yes, change ComImport to ComVisible. In the past, I believe I removed MethodImpl with no issue. – jonathanpeppers Aug 16 '11 at 14:52
  • I've had some luck. Extracted the interface specs from the original type library, edited some attributes, and registered the assembly for COM. The trouble now is that when constructing the object on the client, I get the following exception: Unable to cast object of type 'Interop.TC30Lib.TC30ClientClass' to type 'Interop.TC30Lib.TC30ClientClass'. I have kept the original COM wrapper used by the application, which is what I need to do to support existing applications. Any suggestions? – bigfoot Aug 16 '11 at 19:33
  • Sorry. The last exception was introduced when I tried adding the COM object to GAC because of the following exception: System.IO.FileNotFoundException: Retrieving the COM class factory for component with CLSID {A47E5E67-E8FF-4DD6-B4D7-FDC83B931146} failed due to the following error: 80070002 The system cannot find the file specified. (Exception from HRESULT: 0x80070002). – bigfoot Aug 16 '11 at 20:19
  • Sigh... Assigning codebase when using RegAsm resulted in the same InvalidCastException, so I'm back to the same issue. Apparently there is something different with the interface since it won't cast, or is what I'm trying to do not feasible? – bigfoot Aug 16 '11 at 20:34
  • Are you trying to use your new C# COM interface from C#? That will not work, b/c of the way C# COM is implemented. If you want to use your C# COM interface from C#, then you would just reference it as a usual .Net dll. Only way to test it is to call it from C++, VB6, VBScript, etc. I recommend VBScript since it is so easy. – jonathanpeppers Aug 16 '11 at 20:50
  • Yes, I did try it from C#. At the moment I'm not sure if wee need to use it for existing C# applications, but I have a feeling we might have to. In the application I'm working with, we can use a direct reference, which is our intention. The trouble is the other apps. So there is absolutely no way to make it work? I was just trying from the application I'm working with because that was the easiest approach. I will try it from a C++ app tomorrow. Thanks – bigfoot Aug 16 '11 at 21:18
  • VBScript is really simple, just make a text file with .vbs file extension. You can call CreateObject() and just output to the console to make sure your COM object can be loaded and maybe call a function or two. See here: http://msdn.microsoft.com/en-us/library/dcw63t7z(v=vs.85).aspx – jonathanpeppers Aug 16 '11 at 21:38
  • @Jonathan.Peppers let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/2553/discussion-between-bigfoot-and-jonathan-peppers) – bigfoot Aug 16 '11 at 22:19
  • I'm in the discussion. Quite lonely. – jonathanpeppers Aug 17 '11 at 18:53
1

It seems to be by design to prevent managed COM object from being used from a managed client.

Here are some links to the same problem, as well as this thread, providing some solutions. Also take a look at the answer Jonathan Peppers provided to see how to start out if you only need to use it from unmanaged applications.

The only way I figure I could work around this, is a messy solution where I create the new code in C#, add a COM layer on top of that. Then create an additional COM layer as unmanaged C++ which accesses the first layer so, that my .NET applications can access it. {Unmanaged COM exposing the original interface} => {Managed COM with rewrite of the original logic of the COM}. All existing applications will then access the "{Unmanaged COM...}". If I get desperate enough, that might be a way. For now I'm abandoning this approach and looking for other solutions.

If you can, recompile your managed application to use the new assembly, do so. You can still use that managed COM from VB6, unmanaged C++, etc. If you still need to reference as a COM from managed, you might be able to create new instances using the approach specified in one of the referenced posts, as long as you don't need to create an interop wrapper.

Community
  • 1
  • 1
bigfoot
  • 455
  • 5
  • 12