1

I have a library that is a part of a larger application. Our automated testing and regression suite is a Ruby project. I already know how to access COM visible DLLs in Ruby. What I would like to do however is to generate both, a COM visible binary and a non COM visible binary during the Visual Studio build process. It doesn't really matter what is made visible in the COM visible DLL as its for purely internal use. All of it could, and probably would be best if it was. I see I can set a value in the AssemblyInfo.cs to make everything visible however, like I said, I would need one of each.

Is there a way to do this without manually writing a wrapper around the C# project's functions?

Rig
  • 1,276
  • 3
  • 22
  • 43
  • `"What I would like to do however is to generate both, a COM visible binary and a non COM visible binary during the Visual Studio build process"` -- to clarify, you want to use the _same code_ to generate one COM-visible, and one non-COM-visible DLL? – transistor1 Feb 06 '15 at 22:18
  • Yes, that is correct. If the setting in AssemblyInfo.cs does what I think it does would it not create a com visible dll? I just want both versions – Rig Feb 06 '15 at 22:34
  • There's just no point to it, a [ComVisible] assembly or class just has an attribute, that's all. It doesn't come to life until you register it and the COM interop built-in to the CLR starts doing its job. That code is *not* in your assembly, it is located in mscoree.dll. Using a [ComVisible] assembly as a normal .NET assembly makes it behave like any other, you can't tell the difference. – Hans Passant Feb 06 '15 at 22:44
  • 1
    @Hans the only reason I could think of for doing this would be if a developer wanted to distribute one DLL that is not COM-registerable. – transistor1 Feb 06 '15 at 22:54
  • @transistor1 That is exactly why I want to do this. It is not a requirement for this to be COM visible for distribution however it is very useful within our test framework to share this library while testing other parts of our application stack. I don't have to "ask" if its to do this for the test case but I might if it went to production. – Rig Feb 09 '15 at 14:40

2 Answers2

2

Here is one option -- add a second project to your solution.

The first project would be an implementation of the non-COM visible DLL.

The second project would reference the first, and contains a class which is derived from your first class (which implements the COM methods you want to expose). Since the first class already implements all of your COM-exposed interface elements, you just have to declare the second class as implementing the first one, like so:

namespace ClassLibrary2
{
    [ComVisible(true)]
    [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
    [Guid("BBBBBBBB-BBBB-BBBB-BBBB-BBBBBBBBBBBB")]
    public interface MyCOMInterface
    {
        void MyMethod();
    }

    [ComVisible(true)]
    [Guid("AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA")]
    [ClassInterface(ClassInterfaceType.None)]
    public class Class2 : ClassLibrary1.Class1, MyCOMInterface
    {
        //No implementation is needed here because we're
        //just exposing Class1's methods to COM.
    }
}

By adding a second project, your solution can generate both DLLs in one build.

Just as an aside: you can reference a COM exposed .NET DLL, just as you would any other. That is to say, other .NET projects don't care whether a DLL's methods are exposed to COM-- they can still use all of the DLL's public methods. Using the same DLL, you can export some methods as COM, and have some of them relegated to .NET use only.

I mention this, because you didn't specify the reason behind this architecture.

transistor1
  • 2,915
  • 26
  • 42
  • So, this doesn't have a Main as its not runnable, Its simply a utility library that would also be nice to utilize in some facets of our testing. Would that mean I would then have to implement an interface for each implemented class I needed? – Rig Feb 06 '15 at 22:37
  • I know, that's why I used the word "main" in quotes. When I say your "main" class, I meant the one that implements your COM interface methods. – transistor1 Feb 06 '15 at 22:38
  • @Rig changed the wording, so hopefully it makes more sense to you now. – transistor1 Feb 06 '15 at 22:40
  • Thank you for clarifying. I'll see if I have time to try this out today. – Rig Feb 09 '15 at 14:42
  • @Rig-- from your initial post, it sounds like you are exposing all methods and properties in your class as COM-visible, using the assembly-wide attribute. Keep in mind that this is a different approach, which exposes specific methods rather than the entire assembly. These exposed methods/properties need to be declared in the interface. You can see this [link](http://stackoverflow.com/a/13446863/864414) for an example. – transistor1 Feb 09 '15 at 14:51
  • Yeah, I don't have to expose all of them technically since it is only certain functionality I need to expose however there is a lot of that to itemize so I was originally going to use the sledgehammer approach and just open it all up. Either way, making this work is a much better option than duplicating the functionality in Ruby. – Rig Feb 09 '15 at 14:59
  • On this, is there a way of avoiding requiring I register the DLL? One of my goals was to eliminate having to run an installer on any boxes. I wanted to just include the DLLs in my ruby project and call into them. – Rig Feb 28 '15 at 20:03
  • I don't know Ruby, but it may be possible. There are two approaches to avoid registering the DLL: (1) use [reg-free COM activation](https://msdn.microsoft.com/en-us/library/ms973913.aspx). I've never done this, but I think that the way to do this is by creating a manifest (XML) file that is stored in the same folder as the EXE that actually hosts the COM object. In your case, that might be the actual ruby.exe; I'm not sure. – transistor1 Mar 01 '15 at 16:12
  • 1
    ...Option (2): if you can p/invoke in Ruby, it's probably possible to host the .NET runtime directly in Ruby, thereby allowing you to call .NET assemblies from Ruby. [This](http://stackoverflow.com/a/13333819/864414) is a VBA example of hosting the 2.0 runtime (`CorRuntimeHost` does not work with 4.0). There is a way to host 4.0+ runtime also, but since it's not possible in VBA, I have not done it. [This](https://code.msdn.microsoft.com/windowsdesktop/CppHostCLR-e6581ee0) is an example in C++. [Here](https://msdn.microsoft.com/en-us/library/ms164317(v=vs.110).aspx) is some MS docs on it. – transistor1 Mar 01 '15 at 16:16
  • Thanks for the reply. I'll have to try this out. – Rig Mar 02 '15 at 01:22
2

You can create additional configuration to your project. For this configuration define some definition (e.g. COM_VISIBLE). This can be defined in Project Properties->Build->Conditional compilation symbols

Then, in AssemblyInfo.cs you may use #if directive:

#if COM_VISIBLE
    [assembly:ComVisible(true)]
#endif 
Denis Itskovich
  • 4,383
  • 3
  • 32
  • 53
  • On this, is there a way of avoiding requiring I register the DLL? One of my goals was to eliminate having to run an installer on any boxes. I wanted to just include the DLLs in my ruby project and call into them. – Rig Feb 28 '15 at 20:04