Copying a string to a fixed length byte buffer in a structure - c#

given this structure in c#:
[StructLayout(LayoutKind.Sequential)]
unsafe public struct AppVPEntry
{
public int Num;
public fixed byte CompName[256];
public int VPBeginAddress;
}
Whats the easiest way to copy a string ("c:\path\file.txt") to the fixed length buffer 'CompName'. This is in a structure thats being sent over to an archaic DLL that we've got no choice but to use. Ideally I'd love to use a .NET function but since it's fixed which implies 'unsafe' I know I'm limited here. A more generic function would help since we've got strings like this all over the DLL import space.

// C# to convert a string to a byte array.
public static byte[] StrToByteArray(string str)
{
System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
return encoding.GetBytes(str);
}
You probably want to check to see if the size of the string isn't longer than the size of the buffer.

Try this out. Use an IntPtr in your DllImport wherever you might pass a VPEntry. Pass the "unmanaged" field wherever you call your DLL method.
public sealed class AppVPEntry : IDisposable {
[StructLayout(LayoutKind.Sequential, Size = 264)]
internal struct _AppVPEntry {
[MarshalAs(UnmanagedType.I4)]
public Int32 Num;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
public Byte[] CompName;
[MarshalAs(UnmanagedType.I4)]
public Int32 VPBeginAddress;
}
private readonly IntPtr unmanaged;
private readonly _AppVPEntry managed = new _AppVPEntry();
public AppVPEntry(Int32 num, String path, Int32 beginAddress) {
this.managed.Num = num;
this.managed.CompName = new byte[256];
Buffer.BlockCopy(Encoding.ASCII.GetBytes(path), 0, this.managed.CompName, 0, Math.Min(path.Length, 256));
this.managed.VPBeginAddress = beginAddress;
this.unmanaged = Marshal.AllocHGlobal(264);
Marshal.StructureToPtr(this.managed, this.unmanaged, false);
}
public void Dispose() {
Marshal.FreeHGlobal(this.unmanaged);
}
}

Related

Read Memory Memory Mapped File C++ and C#

I am trying to share a structure coming from C++ to C# using memory mapped file. So far I managed to write on the file, but I am unable to read the content in C#.
SendData in C++
struct Bus_1553 // this is the structure to send
{
string name;
int directions;
};
struct Bus_1553* p_1553; // set the pointer to it
HANDLE handle; // create the handle
// here we define the data to send
string name = "IFF";
int directions = 3;
bool startShare() // Open the shared memory
{
try
{
handle = CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(Bus_1553), L"DataSend");
p_1553 = (struct Bus_1553*) MapViewOfFile(handle, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, sizeof(Bus_1553));
return true;
}
catch (...)
{
return false;
}
}
int main()
{
if (startShare() == true)
{
while (true)
{
if (p_1553 != 0) // populate the memory
{
p_1553->name = name;
p_1553->directions = directions;
}
else
puts("create shared memory error");
}
}
if (handle != NULL)
CloseHandle(handle);
return 0;
}
Trying to read in C#
namespace sharedMemoryGET
{
class sharedMemoryGET
{
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public unsafe struct Bus_Data_1553
{
public string name;
public int directions; // which directions used
}
public static MemoryMappedFile mmf;
public static MemoryMappedViewStream mmfvs;
static public bool MemOpen() // open the mapped file
{
try
{
mmf = MemoryMappedFile.OpenExisting("DataSend");
return true;
}
catch
{
return false;
}
}
public static void readData()
{
if (MemOpen())
{
using (var accessor = mmf.CreateViewAccessor())
{
accessor.Read(0, out Bus_Data_1553 a);
Console.WriteLine(a.name);
Console.WriteLine(a.directions);
}
}
}
}
}
When a string is present in the structure to share, I have the following error:
The specified Type must be a struct containing no references.
When I remove the string and share only the int directions, i get a value of 0. Can someone help me figure this out?
Let's start with what's wrong with the C++ version. I'll bold this to make sure nobody ever passes over this, it's very important: NEVER WRITE POINTERS TO DISK
std::string is a wrapper around a pointer (2 pointers actually) that handle allocation and reallocation for you as needed. You absolutely cannot write them to a "file" anywhere, you must write the contents of those pointers instead.
One simplistic way (and prevalent in C) to do this is to simply define a buffer large enough to hold your data and then use as much of it as needed:
struct Bus_1553 // this is the structure to send
{
char name[128];
int directions;
};
To write to name, use strcpy_s or your OS equivalent.
Now once you write this structure in C++ to your shared file, reading it in C# is about letting the system (the marshaller) decode that soup of bytes into useful managed objects. You do this by using attributes on your structure and field definition:
[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
public struct Bus_Data_1553
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string name;
public int directions; // which directions used
}
Also you don't need unsafe for this if you use the marshaller properly.

Marshal array of struct into Ptr

I am calling a C library from a C# code. The function I am calling take as parameter a struct containing arrays of struct :
struct Example1Struct
{
char* a;
uint16_t b;
AnotherStruct* c;
}
c here is an array of pointer to AnotherStruct.
the struct in my C# code look like this
public struct Example1Struct
{
public IntPtr StationName;//is char*
public UInt16 IdCode;
public IntPtr AnotherStruct; //array of struct AnotherStruct
}
public static IntPtr MarshalToPointer(object data)
{
Type valueType = data.GetType();
IntPtr buf = IntPtr.Zero;
if (valueType.IsArray)
{
if (data is char[])
{
var d = data as char[];
buf = Marshal.AllocHGlobal(Marshal.SizeOf(d.GetType().GetElementType()) * d.Length);
}
else if (data is char[,])
{
var d = data as char[,];
buf = Marshal.AllocHGlobal(Marshal.SizeOf(d.GetType().GetElementType()) * d.Length);
}
else
{
buf = Marshal.AllocHGlobal(Marshal.SizeOf(data.GetType().GetElementType()) * count);
long LongPtr = buf.ToInt64(); // Must work both on x86 and x64
for (int I = 0; I < data.Lenght; I++)
{
IntPtr RectPtr = new IntPtr(LongPtr);
Marshal.StructureToPtr(data[I], RectPtr, false); // You do not need to erase struct in this case
LongPtr += Marshal.SizeOf(typeof(Rect));
}
}
return buf;
}
else
buf = Marshal.AllocHGlobal(Marshal.SizeOf(data));
Marshal.StructureToPtr(data, buf, false);
return buf;
}
my problem here is that I cannot cast data (who is an array of AnotherStruct) to object[] , neither in IEnumerable. So I cannot access to data[I] and don't have data.Lenght
Any idea ?
Usually I'd recommend using the MarshalAs attribute rather than writing manual marshalling code. It looks like:
public struct Example1Struct
{
public IntPtr StationName;//is char*
public UInt16 IdCode;
public IntPtr AnotherStruct; //array of struct AnotherStruct
}
Could be:
public struct Example1Struct
{
[MarshalAs(UnmanagedType.LPStr)]
public string StationName;
public UInt16 IdCode;
[MarshalAs(UnmanagedType.LPArray)]
public AnotherStruct[] OtherStructs;
}
And the marshaller should do the right thing for you when you pass it to unmanaged code.
You can get the length of the array like this:
if (data is Array a)
Console.WriteLine(a.Length);
Arrays in c# always derive from Array, so you can cast it to that.
But if possible in your real code, I'd recommend Damien's answer

C# How to P/Invoke with struct array in struct?

In C#, how to P/Invoke with struct array in struct?
C Lang defined struct is below...
struct 'OuterStruct'
int outerId
InnerStruct[10] innerStruct
struct 'InnerStruct'
int innerId
char[32] name
And C Lang defined Function is:
int ClangFunc(OuterStruct* arg)
'ClangFunc' is set values to 'arg'.
I call 'ClangFunc' from C#...
[DllImport("makefromclang.dll", EntryPoint="ClangFunc")]
public static extern int ClangFunc(IntPtr arg);
[StructLayout(LayoutKind.Sequential)]
public struct OuterStruct
{
public int outerId;
[MarshalAs(UnmanagementType.ByValArray, SizeConst=10)]
public InnerStuct[] innerStruct;
}
[StructLayout(LayoutKind.Sequential)]
public struct InnerStruct
{
public int innerId;
[MarshalAs(UnmanagementType.ByValTStr, SizeConst=32)]
public string name;
}
/* caller */
OuterStruct outerStruct = new OuterStruct();
IntPtr ptr = Marshal.AllocCoTaskMem(Marshal.SizeOf(outerStruct));
Marshal.StructureToPtr(outerStruct, ptr, false);
int result = ClangFunc(ptr);
OuterStruct resultStruct = (OuterStruct)Marshal.PtrToStructure(ptr, typeof(OuterStruct));
Call ClangFunc is succeeded.
Results of OuterStruct.outerId and OuterStruct.innerStruct[0].innerId are set collect values.(in above resultStruct value)
But OuterStruct.innerStruct[0].name is null, why?.
I expected ""(empty string) or any Shift_JIS string. There's no way to set null value.
Thanks all.
The problem is that the value set in char[] is Shift_JIS charset.
When .NET converted char[] to string, it did not consider the character set.
As a result, the string value is corrupted, and the debugger seems to have a null string value.
To solve this problem, I modified the string mapped to the structure to byte[] and convert byte[] to string.
// [MarshalAs(UnmanagementType.ByValTStr, SizeConst=32)]
// public string name;
[MarshalAs(UnmanagedType.ByValArray, ArraySubType=UnmanagedType.ByValTStr, SizeConst=32)]
public byte[] name;
string str = System.Text.Encoding.GetEncoding("Shift_JIS").GetString(name);

Passing parameters from c# to c++

As the topic says, trying to pass a struct from c# environnement to c++.
c++ code that defines both the struct and the interface:
#pragma pack(push, 4)
struct CEA708CONFIG
{
BYTE b608Service;
BYTE bCompactStream;
BYTE pActiveServices[63];
LONG lActiveServiceCount; //
POINT ptAlignmentPosition;
};
#pragma pack(pop)
interface
__declspec(uuid("{some clsid}"))
ICEA708Decoder : IUnknown {
virtual HRESULT SetConfig(IN const CEA708CONFIG* pConfig) = 0;
virtual HRESULT GetConfig(OUT CEA708CONFIG* pConfig) = 0;
};
now to the c# code, i defined the same struct in c#
[StructLayout(LayoutKind.Sequential, Pack = 4), Serializable]
public struct CEA708CONFIG
{
public byte is608Service;
public byte isCompactStream;
//[MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_UI1)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 63)]
public IntPtr activeServices;
public long activeServiceCount;
public Point alignmentPosition;
};
and the corresponding interface that accepts the config structure
[ComVisible(true), ComImport, Guid("same clsid as above"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface ICEA708Decoder
{
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int SetConfig([In, MarshalAs(UnmanagedType.Struct)] ref CEA708CONFIG config);
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int GetConfig([Out, MarshalAs(UnmanagedType.Struct)] out CEA708CONFIG config);
}
my problem occurs whenever i try to pass the structure, i can clearly see that while executing the c# code the entire struct is intialized with "reasonable" values, but once passed to the c++, i see that something has happened during the transaction.
the c# code that makes the magic happen:
CEA708CONFIG cc708Config;
ICEA708Decoder CC708DecoderConfig = CC708Filter as ICEA708Decoder;
if (CC708DecoderConfig == null)
{
throw new ApplicationException("Couldn't get ICEA708Decoder structure");
}
byte[] dataByte = new byte[63];
int size = Marshal.SizeOf(dataByte[0]) * dataByte.Length;
IntPtr pnt = Marshal.AllocHGlobal(size);
dataByte[0] = 1;
Marshal.Copy(dataByte, 0, pnt, dataByte.Length);
cc708Config.activeServices = pnt;
if (0 != (hr = CC708DecoderConfig.SetConfig(ref cc708Config)))
{
throw new ApplicationException("Couldn't SetConfig() because: " + DirectShowLib.DsError.GetErrorText(hr));
}
and the exception triggered by the SetConfig is:
{"Cannot marshal field 'activeServices' of type
'CCReIndexer.Graphs.CEA708CONFIG': Invalid managed/unmanaged type
combination (Int/UInt must be paired with SysInt or SysUInt).":""}
thanks for your help!!
Have you tried transfer array as array?
[StructLayout(LayoutKind.Sequential, Pack = 4), Serializable]
public struct CEA708CONFIG
{
public byte is608Service;
public byte isCompactStream;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 63)]
public byte[] activeServices;
public long activeServiceCount;
public Point alignmentPosition;
};
byte[] dataByte = new byte[63];
cc708Config.activeServices = dataByte;

How to Send C++ Struct Message from C# Socket?

This is the C++ Message that i need to pass from C#:
struct LoginMessage:public NMessage
{
char szUser[ 16 ];
char szPass[ 16 ];
LoginMessage()
{
msgId = CTRL_SESSION_LOGIN;
msgSize = sizeof( LoginMessage );
}
};
struct NMessage
{
DWORD msgSize;
union
{
DWORD msgId;
struct
{
BYTE msgId0;
BYTE msgId1;
BYTE msgId2;
BYTE msgId3;
};
};
NMessage()
{
}
BOOL IsLegal()
{
return msgSize>=sizeof(NMessage) && msgSize
What is the equivalent of this in C# so that C++ can understand this message?
Sample code is greatly appreciated.
To answer my own question..
[StructLayout(LayoutKind.Sequential)]
public struct LoginMessage
{
public int msgSize;
public int msgId;
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 16)]
public string szUser;
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 16)]
public string szPass;
}
It is important to take note of the sequence of the class properties so that the byte array you send to c++ is exactly the same as what it expects.
Have a look at this. it's a nice marshaling guid.
Take a look at the StructLayout attribute on MSDN. There is a good example on that page on how to create a struct that can be marshalled correctly. You can also look at http://www.pinvoke.net to see different examples of how the Windows API structs are defined to be marshaled. Once you have the struct defined right, you should be able to use Marshal.StructureToPtr or Marshal.PtrToStructure. Here's a short article on it: http://geekswithblogs.net/taylorrich/archive/2006/08/21/88665.aspx
I usually manually serialize data using something like this.
class NMessage {
byte[] buffer;
//ctor to create a new message for sending.
public NMessage(int nSize) {
buffer = new byte[nSize];
Buffer.BlockCopy(BitConverter.GetBytes(nSize), 0, buffer, 0, sizeof(UInt32));
}
//ctor to create msg from received data.
publix NMessage(byte[] buffer) {
this.buffer = buffer;
}
public UInt32 MessageId {
get { return BitConverter.ToUInt32(buffer, 4);
set { Buffer.BlockCopy(BitConverter.GetBytes(value), 0, buffer, 4, sizeof(UInt32)); }
}
...
public Byte MessageId2 {
get { return buffer[6]; }
set { buffer[6] = value; }
}
...
public UInt32 Size {
get { return BitConverter.ToUInt32(buffer, 0) }
}
public Byte[] Buffer {
get { return buffer; }
}
}
class LoginMessage : NMessage {
Encoding encoding = new ASCIIEncoding(); //or whatever encoding you need.
public LoginMessage() : base(16 + 16) {
this.MessageId = CTRL_SESSION_LOGIN;
}
public LoginMessage(NMessage message) : base(message.Buffer) {
}
public string User {
get { return encoding.GetString(buffer, 8, 16); }
set { Buffer.BlockCopy(encoding.GetBytes(value), 0, buffer, 8, 16);
}
public string Pass {
get { return encoding.GetString(buffer, 24, 16); }
set { Buffer.BlockCopy(encoding.GetBytes(value), 0, buffer, 24, 16);
}
}
So you simply create you new message, set it's data, then send the Buffer property.
Send((new LoginMessage {
User = "user",
Pass = "pass",
}).Buffer);
On the receiving end, if you have enough data, you can build the message from the byte[] you receive.
byte[] recvBuf = new byte[MAX_RECV];
int recvSize = Receive(recvBuf);
...
var message = new NMessage(recvBuf);
if (message.MessageId == CTRL_SESSION_LOGIN)
var login = new LoginMessage(message);
var user = login.User;
...
That's just a rough overview anyway, the code obviously needs a cleanup as I reduced it to only demonstrate the idea.

Categories