# [.net] Using a class from an unmanaged C++ dll in C# ?

## Recommended Posts

Hi there! I´ve written a tiny graphics engine in unmanaged C++ using unmanaged DirectX. This engine is compiled into a dll. Now using that dll in another C++ project is not a problem, works like a charm. However I thought about getting acquainted with C# and started to code a simple little chess (without AI, just for 2 players ;)). Now for that chess I would like to use that graphics engine of mine to display it. After finding some info about calling C++ functions from C#, I happily started typing that wrapper-class. But during the first function definition I noticed that all those function were static, like this:
[DllImport("Engine.dll")]
public static extern int AddDynamicObject(StringBuilder mesh, StringBuilder effect, StringBuilder colorTex, float x, float y, float z);

Now, that is a problem, because I need to instantiate one object of the Engine class to work with it. So I would need to somehow instantiate that unmanaged C++ class in C# and call the functions of that object. I assume I would somehow have to declare a new class in C# that resembles the C++ class, but as the Engine class of course contains other objects of unmanaged classes I fear I would have to declare classes resembling those two, which would lead me to reconstruct the whole engine structure in C# in the end. To make things easier here is the header of the Engine class, which is used to control that tiny engine:
class __declspec(dllexport) Engine
{
public:
Engine(void);
~Engine(void);

int		Init(HWND inWindowHandle, bool fullScreen);
int		Update(LONGLONG time);
void		ShutDown();

int		AddStaticObject(LPCTSTR meshLoc, LPCTSTR effectLoc, LPCTSTR colorTex, float x, float y, float z);
bool		RemoveStaticObject(int inID);

int		AddDynamicObject(LPCTSTR meshLoc, LPCTSTR effectLoc, LPCTSTR colorTex, float x, float y, float z);
bool		RemoveDynamicObject(int inID);
Renderable*	GetDynamicObject(int inID);

private:

HWND			m_WindowHandle;
GFX::Graphics*		m_pGraphics;
};


I´m quite new to C# and would really appreciate your help with this.

##### Share on other sites
You'll need to write a glue class in C++/CLI that has a managed interface and makes the calls to unmanaged code. This article may come in handy.

##### Share on other sites
If you want to get acquainted with C# I recommend that you write a simple application from scratch, like a password generator or something (preferably with System.Windows.Forms) entirely in C#, instead of invoking a .dll written in C++.

I think you will find it hard to appreciate the language if you base your use of it on that. I mean it isn't the interoperability features that makes the language comfortable to work with (although they are very handy to have).

##### Share on other sites
You might want to take a look at SWIG as it will interpret your C++/C code and create a C# wrapper for it.

##### Share on other sites
I think I understand now how to build that managed wrapper class. Two questions remain:
1. Am I allowed to simply instantiate that managed class after that when I loaded "ManagedEngine.dll" for example? So I could do just something like
EngineWrapper myEngine = new EngineWrapper();

and use myEngine after that?

2. I have a problem that puzzles me writing this wrapper:
Both calling the functions "AddStaticObject" and "AddDynamicObject" of the Engine class (see header in original post) results in "unresolved token" and "unresolved external" errors, yet every other function works fine.
The code for the AddStaticObject function in the wrapper (m_pEngine is the pointer to the Engine object):
int EngineWrapper::AddStaticObject(LPCTSTR meshLoc, LPCTSTR effectLoc, LPCTSTR colorTex, float x, float y, float z){	return this->m_pEngine->AddStaticObject(meshLoc, effectLoc, colorTex, x,y,z);}

I´m using LPCTSTRs as input parameters for the wrapper function, too, because I´ve read about the StringBuilder thing in C#, which should allow me to convert a System::String to LPCTSTR without problems, and all I´ve found about converting a System::String^ to a LPCTSTR manually looked rather confusing to me.

##### Share on other sites
@Rob Loach:
One problem I have with that though is the following:
the SWIG wrapper created a SWIGTYPE_p_HWND. The Init() function of my engine expects a HWND to be passed in there, which is the HWND I use to initialize D3D and so on. The SWIG wrapper Init() function expects a SWIGTYPE_p_HWND to be passed. So I just did the following:
// this is in my C# project and uses the SWIG wrapperEngine GFX = new Engine();// 'this' is a Form, from which I get the handle // and build a SWIGTYPE_p_HWND with it. // The second parameter for the Init() function is fullscreen yes/noGFX.Init(new SWIGTYPE_p_HWND(this.Handle, true), false);

As far as I understand that whole stuff, this should work, yet my engine throws an error which indicates that the HWND it received isn´t usable, hence device creation fails.

@my last post about the functions resulting in unresolved tokens and unresolved externals:

I noticed that the 2 functions causing those errors are the only ones using LPCTSTR as parameters. So perhaps this is the reason for my problems with these functions, though I don´t know what to do about it... changing the unmanaged engine to something differen just to make the wrapper work seems overkill for me.
Taking those 2 functions out, everything compiles fine (okay, it renders the engine practically useless). Now I would be ready to use that thing. However I always get the same error, namely that "ManagedEngine.dll" could not be found. This error appears no matter where I copy that dll (at least I tried every location I deemed worthy to try out).

To make things easier, again some source:

ManagedEngine.h:
#include "Engine.h"using namespace System;namespace ManagedEngine {	public ref class EngineWrapper	{	public:						static void Create();		static void Delete();					static int	Init(System::IntPtr window, bool bFullscreen);		static int	Update(System::Int64 time);		static void	ShutDown();	private:		static		PUKE::Engine*	m_pEngine;	};}

Together with the according ManagedEngine.cpp this compiles fine into ManagedEngine.dll .
After that I created a small C# project in VS2005 called EngineWrapper.
Here is the complete source contained in there:
using System.Runtime.InteropServices;namespace Chess{    public class EngineWrapper    {        [DllImport("ManagedEngine.dll")]        public static extern void Create();        [DllImport("ManagedEngine.dll")]        public static extern void Delete();        [DllImport("ManagedEngine.dll")]        public static extern int Init(System.IntPtr window, bool bFullScreen);        [DllImport("ManagedEngine.dll")]        public static extern int Update(System.Int64 time);        [DllImport("ManagedEngine.dll")]        public static extern void ShutDown();    }}

This is compiled into EngineWrapper.dll.
EngineWrapper.dll is then referenced in my Chess project and the functions are used.
But whenever I call one of these functions I get the error telling me ManagedEngine.dll could not be found. I tried referencing that DLL from the EngineWrapper project, yet no success.
I also tried putting the EngineWrapper.cs into the Chess project directly, same error, of course...

I guess that could be easily fixed if I would know where I should put that ManagedEngine.dll.

Thx for helping!

##### Share on other sites
Quote:
 Original post by matches81I guess that could be easily fixed if I would know where I should put that ManagedEngine.dll.
It should be put in your application directory of your game so that the P/Invoke can find it.

##### Share on other sites
Hi there again!
The application directory being the folder in which the .exe is located, right?
In fact I have copied the ManagedEngine.dll in _every_ folder that solution has (from the folder of the .sln down the tree in every folder)

I got rid of the EngineWrapper.dll and instead tried using ManagedEngine.dll directly. So I put a reference to ManagedEngine.dll into the project and I am "allowed" to do the calls to the functions provided in there, i.e. I am able to compile that stuff.
The header of ManagedEngine hasn´t changed, so I won´t post that again. I referenced the .dll in the /bin/debug folder.
Now this is the code I put into my main form for the event that it is being shown:
private void MainForm_Shown(object sender, EventArgs e){    ManagedEngine.EngineWrapper.Create();    ManagedEngine.EngineWrapper.Init(this.GameViewPanel.Handle, true);}

The corresponding calls to ShutDown() and Delete() are in the FormClosing event function.
Now when I run that I get a System.IO.FileNotFoundException in System.Windows.Forms.dll without any specification which file is not found, which leaves me even more clueless. The exception gets thrown in the line
Application.Run(Form);

where Form is an instance of MainForm.

Any ideas appreciated, I´ve got no more :(

##### Share on other sites
If you're not getting a proper error trace, it probably means the error is in the unmanaged part of the code. Does your ManagedEngine.dll require any other files (config? other DLLs?) to be present that you've forgotten to copy?

##### Share on other sites
Ouch... stupid me... copying the dll of the unmanaged engine into the right folder did the trick.
Thank you very much for the tip.

One problem remains though:
The function
Header:static int	Init(System::IntPtr window, bool bFullscreen);Implementation:int EngineWrapper::Init(System::IntPtr window, bool bFullscreen){	return m_pEngine->Init((HWND)window.ToPointer(), bFullscreen);}

of ManagedEngine wraps the function
int        Init(HWND handle, bool bFullscreen);

of the unmanaged engine. This function is supposed to initialise D3D with the given HWND and works fine as long as I call it from an unmanaged application passing a HWND in there myself.
However calling the wrapper function results in a failure to initialise D3D, which indicates that the HWND passed in from the wrapper isn´t valid.
I call the function like this:
ManagedEngine.EngineWrapper.Init(this.GameViewPanel.Handle, true);

GameViewPanel, as the name indicates, is a panel on the main form of the game.
Do I have to do anything with that Handle in order to get a valid HWND?

##### Share on other sites
You might find the HandleRef useful for that.

##### Share on other sites
However that error was caused by another stupidity of me:
Should have set bFullscreen to false of course. I can only assume that I can´t initialise a fullscreen device with a panel that is a child form or something like that.
Everything works now :)

## Create an account

Register a new account

• ### Forum Statistics

• Total Topics
628368
• Total Posts
2982293

• 10
• 9
• 13
• 24
• 11