[Last Call] Learn how to a build a cloud-first strategyRegister Now

x
?
Solved

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

Posted on 2009-02-13
18
Medium Priority
?
1,148 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

0
Comment
Question by:gp02874
  • 7
  • 7
  • 3
  • +1
18 Comments
 
LVL 33

Expert Comment

by:Todd Gerbert
ID: 23636010
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.
0
 
LVL 86

Expert Comment

by:jkr
ID: 23636306
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")
0
 

Author Comment

by:gp02874
ID: 23636399
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.
0
Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 86

Expert Comment

by:jkr
ID: 23636438
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.
0
 

Author Comment

by:gp02874
ID: 23636442
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.
0
 

Author Comment

by:gp02874
ID: 23636474
so is the answer .. I can only load it staticly, I cannot load it dynamically?
0
 
LVL 86

Expert Comment

by:jkr
ID: 23636481
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

0
 
LVL 86

Expert Comment

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

Author Comment

by:gp02874
ID: 23636719
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).
0
 
LVL 86

Expert Comment

by:jkr
ID: 23636745
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.
0
 

Author Comment

by:gp02874
ID: 23637185
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..


0
 
LVL 86

Expert Comment

by:jkr
ID: 23637218
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

0
 

Author Comment

by:gp02874
ID: 23637569
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.
0
 
LVL 86

Accepted Solution

by:
jkr earned 750 total points
ID: 23637598
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...
0
 
LVL 33

Expert Comment

by:Todd Gerbert
ID: 23637612
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...
0
 

Author Comment

by:gp02874
ID: 23637804
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.
0
 
LVL 12

Assisted Solution

by:williamcampbell
williamcampbell earned 300 total points
ID: 23637870
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

0
 
LVL 33

Assisted Solution

by:Todd Gerbert
Todd Gerbert earned 450 total points
ID: 23638601
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...
0

Featured Post

Free Tool: Path Explorer

An intuitive utility to help find the CSS path to UI elements on a webpage. These paths are used frequently in a variety of front-end development and QA automation tasks.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Go is an acronym of golang, is a programming language developed Google in 2007. Go is a new language that is mostly in the C family, with significant input from Pascal/Modula/Oberon family. Hence Go arisen as low-level language with fast compilation…
Article by: evilrix
Looking for a way to avoid searching through large data sets for data that doesn't exist? A Bloom Filter might be what you need. This data structure is a probabilistic filter that allows you to avoid unnecessary searches when you know the data defin…
The goal of the tutorial is to teach the user how to use functions in C++. The video will cover how to define functions, how to call functions and how to create functions prototypes. Microsoft Visual C++ 2010 Express will be used as a text editor an…
The viewer will learn how to use the return statement in functions in C++. The video will also teach the user how to pass data to a function and have the function return data back for further processing.
Suggested Courses

825 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question