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.
Related
I am new to C# but have worked extensively with C++. I have a C++ function that needs to be called from C#. After reading some answers from SO and some googling, I conclude that I need to make a pure C interface to the function. I have done this, but am still confused about how to call it from C#.
The function in C++ looks like this:
int processImages(
std::string& inputFilePath, // An input file
const std::vector<std::string>& inputListOfDirs, // Input list of dirs
std::vector<InternalStruct>& vecInternalStruct, // Input/Output struct
std::vector<std::vector< int > >& OutputIntsForEachFile,
std::vector< std::vector<SmallStruct> >& vecVecSmallStruct, // Output
int verboseLevel
);
The same function, converted in C, looks like this:
int processImagesC(
char* p_inputFilePath, // An input file
char** p_inputListOfDirs, // Input list of dirs
size_t* p_numInputDirs, // Indicating number of elements
InternalStruct* p_vecInternalStruct, // Input/Output struct
size_t* p_numInternalStructs,
int** p_OutputIntsForEachFile, // a 2d array each row ending with -1
size_t* p_numOutputIntsForEachFile //one number indicating its number of rows
SmallStruct** p_vecVecSmallStruct, // Output
size_t* p_numInVecSmallStruct,
int verboseLevel
);
This is based on this advice.
Now I need to call this from C#, which is where the confusion is. I have tried my best to convert the structures.
The C# code looks like this:
[DllImport(
#"C:\path\to\cppdll.dll", CallingConvention=CallingConvention.Cdecl,
EntryPoint="processImagesC", SetLastError=true)]
[return: MarshalAs(UnmanagedType.I4)]
unsafe public static extern int processImagesC(
String inputFilePath,
String[] inputListOfDirs,
ref uint numInputListOfDirs,
// Should I use ref InternalStruct * vecInternalStruct?
ref InternalStruct[] vecInternalStruct,
ref uint numInternalStruct,
// Or ref int[] or ref int[][] or int[][]?
ref int[][] OutputIntsForEachFile,
ref uint numOutputIntsForEachFile,
// again, ref ..[], [][], or ref [][]?
ref SmallStruct[][] vecVecSmallStruct,
int verboseLevel
);
There are memory allocations for all the output variables (pointers) done within the C/C++ code. This likely means we need to declare the code as unsafe, correct?
How do we handle memory deallocation? Should I write another API (function) that does the deallocation of objects/arrays allocated by C/C++?
The C++ code needs to be standard compliant and platform independent, so I cannot insert any windows-specific things in it.
I hope someone could make sense of this and provide an answer or at least point me in the right direction.
Since there seems to be some interest in using It Just Works (IJW) with C++/CLI, I'll post some info about that, further google searches and research will need to be done to figure it all. C++/CLI can be enabled with a single compiler flag (/CLI, enabled through Property Page->General->Common Language Runtime Support). C++/cli is NOT c++, but rather just another managed language. C++/CLI classes can be compiled into dll's and called directly from other .NET projects (C#, VB.NET, ect). However, unlike the other .NET languages it can directly interact with C++ code.
This is an ok start to learning C++/CLI. The big thing to learn is the decorations that tell you the class is managed (.NET class) and not Vanila C++. The "ref" keyword decalres the definition as a .NET definition:
public ref class Foo{ public: void bar();};//Managed class, visible to C#
public ref struct Foo{};//Managed struct, visible to C#
All reference classes are referred to with Handles rather than pointers or references. A handle is denoted by the ^ operator. To make a new handle, you use gcnew, and to access functions/members of the handle, use the -> operator.
//in main
Foo^ a = gcnew Foo();
a->bar();
You often have to move structures common from C# to native types and back again. (such as managed Array^ or String^ to void* or std::string). This process is called Marshaling. This handy table is pretty useful for figuring that out.
A common task is to create a wrapper for a native class, done like this:
//Foo.h
#include <string>
namespace nativeFoo
{
class Foo
{
private:
std::string fooHeader;
public:
Foo() {fooHeader = "asdf";}
std::string Bar(std::string& b) {return fooHeader+b;}
};
}
//ManagedFoo.h
#include "foo.h"
namespace managedFoo
{
public ref class Foo
{
private:
nativeFoo::Foo* _ptr;
public:
Foo(){_ptr = new nativeFoo::Foo();}
~Foo(){!Foo();}
!Foo(){if (_ptr){delete ptr;ptr = NULL;}}
String^ bar(String^ b)
{
return marshal_as<String^>(_ptr->bar(marshal_as<std::string>(b)));
}
};
}
Warning: I am totally missing a bunch of #include and #using statements, this is just to give a general gist of how to use this.
Begin from this:
How to pass structure as pointer in C dll from C#
And something about marshalling in this:
Deep copying an array c# without serialization
Note that Marshal.Copy also overloads for arrays use. With marshalling you can get rid of ref excepting that you do want to. Just write C/C++ in their way.
And following is a little bit complicated:
Physical disk size not correct (IoCtlDiskGetDriveGeometry)
The 2 ways I've often seen this handled is to either have a 'FreeResource' style API, or specify in the function the size of output buffers.
Method 1
C++
void GetName(char ** _str)
{
if (!_str)
return; // error
*_str = new char[20];
strcpy(*str, "my name");
}
void FreeString(char * _str)
{
delete str;
}
Client (any language)
char * name;
GetName(&name);
...
FreeString(name);
Method 2
C++
void GetName(char * _str, size_t _len)
{
if (_len < 20)
return; // error
strcpy(str, "my name");
}
Client (any language)
char * name = new char[20];
GetName(name, 20);
...
If you are willing to use third party tool, there is a tool named C#/.NET PInvoke Interop SDK may be helpful to you. But you can do it yourself as well. For simple classes with a few methods, you can write your own code in managed C# code.
The basic idea of instantiating a C++ object from .NET world is to allocate exact size of the C++ object from .NET, then call the constructor which is exported from the C++ DLL to initialize the object, then you will be able to call any of the functions to access that C++ object, if any of the method involves other C++ classes, you will need to wrap them in a C# class as well, for methods with primitive types, you can simply P/Invoke them. If you have only a few methods to call, it would be simple, manual coding won't take long. When you are done with the C++ object, you call the destructor method of the C++ object, which is a export function as well. if it does not have one, then you just need to free your memory from .NET.
Here is an example.
public class SampleClass : IDisposable
{
[DllImport("YourDll.dll", EntryPoint="ConstructorOfYourClass", CharSet=CharSet.Ansi, CallingConvention=CallingConvention.ThisCall)]
public extern static void SampleClassConstructor(IntPtr thisObject);
[DllImport("YourDll.dll", EntryPoint="DoSomething", CharSet=CharSet.Ansi, CallingConvention=CallingConvention.ThisCall)]
public extern static void DoSomething(IntPtr thisObject);
[DllImport("YourDll.dll", EntryPoint="DoSomethingElse", CharSet=CharSet.Ansi, CallingConvention=CallingConvention.ThisCall)]
public extern static void DoSomething(IntPtr thisObject, int x);
IntPtr ptr;
public SampleClass(int sizeOfYourCppClass)
{
this.ptr = Marshal.AllocHGlobal(sizeOfYourCppClass);
SampleClassConstructor(this.ptr);
}
public void DoSomething()
{
DoSomething(this.ptr);
}
public void DoSomethingElse(int x)
{
DoSomethingElse(this.ptr, x);
}
public void Dispose()
{
Marshal.FreeHGlobal(this.ptr);
}
}
For the detail, please see the below link,
C#/.NET PInvoke Interop SDK
The tool, xInterop NGen++ 2.0 has been released. Please check it out if you are interested in creating C# wrapper for native C++ DLL.
(I am the author of the SDK tool)
[DllImport(MyUnmanagedLibrary, CallingConvention = CallingConvention.Cdecl)]
internal static extern int MyFunction();
Let's say this is a function that I import from an unmanaged C++ library. In this instance the external function is declared as returning an int. The size of int does not change in managed C# code, but it does change depending on the architecture in unmanaged C++ code. Is it safe to assume that it returns an int? Should I use IntPtr instead, as the return value? Or something else?
int in C# is and will always be System.Int32, the point is that every C++ compiler is (in theory) free to do whatever it wants with types sizes (within certain limits), so, if you want to avoid a mismatch, you should change the C++ side of code, making it return a fixed-size integer (e.g. int32_t). IntPtr cannot help, since int is not defined to have the size of a pointer (and actually, on x86_64 Windows int is 32 bit, while int * is 64 bit).
On the other hand, it's extremely unlikely that any compiler on x86 Windows will ever define int as anything different than a 32 bit 2's-complement integer, for obvious binary compatibility reasons.
You need to set the compiler option with "-m32" or "-m64" for g++ for example. More specific choose the best match based on your "Machine Dependent Options"
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.
I'm using an API (written in c++) to connect to a DVR machine, actually I only have the .dll and the .lib files, and I want to do the job in .NET (C#).
So, the API doc contains definitions to all functions inside the dll, and I'm having a hard time trying to figure out how to translate those functions to equivalente C# definitions. Some of them are straightforward, but some I cannot make it work.
For example, there is this function:
bool searchEvent(int channel, const LONG* condition, bool next)
In my c# class I put the following:
[DllImport("search.dll")]
protected static extern bool searchEvent(int channel,
[MarshalAs(UnmanagedType.I8)]long condition, bool next);
but when I call the searchEvent function, it raises an error (memory error), I guess because of that long var. So my question is how can I translate "const LONG* condition" to c#?
Also, that searchEvent function triggers a callback that returns a struct. I am unable to translate some vars of that struct, like:
char _version[2]
time_t _time
BYTE* _minute
unsigned short int _dwell[MAX]
Can someone help me with this, please? I´m no expert in c++, so any tips are greatly appreciated. Thanks!
[DllImport("search.dll")]
[return: MarshalAs(UnmanagedType.U1)]
private static extern bool searchEvent(
int channel,
ref int condition,
[MarshalAs(UnmanagedType.U1)] bool next
);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
private struct Mumble {
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 2)]
public string version;
public long time;
public IntPtr minute;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX)]
public ushort _dwell[];
}
You will have to marshal the Mumble.Minute value yourself with Marshal.ReadXxxx(). These declarations are not great for interop, writing a wrapper in the C++/CLI language is advisable.
Without getting into the specifics, you need to overcome two hurdles:
1) Since you don't have the original .h (header) file, you're guessing as to what it could be, based on the API documentation. Hopefully you'll get it right, but there may be some subtle things in the header file that won't be in the documentation, such as structure packing, calling convention, name mangling, etc. You can start by assuming the defaults for this stuff. Bottom line you have to experiment.
2) You are attempting to create P/Invoke calls into a C DLL when you don't have the original header file. You don't know even know if you know how to make these calls from C, much less C#.
By doing this straight from C#, you're trying to attack these two issues at the same time. when something doesn't work, you're not sure if it's because you go the P/Invoke syntax wrong, or it's an issue related to your interpretation of what the header file content looks like. I'd try to attack these problems one at a time. First, try to make the DLL calls from a simple C console app. This shouldn't be difficult to create, then hand build a .H file, and very that you can make all the calls.
After you're done with that, you now have to get those calls to work from C#.
Frankly, I avoid P/Invoke unless I can just reuse one that someone else created (from http://PInvoke.net) for the Windows API. If I were you, and I already new how to make the calls from C, I'd just create a C++/CLI wrapper DLL for it. The advantage is that, you make the calls exactly as you do in the console app -- no P/Invoke necessary. And then expose those calls from the C++/CLI DLL as .NET. Your C# code will call the C++/CLI DLL, which looks just like (and is) a .NET component, with no weird P/Invoke stuff in the C# code.
Good luck.
I know how to extern methods in the .dll, how do I extern structs?
I want to create a C method such as
extern __declspec(dllexport) myStructure getStruct();
where myStructure is something like
typedef struct
{
int A;
int B;
char C;
} myStructure;
How can I call getStruct() from a piece of C# code without first defining the same exact struct in C#? I want to keep a centralized declaration of myStructure so that I only have to make code changes in one place.
Thanks a bunch!
What about doing it another way? Define the struct in C# in T4 and then cross gen to the new format in each of the projects:
ie. base.tt
// C# definition of struct
in gencsharp.tt in the C# project
include "base.tt"
... output the C#
in gencplusplus.tt in the CPP project
include "base.tt"
... use C# to gen a cpp .h file
I've done a bit of P/Invoke with C# and I've always had to define structs in C# that correspond to the Win32 struct by definition. The runtime uses the struct definition to marshal the data from unmanaged to managed. Preet's answer is probably the best.