My problem is , i have some functions in a DLL some of these functions are for example :
#include <string>
using namespace std;
extern "C" __declspec(dllexport) string __cdecl encryption(string s)
{
return s;
}
Whenever i try to call this function from C# , here is the code im using :
[DllImport("Packer.dll", EntryPoint = "encryption")]
static extern string encryption(string s);
i get an error :
A call to PInvoke function 'Packer' has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature. Check that the calling convention and parameters of the PInvoke signature match the target unmanaged signature.
im guessing i get this error because i dont have the right declarations for the function
can anyone guide me how to fix that , thanks in advance
std::string can not be used with PInvoke, because C++ does not have an ABI for its objects which is required to properly clean stack, copy objects, etc. This is one of the greatest pains of C++.
You have to use char* pointers and plain C APIs. Simply put, PInvoke does not work with C++.
As I'm sure you already know, the C++ std::string is actually a template. Only when the template is instantiated (as std::basic_string<char> in this case), the exact layout of the objects of that type and signatures of the methods are determined by the C++ compiler.
Unfortunately, only the C++ compiler in question has the access to all the relevant information (such as template source code) to make these kinds of decisions. That's why non-C features such as templates are generally not "transferable" even between different C++ compilers, let alone C++ and C#.
Also, C++ names are typically "mangled" in a C++ compiler-specific manner.
You'll have to change the signature of your native method, so it is becomes a "pure" C function:
Ensure there is no C++ name mangling by declaring the function as extern "C" (you are already doing that).
Use char* parameter instead of std::string.
Return char* result instead of std::string, but be very careful how you do it.
Ensure your DllImportAttribute.CallingConvention matches the __cdecl, __stdcall or __fastcall in your C.
The problem here is, you're using the STL string class which C# doesn't know how to marshal. You have two options here:
refactor your C++ code to work with char * buffers. Or write a wrapper or an overload or something that uses char * instead of string.
Write a C++/CLI wrapper around your C++ functions that uses System::String and calls the STL string versions internally.
If memory serves, if you don't specify otherwise P/Invoke assumes the calling convention is __stdcall. If so, changing your __cdecl to __stdcall should fix the first problem.
As #Adam Rosenfield points out, you probably also need to pass and return a char const *, not a string. C# and C++ almost certainly have somewhat different ideas of what constitutes a string.
Related
I have the following struct defined in C++:
struct GraphicsAdapterDesc {
// ... Just some constructors / operators / destructor here
DEFINE_DEFAULT_CONSTRUCTOR(GraphicsAdapterDesc);
DEFINE_DEFAULT_DESTRUCTOR(GraphicsAdapterDesc);
ALLOW_COPY_ASSIGN_MOVE(GraphicsAdapterDesc);
std::wstring AdapterName;
int32_t AdapterNum;
std::wstring HardwareHash;
int64_t DedicatedVMEM;
int64_t DedicatedSMEM;
int64_t SharedSMEM;
int32_t NumOutputs;
};
In C#, I have a 'mirror' struct declared thusly:
[StructLayout(LayoutKind.Sequential)]
public struct GraphicsAdapterDesc {
string AdapterName;
int AdapterNum;
string HardwareHash;
long DedicatedVMEM;
long DedicatedSMEM;
long SharedSMEM;
int NumOutputs;
};
I've tried to be really careful about matching up the widths of the variables (although I'm a bit unsure on what to do with the strings exactly).
Anyway, I have the following exported C method:
extern "C" __declspec(dllexport) bool GetGraphicsAdapter(int32_t adapterIndex, GraphicsAdapterDesc& outAdapterDesc) {
outAdapterDesc = RENDER_COMPONENT.GetGraphicsAdapter(adapterIndex);
return true;
}
And, the following extern method in my C# app:
[DllImport(InteropUtils.RUNTIME_DLL, EntryPoint = "GetGraphicsAdapter", CallingConvention = CallingConvention.Cdecl)]
internal static extern bool _GetGraphicsAdapter(int adapterIndex, out GraphicsAdapterDesc adapterDesc);
However, this doesn't work right when I call it. I get a different result depending on whether or not I'm compiling in x64 or x86 mode (both the C++ DLL and the C# app are compiled as x86 or x64):
In x86 mode, the call returns, but the struct has 'nonsense' values in, and the strings are all null,
In x64 mode, the call throws a NullPointerException.
My expectation is that I'm doing something wrong marshalling the strings, and that I need to specify 'wide-mode' for the characters, but I don't know how (or if that's even the right option).
Thank you in advance.
C++ types are not compatible with C# unless they're wrapped in managed C++. and you're using std::wstring which cannot be marshaled into .NET.
To interop successfully you'll either need to use a wchar_t[] or a whar_t* and tell C# now to marshal it.
I don't know what your macros are doing but this will only work if your c++ type is POD. c++11 Has an expanded sense of POD but I don't think you meet the expanded criteria anyway. Otherwise you can't guarantee layout. If you want to export your c++ classes to C# I would suggest you use c++\cli. Also you have wstring defined in your stuct which are definitely not POD. When you use DLLImport think C constructs only or you are going to have a bad time.
I have been coding in c# for a bit but I came across some piece of code which does not make any sense to me at all. It looks something like below:
[DllImport(DllName, SetLastError = true,
CallingConvention=CallingConvention.Cdecl)]
static extern byte QLIB_UploadCEFS_File(UInt32 handle, string sFileName,
string sPartitionFileName);
I have no idea how to interpret this code. Can anyone explain me what are they trying to achieve here?
The is a P/Invoke declaration. It declares a function, external to this module, implemented in an unmanaged DLL.
The DllImport attribute specifies the name of the unmanaged DLL, DllName in this instance. The other properties of the DllImport attribute specify, in this instance, the calling convention, and that the function sets the Win32 last error variable.
The function declaration itself specifies the signature of the unmanaged function. In this case the parameters mean that the function has the following unmanaged declaration:
unsigned char QLIB_UploadCEFS_File(
unsigned int handle,
const char *sFileName,
const char *sPartitionFileName
);
From the perspective of the managed code that calls the function, it's just like any other function. You call it passing parameters as specified in the code in your question.
For what it is worth, I do suspect that the declaration is incorrect. The first parameter is a handle and these are almost always pointer sized. So, whilst the code is probably just fine under 32 bit, it is likely to break under 64 bit. I would expect to see that first parameter declared as IntPtr. Of course, this is speculation because I cannot see the actual unmanaged function declaration.
I created a DLL inside of MV C++ 2012 and when I used
Dumpbin /Exports filename
The name of the function inside of the DLL file has an equal sign inside of it. I had to use Common Language Runtime Support (/crl) because I used a DLL from C#. Is this why the name of the function would show up with an equals sign? My header file:
#ifdef ColorDLL_EXPORTS
#define ColorDLL_API __declspec(dllexport)
#else
#define ColorDLL_API __declspec(dllexport)
#endif
extern "C"{
ColorDLL_API int ColorSelect(int i);
}
ColorDLL.cpp
#include "stdafx.h"
#include "ColorDLL.h"
#using <ColorDiologeClass.dll>
extern "C"{
ColorDLL_API int ColorSelect(){
ColorDiologeClass::Class1::ColorReturn(1);
return 1;
}
}
When I used Dumpbin the name showed up as this:
Name
ColorSelect = _ColorSelect
Why is this? I am expecting it to show up as ColorSelect, not ColorSelect = _ColorSelect. And if I were to leave it this way, how would I call this function from a program like JMP where it needs the exact function name? Would it be ColorSelect? Or would it be ColorSelect = _ColorSelect?
The name is "mangled" - the return type and the parameters are enchoded into the name of the function. Should you wish to NOT have that, you would use extern "C" before the function name (or around a block of functions).
That would be name mangling, which is the under-the-covers feature of c++ that allows it to support function overloading (since it incorporates the argument types of the function into its name).
Here's another question that goes into greater detail.
Microsoft calls this "decorating" instead of mangling. They include a command line tool named "undname" that will produce the original name from the decorated name:
C:\>undname ?ColorSelect##YAHXZ
Microsoft (R) C++ Name Undecorator
Copyright (C) Microsoft Corporation. All rights reserved.
Undecoration of :- "?ColorSelect##YAHXZ"
is :- "int __cdecl ColorSelect(void)"
If you want to do the same in your own code, you can do that too, using UnDecorateSymbolName.
For what it's worth, decorating/mangling supports not only overloading, but typesafe linking. Typesafe linking stems from function overloading though it isn't really function overloading in itself.
Specifically, typesafe linking deals with (for example) how to deal with C++ code that has overloads of, say, sqrt for float, double, long double, and probably complex as well, but links to a C library that provides a double sqrt(double), but not the other overloads. In this case, we typically want that to be used when the right arguments were/are used, but not otherwise.
This can (or could) arise even without function overloading being involved. For example, in pure C you could do something like this:
#include <stdio.h>
extern int sqrt(int);
// ...
printf("%d", sqrt(100));
Now, we've told the compiler we're using a version of sqrt that takes (and returns) an int. Unfortunately, the linker doesn't realize that, so it still links with the sqrt in the standard library that takes and returns double. As a result, the code above will print some thoroughly useless result (typically 0, not that it matters a lot).
Typesafe linkage prevents that -- even though it isn't exactly function overloading, we still have two functions with the same name, but different types by the time we're linking. By encoding the parameter type(s) into the name, the linker can keep this sorted out just as well as the compiler can.
The same can (and frequently does) arise in C when we have name collisions between different libraries. With a traditional C compiler, straightening out this sort of mess can be extremely difficult (at best). With a C++ compiler, unless the two libraries use not only the same names, but identical number and types of parameters, it's never a problem at all.
In my C# application, I would like to write a part of the code in C.
I plan to write a DLL witch would be interoperable with .Net.
How can I do that?
There are essentially three right ways to do it:
Use C++/CLI. This is the optimal way if this DLL is going to be used only by .NET.
Use an "extern "C"" compatible API, like the Windows API itself. This is the most portable, but isn't as convenient for your callers as using a class model to represent your objects.
This is the best option if you really intend to write in ANSI C (not C++).
For this path, you write your functions as extern "C" returntype __stdcall __declspec(dllexport) func(params) { ... }
You should also use a "caller-provides-the-buffer" memory model, rather than returning a buffer allocated inside your library. In the cases where you do need to allocate memory for internal state, the caller should see it as an opaque handle and you should provide accessor functions for the caller to extract data. Under no circumstances should the caller be expected to deallocate memory allocated inside your library, however it is ok for the caller to ask the library to do the deallocation.
Use COM, or a COM-like API. Here you return (often via out parameter) a pointer to an interface, which is a class with pure virtual functions, no non-virtual functions and no data.
The implementation is in concrete classes derived from this abstract interface, they can have data and helper functions galore, since that doesn't affect the binary interface.
This is a lot more work in the library but extremely portable and easy for the consumer to use.
And there is one thing absolutely NOT to do:
use __declspec(dllexport) on C++ classes.
EDIT: I want to also explain some good practices for option #2 which will maximize portability and make the native C/C++ parts usable from unmanaged applications as well.
You can make that easier with a macro, the usual way of doing it is:
In your header file, all the function declarations look like
MYPROJECTAPI(returntype) PublicFunc(params);
In your project, the definition is
#define MYPROJECTAPI(returntype) \
extern "C" returntype __stdcall __declspec(dllexport)
In consumer projects
#define MYPROJECTAPI(returntype) \
extern "C" returntype __stdcall __declspec(dllimport)
and then you can define the macro differently for other compilers like gcc which don't use __declspec.
The complete solution would look like (in public header file myproject.h):
#if _WIN32
# if BUILDMYPROJECT
# define MYPROJECTAPI(returntype) \
extern "C" returntype __stdcall __declspec(dllexport)
# else
# define MYPROJECTAPI(returntype) \
extern "C" returntype __stdcall __declspec(dllimport)
# endif
#else
# define MYPROJECTAPI(returntype) extern "C" returntype
#endif
and then your Visual C++ project would cause BUILDMYPROJECT to be defined when building myproject.dll
In a nutshell:
(1) Create a new C++/CLI library project.
(2) Write your code. For classes that need to be accessible from your C# project, make sure to create them as CLR classes:
public ref class R {/*...*/}; // CLR class
public value class V {/*...*/}; // CLR struct
public interface class I {/*...*/}; // CLR interface
(3) Compile the project and add a reference to it in your C# project.
Through P/Invoke layer.
http://en.wikipedia.org/wiki/Platform_Invocation_Services
Below is an example for an application where I had to do just that. In my case, I needed a DLL to wrap calls to functions that were only available in a .lib. The key part is the extern "C" __declspec (dllexport) in the declaration. That's basically all you need. The rest was merely using dllimport in the C# app and getting the marshalling right.
extern "C" __declspec (dllexport) LONG EstablishContext(DWORD dwScope,
LPCVOID pvReserved1,
LPCVOID pvReserved2,
LPSCARDCONTEXT phContext)
{
return SCardEstablishContext(dwScope, pvReserved1, pvReserved2, phContext);
}
In my C# application, I would like to write a part of the code in C.
I plan to write a DLL witch would be interoperable with .Net.
How can I do that?
There are essentially three right ways to do it:
Use C++/CLI. This is the optimal way if this DLL is going to be used only by .NET.
Use an "extern "C"" compatible API, like the Windows API itself. This is the most portable, but isn't as convenient for your callers as using a class model to represent your objects.
This is the best option if you really intend to write in ANSI C (not C++).
For this path, you write your functions as extern "C" returntype __stdcall __declspec(dllexport) func(params) { ... }
You should also use a "caller-provides-the-buffer" memory model, rather than returning a buffer allocated inside your library. In the cases where you do need to allocate memory for internal state, the caller should see it as an opaque handle and you should provide accessor functions for the caller to extract data. Under no circumstances should the caller be expected to deallocate memory allocated inside your library, however it is ok for the caller to ask the library to do the deallocation.
Use COM, or a COM-like API. Here you return (often via out parameter) a pointer to an interface, which is a class with pure virtual functions, no non-virtual functions and no data.
The implementation is in concrete classes derived from this abstract interface, they can have data and helper functions galore, since that doesn't affect the binary interface.
This is a lot more work in the library but extremely portable and easy for the consumer to use.
And there is one thing absolutely NOT to do:
use __declspec(dllexport) on C++ classes.
EDIT: I want to also explain some good practices for option #2 which will maximize portability and make the native C/C++ parts usable from unmanaged applications as well.
You can make that easier with a macro, the usual way of doing it is:
In your header file, all the function declarations look like
MYPROJECTAPI(returntype) PublicFunc(params);
In your project, the definition is
#define MYPROJECTAPI(returntype) \
extern "C" returntype __stdcall __declspec(dllexport)
In consumer projects
#define MYPROJECTAPI(returntype) \
extern "C" returntype __stdcall __declspec(dllimport)
and then you can define the macro differently for other compilers like gcc which don't use __declspec.
The complete solution would look like (in public header file myproject.h):
#if _WIN32
# if BUILDMYPROJECT
# define MYPROJECTAPI(returntype) \
extern "C" returntype __stdcall __declspec(dllexport)
# else
# define MYPROJECTAPI(returntype) \
extern "C" returntype __stdcall __declspec(dllimport)
# endif
#else
# define MYPROJECTAPI(returntype) extern "C" returntype
#endif
and then your Visual C++ project would cause BUILDMYPROJECT to be defined when building myproject.dll
In a nutshell:
(1) Create a new C++/CLI library project.
(2) Write your code. For classes that need to be accessible from your C# project, make sure to create them as CLR classes:
public ref class R {/*...*/}; // CLR class
public value class V {/*...*/}; // CLR struct
public interface class I {/*...*/}; // CLR interface
(3) Compile the project and add a reference to it in your C# project.
Through P/Invoke layer.
http://en.wikipedia.org/wiki/Platform_Invocation_Services
Below is an example for an application where I had to do just that. In my case, I needed a DLL to wrap calls to functions that were only available in a .lib. The key part is the extern "C" __declspec (dllexport) in the declaration. That's basically all you need. The rest was merely using dllimport in the C# app and getting the marshalling right.
extern "C" __declspec (dllexport) LONG EstablishContext(DWORD dwScope,
LPCVOID pvReserved1,
LPCVOID pvReserved2,
LPSCARDCONTEXT phContext)
{
return SCardEstablishContext(dwScope, pvReserved1, pvReserved2, phContext);
}