I'm developing an app for C#, and I want to use DirectX (mostly Direct2D) for the graphical component of it. So I'm trying use use C++/CLI as an intermediary layer between the native C++ code and the managed code of C#. So far a have 3 projects in my solution: A C# project (which I won't really discuss since it's not giving me any problems yet), a C++ static library that includes Windows.h, and a dynamic C++/CLI library that's intended to marshal information between the other two projects. Here is my code so far:
In the native C++ project, I have a class named RenderWindowImpl that so for only contains 2 methods:
//RenderWindowImpl.h
#pragma once
#include <Windows.h>
class RenderWindowImpl final
{
public:
RenderWindowImpl() = default;
~RenderWindowImpl() = default;
int test();
private:
static void InitializeWin32Class();
};
// RenderWindowImpl.cpp
#include "RenderWindowImpl.h"
int RenderWindowImpl::test()
{
return 5;
}
void RenderWindowImpl::InitializeWin32Class()
{
WNDCLASSEXW wc = { 0 };
wc.cbSize = sizeof(WNDCLASSEXW);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = nullptr;
wc.hInstance = GetModuleHandleW(0);
wc.hCursor = LoadCursorW(nullptr, IDC_ARROW);
//wc.lpszClassName = L"wz.1RenderWindowImpl";
//// TODO: error check
//RegisterClassExW(&wc);
}
And in my C++/CLI project, I have a class named RenderWindow that acts as a wrapper around RenderWindowImpl:
// wzRenderWindow.h
#pragma once
//#pragma managed(push, off)
#include "RenderWindowImpl.h"
//#pragma managed(pop)
using namespace System;
namespace wzRenderWindow {
public ref class RenderWindow sealed
{
public:
RenderWindow();
~RenderWindow();
int test();
private:
RenderWindowImpl* impl;
};
}
// wzRenderWindow.h.
#include "stdafx.h"
#include "wzRenderWindow.h"
wzRenderWindow::RenderWindow::RenderWindow()
{
// Initialize unmanaged resource
impl = new RenderWindowImpl();
try
{
// Any factory logic can go here
}
catch (...)
{
// Catch any exception and avoid memory leak
delete impl;
throw;
}
}
wzRenderWindow::RenderWindow::~RenderWindow()
{
// Delete unmanaged resource
delete impl;
}
int wzRenderWindow::RenderWindow::test()
{
return impl->test();
}
When I compile my project, I get the following warnings and errors:
Error LNK1120 1 unresolved externals wzRenderWindow d:\documents\visual studio 2015\Projects\WizEngCS\Debug\wzRenderWindow.dll 1
Warning LNK4075 ignoring '/EDITANDCONTINUE' due to '/OPT:LBR' specification wzRenderWindow d:\documents\visual studio 2015\Projects\WizEngCS\wzRenderWindow\wzRenderWindowImpl.lib(RenderWindowImpl.obj) 1
Error LNK2019 unresolved external symbol __imp__LoadCursorW#8 referenced in function "private: static void __cdecl RenderWindowImpl::InitializeWin32Class(void)" (?InitializeWin32Class#RenderWindowImpl##CAXXZ) wzRenderWindow d:\documents\visual studio 2015\Projects\WizEngCS\wzRenderWindow\wzRenderWindowImpl.lib(RenderWindowImpl.obj) 1
It seems to be the call to LoadCursorW that C++/CLI doesn't like, as the code compiles fine if I comment out that line. With the Win32 function calls removed, I was able to successfully call RenderWindow::test() from a C# application, outputting the expected result of 5.
I'm a bit of a loss because my understanding of C++/CLI is that it's very good at wrapping native C++ classes for consumption by managed .NET applications. I would really like to understand why my code is not compiling.
As a related follow-up question, am I barking up the wrong tree here? What's the conventional way to access DirectX methods (or similar COM-based C/C++ libraries) from .NET? I'd like to avoid using 3rd-party wrapper libraries like SharpDX.
I fixed the problem by putting #pragma comment(lib, "User32.lib") at the top of my RenderWindowImpl.cpp. Thanks to #andlabs for the fix. I'm not sure why this fixed the problem (I've never needed to explicitly link to user32.lib in any of my previous projects).
Related
SOLVED: Thanks to Casey Price for their answer. I then ran into 2 other errors: BadImageFormatException and FileNotFoundException, the former was solved by matching the platform target (x64 or x86) for each project and the latter was solved by setting the output directory of the C# project to the directory containing the dll file.
I'm working on a game 'engine' which currently has a working graphics subsystem that draws/textures movable models. I'm trying to write a C++/CLR wrapper so I can use the engine in a C# program (as a designer tool).
My wrapper project is a C++/CLR class library and contains the following 2 files (as well as resource.h/cpp and Stdafx.h/cpp)
// pEngineWrapper.h
#pragma once
#define null NULL
#include "..\pEngine\pEntry.h"
using namespace System;
namespace pEngineWrapper
{
public ref class EngineWrapper
{
public:
EngineWrapper();
~EngineWrapper();
bool Initialise();
private:
pEntry* engine;
};
}
and the .cpp file
// This is the main DLL file.
#include "stdafx.h"
#include "pEngineWrapper.h"
pEngineWrapper::EngineWrapper::EngineWrapper()
{
engine = null;
}
pEngineWrapper::EngineWrapper::~EngineWrapper()
{
delete engine;
engine = null;
}
bool pEngineWrapper::EngineWrapper::Initialise()
{
bool result;
engine = new pEntry;
result = engine->Initialise();
if( result == false )
{
return false;
}
return true;
}
When I go to build this project however I get 14 errors: LNK2028, LNK2019, and LNK2001 which points to some classes within the engine. I have included the errors in the file below.
https://www.dropbox.com/s/ewhaas8d1te7bh3/error.txt?dl=0
I also get a lot of warnings regarding XMFLOAT/XMMATRIX which you may notice.
In all of the engine classes I use the dllexport attribute
class __declspec(dllexport) pEntry
I feel like I'm missing the point and doing it all wrong seeing all of these errors but I haven't found any documents telling me anything considerably different than what I'm doing here
you have to add a reference to the static .lib files for the game engine you are using as well as it's dll's or load the dll's manually
to add the references to the .lib files right click your project->Properties->Linker->input add the lib file to the additional dependencies
See also: DLL References in Visual C++
I have a C++ Windows application developped with RAD Studio (C++Builder) XE4. It has some plugins, which are DLLs (always written with RAD Studio) that are dynamically loaded with this technique.
Now in one of this plugins I need reflection capabilities. While it seems I cannot achieve them with C++ (reflection is needed on a third-party COM DLL that I cannot modify) I decided to rewrite this plugin in C# (which has powerful reflection capabilities), thus creating a .NET assembly.
I know I should expose the assembly via COM, but I can't (we don't want to change the way the main application loads all DLLs).
My aim is to dynamically load the .NET assembly and invoke its functions (for instance here we call SetParam function) with something like the following, like I do with the other plugins.
//load DLL
HINSTANCE handleDll = LoadLibraryW("C:/Path/to/the/assembly.dll");
//get reference to the function
void* ptr = GetProcAddress(handleDll, "_SetParam");
ptr_SetParam ptrFunc = reinterpret_cast<ptr_SetParam>(ptr);
//invoke function
int result = (*ptrFunc)(String("mykey").c_str(), String("myvalue").c_str());
where ptr_SetParam is defined as
typedef int(*ptr_SetParam)(const wchar_t*, const wchar_t*);
Is there a way?
Thanks to #HansPassant's comment I found a way.
I created following Visual Studio projects.
MyDllCore .NET assembly project, written in C# or any other .NET language. Here I have my managed class like the following, where the real logic of the assembly is implemented.
using System;
using System.Collections.Generic;
//more usings...
namespace MyNamespace
{
public class HostDllB1
{
private Dictionary<string, string> Parameters = new Dictionary<string, string>();
public HostDllB1()
{
}
public int SetParam(string name, string value)
{
Parameters[name] = value;
return 1;
}
}
}
MyDllBridge DLL project, written in C++/CLI, with /clr compiler option. It is just a "bridge" project, it has a dependancy on MyDllCore project and has just one .cpp o .h file like the following, where I map the methods from the program that loads the DLL to the methods in the .NET assembly.
using namespace std;
using namespace System;
using namespace MyNamespace;
//more namespaces...
#pragma once
#define __dll__
#include <string.h>
#include <wchar.h>
#include "vcclr.h"
//more includes...
//References to the managed objects (mainly written in C#)
ref class ManagedGlobals
{
public:
static MyManagedClass^ m = gcnew MyManagedClass;
};
int SetParam(const wchar_t* name, const wchar_t* value)
{
return ManagedGlobals::m->SetParam(gcnew String(name), gcnew String(value));
}
Finally I have a C++Builder program that loads MyDllBridge.dll and uses its methods calling them like in the following.
//load DLL
HINSTANCE handleDll = LoadLibraryW("C:/Path/to/the/MyDllBridge.dll");
//get reference to the function
void* ptr = GetProcAddress(handleDll, "SetParam");
ptr_SetParam ptrFunc = reinterpret_cast<ptr_SetParam>(ptr);
//invoke function
int result = (*ptrFunc)(String("mykey").c_str(), String("myvalue").c_str());
After a long reading time I didn't get the dll working...
I tried so much different ways but no way worked..
I did the following things: (IDE: VS2013Ultimate)
I added a clean c++ project. There I added 1 header file [header.h]:
#pragma once
class myClass{
public:
myClass(double varx, double vary);
double sumxy();
private:
double x;
double y;
};
I added a body.cpp file:
#pragma once
#include "header.h"
myClass::myClass(double varx, double vary){
x = varx;
y = vary;
}
double myClass::sumxy(){
return x + y;
}
That's all the code I would need. I only want a working example code.
I added one class [main.cpp]:
#include "header.h"
#include "body.cpp"
extern "C" __declspec(dllexport) double sumxy(double var_x, double var_y){
myClass MC(var_x, var_y);
return MC.sumxy();
}
After this I compiled this dll and I got it without any compile errors. `I copied it to the debug folder of the c# Console Application
C# Console Application:
using System.Runtime.InteropServices;
//all using directories
namespace Klassen_Tester {
class Program {
[DllImport("CppClassDll.dll")]
public static extern double sumxy(double var_x, double var_y);
static void Main(string[] args) {
Console.WriteLine(sumxy(3, 5).ToString());
Console.ReadLine();
}
}
}
Please help me. Don't know what to do.. And sorry for my bad english.
Edit: There is an error: System.DllNotFoundException in Klassen_Tester.exe. DLL "CppClassDll.dll" could not be found. HRESULT: 0x8007007E) could not be load.
You might consider using an interop between C++ and C# so you don't have to mess with pinvoke. I'm not saying pinvoke is bad but interops are nice.
You are going to want to write a CLR wrapper to act as an intermediary between the managed and unmanaged code. It looks like you have the C# and C++ mostly written so I'll describe the CLR portion.
Create a CLR project in Visual Studio
Add your native C++ project as a reference
Write your wrapper
Add reference to CLR project from C# project
Call CLR wrapper
Create a class with your C++ signatures likes this:
namespace YourWrapper
{
public ref class WrappedFunction
{
public:
// Adjust according to your c++ class and function
double sumxy(double a, double b) {
return myClass::sumxy(a, b);
}
};
}
For a full example, see my project https://github.com/corytodd/interop-example:
I am very new to the .NET platform (coming from the JVM and having some limited C/C++ experience) and trying my first managed C++ class library. This is to serve as a bridge to a third-party DLL I got and I have to interface. I tried it with BridJ and Java but had no success so far, so I am now trying to write the program which uses the third-party DLL in C#.
The third-party DLL is unmanaged C++.
My ManagedBridge.h so far looks similar to this:
#pragma once
#include "thirdparty.h"
using namespace System;
namespace ManagedBridge {
class __declspec(dllexport) BridgedThirdPartyThing {
private:
THIRDPARTYNS::ThirdPartyThing* _delegate;
public:
BridgedThirdPartyThing();
~BridgedThirdPartyThing();
void foo();
// more methods
};
}
My ManagedBridge.cpp so far looks like this:
#include "stdafx.h"
#include "ManagedBridge.h"
namespace ManagedBridge {
BridgedThirdPartyThing::BridgedThirdPartyThing() {
_delegate = new THIRDPARTYNS::ThirdPartyThing();
}
BridgedThirdPartyThing::~BridgedThirdPartyThing() {
delete _delegate;
}
void BridgedThirdPartyThing::foo() {
_delegate -> foo();
}
// similar for the other methods
}
}
Now, when I build this, I get no errors, and a ManagedBridge.dll is created.
I then created a C# Console application to test my DLL, added it as a reference, but I cannot access the class I exported with __declspec(dllexport). Only the namespace is shown in the object browser.
What am I missing?
It's
public ref class BridgedThirdPartyThing
for C++/CLI. You don't use __declspec(dllexport). Note that the class needs to be public to be visible to comsuming assemblies.
I'm trying to call some Windows basic functions from C#, in particular this one.
Since the moment I want to learn the C++/CLI Language too, I've written down this code:
#pragma once
#include <string>
#include <Windows.h>
using namespace System;
namespace InformazioniSchermo {
public class Native_InformazioniDaSistema
{
public:
int m_nAltezzaPannello;
int m_nLarghezzaPannello;
Native_InformazioniDaSistema(void)
{
DISPLAY_DEVICE dd;
DWORD dev = 0;
dd.cb = sizeof(dd);
EnumDisplayDevices(0, dev, &dd, 0);
m_nAltezzaPannello = 100;
m_nLarghezzaPannello = 100;
}
};
public ref class InformazioniDaSistema
{
public:
InformazioniDaSistema();
~InformazioniDaSistema();
public:
int m_nHeight;
int m_nWidth;
};
InformazioniDaSistema::InformazioniDaSistema()
{
Native_InformazioniDaSistema foo;
m_nHeight = foo.m_nAltezzaPannello;
m_nWidth = foo.m_nLarghezzaPannello;
}
InformazioniDaSistema::~InformazioniDaSistema()
{
}
}
but when I compile, I get this error:
Error 3 error LNK2028: at unresolved token (0A0003B4) "extern "C" int __stdcall EnumDisplayDevicesW(wchar_t const *,unsigned long,struct _DISPLAY_DEVICEW *,unsigned long)" (?EnumDisplayDevicesW##$$J216YGHPB_WKPAU_DISPLAY_DEVICEW##K#Z) referencing in function "public: __thiscall InformazioniSchermo::Native_InformazioniDaSistema::Native_InformazioniDaSistema(void)" (??0Native_InformazioniDaSistema#InformazioniSchermo##$$FQAE#XZ) c:\Users\massimiliano\documents\visual studio 2013\Projects\InformazioniSchermo\InformazioniSchermo\InformazioniSchermo.obj InformazioniSchermo
Where am I doing wrong?
You need to link against user32.lib (the library for the EnumDisplayDevices function, as you'll see in the MSDN page you linked to).
You can do this by going to project properties->Linker->Input and adding user32.lib to the "Additional Dependencies" list.
I notice that the default Visual Studio project settings for C++/CLI don't include the common Windows API libraries by default (regular C++ projects have kernel32.lib, user32.lib, shell32.lib and others added to the project's library dependencies in new projects) so you have to add these libraries yourself if you're using them.
error LNK2028: ... (?EnumDisplayDevicesW##$$J216YGHPB_WKPAU_DISPLAY_DEVICEW##K#Z) ...
That is the name the linker is looking for. That is not it's name, it is a C function and does not have the C++ name mangling. Pretty unclear how you did that, especially since you obfuscated your #includes. But the only reasonable guess is that you declared this function yourself instead of using its declaration in the SDK header.
Never do that. Instead use:
#include <Windows.h>
#pragma comment(lib, "user32.lib")
With the #pragma helpful so you can't forget to link to user32