We help IT Professionals succeed at work.

dynamically link c# dll (COM) into a c++ rpogram

gp02874
gp02874 asked
on
Medium Priority
1,170 Views
Last Modified: 2012-05-06
I have a c++ program, in which I am trying to load a c# dll.   I have got as far as being able to statically load it, by compiling with the "Register for COM Interop" setting, and generating it .tlb file.

From what I could see from other examples, it looks like normally, I should use LoadLibrary and  GetProcAddress to get the function address... sometihng like:

m_hHandle = ::LoadLibrary(m_strName);
P=GetProcAddress(m_hHandle,_T("ICalculator"));
(see below for c# code)

but I cannot seem to get a valid address, no matter how I play with it....
Am I on the right track at all?  I'm trying to get this to work like a plugin...
Thanks.

//------- C# dll being call
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
 
namespace WoodEngineLink
{
	public class Class1
	{
		public interface ICalculator
		{
			int Add(int Number1, int Number2);
		};
		// Interface implementation.
		public class ManagedClass : ICalculator
		{
			public int Add(int Number1, int Number2)
			{
				return Number1 + Number2;
			}
		}
	}
}
 
//------------  c++ code

Open in new window

Comment
Watch Question

Todd GerbertSenior Engineer
CERTIFIED EXPERT
Top Expert 2010

Commented:
I don't think you can LoadLibrary() a .Net library from C++ code (although the opposite direction is possible) - since your C# .dll is actually intermediate code that's compiled at run-time by the Common Language Runtime.
jkr
CERTIFIED EXPERT
Top Expert 2012

Commented:
You cannot do that this way at all - If you want to call managed code from unmanaged code, you should consider COM Interop. For your particular setup, see http://msdn.microsoft.com/en-us/library/aa645738(VS.71).aspx ("COM Interop Part 2: C# Server Tutorial")

Author

Commented:
let me clarify...
My understanding is I am creating a COM Interop of the c# dll, through the visual studio function.. which works similar to using regasm.exe.
I am able to get a (hopefully) valid address through the LoadLibrary() function.
jkr
CERTIFIED EXPERT
Top Expert 2012

Commented:
You cannot get a valid address because there is no such thing as an address in managed code, sorry. That only works for unmanaged binaries.

Author

Commented:
again to clarify.. I am able to sucessfully see access my c# functions (through  the COM created).  My problem is that I need to do it by dynamical loading, and not static loading.

Author

Commented:
so is the answer .. I can only load it staticly, I cannot load it dynamically?
jkr
CERTIFIED EXPERT
Top Expert 2012

Commented:
You can dynamically instantiate an object using 'CoCreateInstance()', but not via 'LoadLibrary()' as in the example above:
// COMClient.cpp
// Build with "cl COMClient.cpp"
// arguments: friend
 
#include <windows.h>
#include <stdio.h>
 
#pragma warning (disable: 4278)
 
// To use managed-code servers like the C# server, 
// we have to import the common language runtime:
#import <mscorlib.tlb> raw_interfaces_only
 
// For simplicity, we ignore the server namespace and use named guids:
#if defined (USINGPROJECTSYSTEM)
#import "..\RegisterCSharpServerAndExportTLB\CSharpServer.tlb" no_namespace named_guids
#else  // Compiling from the command line, all files in the same directory
#import "CSharpServer.tlb" no_namespace named_guids
#endif
int main(int argc, char* argv[])
{
   IManagedInterface *cpi = NULL;
   int retval = 1;
 
   // Initialize COM and create an instance of the InterfaceImplementation class:
   CoInitialize(NULL);
   HRESULT hr = CoCreateInstance(CLSID_InterfaceImplementation,
               NULL, CLSCTX_INPROC_SERVER,
               IID_IManagedInterface, reinterpret_cast<void**>(&cpi));
 
   if (FAILED(hr))
   {
      printf("Couldn't create the instance!... 0x%x\n", hr);
   }
   else
   {
      if (argc > 1)
      {
         printf("Calling function.\n");
         fflush(stdout);
         // The variable cpi now holds an interface pointer 
         // to the managed interface.
         // If you are on an OS that uses ASCII characters at the 
         // command prompt, notice that the ASCII characters are 
         // automatically marshaled to Unicode for the C# code.
         if (cpi->PrintHi(argv[1]) == 33)
            retval = 0;
         printf("Returned from function.\n");
      }
      else
         printf ("Usage:  COMClient <name>\n");
      cpi->Release();
      cpi = NULL;
   }
 
   // Be a good citizen and clean up COM:
   CoUninitialize();
   return retval;
}

Open in new window

jkr
CERTIFIED EXPERT
Top Expert 2012

Commented:
Well, to sum it up - 'LoadLibrary()' won't work at all in this context, but using 'CoCreateInstance()' should do the job for you.

Author

Commented:
but isn't this statically loading it as well?  If the file CSharpServer.tlb above is not present, than the code will not compile? no?
Remember, I'm trying to set this up like a Plug-in, so the file might be there or not (or even chosen through a dialog box).
jkr
CERTIFIED EXPERT
Top Expert 2012

Commented:
No, that isn't static at all. And compiling is different from running. Such as you need the .tlb file for compiling here, you'd need a valid function prototype to use with 'LoadLibrary()'/'GetProcAddress()', there is not much of a difference here.

Author

Commented:
sorry for being dense...

My vision of what I'm trying to to get to work is like the following:
   ...I have two different c# dll(converted to COM)... Eval-methodA.dll and Eval-methodB.dll
   ... The user of the main c++ program will be able to choose the method (dll) to use on start up..
   ... At some point in the future, there will be a Eval-methodC.dll, which I would like to not have to recompile the c++ program.
(Please don't figure out why I'm doing this, or if there's a better way, just take it on faith).

So the "Load Library" method would work for this, in so far as I don't need to know the name of the dll (or tlb) before I compile, and if I don't know the name.
With the "CoCreateInstance" example above, it might not load it untill I need it, but it looks like I have to have it available when I compile.

Hopefully this make sense.
I'm thinking I might have to have some sort of plain function  "wrapper" around my class, or something...
I'm also thinking..eh.. it just might not be possible to do what I want..


jkr
CERTIFIED EXPERT
Top Expert 2012

Commented:
Well, in this case you'd distinguish between these two DLLs not by using their names, but via their respective CLSIDs, e.g.
   HRESULT hr;
 
   if(bUseEvalMethodA)
           hr = CoCreateInstance(CLSID_EvalMethodA,
               NULL, CLSCTX_INPROC_SERVER,
               IID_IManagedInterface, reinterpret_cast<void**>(&cpi));
    else
           hr = CoCreateInstance(CLSID_EvalMethodB,
               NULL, CLSCTX_INPROC_SERVER,
               IID_IManagedInterface, reinterpret_cast<void**>(&cpi));

Open in new window

Author

Commented:
but I need to know these when I compile, both at the CoCreateInstance() and in the #Import statements.  I need to compile again when I have a method "C" .. (or method "ZZ"), right?

I'm not so much worred about dynamically creating an instance of the object class, than if the COM is available both at compile and run time.
CERTIFIED EXPERT
Top Expert 2012
Commented:
Such as you would need to know the names of the DLLs, yes. But, you could store the CLSIDs of available managed COM servers in the registry, for example...

Not the solution you were looking for? Getting a personalized solution is easy.

Ask the Experts
Todd GerbertSenior Engineer
CERTIFIED EXPERT
Top Expert 2010

Commented:
Not sure if this would work for you (or at all, for that matter)...is a mixed managed/unmanaged C++ app possible?  If so you might be able to make use of System::Reflection::Assembly::LoadFrom to get a managed pointer to your C# assembly.

Or perhaps a mixed C++ DLL as a wrapper...

Author

Commented:
tgerbert.. thats is more along the lines of what I'm after.  Unfortunately, the C++ is unmanaged, and to messy to convert.  I think Reflection will only work with managed...
I was hoping that when I converted my c# dll to a COM object, I would be going from unmanaged to unmanaged, and it would be as bad.
FACADE PATTERN

I think what you need to do it this:

 Create your COM Interop C# DLL *AND* Create a C++ DLL that calls it.
The C++ DLL does a CoCreateInstance on The C# DLL and builds a FACADE to the functions inside it.
The C++ DLL hides the registry and .tld info as that is compiled into the C# DLL

Then You can scan the folder for the C++ DLL's the C# Implementation is hidden

The Drawback is you would now have to DLL's and have to do some work to expose the functions. But since the functions are minimal (at least now) I think is a worthwhile trade off.

The alternative is .. it can't be done.

wc

Todd GerbertSenior Engineer
CERTIFIED EXPERT
Top Expert 2010
Commented:
Granted, my knowledge of C++ is fuzzy at best, however it's my understanding that it is possible to compile an assembly that contains both .Net managed code and native unmanaged code.  Therefore, it *might* be possible to add such a mixed C++ .dll to your project, whose sole function is to act as proxy between your native C++ application and the managed C# assemblies; exposing itself to the C++ code using __declspec(dllexport) and to managed code using the System::Reflection namespace.

Just a thought...
Access more of Experts Exchange with a free account
Thanks for using Experts Exchange.

Create a free account to continue.

Limited access with a free account allows you to:

  • View three pieces of content (articles, solutions, posts, and videos)
  • Ask the experts questions (counted toward content limit)
  • Customize your dashboard and profile

*This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

OR

Please enter a first name

Please enter a last name

8+ characters (letters, numbers, and a symbol)

By clicking, you agree to the Terms of Use and Privacy Policy.