Sending Byte[][] inbetween C++ unmanaged dll and C# managed dll - c#

I have an unmanaged C++ dll that exports the following methods:
ERASURE_API void encode(unsigned char ** inp, unsigned char ** outp,
unsigned int *block_nums, size_t num_block_nums, size_t sz);
ERASURE_API void decode(unsigned char ** inp, unsigned char ** outp,
unsigned int * index, size_t sz);
Size of inp and outp can be as large as 10KB, What would be the best performance way to call these methods from C# managed code?
EDIT: I did the following implementation, and It works, but is it the most efficient way to do this.
C++ :
ERASURE_API void encode_w(unsigned char * inpbuf,int k, unsigned char * outpbuf,
int nfecs, unsigned int * block_nums, size_t num_block_nums, size_t sz)
{
unsigned char ** inp= new unsigned char*[k];
for(i=0;i<k;i++){
inp[i] = inpbuf+i*sz;
}
unsigned char ** outp= new unsigned char *[nfecs];
for(i=0;i<nfecs;i++){
outp[i] =outpbuf+i*sz;
}
encode(inp,outp,block_nums,num_block_nums,sz);
delete [] inp;
delete [] outp;
}
C#:
[DllImport("erasure.dll")]
public static extern void encode_w([In] byte[] inpbuf,int k,[Out] byte[] outpbuf,
int nfecs, uint[] block_nums, int num_block_nums, int sz);

Is C++/CLI an option? IMO, it's these sorts of complex interop/custom marshaling scenarios for which it was designed.

Errg that's some awesome Marshalling to be done there.
I've only ever come across one good article that deals with this sort of thing:
Marshalling a Variable-Length Array From Unmanaged Code In C#

Do you have to marshal data between managed and native heap? If you move all operation on the buffer to your native DLL you can avoid the cost of data copy between heaps.
That means you need to allocate the data on the native heap, return it in a ref IntPtr parameter, then hold the buffer's address in IntPtr (.Net equivalent of void*) and pass it around. After you are done with the buffer you can call another function in your native dll to delete the buffer. Use System.Runtime.InteropServices.Marshal.Copy when you have to copy the data to the managed heap (that's what the CLR marshaller called for marshalling built-in types).
Creating COM wrappers of the buffer-operating functions would be a little slower but makes the code more readable. However, allocating on the COM heap could be a little slower because managed code can also locking the COM heap.

I would recommend creating COM wrapper for these functions.

Related

Convert C++ structure to C# to read memory Mapped File/Shared Memory Object

While SharedMemoryObject (SMO)/ Memory Mapped Files are created and opened in same language looks good.
But, in my situation, SMO gets created in C++ for below structure.
typedef struct {
BOOL IsLoaded;
BYTE pBuffer[120];
} MyStruct;
typedef struct {
DWORD threadId;
DWORD values[5];
char exePath[250];
BOOL isThreadAlive;
MyStruct myData;
DWORD CurrentThreadInfo;
} SharedDataArea;
I need to read this detail using C#. So currently I am able to read Integers and Bytes, but Char pointers and arrays are giving me trouble.
Is there any way to convert/map the above C++ structure into C# code, so I can read SMO in standard way? I don't want to hardcode the offsets of the parameter.

Memory Leaks while calling C++ dll from C#

I have a C++ dll that I am calling from C# code.
the dll takes in couple params and returns an int..
C++ code.
extern "C" __declspec(dllexport) int DoSomething(char* input1, char* buffer)
{
gss_buffer_desc token;
std::string encodedTokenStr = base64_encode((unsigned char *)token.value, token.length).c_str();
std::copy(encodedTokenStr.begin(), encodedTokenStr.end(), buffer);
return value;
}
C#
public sealed class MyClass
{
public int DoSomething(string input1, out StringBuilder buffer)
{
buffer = new StringBuilder(10000);
return DoSomething(input1, buffer)
}
[DllImport("mycppcode.dll")]
private static extern int DoSomething(string input1, StringBuilder buffer)
}
sometimes I see that a lot of memory is being used by this application and my first thought was about memory leaks.
Does garbage collector takes care of all the objects that are initialized in the C++ code?
does C++ code initialize some memory for the string builder ("buffer") even if it is initialized in C#. I cannot dispose this in the C++ because i need to collect the data from the string builder.
I have never worked on C++ but I see that few objects that were declared in C++ dll are being cleared.
I might be doing something wrong in the way that I am calling the C++ code.
can this string builder cause memory leaks??
C# doesn't free any memory allocated by C++ (with some small exceptions if you use COM allocators/BSTR).
In your specific case
I don't see any problem in your case. There is no memory allocation in C++ code, and the StringBuilder is "built" C#-side, and being 10000 characters big, I do hope it is enough (note that normally you would pass to C++ the size of the buffer, or you would first ask to another C++ method the size of the needed buffer).
On a tangential note, I would suggest using wchar_t in C++, to maintain full compatibility with C# strings/chars/...

C++ int & long marshalling in C#

I'm currently porting a proprietary dll (an API) to C#, and I have some problems when marshalling some of the used data types.
For example the API header file defines the following types:
typedef unsigned int tINT; /* natural unsigned */
typedef signed int tINTs; /* natural signed */
typedef unsigned int tLONG; /* 32 bit unsigned */
typedef unsigned long tPTR; /* 32/64-bit pointer */
Then I for example have the following function definition:
tINTs SomeApiFunction ( const tPTR hdl, tINT param );
I'm usually marshalling the int directly with C#'s int (Int32), or the unsigned version. The API's tLONG and tINT are as such the same (at least for the API). But I'm not sure with the tPTR. I have now made it a uint in C#, because it is a 32bit int, but I'm not sure how the long would behave on a 64bit machine.
What do I have to do to make sure that the API will work on both 32bit and 64bit machines correctly?
This is what I would have done now:
[DllImport( "api.dll" )]
public static extern int SomeApiFunction ( uint hdl, uint param );
Sadly the API is not really well documented, as such I am not sure what the intention was with all those (overlapping) typedefs. Apart from the header comments I have included above there is no information about the actual types.
Try to Marshal it as an IntPtr. IntPtr is 4 bytes on a 32bit system and 8 bytes on a 64bit system in .Net.
It is an implementation detail of the C/C++ compiler. You should go by the comment and declare it IntPtr. If the code is compiled by the MSVC compiler then it isn't going to work in 64-bit mode, it uses 32-bits for a long. Not enough to store a pointer.
Here is good list of type correspondence.
For pointer types is usually IntPtr in C#.
If I remember correctly int uint and other primitive types are the right type for the underlying windows API.
For pointers however, its best to use IntPtr on C# side and void* on C/C++ side.

Ways To Marshal A Pointer of Array of Struct

I'm calling functions from C++ that returns a pointer to an array of struct and I'm having problems since I'm new to this operation/implementation.
My C++ codes:
// My C++ Structs
typedef struct _MainData {
double dCount;
DataS1 *DS1;
int iCount1;
DataS2 *DS2;
int iCount2;
}MainData;
typedef struct _DataS1 {
unsigned int uiCount1;
unsigned int uiCount2;
int iCount;
void *pA;
void *pB;
} DataS1;
typedef struct _DataS2 {
unsigned int uiCount1;
unsigned int uiCount2;
unsigned int uiCount3;
unsigned int uiCount4;
double dCount;
int iCount1;
char strLbl[64];
} DataS2;
// My C++ Function
MainData* GetData(const int ID)
{
MainData* mData;
int iLength = Get_Count();
mData = new MainData[iLength];
for(int x = 0;x < VarCounter; x++)
{
// Codes here assign data to mData[x]
}
return mData;
}
Question:
How can I call the C++ function GetData to C#?
My current codes in C# are:
[DllImport(".\\sdata.dll")]
[return: MarshalAs(UnmanagedType.LPArray)]
private static unsafe extern MainData[] GetData(int ID);
// The struct MainData in my C# side is already "Marshalled"...
//My function call is here:
MainData[] SmpMapData = GetData(ID);
When I compiled it, there's an exception:
"Cannot marshal 'return value': Invalid managed/unmanaged type combination."
Sorry for the poor coding... Please help...
First, you need to remember that MarshalAs (explicit or implicit) for the return value essentially means "copy native structures content into managed structures".
Second, since the CLR marshaler only copies the data, if you do not free the memory you're allocated in the C++ function, you've got a memory leak to manage.
Third, this error is mainly due to the fact that the CLR marshaler has no way of knowing the length of the array returned by the native code, since you're basically returning a memory pointer and no length.
If you want to keep these memory structures as-is, I strongly suggest you to look into C++/CLI. You'll be able to wrap those complex types into mixed native/managed classes that will avoid you to copy the data around. This will help you keep the data marshaling to the bare minimum between native and managed code.
If you still want to use C# and no C++/CLI, you'll have to write a somehow smarter piece of code to unmarshal the data returned by the native code into managed data. You can look into Custom Marshaling for that.
I don't see how the .NET runtime could possibly know how many MainData are allocated in GetData(...).
Refactor your C++ code to consume an array to populate or return single MainDatas.

Pass C# string to C++ and pass C++ result (string, char*.. whatever) to C#

I tried different things but i'm getting mad with Interop.
(here the word string is not referred to a variabile type but "a collection of char"):
I have an unmanaged C++ function, defined in a dll, that i'm trying to access from C#, this function has a string parameter and a string return value like this:
string myFunction(string inputString)
{
}
What should be string in C++ side? and C# one? and what parameters need DllImport for this?
What I've found to work best is to be more explicit about what's going on here. Having a string as return type is probably not recommended in this situation.
A common approach is to have the C++ side be passed the buffer and buffer size. If it's not big enough for what GetString has to put in it, the bufferSize variable is modified to indicate what an appropriate size would be. The calling program (C#) would then increase the size of the buffer to the appropriate size.
If this is your exported dll function (C++):
extern "C" __declspec void GetString( char* buffer, int* bufferSize );
Matching C# would be the following:
void GetString( StringBuilder buffer, ref int bufferSize );
So to use this in C# you would then do something like the following:
int bufferSize = 512;
StringBuilder buffer = new StringBuilder( bufferSize );
GetString( buffer, ref bufferSize );
The only good way that I know of doing this is to write a .NET C++ wrapper class using Managed C++ Extensions, and within the .NET C++ object call your native C++ code. There are functions in the managed extensions to convert a System.String to a char* or any other type of unmanaged string.
Basically you create a .NET class using C++ and expose it from an assembly, and internally to that assembly you can call your native C++ code. The other way is to add a pure C function to your C++ code using P/Invoke and then call your C code from C# and have your C function call your C++ code. This will work, but I tend to try to use managed code as much as possible.
The biggest problem with passing strings from C++ back to C# is the memory allocation. The GC should be able to know how to cleanup the memory allocated for this string. Since C# has extensive COm interop support, it does know about COM BSTRs and how to allocate and deallocate these. Thus the easiest way to do this would be to use BSTR on the C++ side and string on the C# side.
Note, using BSTRs does not imply that your function has to be expose through COM.
The "string" return value is the problem. The P/Invoke marshaller is going to call CoTaskMemFree() on the pointer you return. That's not going to work well unless you used CoTaskMemAlloc() in your C/C++ code to allocate the string buffer. Which is a fairly unusual thing to do.
The best solution is to allow the caller of your code to pass a pointer to a buffer and the buffer length to you as arguments. That way all memory allocation happens on one side. Scott showed you how to do this.
I had to convert a unicode C# string to a multibyte representation in order to convert to char* in c++ (this is partial one way solution)
I found the following very useful
string st;
IntPtr stPtr = Marshal.StringToHGlobalAnsi(st);
// Do your thing in C++
Marshal.FreeHGlobal(stPtr);
This may be inefficient and not in C# manner, I'm new to C#.

Categories