Create C++/CLI Wrapper for native C++ code - c++-cli

I currently need to write a C++/CLI wrapper for a native C++ library (TetGen) for later use in a C# project. I have read quite a few articles on how to do this but am at a loss with my linker errors. A chap called "Sam Agten" tried to accomplish this too, as he states in his Blog which led me to a series on C++ Interop (Part 1, Part 2, Part 3, Part 4, Part 5), where Part 3 is supposed to explain how to do what I accomplish.
Because Sam wanted to integrate TetGen with Unity he at last had to resort to C-Style-Wrappers. Now, if I'm not entirely mistaken, I have tried using C-Style-Wrappers on a previous, related project.
Because my wrapped TetGen must run on a Windows Server (2008 I think) my first question is:
Will a C++/CLI-Wrapper work in a Server-Environment?
I started writing a very simple wrapper according to aforementioned Part 3, which failed miserably. Anyway I tried to create a simple wrapper library along the line of thought, which I want to share with you. I began by creating a C#-project which ultimatly should use my wrapped library and added first a new project from the "Win32"-subsection of C++-projects "SimpleAdd".
//SimpleAdd.h
class _declspec(dllexport) SimpleAdd
{
public:
SimpleAdd();
~SimpleAdd();
int Add(int a,int b);
};
//SimpleAdd.cpp
#include "SimpleAdd.h"
int SimpleAdd::Add(int a, int b)
{
return a+b;
}
In the project settings I made sure, that it compiled to a static library (*.lib) and no CLR-Support was used. From the preprocessor definition I assume the lib is to target x86 systems: "WIN32". Everything is left pretty much on standard values. If I right-click => build it will successfully build a lib called "SimpleAdd.lib".
One thing I was pointed to through other posts: If I open the lib in DependencyWalker, I get an error saying: "No DOS or PE signature found. This file is not a valid 32-bit or 64-bit Windows module" which is kind of frustrating. What do I have to to to make DependencyWalker stop showing this error, since I think this 32/64-problem might hint to a solution.
Anyway, I have my lib, now I want to wrap it up. So I created a new project in my solution called "AddWrapper", using the CLR-ClassLibrary-template of the C++-branch.
// AddWrapper.h
#pragma once
#include "..\SimpleAdd\SimpleAdd.h"
using namespace System;
namespace AddWrapper {
public ref class ManagedSimpleAdd
{
private:
SimpleAdd *sa;
public:
ManagedSimpleAdd();
~ManagedSimpleAdd();
int Add(int a, int b);
};
}
//AddWrapper.cpp
#include "AddWrapper.h"
using namespace AddWrapper;
ManagedSimpleAdd::ManagedSimpleAdd() : sa(new SimpleAdd())
{
}
ManagedSimpleAdd::~ManagedSimpleAdd(){delete sa;}
int ManagedSimpleAdd::Add(int a, int b)
{
return sa->Add(a,b);
}
As both C++-projects are in seperate folders on the same hierarchical level, I specified the relative path to the headerfile. In the project settings I now have the configuration standard .dll and the /clr-switch. In my VC++ folders I added the path to the Debug-directory of my whole soultion, because this is where the referenced lib is compiled to. In my linker-settings under "additional dependencies" I added the name: "SimpleAdd.lib".
When I want to build my wrapper however, there is a problem, or rather 5 of them:
Fehler 3 error LNK2028: Nicht aufgel÷stes Token (0A00000B) ""public: __thiscall SimpleAdd::SimpleAdd(void)" (??0SimpleAdd##$$FQAE#XZ)", auf das in Funktion ""public: __clrcall AddWrapper::ManagedSimpleAdd::ManagedSimpleAdd(void)" (??0ManagedSimpleAdd#AddWrapper##$$FQ$AAM#XZ)" verwiesen wird. %path%\AddWrapper\AddWrapper.obj AddWrapper
Fehler 4 error LNK2028: Nicht aufgel÷stes Token (0A00000C) ""public: __thiscall SimpleAdd::~SimpleAdd(void)" (??1SimpleAdd##$$FQAE#XZ)", auf das in Funktion ""public: void * __thiscall SimpleAdd::scalar deleting destructor'(unsigned int)" (??_GSimpleAdd##$$FQAEPAXI#Z)" verwiesen wird. %path%\AddWrapper\AddWrapper.obj AddWrapper
Fehler 5 error LNK2019: Verweis auf nicht aufgel÷stes externes Symbol ""public: __thiscall SimpleAdd::SimpleAdd(void)" (??0SimpleAdd##$$FQAE#XZ)" in Funktion ""public: __clrcall AddWrapper::ManagedSimpleAdd::ManagedSimpleAdd(void)" (??0ManagedSimpleAdd#AddWrapper##$$FQ$AAM#XZ)". %path%\AddWrapper\AddWrapper.obj AddWrapper
Fehler 6 error LNK2019: Verweis auf nicht aufgel÷stes externes Symbol ""public: __thiscall SimpleAdd::~SimpleAdd(void)" (??1SimpleAdd##$$FQAE#XZ)" in Funktion ""public: void * __thiscall SimpleAdd::scalar deleting destructor'(unsigned int)" (??_GSimpleAdd##$$FQAEPAXI#Z)". %path%\AddWrapper\AddWrapper.obj AddWrapper
Fehler 7 error LNK1120: 4 nicht aufgel÷ste Externe %path%\Debug\AddWrapper.dll 1 1 AddWrapper
Now, what could be the cause of these errors? Is it the platform-befuddeledness of the lib? Did I miss to specify something else?

What do I have to to to make DependencyWalker stop showing this error
Do not compile to a lib file! It's not an executable and it's not a dynamic link library so it has not PE signature. You can build a static library and link it inside your C++/CLI assembly (if you wish so) or you can simply put all that code inside your C++/CLI assembly. Up to you.
what could be the cause of these errors?
You directly included header file, let me highlight your code:
class _declspec(dllexport) SimpleAdd
As you can see you're actually compiling with _declspec(dllexport), you're not telling the compiler to import such class from a library but that you're exporting it (and linker will complain because you declared that class but there is not any implementation). When you include a class it has to be declared with _declspec(dllimport).
class _declspec(dllimport) SimpleAdd
Usually this trick is done with preprocessor macros. Let me make a simplified example. In your SimpleAdd.h file you can declare:
#if defined(SIMPLEADD_LIB)
#define SIMPLEADD_EXPORTED _declspec(dllexport)
#else
#define SIMPLEADD_EXPORTED _declspec(dllimport)
#endif
Then change your code to
class SIMPLEADD_EXPORTED SimpleAdd
In your SimpleAdd project you define a macro (from Project settings) named SIMPLEADD_LIB. This will cause your library to be built with dllexport (because from library you export that class) but in your C++/CLI wrapper to be declared as dllimport (right because SIMPLEADD_LIB isn't declared and there you're importing that class).

Related

Linking an unknown dll

I've got this dll (called unknown.dll) from which i only know what Ghidra told me. I'm using LoadLibraryW to load it, but (in x86) it throws me the error 126. However, in 64x it gives me the error 193, so i don't think that the problem is that my program can't find my dll...
Here is my code :
#include <iostream>
#include <Windows.h>
typedef int(__cdecl* FunctionIWant)();
int main()
{
HMODULE hmod = LoadLibraryW(L"C:\\unknown.dll");
if (hmod != NULL)
{
...
}
else
std::cout << GetLastError();
return 0;
}
What am I doing wrong ?
126 is ERROR_MOD_NOT_FOUND. Either your DLL can't be found, or more likely one if it's dependencies can't be found.
193 is ERROR_BAD_EXE_FORMAT. This is because you can't mix 32 and 64 bit DLLs.
The fact that you get ERROR_BAD_EXE_FORMAT when you run under 64 bit tells you that your DLL is found. Therefore we can conclude that its dependencies are not present.
Consult the documentation to discover what dependencies are required.

How do we add an event in C++/WinRt project?

I am currently work on a C++/WinRt which connects(Sender) to one of the available devices(Client) around a specific region using WifiDirect. When the device wants to connect, it sends a connection request to the sender. The sender needs to detect the connection request sent by the client and connect to the client. For this I need to add an event - (On Connection requested). As soon as I add it should execute the code of OnConnectionRequested.
#include "pch.h"
#include <winrt/Windows.Foundation.Collections.h>
#include "winrt/Windows.Devices.WiFiDirect.h"
#pragma once
using namespace winrt;
using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections;
using namespace Windows::Devices::WiFiDirect;
using namespace Windows::Storage::Streams;
using namespace winrt::Windows::Devices::WiFiDirect;
using namespace winrt::Windows::Devices::Enumeration;
enum class NotifyType
{
StatusMessage,
ErrorMessage
};
enum class CallbackContext
{
Any,
Same
};
class st
{
public:
void OnConnectionRequested(WiFiDirectConnectionListener sender,
WiFiDirectConnectionRequestedEventArgsconnection EventArgs)
{
WiFiDirectConnectionRequest connectionRequest = connectionEventArgs.GetConnectionRequest();
printf("Connection request received from ", connectionRequest.DeviceInformation().Name(), "Connection Request");
printf("Connecting to ", connectionRequest.DeviceInformation().Name(), NotifyType::StatusMessage);
}
void start()
{
Windows::Devices::WiFiDirect::WiFiDirectAdvertisementPublisher _publisher;
Windows::Devices::WiFiDirect::WiFiDirectConnectionListener _listener;
winrt::event_token _connectionRequestedToken;
try
{
_connectionRequestedToken = _listener.ConnectionRequested({this, &st::OnConnectionRequested});
_publisher.Start();
printf("Advertisement started, waiting for StatusChangedcallback...", NotifyType::StatusMessage);
}
catch (...)
{
printf("Error starting Advertisement: ", NotifyType::ErrorMessage);
}
getchar();
}
};
int main()
{
st s;
s.start();
}
Is this the right way to add an event in C++/WinRt :
_connectionRequestedToken = _listener.ConnectionRequested({this, &st::OnConnectionRequested});
The errors are :
LNK1120 1 unresolved externals - error in Winrt.exe file
LNK2019
unresolved external symbol "public: struct winrt::hstring __thiscall
winrt::impl::consume_Windows_Devices_Enumeration_IDeviceInformation::Name(void)const
"
(?Name#?$consume_Windows_Devices_Enumeration_IDeviceInformation#UIDeviceInformation#Enumeration#Devices#Windows#winrt###impl#winrt##QBE?AUhstring#3#XZ)
referenced in function "public: void __thiscall
st::OnConnectionRequested(struct
winrt::Windows::Devices::WiFiDirect::WiFiDirectConnectionListener,struct
winrt::Windows::Devices::WiFiDirect::WiFiDirectConnectionRequestedEventArgs)"
(?OnConnectionRequested#st##QAEXUWiFiDirectConnectionListener#WiFiDirect#Devices#Windows#winrt##UWiFiDirectConnectionRequestedEventArgs#3456##Z) - error in Program.obj 1
What changes should I make at this line to clear the error? What does the error actually mean ? Or is there any other way to add an event in C++/WinRt project?
You are missing an #include directive:
#include <winrt/Windows.Devices.Enumeration.h>
See Why is the linker giving me a "LNK2019: Unresolved external symbol" error?:
If the unresolved symbol is an API from the Windows namespace headers for the C++/WinRT projection (in the winrt namespace), then the API is forward-declared in a header that you've included, but its definition is in a header that you haven't yet included. Include the header named for the API's namespace, and rebuild. For more info, see C++/WinRT projection headers.
For general instructions on how to handle events see Handle events by using delegates in C++/WinRT.
Capturing this (raw pointer) into the delegate might be dangerous, too. This breaks the link between object lifetime and visibility, and puts the burden of lifetime management on you. See Safely accessing the this pointer with an event-handling delegate for safer, more manageable alternatives.

Building LLVM pass out of source - undefined symbols

I'm attempting to build an LLVM pass using the instructions here and link it against the copy of LLVM installed by Julia. The pass is currently being compiled successfully, but cmake fails on linking with undefined symbol errors.
[ 50%] Building CXX object VectorizePass/CMakeFiles/VectorizePass.dir/VectorizePass.cpp.o
[100%] Linking CXX shared module libVectorizePass.so
Undefined symbols for architecture x86_64:
"llvm::raw_ostream::write_escaped(llvm::StringRef, bool)", referenced from:
(anonymous namespace)::Hello::runOnFunction(llvm::Function&) in VectorizePass.cpp.o
"llvm::raw_ostream::write(char const*, unsigned long)", referenced from:
llvm::raw_ostream::operator<<(llvm::StringRef) in VectorizePass.cpp.o
There are dozens of undefined symbol errors followed by
"vtable for llvm::Pass", referenced from:
llvm::Pass::Pass(llvm::PassKind, char&) in VectorizePass.cpp.o
NOTE: a missing vtable usually means the first non-inline virtual
member function has no definition.
VectorizePass.cpp
#include "llvm/Pass.h"
#include "llvm/IR/Function.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
namespace {
struct Hello : public FunctionPass {
static char ID;
Hello() : FunctionPass(ID) {}
bool runOnFunction(Function &F) override {
errs() << "Hello: ";
errs().write_escaped(F.getName()) << "\n";
return false;
}
};
}
char Hello::ID = 0;
static RegisterPass<Hello> X("Hello", "My Hello World Pass", false, false);
This is exactly as it is in the tutorial.
CMakeLists.txt (1)
add_library(VectorizePass MODULE VectorizePass.cpp)
SET(CMAKE_CXX_FLAGS "-std=c++11 -Wall -fno-rtti -D__STDC_CONSTANT_MACROS -D__STDC_LIMIT_MACROS -fPIC")
CMakeLists.txt (2)
project(VectorizePass)
set(LLVM_DIR "/Users/user/julia5/deps/build/llvm-3.7.1/build_Release")
set(LLVM_TOOLS_BINARY_DIR "/Users/user/julia5/deps/build/llvm-3.7.1/build_Release/Release/bin")
include_directories(${LLVM_INCLUDE_DIRS})
include_directories("/Users/user/julia5/deps/build/llvm-3.7.1/build_Release/include")
include_directories("/Users/user/julia5/deps/srccache/llvm-3.7.1/include")
link_directories("/Users/user/julia5/deps/build/llvm-3.7.1/build_Release/Release/lib")
link_directories("/Users/user/julia5/deps/build/llvm-3.7.1/build_Release/lib")
link_directories(${LLVM_LINK_DIRS})
add_definitions(${LLVM_DEFINITIONS})
add_subdirectory(VectorizePass)
Clearly, for some reason CMake isn't finding the appropriate object files even though they are in the directories that I have in the link_directories statements. What am I missing? I'm fairly new to CMake so it may be something obvious.
I've attempted to include(AddLLVM) as suggested here but CMake reports that it cannot find AddLLVM. This Stack post also suggests using a regular Makefile but that does not work when compiling out-of-source passes as all the paths in the regular LLVM Makefile.common/Makefile.config are relative and don't work at all.
Have you read this? You need to use add_llvm_loadable_module() macro to define target for your pass.

DLL import failure in using WinDivert

I am going to design a program using WinDivert to manipulate the network traffic.
The language I use is C++ and the program is designed under Visual Studio 2008.
Firstly I create a project in visual C++ CLR (Windows Forms Application) so I can implement the UI simply.
For importing the WinDirvert Library, I have done the following setting in project properties:
Configuaration Properties: General
Common Language Runtime support: Common Language Runtime Support(/ctr)
Configuaration Properties: Linker
Additional Dependencies: link of WinDivert.lib
Module Definition File: link of windivert.def
Within the project I have created, I also added the windivert.h in the header files.
Also, windivert.h is included in the main entry point of my project (ProjectG.cpp):
#include "stdafx.h"
#include "Form1.h"
#pragma managed(push, off)
#include "windivert.h"
#pragma managed(pop)
using namespace ProjectG;
[STAThreadAttribute]
int main(array<System::String ^> ^args)
{
// Enabling Windows XP visual effects before any controls are created
Application::EnableVisualStyles();
Application::SetCompatibleTextRenderingDefault(false);
// Create the main window and run it
Application::Run(gcnew Form1());
HANDLE handle;
unsigned char packet[8192];
UINT packet_len;
WINDIVERT_ADDRESS addr;
handle = WinDivertOpen("udp", WINDIVERT_LAYER_NETWORK, 0,
WINDIVERT_FLAG_DROP);
if (handle == INVALID_HANDLE_VALUE)
{
Application::Exit();
}
while (TRUE)
{
// Read a matching packet.
if (!WinDivertRecv(handle, packet, sizeof(packet), &addr, &packet_len))
{
MessageBox::Show("Fail");
continue;
}
}
return 0;
}
Finally, I put the {WinDivert.dll, windivert.h, WinDivert.lib, WinDivert32.sys} under the project directory.
However, the following error is shown:
fatal error LNK1306: DLL entry point "int __clrcall main(cli::array<class
System::String ^ >^)" (?main##$$HYMHP$01AP$AAVString#System###Z) cannot be managed;
compile to native ProjectG.obj ProjectG
Additional: (a warning)
warning LNK4070: /OUT:WinDivert.dll directive in .EXP differs from output filename
'C:\Users\David\Desktop\css\ProjectG\Debug\ProjectG.exe'; ignoring directive
ProjectG.exp ProjectG
Question:
How can I resolve this situation?
a) your main source is .cpp, so you can delete [STAThreadAttribute] and change
int main(array<System::String ^> ^args) to int _tmain(int argc, _TCHAR* argv[])
b) exclude windivert.def from linker Module Definition File, this only when you are creating a DLL
c) the DLL/SYS files would need to be copied to the Debug and Release folders

visual studio 2012 can't resolve static fields in a dll lib

I'm compiling openexr2.0.0 using visual studio 2012 x64 dll, I got this error:
ImfLut.obj : error LNK2001: unresolved external symbol "private: static union half::uif const * const half::_toFloat" (?_toFloat#half##0QBTuif#1#B)
ImfRgbaYca.obj : error LNK2001: unresolved external symbol "private: static unsigned short const * const half::_eLut" (?_eLut#half##0QBGB)
And I looked up in the half.lib using dumpbin /exports:
Another look up using dumpbin /exports on half.dll:
The two symbols are there. And interestingly, when I remove half.lib from dependency, VS complain convert is also unresolved. This shows that it could find convert but not _toFloat and _eLut. The differences are: _toFloat and _eLut are both static fields, convert is a static method.
class half
{
...
public:
union uif
{
unsigned int i;
float f;
};
private:
HALF_EXPORT static short convert (int i);
HALF_EXPORT static const uif _toFloat[1 << 16];
HALF_EXPORT static const unsigned short _eLut[1 << 9];
...
};
My system is windows 8 x64. Does anyone know how to fix this problem?
You're trying to link against __declspec(dllexport)-ed symbols.
This means you need to make sure you're __declspec(dllimport)-ing those symbols in your project file.
Specifically to half - there's a #define you can add: OPENEXR_DLL that is being checked for appearance in halfExport.h and will do that for you.
Step 14 in the following link solved the problem for me:
https://groups.google.com/forum/#!topic/openvdb-forum/-jFJQ2N4BGc
In your project, add OPENEXR_DLL to "Preprocessor Definitions" in "project properties->C/C++->Preprocessor"