I'm working on some code that is not mine, and it passes an argument in c++ to a (managed COM) c# method that doesn't have any parameters. The code works fine, but I don't know why.
Can someone explain what's going on or point me to the c++ constructs that make it possible?
Here's the code:
//---------- C++ ----------
#import "wrapper.tlb" named_guids raw_interfaces_only
BSTR b;
m_wrapper->getException(&b);
CW2A conv(b);
std::string s(conv);
if (! s.empty() ) {
//Perform exception processing
{
//---------- C# Managed COM ----------
public class wrapper : Iwrapper
{
private exceptionStr = String.Empty; // 'exceptionStr' set elsewhere in C# for an eventual pull by C++
public string getException()
{
return exceptionStr;
}
//... other C# methods that may set 'exceptionStr'
}
The COM standard interface does not allow return parametres, since any function returns an HRESULT. So what appear to be as a return value in the COM object in C# is marshalled as a reference in the C++ side.
Related
My project requires me to use C# to provide a user interface to C++. One of the C++ function I call does a bunch of work and provides periodic progress updates through another "object." Here's a example of what I mean.
C++
class AppDelegate : public ProgressDelegate
{
void AppDelegate::UpdateStatusText(const char* text)
{
// Go() will end up calling me at some point.
OutputDebugString(text);
}
void AppDelegate::ShowMessage(const char* text)
{
// Go() will end up calling me at some point.
OutputDebugString(text);
}
};
int CppWrapper::Go()
{
return cppInstance->Go()
}
CSharp
void UpdateStatusText(String text)
{
//update UI
}
void ShowMessage(String text)
{
//update UI
}
What I want to do is take updateStatusText and ShowMessage and pass the text over to C# to update my UI. My question is how do I expose the appropriate C# methods so that they can be called by my C++ code? Note that modifying Go is not an option for me.
Maybe this example can help you:
Write a Managed DLL
To create a simple managed DLL that has a public method to add two numbers and return the result, follow these steps:
Start Microsoft Visual Studio .NET or Microsoft Visual Studio 2005.
On the File menu, point to New, and then click Project. The New Project dialog box opens.
Under Project Types, click Visual C# Projects.
Note In Visual Studio 2005, click Visual C# under Project Types.
Under Templates, click Class Library.
In the Name text box, type sManagedDLL, and then click OK.
Open the Class1.cs file in Code view.
To declare a public interface that has a method to add two numbers, add the following code to the Class1.cs file:
// Interface declaration.
public interface ICalculator
{
int Add(int Number1, int Number2);
};
To implement this public interface in a class, add the following code to the Class1.cs file:
// Interface implementation.
public class ManagedClass:ICalculator
{
public int Add(int Number1,int Number2)
{
return Number1+Number2;
}
}
Register the Managed DLL for Use with COM or with Native C++
To use the managed DLL with COM or with native C++, you must register the assembly information of your DLL in the Windows registry. To do this, follow these steps:
Call the Managed DLL from Native C++ Code
// Import the type library.
#import "..\ManagedDLL\bin\Debug\ManagedDLL.tlb" raw_interfaces_only
Change the path of the type library if the path on your computer differs from this path.
To declare the namespace to use, add the following code to the CPPClient.cpp file:
using namespace ManagedDLL;
Complete Code Listing
//Managed DLL
// Class1.cs
// A simple managed DLL that contains a method to add two numbers.
using System;
namespace ManagedDLL
{
// Interface declaration.
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++ Client
// CPPClient.cpp: Defines the entry point for the console application.
// C++ client that calls a managed DLL.
#include "stdafx.h"
#include "tchar.h"
// Import the type library.
#import "..\ManagedDLL\bin\Debug\ManagedDLL.tlb" raw_interfaces_only
using namespace ManagedDLL;
int _tmain(int argc, _TCHAR* argv[])
{
// Initialize COM.
HRESULT hr = CoInitialize(NULL);
// Create the interface pointer.
ICalculatorPtr pICalc(__uuidof(ManagedClass));
long lResult = 0;
// Call the Add method.
pICalc->Add(5, 10, &lResult);
wprintf(L"The result is %d\n", lResult);
// Uninitialize COM.
CoUninitialize();
return 0;
}
Reference:
How to call a managed DLL from native Visual C++ code in Visual Studio.NET or in Visual Studio 2005
Alternatively, what I used to do (before I switched to using P/Invoke method to call from C# to C++) was to have 3 projects (as StraightLine has mentioned) but I'd have C#, Managed C++, and Native C++, and have the Managed C++ be my bridge/proxy to talk between the two (Native C++ and C#). It made it more easier to work on my Native C++ side that way. One caution is that some of the STL (mostly containers) are not supported by managed or sometimes, behaviors of std::string (Managed C++ version) would cause exceptions when used in Native C++ std::string, so do pay attention to which STL libraries are Managed C++ supported.
Also, as StraightLine has mentioned, the bridged code (in my case, Manaaged C++) would have to have a wrapper which will marshal from Managed to Native and vice versa (i.e. your "const char*" to System.String, if your char is 8-bits, etc)
You would have to build relevant C# code into an assembly, then reference that assembly inside C++ CLI project. Inside your C++ CLI wrapper, you will make calls to functions exposed through this C# assembly, also you will make calls into native C++ code.
The C++ CLI project can contain native C++ code, just make sure that the CLR switch is not enabled for native files. So, you would have 3 projects -
C# project for GUI which will invoke main. Also references the assembly used to expose C# functions to be called via C++ interop.
C# assembly that exposes certain managed functions (thus callable from C++ CLI)
C++ CLI project which will wrap and also contain native code.
I currently have a simple unmanaged C++ method which just displays a messageBox. I have created a managed c++ wrapper for this, which I then reference in my c# application.
I need to be able to pass strings and other variables from the c# into the unmanaged c++ and back again but I am having trouble with the conversion of the strings as my c++ knowledge is very basic.
My end goal is to be able to call complicated functions from our legacy unmanaged c++ libraries via my new c# app (not using DLLImport or COM) but I am trying to create a simple example before progressing.
Thanks in advance.
Rich
Assuming the unmanaged C++ method looks something like this:
namespace UnmanagedCpp
{
class MessageBox
{
public:
static void Show(LPCTSTR lpszMessage)
{
::MessageBoxW(NULL, lpszMessage, L"Message", 0);
}
};
}
You could wrap it in something like this:
using namespace System::Runtime::InteropServices;
namespace ManagedCpp
{
public ref class MessageBox
{
public:
static void Show(String^ message)
{
#if defined(UNICODE) || defined(_UNICODE)
IntPtr intPtr = Marshal::StringToHGlobalUni(message);
#else
IntPtr intPtr = Marshal::StringToHGlobalAnsi(message);
#endif
UnmanagedCpp::MessageBox::Show(static_cast<LPTSTR>(intPtr.ToPointer()));
Marshal::FreeHGlobal(intPtr);
}
};
}
If you're planning on doing more extensive interop between C# and C++, I recommend COM as it doesn't require you to write any wrappers (which get really messy in more complicated scenarios), although of course it requires you to modify the existing C++ code.
I have a unmanaged c++ application as COM client and a C# COM server.
now i want COM server can invoke a c++ function.
C# :
[ClassInterface(ClassInterfaceType.AutoDual)]
public class SomeType
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void DeleCallBack(string info);
public DeleCallBack CallBack;
public void SetCallBack(ref IntPtr ptr)
{
CallBack = (DeleCallBack)Marshal.GetDelegateForFunctionPointer(ptr, typeof(DeleCallBack));
}
}
C++:
HRESULT hr = E_FAIL;
CComPtr<WindowsFormsApplicationVC9::_SomeType> spTmp;
hr = spTmp.CoCreateInstance(__uuidof(WindowsFormsApplicationVC9::SomeType));
if (SUCCEEDED(hr))
{
spTmp->SetCallBack(OnCallBack);
}
void OnCallBack(BSTR info)
{
// c++ function call...;
}
I'm not sure it is the right way to just pass the OnCallBack function pointer to SetCallBack.
I noticed that some sample of calling GetDelegateForFunctionPointer should GetProcessAddress to get the function pointer address, but i can't do that since there are may be different c++ COM client with different function name.
any suggestion?
One way to do it is to expose your C++ functions via __declspec(dllexport), then simply write P/Invoke for those functions. Now you can use those P/Invoke functions as delegates.
Deep down this is essentially the GetProcAddress approach but the C# compiler makes it syntactically nicer for you.
Title explains. I have native C++ dlls that I'm writing C++/CLI wrappers for, which will in turn will be imported in C# as reference.
The problem is that in C# I don't see the classes I have in wrapper (imported from DLL).
What keywords should I use and HOW to re-declare my native C++ objects to become visible in C#?
Ok, tutorial. You have a C++ class NativeClass that you want to expose to C#.
class NativeClass {
public:
void Method();
};
1) Create a C++/CLI project. Link to your C++ library and headers.
2) Create a wrapper class that exposes the methods you want. Example:
#include "NativeClass.h"
public ref class NativeClassWrapper {
NativeClass* m_nativeClass;
public:
NativeClassWrapper() { m_nativeClass = new NativeClass(); }
~NativeClassWrapper() { this->!NativeClassWrapper(); }
!NativeClassWrapper() { delete m_nativeClass; }
void Method() {
m_nativeClass->Method();
}
};
3) Add a reference to your C++/CLI project in your C# project.
4) Use the wrapper type within a using statement:
using (var nativeObject = new NativeClassWrapper()) {
nativeObject.Method();
}
The using statement ensures Dispose() is called, which immediately runs the destructor and destroys the native object. You will otherwise have memory leaks and probably will die horribly (not you, the program). Note : The Dispose() method is magically created for you.
I have written a dll in C#, offering a class for use. The dll is called by a C program that I have written. (It’s a plugin to some program. I have to write the plugin’s code in C, but I want to use the functionality of .NET, therefore the dll).
In the dll, I want to open up a stream and do other stuff that should be persistent between two calls to the dll. That is represented in the following code by the private member Connector.
namespace myCSharpDll
{
// the c++ program calls this methods
public interface IAccess
{
double Initialize();
double Timestep(double time, double[] values);
...
}
// E is the beginning of another program my dll should connect to, therefore the names
public class EAccess : IAccess
{
// EConnector is another class I defined in the same dll
private EConnector Connector;
public double InitializeE()
{
Connector = new EPConnector();
}
public double Timestep(double time, double[] values)
{
return Connector.Connect();
}
When I make a call to InitializeE() and later one to Timestep() the Connector oject points to NULL.
What do I have to do that when I call Timestep() from my C code, that I can access the before created instance of Connector?
I probably search in the wrong direction at all. Any tips are appreciated.
If I am not wrong you want to maintain a single object throughout the use of dll in c. If that is the case try something similar to singleton pattern.
http://en.wikipedia.org/wiki/Singleton_pattern
What singleton emphazises is you create only single object for a class and use it to perform all the work you need. Basically you might need a function that does something like this,
public class EAccess : IAccess
{
private static EConnector Connector
public EConnector getConnector(){
if(Connector == null){
Connector = new EConnector();
}
return Connector;
}
public double Timestep(double time, double[] values)
{
return getConnector().Connect();
}
};
Even though this is not the traditional way of doing things using singleton but I think it still does the work. I may be wrong. Please correct me if I have misunderstood something.
Thanks SLaks for asking for my C/C++ code. That is where the problem was located. It was simpler than I thought. I found the mistake while putting together the code to show you.
I know that C and C++ is not the same, the plugin structure is just a little weird. Most of the code was generated by a wizard. I just had to fill in my code. It's a cpp file, but the code seems to be C. Well, I think that is off topic.
Here it is, I extraced the most important lines.
// the dll is connected via COM, using the type library file that regasm generated
#import "[path]\myCSharpDll.tlb" raw_interfaces_only
using namespace myCSharpDll;
static void OnActivate (IfmDocument, Widget);
//off topic: this are the weird lines the wizard put in my way
#ifdef __cplusplus
extern "C"
#endif /* __cplusplus */
// when the plugin is called by the host program, this function is called
static void OnActivate (IfmDocument pDoc, Widget button)
{
InitializeIntermediate(pDoc);
Timestep1(...);
}
static void InitializeIntermediate(IfmDocument pDoc)
{
// Initialize COM.
HRESULT hr = CoInitialize(NULL);
IEPAccessPtr pIEPAccess(__uuidof(EPAccess));
double result = -1;
pIEPAccess->InitializeEP (&result);
...
}
static void Timestep1(...)
{
IEPAccessPtr pIEPAccess(__uuidof(EPAccess));
double result = -1.1;
pIEPAccess->Timestep (...);
...
// now I get a wrong result back here, because this call leads to nowhere as
// the connector object in the dll is void
}
I realized that I am requesting a second instance with that line
IEPAccessPtr pIEPAccess(__uuidof(EPAccess));
So I changed that pointer to one single instance and everything is fine. Thanks for your comments!