I have a 3rd party C++ .lib that I would like to use in a C# application.
My intent is to write a managed C++ wrapper .dll for the .lib, and then include that wrapper dll in my C# application, using a process roughly like the one outlined at this link.
The header file for the .lib does not indicate any class declaration or namespace, just method names:
int CheckNamedVariable(const char* _name);
int RegisterNamedVariable(const char* _name);
double GetNamedVariableValue(int _id);
double GetNamedVariableTypedValue(int _id, int _units);
void SetNamedVariableValue(int _id, double _value);
... (etc)
I checked the lib with DUMPBIN /exports to see if I could glean any additional information, and got the following:
File Type: LIBRARY
Exports
ordinal name
?AircraftVarGet##YANHHH#Z (double __cdecl AircraftVarGet(int,int,int))
?CheckNamedVariable##YAHPBD#Z (int __cdecl CheckNamedVariable(char const *))
?DLLStart##YGXXZ (void __stdcall DLLStart(void))
?DLLStop##YGXXZ (void __stdcall DLLStop(void))
?GetAircraftVarEnum##YAHPBD#Z (int __cdecl GetAircraftVarEnum(char const *))
?GetModuleVar##YANW4GAUGE_TOKEN###Z (double __cdecl GetModuleVar(enum GAUGE_TOKEN))
?GetNameOfNamedVariable##YAPBDH#Z (char const * __cdecl GetNameOfNamedVariable(int))
?GetNamedVariableTypedValue##YANHH#Z (double __cdecl GetNamedVariableTypedValue(int,int))
?GetNamedVariableValue##YANH#Z (double __cdecl GetNamedVariableValue(int))
?GetUnitsEnum##YAHPBD#Z (int __cdecl GetUnitsEnum(char const *))
?IsPanelWindowVisibleIdent##YAHI#Z (int __cdecl IsPanelWindowVisibleIdent(unsigned int))
?Panels##3PAUPANELS##A (struct PANELS * Panels)
?RegisterNamedVariable##YAHPBD#Z (int __cdecl RegisterNamedVariable(char const *))
?SendKeyEvent##YAXII#Z (void __cdecl SendKeyEvent(unsigned int,unsigned int))
?SetNamedVariableTypedValue##YAXHNH#Z (void __cdecl SetNamedVariableTypedValue(int,double,int))
?SetNamedVariableValue##YAXHN#Z (void __cdecl SetNamedVariableValue(int,double))
_ImportTable
_Linkage
Summary
ED .debug$S
14 .idata$2
14 .idata$3
4 .idata$4
4 .idata$5
1A .idata$6
In my managed wrapper project, I can point the Linker input to the .lib, but I'm not sure how to call the methods in the lib (the tutorial I linked to makes use of namespaces and classes). If it were a plain old DLL in a C#/VB project, I could view the object browser and get some idea of how to call the library.
But given my C++ ignorance, I'm in the dark. Any help appreciated.
EDIT:
I tried just creating methods with the same name as the methods listed in the header, e.g.:
int VariableExporter::IsPanelWindowVisibleIdent(unsigned int panel_id)
{
return IsPanelWindowVisibleIdent(panel_id);
}
This is not doing what I'd hope, it's just recursively calling itself (until it has a - wait for it - Stack Overflow).
Then I tried renaming the wrapper method like this:
int VariableExporter::IsPanelWindowVisibleIdent_New_Name(unsigned int panel_id)
{
return IsPanelWindowVisibleIdent(panel_id);
}
Now, although the C++ .DLL project and the C# project both compile, the C# project throws a FileNotFound exception when it tries to load that .DLL.
First thing, to call global level function, you need to use the scope resolution operator ::
int VariableExporter::IsPanelWindowVisibleIdent(unsigned int panel_id)
{
return ::IsPanelWindowVisibleIdent(panel_id);
}
There is no need (at least for this simple case), to rename your method.
Secondly, for DLL-not-found issue, check that DLL is present in the bin path of your C# application. Also, check that wrapper-DLL itself is loadable (use Dependency Walker). Ensure that bit-ness of wrapper DLL and C# application also matches. A 64-bit application cannot load 32-bit DLL (and vice versa).
Related
I have a vc++ dll in _cdecl calling convention. I want to use the exported function of that dll from the code in _stdcall calling convention. I m getting the linker error as expected. But how can i do that.
If you don't have sources to the stdcall DLL, You could load dynamically and specify the calling convention on the function pointer, otherwise project setting is used. Don't do this:
BOOL (*pBeep)(UINT) = 0; // defaults to project calling convention setting (/Gd etc)
If your project is set to __cdecl which is default, you'd actually want to explicitly set the function pointer to __stdcall:
BOOL (__stdcall *pBeep)(UINT) = 0;
make sure to cast correctly when assigning the function pointer as well:
pBeep = (BOOL (__stdcall *)(UINT))GetProcAddress(beepLibrary, "MessageBeep");
I came across a problem recently.
I have three files, A.h, B.cpp, C.cpp:
A.h
#ifndef __A_H__
#define __A_H__
int M()
{
return 1;
}
#endif // __A_H__
B.cpp
#include "A.h"
C.cpp
#include "A.h"
As I comile the three files by MSVC, there is a error:
C.obj : error LNK2005: "int __cdecl M(void)" (?M##YAHXZ) already defined in B.obj
It is easy understanding, as we know, B.obj has a symbol named "M", also C.obj has a "M".
Here the error comes.
However, if I change M method to a class which contain a method M like this below:
A.h
#ifndef __A_H__
#define __A_H__
class CA
{
public:
int M()
{
return 1;
}
};
#endif // __A_H__
there is no more errors!! Could somebody tell me what is happening?
If B.cpp and C.cpp include A.h, then both are compiled with your definition of M, so both object files will contain code for M. When the linker gathers all the functions, he sees that M is defined in two object files and does not know which one to use. Thus the linker raises an LNK2005.
If you put your function M into a class declaration, then the compiler marks/handles M as an inline function. This information is written into the object file. The linker sees that both object files contain a definition for an inline version of CA::M, so he assumes that both are equal and picks up randomly one of the two definitions.
If you had written
class CA {
public:
int M();
};
int CA::M()
{
return 1;
}
this would have caused the same problems (LNK2005) as your initial version, because then CA::M would not have been inline any more.
So as you might guess by now, there are two solutions for you. If you want M to be inlined, then change your code to
__inline int M()
{
return 1;
}
If you don't care about inlining, then please do it the standard way and put the function declaration into the header file:
extern int M();
And put the function definition into a cpp file (for A.h this would ideally be A.cpp):
int M()
{
return 1;
}
Please note that the extern is not really necessary in the header file.
Another user suggested that you write
static int M()
{
return 1;
}
I'd not recommend this. This would mean that the compiler puts M into both of your object files and marks M as being a function that is only visible in each object file itself. If the linker sees that a function in B.cpp calls M, it finds M in B.obj and in C.obj. Both have M marked as static, so the linker ignores M in C.obj and picks the M from B.obj. Vice versa if a function in C.cpp calls M, the linker picks the M from C.obj. You will end up with multiple definitions of M, all with the same implementation. This is a waste of space.
See http://faculty.cs.niu.edu/~mcmahon/CS241/c241man/node90.html how to do ifdef guards. You have to start with ifndef before the define.
Edit: Ah no, while your guard is wrong that's not the issue. Put static in front of your function to make it work. Classes are different because they define types.
I don't know what's under the hood, but if you don't need a class I guess that the compiler will automatically add the "extern" key to your functions, so you'll get the error including the header 2 times.
You can add the static keyword to M() method so you'll have only one copy of that function in memory and no errors at compile time.
By the way: I see you have a #endif, but not a #ifdef or #ifndef, is it a copy/paste error?
Trying to get this to work
Using VisualStudio VC++ I'm trying to detect the cursor's position, take both x and y and use it for stuff, but no matter what - linking errors occur.
public: System::Void pictureDrag_MouseDown(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) {
POINT coord;
GetCursorPos(&coord);
int xx = coord.x, yy = coord.y;
pictureBox2->Location = Drawing::Point(xx, yy);
}
Gets me these two linking errors
Stuff.obj : error LNK2028: unresolved token (0A000011) "extern "C" int __stdcall GetCursorPos(struct tagPOINT *)" (?GetCursorPos##$$J14YGHPAUtagPOINT###Z) referenced in function "public: void __clrcall Stuff::Form1::pictureDrag_MouseDown(class System::Object ^,class System::Windows::Forms::MouseEventArgs ^)" (?pictureDrag_MouseDown#Form1#Stuff##$$FQ$AAMXP$AAVObject#System##P$AAVMouseEventArgs#Forms#Windows#4##Z)
Stuff.obj : error LNK2019: unresolved external symbol "extern "C" int __stdcall GetCursorPos(struct tagPOINT *)" (?GetCursorPos##$$J14YGHPAUtagPOINT###Z) referenced in function "public: void __clrcall Stuff::Form1::pictureDrag_MouseDown(class System::Object ^,class System::Windows::Forms::MouseEventArgs ^)" (?pictureDrag_MouseDown#Form1#Stuff##$$FQ$AAMXP$AAVObject#System##P$AAVMouseEventArgs#Forms#Windows#4##Z)
Please note, that there is a
#include
at the beginning and I also tried to include the user32.lib to the project, did no result at all. The code itself is to damn simple to miss something big, this looks like I've forgotten to include something, but just cant figure out what. I'm new at C++ and we are so breaking up soon! As soon as I figure this one out.
Searched the world for help, cant stand any more. Please, help an idiot!
I need a C# interface to call some native C++ code via the CLI dialect. The C# interface uses the out attribute specifier in front of the required parameters. That translates to a % tracking reference in C++/CLI.
The method I has the following signature and body (it is calling another native method to do the job):
virtual void __clrcall GetMetrics(unsigned int %width, unsigned int %height, unsigned int %colourDepth, int %left, int %top) sealed
{
mRenderWindow->getMetrics(width, height, colourDepth, left, top);
}
Now the code won't compile because of a few compile time errors (all being related to not being able to convert parameter 1 from 'unsigned int' to 'unsigned int &').
As a modest C++ programmer, to me CLI is looking like Dutch to a German speaker. What can be done to make this wrapper work properly in CLI?
Like it was also suggested in a deleted answer, I did the obvious and used local variables to pass the relevant values around:
virtual void __clrcall GetMetrics(unsigned int %width, unsigned int %height, unsigned int %colourDepth, int %left, int %top) sealed
{
unsigned int w = width, h = height, c = colourDepth;
int l = left, t = top;
mRenderWindow->getMetrics(w, h, c, l, t);
width = w; height = h; colourDepth = c; left = l; top = t;
}
It was a bit obvious since the rather intuitive mechanism of tracked references: they're affected by the garbage collector's work and are not really that static/constant as normal &references when they're prone to be put somewhere else in memory. Thus this is the only way reliable enough to overcome the issue. Thanks to the initial answer.
If your parameters use 'out' on the C# side, you need to define your C++/CLI parameters like this: [Out] unsigned int ^%width
Here's an example:
virtual void __clrcall GetMetrics([Out] unsigned int ^%width)
{
width = gcnew UInt32(42);
}
Then on your C# side, you'll get back 42:
ValueType vt;
var res = cppClass.GetMetrics(out vt);
//vt == 42
In order to use the [Out] parameter on the C++/CLI side you'll need to include:
using namespace System::Runtime::InteropServices;
Hope this helps!
You can use pin_ptr so that 'width' doesn't move when native code changes it. The managed side suffers from pin_ptr, but I don't think you can get around that if you want native code directly access it without 'w'.
virtual void __clrcall GetMetrics(unsigned int %width, unsigned int %height, unsigned int %colourDepth, int %left, int %top) sealed
{
pin_ptr<unsigned int> pw = &width; //do the same for height
mRenderWindow->getMetrics(*pw, h, c, l, t);
}
Im using Eclipse with the DDT plugin and DMD 2.06 as the compiler. When I try to to use functions like dlopen, dlsym usw I get "unresolved reference" errors, in C and GCC I fixed them by linking with -ldl, -lsdl usw... but the DMD2 compiler doesnt have options like that, is there another way to link with specific libraries?
btw I define the C functions the following way
extern(C)
{
/* From <dlfcn.h>
* See http://www.opengroup.org/onlinepubs/007908799/xsh/dlsym.html
*/
const int RTLD_NOW = 2;
void *dlopen(const(char)* file, int mode);
int dlclose(void* handle);
void *dlsym(void* handle, const(char*) name);
const(char)* dlerror();
}
would be happy about any help.
D does have link pragmas:
pragma(lib, "dl");
which will cause DMD to emit "-L-ldl" (or the system-appropriate link flag) to the linker. If the linker is order-sensitive (as ld is), you need to specify the pragmas in the order which you manually pass them.
Just pass -L-ldl.
Also, you don't need to redefine all of these. They are available in the core.sys.posix.dlfcn module.