Reputation: 49
I am trying to figure how to correctly Marshal a C structure containing a byte * field so I can send commands to a third-party API from a C# application. However I keep receiving errors complaining about parameters. This is what I have, from C code:
struct MY_STRUCT
{
int32 Object; // Object index, dependent of the context
int32 Cmd; // Command code
byte *Params; // pointer to parameters
};
int32 stdcall SendCommand( int32 DeviceId, MY_STRUCT *Cmd );
This is how I'm currently referencing to it on my C# code:
public struct MY_STRUCT
{
public Int32 Object; // Object index, dependent of the context
public Int32 Cmd; // Command code
public IntPtr Params; // pointer to parameters
};
[DllImport("C:\\Client.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern Int32 SendCommand( Int32 DeviceId, ref MY_STRUCT Cmd );
Int32 CSharpSendCommand( int placa, int chan, int code, String parametros )
{
MY_STRUCT cmd = new MY_STRUCT();
cmd.Cmd = code;
cmd.Object = chan;
byte[] bytes = new byte[parametros.Length * sizeof(char)];
System.Buffer.BlockCopy(parametros.ToCharArray(), 0, bytes, 0, bytes.Length);
IntPtr p = Marshal.AllocHGlobal(bytes.Length);
try {
Marshal.Copy(bytes, 0, p, parametros.Length);
cmd.Params = p;
Int32 ret = Translated.SendCommand( placa, ref cmd );
if (ret != (int)LibraryStatus.Success)
Console.Out.Write("Erro: Comando 0x{0} retornou: {1}\n", code, ret);
return ret;
}
finally
{
Marshal.FreeHGlobal(p);
}
}
Any guesses? It works really fine when sending primitive types like Byte, Int32, so on an so forth...
Upvotes: 0
Views: 382
Reputation: 133975
Most likely the problem is that you're confusing bytes and characters. In particular, you have this:
byte[] bytes = new byte[parametros.Length * sizeof(char)];
System.Buffer.BlockCopy(parametros.ToCharArray(), 0, bytes, 0, bytes.Length);
IntPtr p = Marshal.AllocHGlobal(bytes.Length);
This is essentially correct in that you're taking into account that a char
is two bytes in length. But then you have:
Marshal.Copy(bytes, 0, p, parametros.Length);
This is a problem because you're only copying parametros.Length
bytes, which is exactly half the length of the bytes
array.
I think you want to change that to bytes.Length
.
The other problem I see is that you're not supplying anything that says how long that sequence of bytes is (i.e. no cbParams
value, or similar), and no null terminator if it's supposed to be treated like a string.
And, as Hans Passant points out, it's unlikely that the C code would understand a bunch of 2-byte "characters", anyway. His suggestion to use Marshal.StringToHGlobalAnsi
is probably right on the money.
Upvotes: 3