Passing list of struct to C++ DLL from C# Application - c#

I am trying to pass struct as a parameter from C# application to the C++ MFC DLL. DLL fills records in the struct object and return back to the C# application. So here I used "out" keyword in C# application to call C++ method. When I execute it, it fails with error "Attempted to read or write protected memory. This is often an indication that other memory is corrupt." I am allocating memory at C++ DLL to store the records and assign it to out parameter. I could send data in struct object and print it in C++ DLL but not able to do modification in struct object at DLL side and return back to C# application.
Could anybody please help on this.
NativeDLLHelper.cs
class NativeDLLHelper
{
const int FIELD_LENGTH = 255;
[StructLayout(LayoutKind.Sequential)]
public struct APPDATA
{
public int ID;
public int Version;
[MarshalAs(UnmanagedType.LPTStr, SizeConst = FIELD_LENGTH)]
public string AppName;
};
[DllImport("Parser.dll", EntryPoint = "?FillLstStructData#DLLWrapper##QAEXAAPAU_APPDATA#1##Z")]
public static extern void FillLstStructData(out APPDATA[] AppDataStruct);
}
Main.cs
NativeDLLHelper.APPDATA[] lstFillAppDataStruct;
NativeDLLHelper.FillLstStructData(out lstFillAppDataStruct);
for (int i = 0; i < lstFillAppDataStruct.Length; i++)
{
Console.WriteLine(" ID[{0:G}]:{1:G}", i, lstFillAppDataStruct[i].ID);
Console.WriteLine(" Version[{0:G}]:{1:G}", i, lstFillAppDataStruct[i].Version);
Console.WriteLine(" AppName[{0:G}]:{1:G}", i, lstFillAppDataStruct[i].AppName);
}
C++ DLL:
static const int FIELD_LENGTH = 128;
typedef struct _APPDATA
{
int ID;
double Version;
TCHAR AppName[FIELD_LENGTH];
}APPDATA;
extern "C" __declspec(dllexport) FillLstStructData(APPDATA* &pAppData)
{
APPDATA *localAppDataStruct = new APPDATA[2];
localAppDataStruct[0].ID = 1;
localAppDataStruct[0].Version = 1.1;
_tcscpy(localAppDataStruct[0].AppName, L"MS Visual Studio 2010");
localAppDataStruct[1].ID = 2;
localAppDataStruct[1].Version = 2.1;
_tcscpy(localAppDataStruct[1].AppName, L"MS Office 2010");
pAppData = localAppDataStruct;
}

Your exported function unmangles to:
void __thiscall DLLWrapper::FillLstStructData(struct DLLWrapper::_APPDATA * &)
That makes it an instance method of a C++ class. You cannot pinvoke such methods, they require the C++ object to be created first and you cannot do this reliably with pinvoke. Only static C++ member functions can be pinvoked. Do note that this function doesn't need to be an instance method at all, it doesn't actually use instance members of the class.
That isn't the only problem, there's also a nasty memory management issue. The function allocates memory with ::operator new and memory needs to be released by the caller. But a C# program cannot do this, it doesn't have access to the ::operator delete that the C++ code uses. This kind of function is very hard to use reliably from a C++ program for that reason, it doesn't get better when you do it from C#. The proper way to do it is to allow the caller to pass the array. In other words, APPDATA[] and an extra argument that says how long the passed array is.
If you can't rewrite this function then you must write a wrapper for this class using the C++/CLI language. Very important that this wrapper uses the exact same compiler and CRT version as the C++ DLL.

Related

C DLL does NOT Work in C# but Does work in C++

I don`t understand this code below, that it is working with my c++ but it just does not want to work with c#.
Can you please help me to understand what is wrong here and i think i have to say that i am absolutely new to C#.
My_Lib.h
extern "C" __declspec(dllexport) void __cdecl get(char** buffer);
My_lib.c
void get(char** buffer)
{
*buffer = (char*)calloc(6, sizeof(char));
assert(*buffer);
buffer[5] = '\0';
*buffer = "Hello";
}
in my C#----->
public static class NativeMethods
{
[DllImport("My_C_Lib.dll", CallingConvention = CallingConvention.Cdecl)]
unsafe public static extern void get(char** buffer);
}
//////////////////// Main()///////
unsafe
{
char* my_buf;
NativeMethods.get(&my_buf);
string s = new string(my_buf);
Console.WriteLine(s);
}
NOTE: Actually my DLL does Work when i call this c function from c++ but as i said above NOT in C#, there is no Errors but string s variable in C# prints some undefined sibols, but DLL "works"
thanks in advance!!!
The code is nearly correct, but...
C DLL does NOT Work in C# but Does work in C++
In C and C++ char is a 8-bit data type. In C# char is a 16-bit data type.
This means that C# expects that the pointer returned by the get() function is expected to be a "wide string", while in C++ expects an "ANSI string".
I simply changed one single line in your program:
*buffer = "H\0e\0l\0l\0o\0\0\0";
... and it works!
You may of course also use the "wide string" functions of the modern C compilers:
void get(wchar_t** buffer)
{
*buffer = L"Hello";
}
By the way
There is another error in your program:
*buffer = (char*)calloc(6, sizeof(char));
...
*buffer = "Hello";
This makes no sense:
The second line (*buffer = "Hello";) will not copy the string to the memory allocated by calloc, but it will write the address of the string "Hello" to the variable buffer and the value (address) returned by calloc is overwritten (and lost).
Your best bet is to change the PInvoke signature of your C function to taking a ref IntPtr:
[DllImport("My_C_Lib.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void get(ref IntPtr buffer);
To call this, you'll need to instantiate an IntPtr and pass it by reference to the C function (this is analagous to declaring a char * in C and passing it by address):
IntPtr ptr;
get(ref ptr);
// ptr is now an unmanaged pointer to the allocated string
Now, you need to convert the pointed-to string to a managed string. To do this you must make a copy of the string.
string str = Marshal.PtrToStringAnsi(ptr);
Be sure you understand the distinctions between ANSI and Unicode strings and make sure you call the appropriate PtrToString...() variant.
Note that the .NET runtime will not manage the allocated ptr for you since it was allocated by unmanaged code. You must free it yourself using whatever the appropriate mechanism is for your DLL (ideally, the DLL should provide a corresponding free function since it is the one who allocated the memory in the first place).
For robustness, add appropriate error / null pointer checking. Make sure get() succeeded and that ptr is not IntPtr.Zero.
To avoid leaks, if there is any possibility of any code throwing an exception between the time get() returns and the time you free the pointer, the try/finally construct is your friend.
(Aside: Note that get is a Contextual Keyword in C# and thus while you can use it as an identifier, you may prefer not to in order to avoid confusion.)

Access Violation while exporting an unmanaged function pointer

I have been trying for the past 4 hours to solve a very mysterious problem.
I am writing some plugin for Notepad++. To achieve syntax highlighting one has to export such a function:
//this function is exported via exports.def file
LexerFactoryFunction SCI_METHOD GetLexerFactory(unsigned int index)
{
return (index == 0) ? RTextLexer::LexerFactory : nullptr;
}
where,
LexerFactoryFunction is typedef ILexer *(*LexerFactoryFunction)();
#define SCI_METHOD __stdcall
I have managed to get this thing working perfectly with C++, however another part of the plugin is written in C#, so I tried to merge the two using Fody Costura NuGet package ( so that the CLI .dll is embedded into the main .dll ), however with no success.
What I've tried :
public ref class RTextLexerCliWrapper
{
public:
delegate ILexer * GetLexerFactoryDelegate();
IntPtr GetLexerFactory()
{
return System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(_lexerFactoryPtr);
}
RTextLexerCliWrapper();
private:
GetLexerFactoryDelegate ^ _lexerFactoryPtr;
GCHandle gch;
~RTextLexerCliWrapper();
};
RTextLexerCliWrapper::RTextLexerCliWrapper()
{
_lexerFactoryPtr = gcnew GetLexerFactoryDelegate(&RTextLexer::LexerFactory);
gch = GCHandle::Alloc(_lexerFactoryPtr);
}
RTextLexerCliWrapper::~RTextLexerCliWrapper()
{
gch.Free();
}
This CLI wrapper, is referenced in my main .dll like this :
static RTextLexerCliWrapper _lexerWrapper = new RTextLexerCliWrapper();
[DllExport(CallingConvention = CallingConvention.Cdecl)]
static IntPtr GetLexerFactory(uint index)
{
return (index == 0) ? _lexerWrapper.GetLexerFactory() : IntPtr.Zero;
}
So what happens is, my .net function gets indeed called and the cli wrapper function is also called, and a function pointer is indeed returned. However any attempts to call that function pointer results in an access violation. Which means that either the type of the pointer is wrong or something else which I am currently missing. I have tried countless variations of the .net exported function with void *, StdCall etc. All result in the same problem.
Is there any other way to return a function pointer of a C++ class? Or well am I doing something completely wrong?
Thanks in advance!
So I have finally managed to found the solution to my problem.
First step was exporting the functions with the correct calling convention:
static RTextLexerCliWrapper _lexerWrapper = new RTextLexerCliWrapper();
[DllExport(CallingConvention = CallingConvention.StdCall)]
static IntPtr GetLexerFactory(uint index)
{
return (index == 0) ? _lexerWrapper.GetLexerFactory() : IntPtr.Zero;
}
The convention in this case had to be StdCall. Otherwise the stack pointer is invalidated, hence the exceptions.
Now in order to return a function pointer of a C++ instance things were a bit more tricky.
I am statically storing an instance of the CLI wrapper class so that it doesn't get GCed. ( _lexerWrapper ).
This instance has a function called GetLexerFactory which return a function pointer of the C++ instance ( which is then used by some other .dll to get actual instances of some object ).
The CLI Wrapper class looks like this:
public ref class RTextLexerCliWrapper
{
public:
delegate ILexer * GetLexerFactoryDelegate();
IntPtr GetLexerFactory()
{
return System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(_lexerFactoryPtr);
}
RTextLexerCliWrapper();
private:
GetLexerFactoryDelegate ^ _lexerFactoryPtr;
GCHandle gch;
~RTextLexerCliWrapper();
};
Where ILexer * is the type of the object we shall be returning later on.
RTextLexerCliWrapper::RTextLexerCliWrapper()
{
_lexerFactoryPtr = gcnew GetLexerFactoryDelegate(&RTextLexer::LexerFactory);
gch = GCHandle::Alloc(_lexerFactoryPtr);
}
RTextLexerCliWrapper::~RTextLexerCliWrapper()
{
gch.Free();
}
So, what we have managed here is, exporting through .NET a function pointer which is able to return a pure C++ object.

Passing an array of unknown size of structs from c# to c++ dll and back

I asked a similar question yesterday, but this is slightly different. I am having problems passing arrays of struct from c# to c++, and getting this back again.
Here is the c++ code. Firstly, the struct:
struct Tri
{
public:
int v1, v2, v3;
}
and now the c++ dll part:
extern "C" __declspec(dllexport) void Dll_TriArray(Tri *tri)
{
int num = 10;
tri = new Tri[num];
for (int i = 0; i < num; i++)
{
tri[i].v1 = i + 5;
tri[i].v2 = i + 10;
tri[i].v3 = i + 25;
}
}
and here's the c# code, again starting with the struct:
[StructLayout(LayoutKind.Sequential)]
public struct Tri
{
public int v1, v2, v3;
}
public class Testing
{
[DllImport("testing.dll")]
static extern void Dll_TriArray(out Tri[] tryArray);
public GetTriArray()
{
Tri[] triArray;
Dll_TriArray(out triArray);
}
}
So the triArray i get when calling the GetTriArray method will come back as null. I have thought about passing an IntPtr in as the argument, but then how does one marshal an intptr into/from an array of struct?
BTW - at this stage, i'm not interested in memory leaks.
I'm not an expert (by any means) in C# but the C++ part gets passed a pointer to Tri-struct, which in C++ can be used like an dynamic array you allocate and fill that correctly but you don't have a way to get it back, because from C-perspective you'd need to modify the caller's (C#) pointer but you only get a copy and not a reference to the original.
In C++ the closest thing to what you are tying to do, would be to change the prototype to void Dll_TriArray(Tri *&tri) (call by ref, not call by copy) but I'm not sure how to interface that with C# (probably Dll_TriArray(ref triArray); ).

How do I call this c function in c# (unmarshalling return struct)?

I want to use c# interop to call a function from a dll written in c. I have the header files.
Take a look at this:
enum CTMBeginTransactionError {
CTM_BEGIN_TRX_SUCCESS = 0,
CTM_BEGIN_TRX_ERROR_ALREADY_IN_PROGRESS,
CTM_BEGIN_TRX_ERROR_NOT_CONNECTED
};
#pragma pack(push)
#pragma pack(1)
struct CTMBeginTransactionResult {
char * szTransactionID;
enum CTMBeginTransactionError error;
};
struct CTMBeginTransactionResult ctm_begin_customer_transaction(const char * szTransactionID);
How do I call ctm_begin_customer_transaction from c#. The const char * mapps well to string, but despite various attempts (looking at stackoverflow and other sites), I fail to marshal the return structure. If I define the function to return IntPtr it works ok...
Edit
I changed the return type to IntPtr and use:
CTMBeginTransactionResult structure = (CTMBeginTransactionResult)Marshal.PtrToStructure(ptr, typeof(CTMBeginTransactionResult));
but it throws AccessViolationException
I also tried:
IntPtr ptr = Transactions.ctm_begin_customer_transaction("");
int size = 50;
byte[] byteArray = new byte[size];
Marshal.Copy(ptr, byteArray, 0, size);
string stringData = Encoding.ASCII.GetString(byteArray);
stringData == "70e3589b-2de0-4d1e-978d-55e22225be95\0\"\0\0\a\0\0\b\b?" at this point. The "70e3589b-2de0-4d1e-978d-55e22225be95" is the szTransactionID from the struct. Where is the Enum? Is it the next byte?
There's a memory management problem hidden in this struct. Who owns the C string pointer? The pinvoke marshaller will always assume that the caller owns it so it will try to release the string. And passes the pointer to CoTaskMemFree(), same function as the one called by Marshal.FreeCoTaskMem(). These functions use the COM memory allocator, the universal interop memory manager in Windows.
This rarely comes to a good end, C code does not typically use that allocator unless the programmer designed his code with interop in mind. In which case he'd never have used a struct as a return value, interop always works much less trouble-free when the caller supplies buffers.
So you cannot afford to let the marshaller do its normal duty. You must declare the return value type as IntPtr so it doesn't try to release the string. And you must marshal it yourself with Marshal.PtrToStructure().
That however still leaves the question unanswered, who owns the string? There is nothing you can do to release the string buffer, you don't have access to the allocator used in the C code. The only hope you have is that the string wasn't actually allocated on the heap. That's possible, the C program might be using string literals. You need to verify that guess. Call the function a billion times in a test program. If that doesn't explode the program then you're good. If not then only C++/CLI can solve your problem. Given the nature of the string, a "transaction ID" ought to change a lot, I'd say you do have a problem.
I hate to answer my own question, but I found the solution to marshal the resulting struct. The struct is 8 bytes long (4 bytes for the char * and 4 bytes for enum). Marshalling the string does not work automatically, but the following works:
// Native (unmanaged)
public enum CTMBeginTransactionError
{
CTM_BEGIN_TRX_SUCCESS = 0,
CTM_BEGIN_TRX_ERROR_ALREADY_IN_PROGRESS,
CTM_BEGIN_TRX_ERROR_NOT_CONNECTED
};
// Native (unmanaged)
[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
internal struct CTMBeginTransactionResult
{
public IntPtr szTransactionID;
public CTMBeginTransactionError error;
};
// Managed wrapper around native struct
public class BeginTransactionResult
{
public string TransactionID;
public CTMBeginTransactionError Error;
internal BeginTransactionResult(CTMBeginTransactionResult nativeStruct)
{
// Manually marshal the string
if (nativeStruct.szTransactionID == IntPtr.Zero) this.TransactionID = "";
else this.TransactionID = Marshal.PtrToStringAnsi(nativeStruct.szTransactionID);
this.Error = nativeStruct.error;
}
}
[DllImport("libctmclient-0.dll")]
internal static extern CTMBeginTransactionResult ctm_begin_customer_transaction(string ptr);
public static BeginTransactionResult BeginCustomerTransaction(string transactionId)
{
CTMBeginTransactionResult nativeResult = Transactions.ctm_begin_customer_transaction(transactionId);
return new BeginTransactionResult(nativeResult);
}
The code works, but I still need to investigate, if calling the unmanaged code results in memory leaks.

mixed programming for a string between C# managed code and c++ unmanaged code

I'm working on a c++ project which uses a string path to call an XML file. When i compiled the c++ everything works perfectly and i'm able to use the XML file as my project requires.
I needed to use a C# GUI, so i made a wrapper to call all my functions from my C++ file. One problem arises after debugging between the both platforms, c# does not recognize the string path to call my file, the error that i got is that it can not find the given path. Does anyone know how to send a valid string path between both platforms?
Thanks in advance,
Carolina
int ClassUnmanaged::ReadFile(string path_to_file)
{
int status = XMLClass->ReadConfigFile(path_to_file);
if(status)
{
return status; //Error
}
else
{
return 0;
}
}
Wrapper.h for the C++ class
public __gc class Wrapper
{
public: Wrapper(void);
public: ~Wrapper(void);
/** Unmanaged pointer to Class Unmanaged API
*
*/
private: ClassUnmanaged__nogc* pointerUnmanaged;
public: int NewReadfile(string path_to_file);
}
Wrapper.cpp
int Wrapper::NewReadFile(string path)
{
pointerUnmanaged->ReadFile(path);//here i access to my class unmanaged
return 0;
}
UI.cs
In the UI.cs i can not call the function NewReadfile from the wrapper because of the string type that c++ uses. Any idea how to solve this?
You will need to change the NewReadFile method to public and then change it to take as input a type that C# know about like Sytem::String it should look like this (using the new managed C++ syntax adapt to the old one if needed)
int Wrapper::NewReadFile(System::String^ path)
{
char* pathAsCharArray = (char*)(void*)Marshal::StringToHGlobalAnsi(str);
try
{
std::string pathAsStdString(pathAsCharArray);
pointerUnmanaged->ReadFile(pathAsStdString);
}
finally
{
Marshal::FreeHGlobal(pathUnmanaged);
}
return 0;
}
There is a KB article named "How to convert from System::String* to Char* in Visual C++" that explain the concept. If your underlying API could support unicode and you use the new syntax a better way to do the conversion is something like :
pin_ptr<const wchar_t> pathAsCharArray = PtrToStringChars(path);
std::wstring pathAsStrign(pathAsCharArray);

Categories

Resources