C# PInvoke out strings declaration - c#

In C# PInvoke, how do I pass a string buffer so that the C DLL fills it and returns? What will be the PInvoke declaration?
The C function declaration is
int GetData(char* data, int buflength);
In C#, I have declared it as
[DllImport(DllName)]
static extern Int32 GetData([MarshalAs(UnmanagedType.LPStr)]StringBuilder receiveddata, Int32 buflen);
Is it correct? I'm passing the StringBuilder variable like this
int bufferLength = 32;
StringBuilder data = new StringBuilder(bufferLength);
int result = GetData(data, bufferLength);
I would like to know is it correct or not?
Thanks

I believe it's correct.
[DllImport(DllName)]
static extern int GetData(StringBuilder data, int length);
which is called like this:
StringBuilder data = new StringBuilder(32);
GetData(data, data.Capacity);
I once wanted to have more control over the bytes returned by my function and did it like this:
[DllImport(DllName)]
private unsafe static bool GetData(byte* data, int length);
used like this:
byte[] bytes = new byte[length];
fixed(byte* ptr = bytes)
{
bool success = Library.GetData(ptr, length);
if (!success)
Library.GetError();
return Encoding.UTF8.GetString(bytes);
}

I don't think that using MarshalAs attribute necessary here. StringBuilder is a right choice for char* out.
I guess it will be good to add the CharSet property since you are dealing with strings here.
Like this:
[DllImport(DllName, CharSet=CharSet.Auto)]

Related

Pinvoke cdecl convention with char**

In summary:
I`m trying to use a C++ dll with cdecl calling convention all ran fine unless i get to this method signature:
int SaveToBuffer( char **buf, int *buf_size );
from what i have read i should use it like this:
[DllImport("entry.dll",
CallingConvention = CallingConvention.Cdecl,
EntryPoint = "SaveToBuffer")]
private static int SaveToBuffer( ref sbyte[] buf, ref int buf_size );
This does not work if this function is called from C# program crashes.
I suppose this is related to Cdecl calling model and should use Marshal.AllocHGlobal(value),
I can`t imagine how should it be done correct.
I also tryed this:
[DllImport("entry.dll",
CallingConvention = CallingConvention.Cdecl,
EntryPoint = "SaveToBuffer")]
private static int SaveToBuffer( IntPtr buf, ref int buf_size );
And then alocate enough memory
IntPtr data=Marshal.AllocHGlobal(128000);
int bufSize=128000;
var sCode=SaveToBuffer(data,bufSize ); /* value of scode idicate succses*/
Calling this way i get return value from SaveToBuffer indicating function succseeded but: bufSize returns to 0 and how should i read my data from IntPtr.
I`m completly stuck on this.
This is not an issue with the calling convention. The problem is in the buffer handling.
There's really only one sensible way to interpret the C++ argument types and the apparent intent to return an array of bytes. That is that the buffer is allocated and populated by the callee, and its address returned in buf. The buffer length is returned in buf_size.
With these semantics the function arguments cannot be marshalled automatically and you'll have to do it manually:
[DllImport("entry.dll", CallingConvention = CallingConvention.Cdecl)]
private static int SaveToBuffer(out IntPtr buf, out int buf_size);
Call like this
IntPtr buf;
int buf_size;
int retval SaveToBuffer(out buf, out buf_size);
// check retval
Then copy to byte array like this:
byte[] buffer = new byte[buf_size];
Marshal.Copy(buf, buffer, 0, buf_size);
The DLL will also need to export a function to deallocate the unmanaged buffer.

Additional information: Attempted to read or write protected memory. This is often an indication that other memory is corrupt

I am trying to pass a byte array to a c++ dll:
c++:
extern "C" __declspec(dllexport) char* myfunction(byte bytes[])
{
char *byteschar = (char*)bytes;
//do somethings with it
return byteschar;
}
c#:
[DllImport("mydll", CallingConvention = System.Runtime.InteropServices.CallingConvention.Cdecl
,CharSet = CharSet.Ansi)]
[return: MarshalAs(UnmanagedType.LPStr)]
public static extern string myfunction(byte[] bytes);
but I get a System.AccessViolationException when I call myfunction.
When I run the executable without the debugger it seems to be working fine
If you want a buffer be allocated in C# and filled in C++, the approach is a little bit different.
You should allocate a kind of "unmanaged" buffer, pass to the DLL and then convert the result and free the buffer. It's exactly the same way in C, but calling from a managed environment.
Your C++ code should be something like:
extern "C" __declspec(dllexport) void myfunction(char* buffer, int length)
{
//Fill buffer with something observing the maximum length of the buffer.
}
The signature of your DLL in C# should be:
[DllImport("mydll", CallingConvention = System.Runtime.InteropServices.CallingConvention.Cdecl
,CharSet = CharSet.Ansi)]
public static extern string myfunction(IntPtr buffer, Int32 length);
To call it from C#, you should do:
IntPtr unmanagedBuffer = Marshal.AllocHGlobal(100);
// Your Unmanaged Call
myfunction(unmanagedBbuffer, 100);
string yourString = Marshal.PtrToStringUni(unmanagedBuffer);
Marshal.FreeHGlobal(unmanagedBuffer);
Don't forget to call FreeHGlobal if you don't want a memory leak in your app. It's interesting to protect this in "try/finally" clauses.
Other observation is the encoding of the string. Uni, means Unicode. If you use another string representation, check for an equivalent PtrToStringXXX function.
It suppose to be:
extern "C" __declspec(dllexport) char* myfunction(unsigned char * bytes)
{
//do somethings with it
return bytes;
}

C# DllImport and Marshaling char**

I'm working in c# and I need to use this function from a c++ dll:
extern "C" char IMPEXP __stdcall service_GetParameter ( const char* parameter, const int value_lenght, char** value );
I have used it in c++ code as follow:
char *val = new char[256];
service_GetParameter("firmware_version", 255, &val);
AnsiString FirmwareVersion = val;
delete[] val;
How can I import this function and use it in c#?
Thanks in advance
If this function allocates memory and makes the caller responsible for freeing it, I'm afraid you'll have to manage this manually: Declare the parameter as a ref IntPtr and use the methods of the Marshal class to get a String with a copy of the pointed data.
Then call the appropriate function for freeing the memory (as Dirk said, we can't say more about this without more information on the function).
If it really must be allocated before calling, it should be something looking like this:
[DllImport("yourfile.dll", CharSet = CharSet.Ansi)]
public static extern sbyte service_GetParameter ( String parameter, Int32 length, ref IntPtr val);
public static string ServiceGetParameter(string parameter, int maxLength)
{
string ret = null;
IntPtr buf = Marshal.AllocCoTaskMem(maxLength+1);
try
{
Marshal.WriteByte(buf, maxLength, 0); //Ensure there will be a null byte after call
IntPtr buf2 = buf;
service_GetParameter(parameter, maxLength, ref buf2);
System.Diagnostics.Debug.Assert(buf == buf2, "The C++ function modified the pointer, it wasn't supposed to do that!");
ret = Marshal.PtrToStringAnsi(buf);
}
finally { Marshal.FreeCoTaskMem(buf); }
return ret;
}
I'd start with something like this:
[DllImport("yourfile.dll", CharSet = CharSet.Ansi]
public static extern Int32 service_GetParameter([MarshalAs(UnmanagedType.LPStr)] String szParameter, Int32 value_length, [Out] StringBuilder sbValue);

get pointer on byte array from unmanaged c++ dll in c#

in c++ I have such function
extern "C" _declspec(dllexport) uint8* bufferOperations(uint8* incoming, int size)
I am trying to call it from c# like this
[DllImport("MagicLib.DLL", CallingConvention = CallingConvention.Cdecl)]
//[return: MarshalAs(UnmanagedType.ByValArray)]//, ArraySubType=UnmanagedType.SysUInt)]
public static extern byte[] bufferOperations(byte[] incoming, int size);
But I get the
Cannot marshal 'return value': Invalid managed/unmanaged type combination
((( The question is - how to marshal this correctly?
Thanks for reading my question
byte[] is a .Net array type with known length. You can't marshal byte* to it, because .Net does not know the length of output array. You should try manual marshalling. Replace byte[] with byte*. Then, do like this:
[DllImport("MagicLib.DLL", CallingConvention = CallingConvention.Cdecl)]
public static extern byte* bufferOperations(byte* incoming, int size);
public void TestMethod()
{
var incoming = new byte[100];
fixed (byte* inBuf = incoming)
{
byte* outBuf = bufferOperations(inBuf, incoming.Length);
// Assume, that the same buffer is returned, only with data changed.
// Or by any other means, get the real lenght of output buffer (e.g. from library docs, etc).
for (int i = 0; i < incoming.Length; i++)
incoming[i] = outBuf[i];
}
}
You don't need to use unsafe contexts in this case. Just use IntPtr.
[DllImport("MagicLib.DLL", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr bufferOperations(IntPtr incoming, int size);
And then you can use Marshal.Copy to get your byte-array from it.

How can I pass a pointer to an integer in C#

I have a C API with the signature:
int GetBuffer(char* buffer, int* maxSize)
In C, I will call it this way:
char buffer[4096];
int maxSize = 4096;
GetBuffer(buffer, &maxSize);
maxSize is set to the buffer size, and set with the actual size filled.
I need to call it from C#. How do I do that under "safe mode"?
One option is simply to use C# pointer types - this requires unsafe block (or modifier on method/class), and compiling with /unsafe:
[DllImport(...)]
static extern int GetBuffer(byte* buffer, ref int maxSize);
Buffer can be allocated in several different ways. One would be to use a pinned heap array:
fixed (byte* buffer = new byte[4096])
{
int maxSize = buffer.Length;
GetBuffer(buffer, ref maxSize);
}
Another is to use stackalloc, though this is only feasible for small buffers:
byte* buffer = stackalloc byte[4096];
int maxSize = 4096;
GetBuffer(buffer, ref maxSize);
This particular approach is virtually identical to your C code in terms of performance and allocation patterns.
Another option altogether is to use marshaling for heap arrays, and avoid pointers entirely.
[DllImport(...)]
static extern int GetBuffer([Out] byte[] buffer, ref int maxSize);
byte[] buffer = new byte[4096];
int maxSize = buffer.Length;
GetBuffer(buffer, ref maxSize);
This should work without unsafe code.
extern int GetBuffer(IntPtr buffer, ref int bufSize);
// ...
byte[] buf = new byte[kBufSize];
GCHandle handle = GCHandle.Alloc(buf, GCHandleType.Pinned); // possibly expensive call
IntPtr p = handle.AddrOfPinnedObject();
int size = buf.Length;
int ret = GetBuffer(p, ref size);
handle.Free();
You need to use what is called P\Invoke, and generate a function declaration that to reference the C function in the Dll from C#.
However, you have to be very careful when passing buffers in/out of unmanaged code. The framework will take care of some things for you but you may need to ensure that memory that you pass into the unmanaged call doesn't get moved by the Garbage collector.
[DllImport("Kernel32.dll", SetLastError=true)]
static extern Int32 GetBuffer(byte[] buffer,ref Int32 maxSize);
And to use it:
byte[] myBuf = new myBuf[4096];
Int32 maxSize = myBuf.Length;
GetBuffer(myBuf, ref maxSize);
Having a handle to the pointer doesn't fit the "safe mode" model at all; if the resource isn't managed by the Framework, it is unsafe.
One easy and safe option is to create a simple class that wraps the value, or any value like the following code:
public class Value<T> where T: struct
{
public static implicit operator T(Value<T> val)
{
return val.Value;
}
private T _value;
public Value(T value)
{
_value = value;
}
public Value() : this(default)
{
}
public T Value
{
get
{
return _value;
}
set
{
_value = value;
}
}
public override string ToString()
{
return _value.ToString();
}
}
Passing on instances of this class instead of the value itself works almost like working with pointers.

Categories

Resources