I am writing windows service to communication with minifilter (kernel).
Using FltSendMessage in minifilter
Using FilterGetMessage in service
The status of FilterGetMessage is success (status = 0). But the buffer is always null. What is not correct?
This is my code in minifilter: C++ code
status = FltSendMessage(
gFilterHandle,
&gClientPort,
(PVOID)FltObjects->FileObject->FileName.Buffer,
FltObjects->FileObject->FileName.MaximumLength,
NULL,
NULL,
NULL);
p/s: above code is put PreCreate callback
This is my code in service: C# code
// Constant buffer size
public const int BUFFER_SIZE = 1024;
// message header struct
[StructLayout(LayoutKind.Sequential)]
public struct FILTER_MESSAGE_HEADER {
public uint replyLength;
public ulong messageId;
}
// message receive struct
public struct DATA_RECEIVE
{
public FILTER_MESSAGE_HEADER messageHeader;
public byte[] messageContent;
}
DATA_RECEIVE dataReceive = new DATA_RECEIVE();
dataReceive.messageContent = new byte[BUFFER_SIZE];
int headerSize = Marshal.SizeOf(dataReceive.messageHeader);
int dataSize = BUFFER_SIZE + headerSize;
status = FilterGetMessage(hPort, out dataReceive.messageHeader, dataSize, IntPtr.Zero);
UPDATE: Although, status is success, the value of message header is 0
1. dataReceive.messageHeader.messageId = 0
2. dataReceive.messageHeader.replyLength = 0
UPDATE ANSWER:
1. Update old code:
// message receive struct
public struct DATA_RECEIVE
{
public FILTER_MESSAGE_HEADER messageHeader;
public fixed byte messageContent[BUFFER_SIZE];
}
And:
// Get data size
int dataSize = Marshal.SizeOf(dataReceive);
Add new code: After use FilterGetMessage function
// Check status and get content message
int status = FilterGetMessage(hPort, out dataReceive.messageHeader, dataSize, IntPtr.Zero);
if (status == 0)
{
int index = 0;
unsafe
{
byte* ptr = dataReceive.messageContent;
for (; *ptr != 0; ptr += 2)
{
messageContent[index++] = *ptr;
}
};
filePath = Encoding.ASCII.GetString(messageContent);
}
I can see one problem right away with this code:
public struct DATA_RECEIVE
{
public FILTER_MESSAGE_HEADER messageHeader;
public byte[] messageContent;
}
This does not create a continuous struct. This will create a struct with one FILTER_MESSAGE_HEADER and one reference to an array (which is not what the function expects). A byte[] is a reference, so the array data is not in the struct. If you try Marshal.SizeOf on the struct, you'll get 16 or 20 bytes, not sizeof( FILTER_MESSAGE_HEADER ) + the length of the buffer. What you need here is to use unsafe code with a fixed buffer:
public unsafe struct DATA_RECEIVE
{
public FILTER_MESSAGE_HEADER messageHeader;
public fixed byte messageContent[BUFFER_SIZE];
}
That will create a continuous struct, with the array data in the struct itself instead of somewhere else. Try this and verify the final size with Marshal.SizeOf.
Related
Following my other question here, I have been able to share string from C++ to C# thanks to this community.
However, I need to go one level above and I need to share chained structures from C++ to C# using memory mapping.
In an example scenario:
My C++ structures:
struct STRUCT_2
{
char Name[260];
};
struct STRUCT_1
{
void Init()
{
this->Count = 0;
this->Index = 0;
}
DWORD Count;
DWORD Index;
STRUCT_2 Table[256];
};
And me trying to "transfer" it to C#:
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi)]
public unsafe struct STRUCT_2
{
[FieldOffset(0)]
public fixed char Name[260];
}
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi)]
public unsafe struct STRUCT_1
{
void Init()
{
this.Count = 0;
this.Index = 0;
}
[FieldOffset(0)]
public uint Count;
[FieldOffset(0)]
public uint Index;
[FieldOffset(100)]
[MarshalAs(UnmanagedType.LPStruct, SizeConst = 256)]
public STRUCT_2 Table;
}
This works partially, basically I can see the values from Count and Index, however I cannot see values or even get them in STRUCT_2.
I have tried to change:
public STRUCT_2[] Table;
But then the compiler tells me:
"The specified Type must be a struct containing no references."
So my question would be, how can you read structures within structures using MemoryMappedFile in C# ?
Advice, thoughts or examples are very welcomed.
Update:
Complete testable code in C#:
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi)]
public unsafe struct STRUCT_2
{
[FieldOffset(0)]
public fixed byte Name[260];
// Fix thanks to Ben Voigt
}
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi)]
public unsafe struct STRUCT_1
{
void Init()
{
this.Count = 0;
this.Index = 0;
}
[FieldOffset(0)]
public uint Count;
[FieldOffset(0)]
public uint Index;
[FieldOffset(100)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
public STRUCT_2[] Table;
}
static void Main(string[] args)
{
MemoryMappedFileSecurity CustomSecurity = new MemoryMappedFileSecurity();
CustomSecurity.AddAccessRule(new System.Security.AccessControl.AccessRule<MemoryMappedFileRights>("everyone", MemoryMappedFileRights.FullControl, System.Security.AccessControl.AccessControlType.Allow));
var mappedFile = MemoryMappedFile.CreateOrOpen("Local\\STRUCT_MAPPING", 1024, MemoryMappedFileAccess.ReadWriteExecute, MemoryMappedFileOptions.None, CustomSecurity, System.IO.HandleInheritability.Inheritable);
using (var accessor = mappedFile.CreateViewAccessor())
{
STRUCT_1 data;
accessor.Read<STRUCT_1>(0, out data); // ERROR !
//The specified Type must be a struct containing no references.
Console.WriteLine(data.Count);
Console.WriteLine(data.Index);
}
}
Check this out.
Tested on Visual Studio 2017, Windows 7 x64.
Write and Read data works find.
It's also good study for me.
Take time on this.
Godspeed!
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct STRUCT_2
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 260)]
public byte[] Name;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 4)]
public struct STRUCT_1
{
void Init()
{
this.Count = 0;
this.Index = 0;
}
public uint Count;
public uint Index;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] //! array size of 10.
public STRUCT_2 [] Table;
}
static void test3()
{
MemoryMappedFileSecurity CustomSecurity = new MemoryMappedFileSecurity();
CustomSecurity.AddAccessRule(new System.Security.AccessControl.AccessRule<MemoryMappedFileRights>
( "everyone"
, MemoryMappedFileRights.FullControl
, System.Security.AccessControl.AccessControlType.Allow));
using (var mappedFile = MemoryMappedFile.CreateOrOpen("Local\\STRUCT_MAPPING"
, 10 * 1024
, MemoryMappedFileAccess.ReadWriteExecute
, MemoryMappedFileOptions.None
, CustomSecurity
, System.IO.HandleInheritability.Inheritable))
{
using (var accessor = mappedFile.CreateViewAccessor())
{
//! test setting.
int table_count = 5;
//! write data.
STRUCT_1 write_data;
write_data.Index = 1;
write_data.Count = 2;
write_data.Table = new STRUCT_2[10];
for (int i = 0; i < 10; i++)
{
write_data.Table[i].Name = new byte[260];
write_data.Table[i].Name[0] = (byte)i;
}
//! ----------------------------
// Get size of struct
int size = Marshal.SizeOf(typeof(STRUCT_1));
byte[] data = new byte[size];
// Initialize unmanaged memory.
IntPtr p = Marshal.AllocHGlobal(size);
// Copy struct to unmanaged memory.
Marshal.StructureToPtr(write_data, p, false);
// Copy from unmanaged memory to byte array.
Marshal.Copy(p, data, 0, size);
// Write to memory mapped file.
accessor.WriteArray<byte>(0, data, 0, data.Length);
// Free unmanaged memory.
Marshal.FreeHGlobal(p);
p = IntPtr.Zero;
//! ----------------------------------------------
STRUCT_1 read_data;
size = Marshal.SizeOf(typeof(STRUCT_1));
data = new byte[size];
// Initialize unmanaged memory.
p = Marshal.AllocHGlobal(size);
// Read from memory mapped file.
accessor.ReadArray<byte>(0, data, 0, data.Length);
// Copy from byte array to unmanaged memory.
Marshal.Copy(data, 0, p, size);
// Copy unmanaged memory to struct.
read_data = (STRUCT_1)Marshal.PtrToStructure(p, typeof(STRUCT_1));
// Free unmanaged memory.
Marshal.FreeHGlobal(p);
p = IntPtr.Zero;
Console.WriteLine(read_data.Index);
Console.WriteLine(read_data.Count);
}
}
}
How do I convert a structure that contains an array to a byte array in C#?
There was a question here about a struct without array.
But if the struct contains an array like this:
public struct DiObject
{
public byte Command;
public byte ErrorClass;
public byte Reserved;
public byte Flags;
}
public struct MyPacket
{
public uint ProtocolIdentifier;
public uint NumDi;
public DiObject[] Di;
}
It results with an access violation exception when converting the struct in a byte:
private static byte[] GetBytes(MyPacket packet, int packetSize)
{
var data = new byte[packetSize];
var ptr = Marshal.AllocHGlobal(packetSize);
// ==== Access violation exception occurs here ====
Marshal.StructureToPtr(packet, ptr, true);
Marshal.Copy(ptr, data, 0, packetSize);
Marshal.FreeHGlobal(ptr);
return data;
}
My goal is to send a message in bytes in a message queue with MSMQ.
Here the complete code that compiles and reproduce the problem.
using System;
//using System.IO;
//using System.Messaging;
using System.Runtime.InteropServices;
namespace StructToBytes
{
// 4 bytes
[Serializable]
public struct DiObject
{
public byte Command;
public byte ErrorClass;
public byte Reserved;
public byte Flags;
}
// 8 + (numDi*4) bytes
[Serializable]
public struct MyPacket
{
public uint ProtocolIdentifier;
public uint NumDi;
public DiObject[] Di;
}
internal class Program
{
private static byte[] GetBytes(MyPacket packet, int packetSize)
{
var data = new byte[packetSize];
var ptr = Marshal.AllocHGlobal(packetSize);
// ==== Access violation exception occurs here ====
Marshal.StructureToPtr(packet, ptr, true);
Marshal.Copy(ptr, data, 0, packetSize);
Marshal.FreeHGlobal(ptr);
return data;
}
private static MyPacket FromBytes(byte[] data)
{
var packet = new MyPacket();
var dataSize = Marshal.SizeOf(packet);
var ptr = Marshal.AllocHGlobal(dataSize);
Marshal.Copy(data, 0, ptr, dataSize);
packet = (MyPacket) Marshal.PtrToStructure(ptr, packet.GetType());
Marshal.FreeHGlobal(ptr);
return packet;
}
private static void Main(string[] args)
{
const string queuePath = #".\private$\test_msmq";
// Create the packet
var packet = new MyPacket();
// 8 bytes
packet.ProtocolIdentifier = 1;
packet.NumDi = 2;
// 8 bytes
packet.Di = new DiObject[packet.NumDi];
packet.Di[0].Command = 2;
packet.Di[0].ErrorClass = 3;
packet.Di[0].Flags = 4;
packet.Di[0].Reserved = 5;
packet.Di[1].Command = 6;
packet.Di[1].ErrorClass = 7;
packet.Di[1].Flags = 8;
packet.Di[1].Reserved = 9;
// Convert the struct in bytes
const int packetSize = 16;
var packetBytes = GetBytes(packet, packetSize);
// Create the message
/*
var msg = new Message();
msg.BodyStream = new MemoryStream(packetBytes);
// Open or create the message queue
if (!MessageQueue.Exists(queuePath))
MessageQueue.Create(queuePath);
// Open the queue
var q = new MessageQueue(queuePath); // {Formatter = new BinaryMessageFormatter()};
// Send the message to the queue
q.Send(msg);
*/
}
}
}
The problem lies with wrong assumption about how structure is represented in C#
// 8 + (numDi*4) bytes
[Serializable]
public struct MyPacket
{
public uint ProtocolIdentifier;
public uint NumDi;
public DiObject[] Di;
}
The assumption that size of public DiObject[] Di member is numDi * 4 is not true. In place of this field there is a pointer to the array of structures. Array is a class in .NET and is not included in place in structure declaration.
To solve this problem one can use fixed arrays. I understand that the idea behind the design is to get variable length array and it is presented in next code listing.
This code does not raise AccessViolationException during executin:
using System;
using System.IO;
using System.Messaging;
using System.Runtime.InteropServices;
namespace StructToBytes
{
// 4 bytes
[Serializable]
[StructLayout(LayoutKind.Explicit)]
public unsafe struct DiObject
{
[FieldOffset(0)]
public byte Command;
[FieldOffset(1)]
public byte ErrorClass;
[FieldOffset(2)]
public byte Reserved;
[FieldOffset(3)]
public byte Flags;
}
// 8 + (numDi*4) bytes
[Serializable]
public unsafe struct MyPacket
{
public uint ProtocolIdentifier;
public uint NumDi;
public fixed byte Di[2 * 4];
}
internal unsafe class Program
{
private static byte[] GetBytes(MyPacket packet, int packetSize)
{
var data = new byte[packetSize];
var ptr = Marshal.AllocHGlobal(packetSize);
// ==== Access violation exception occurs here ====
Marshal.StructureToPtr(packet, ptr, true);
Marshal.Copy(ptr, data, 0, packetSize);
Marshal.FreeHGlobal(ptr);
return data;
}
private static MyPacket FromBytes(byte[] data)
{
var packet = new MyPacket();
var dataSize = Marshal.SizeOf(packet);
var ptr = Marshal.AllocHGlobal(dataSize);
Marshal.Copy(data, 0, ptr, dataSize);
packet = (MyPacket)Marshal.PtrToStructure(ptr, packet.GetType());
Marshal.FreeHGlobal(ptr);
return packet;
}
private static void Main(string[] args)
{
const string queuePath = #".\private$\test_msmq";
// Create the packet
var packet = new MyPacket();
// 8 bytes
packet.ProtocolIdentifier = 1;
packet.NumDi = 2;
// 8 bytes
// packet.Di = new DiObject[packet.NumDi];
packet.Di[0] = 2;
packet.Di[1] = 3;
packet.Di[2] = 4;
packet.Di[3] = 5;
packet.Di[4] = 6;
packet.Di[5] = 7;
packet.Di[6] = 8;
packet.Di[7] = 9;
// Convert the struct in bytes
int packetSize = Marshal.SizeOf<MyPacket>();
var packetBytes = GetBytes(packet, packetSize);
// Create the message
var msg = new Message();
msg.BodyStream = new MemoryStream(packetBytes);
// Open or create the message queue
if (!MessageQueue.Exists(queuePath))
MessageQueue.Create(queuePath);
// Open the queue
var q = new MessageQueue(queuePath); // {Formatter = new BinaryMessageFormatter()};
// Send the message to the queue
q.Send(msg);
}
}
}
Code below provides efficient conversion to byte array and from byte array for MyPacket struct with variable internal array size. Implementation avoids casts and bounds checks by using unsafe pointer arithmetic.
using System;
using System.IO;
using System.Messaging;
using System.Runtime.InteropServices;
namespace StructToBytes
{
// 4 bytes
[Serializable]
[StructLayout(LayoutKind.Explicit)]
public unsafe struct DiObject
{
[FieldOffset(0)]
public byte Command;
[FieldOffset(1)]
public byte ErrorClass;
[FieldOffset(2)]
public byte Reserved;
[FieldOffset(3)]
public byte Flags;
}
[Serializable]
public unsafe struct MyPacket
{
public uint ProtocolIdentifier;
public uint NumDi;
public DiObject[] Di;
public byte[] ToBytes()
{
byte[] buffer = new byte[NumDi];
fixed(DiObject* pDi = Di)
fixed(byte* pBuff = buffer)
{
var pBuffDi = (DiObject*)pBuff;
var pDiPtr = pDi;
for (int i = 0; i < NumDi; i++)
*pBuffDi++ = *pDiPtr++;
}
return buffer;
}
public static MyPacket Create(byte[] buffer)
{
// argument checking code here
var packet = new MyPacket();
packet.ProtocolIdentifier = buffer[0];
packet.NumDi = buffer[1];
packet.Di = new DiObject[packet.NumDi];
fixed (byte* pBuf = buffer)
fixed (DiObject* pDi = packet.Di)
{
byte* pBufPtr = pBuf;
pBufPtr += 2;
var pBufDi = (DiObject*)pBufPtr;
var pDiPtr = pDi;
for (int i = 0; i < packet.NumDi; i++)
*pDiPtr++ = *pBufDi++;
}
return packet;
}
}
internal unsafe class Program
{
private static void Main(string[] args)
{
const string queuePath = #".\private$\test_msmq";
// Create the packet
var packet = new MyPacket();
// 8 bytes
packet.ProtocolIdentifier = 1;
packet.NumDi = 5;
// 8 bytes
packet.Di = new DiObject[packet.NumDi];
packet.Di[0].Command = 2;
packet.Di[0].ErrorClass = 3;
packet.Di[0].Flags = 4;
packet.Di[0].Reserved = 5;
packet.Di[1].Command = 6;
packet.Di[1].ErrorClass = 7;
packet.Di[1].Flags = 8;
packet.Di[1].Reserved = 9;
packet.Di[2].Command = 6;
packet.Di[2].ErrorClass = 7;
packet.Di[2].Flags = 8;
packet.Di[2].Reserved = 9;
packet.Di[3].Command = 6;
packet.Di[3].ErrorClass = 7;
packet.Di[3].Flags = 8;
packet.Di[3].Reserved = 9;
// Create the message
var msg = new Message();
msg.BodyStream = new MemoryStream(packet.ToBytes());
// Open or create the message queue
if (!MessageQueue.Exists(queuePath))
MessageQueue.Create(queuePath);
// Open the queue
var q = new MessageQueue(queuePath);
// Send the message to the queue
q.Send(msg);
}
}
}
Last night I was working on cleaning up some code I found that was a port of an old game I used to play. One of the tricks I used to clean up data was to get rid of the home-brew DataOffsetAttribute that was made for a struct and just make it a plain jane struct and then I can (later) convert it to something a little more useful. Kind of like a person would do with a DataLayer. This worked really good for fixed sized data from a file. Using this method I was about to convert between the two "data types".
public static byte[] ToByteArray<T>(this T dataStructure) where T : struct
{
int size = Marshal.SizeOf(dataStructure);
byte[] arr = new byte[size];
IntPtr ptr = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(dataStructure, ptr, true);
Marshal.Copy(ptr, arr, 0, size);
Marshal.FreeHGlobal(ptr);
return arr;
}
public static T MarshalAs<T>(this byte[] rawDataStructure) where T : struct
{
var type = typeof(T);
int size = Marshal.SizeOf(type);
IntPtr ptr = Marshal.AllocHGlobal(size);
Marshal.Copy(rawDataStructure, 0, ptr, size);
T structure = (T)Marshal.PtrToStructure(ptr, type);
Marshal.FreeHGlobal(ptr);
return structure;
}
So then I started to wonder if this would work on another project I worked on a long time ago where the data was variable. Here is what I was hoping my data structure would look like
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct RfidReaderResponse
{
public byte PreambleA;
public byte PreambleB;
public byte Length;
public byte LengthLRC;
public ushort ReaderId;
public byte CommandId;
public byte ErrorCode;
public byte[] Data;
public byte LRC;
}
I would probably combine the preamble bytes into a ushort and check if it is a specific value... but that is a different discussion. I have 3 known good responses that I used for testing back in the day. I put those into linqpad as well as my hopeful datastructure. So here is what I am currently using for testing
void Main()
{
var readerResponses = new string[]
{
//data is null because of error EC
"AA-BB-05-05-02-39-0C-EC-21",
//data is 44-00
"AA-BB-07-07-02-39-0C-00-44-00-8B",
//data is 44-00-20-04-13-5E-1A-A4-33-80
"AA-BB-0F-0F-02-39-10-00-44-00-20-04-13-5E-1A-A4-33-80-FB",
};
readerResponses
.Select(x=> x.ToByteArray().MarshalAs<RfidReaderResponse>())
.Dump();
}
now if I comment out the last two fields of the struct I get back what I expect for the first part of the response, but I am just lost on how to get back the data portion. I would prefer to have it as I have it with some magical attribute that the Marshaler understands but so far I can't figure it out. I have tried
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct RfidReaderResponse
{
public byte PreambleA;
public byte PreambleB;
public byte Length;
public byte LengthLRC;
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)]
public IntPtr Data;
}
and
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct RfidReaderResponse
{
public byte PreambleA;
public byte PreambleB;
public byte Length;
public byte LengthLRC;
public ushort ReaderId;
public byte CommandId;
public byte ErrorCode;
[MarshalAs(UnmanagedType.SafeArray, SafeArraySubType=VarEnum.VT_I1)]
public byte[] Data;
public byte LRC;
}
after a few hours of research. Both of which didn't work stating that my method couldn't make a size for my structure. So I dunno. What am I doing wrong?
EDIT
forgot the method for converting hex strings to byte arrays
public static byte[] ToByteArray(this string hexString)
{
hexString = System.Text.RegularExpressions.Regex.Replace(hexString, "[^0-9A-F.]", "").ToUpper();
if (hexString.Length % 2 == 1)
throw new Exception("The binary key cannot have an odd number of digits");
byte[] arr = new byte[hexString.Length >> 1];
for (int i = 0; i < hexString.Length >> 1; ++i)
{
arr[i] = (byte)((GetHexVal(hexString[i << 1]) << 4) + (GetHexVal(hexString[(i << 1) + 1])));
}
return arr;
}
private static int GetHexVal(char hex)
{
int val = (int)hex;
return val - (val < 58 ? 48 : 55);
}
In the following bit of code, I am observing that the marshaler is reading past the 3 byte source array to populate another 8 bytes of data. With time, the code eventually throws a memory access violation. Is there a way to tell the marshaller to only marshal the first 3 bytes when converting a pointer to a structure? If I make the Articulations array "NonSerialized" then the constructor will throw an access violation when processing a source array of 11 bytes.
using System;
using System.Runtime.InteropServices;
namespace MarshallingTest
{
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct Articulation
{
public const int BaseSize = 8;
public byte attribute1;
public byte attribute2;
public byte attribute3;
public byte attribute4;
public byte attribute5;
public byte attribute6;
public byte attribute7;
public byte attribute8;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public class TestEntity
{
public const int BaseSize = 3;
public byte EntityId; // 1 byte
public byte Variable; // 1 byte
public byte NumArticulations; // 1 byte
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct)]
public Articulation[] Articulations; // 8 bytes each
public TestEntity(byte[] rawData)
{
unsafe
{
fixed (byte* pData = rawData)
{
// I am observing that the marshaler is reading past the 3 bytes
// to populate another 8 bytes of data. With time, the code
// will eventually throw a memory access violation.
//
// Is there a way to tell the marshaller to only marshal the
// first 3 bytes when converting a pointer to a structure?
Marshal.PtrToStructure((IntPtr) pData, this);
for (int i = 0; i < BaseSize + Articulation.BaseSize; i++)
{
Console.WriteLine("pData + " + i + " = " + *(pData + i));
}
}
}
}
public byte[] ToRaw()
{
byte[] byteArray = new byte[BaseSize + Articulation.BaseSize*Articulations.Length];
unsafe
{
fixed (byte* pData = byteArray)
{
Marshal.StructureToPtr(this, (IntPtr) pData, false);
}
}
return byteArray;
}
}
internal class Program
{
private const int TestDataSize = TestEntity.BaseSize;
private static void Main()
{
byte[] testData = new byte[TestDataSize];
for (int i = 0; i < TestDataSize; i++)
{
testData[i] = (byte) (i + 1);
}
TestEntity test = new TestEntity(testData);
// Print resulting array. You'll see that data outside the source
// byte array was marshalled into the test structure.
Console.WriteLine(test.EntityId);
Console.WriteLine(test.Variable);
Console.WriteLine(test.NumArticulations);
Console.WriteLine(test.Articulations[0].attribute1);
Console.WriteLine(test.Articulations[0].attribute2);
Console.WriteLine(test.Articulations[0].attribute3);
Console.WriteLine(test.Articulations[0].attribute4);
Console.WriteLine(test.Articulations[0].attribute5);
Console.WriteLine(test.Articulations[0].attribute6);
Console.WriteLine(test.Articulations[0].attribute7);
Console.WriteLine(test.Articulations[0].attribute8);
Console.WriteLine("Test complete.");
}
}
}
I'm working in C# with a Borland C API that uses a lot of byte pointers for strings. I've been faced with the need to pass some C# strings as (short lived) byte*.
It would be my natural assumption that a const object would not be allocated on the heap, but would be stored directly in program memory, but I've been unable to verify this in any documentation.
Here's an example of what I've done in order to generate a pointer to a constant string. This does work as intended in testing, I'm just not sure if it's really safe, or it's only working via luck.
private const string pinnedStringGetWeight = "getWeight";
unsafe public static byte* ExampleReturnWeightPtr(int serial)
{
fixed (byte* pGetWeight = ASCIIEncoding.ASCII.GetBytes(pinnedStringGetWeight))
return pGetWeight;
}
Is this const really pinned, or is there a chance it could be moved?
#Kragen:
Here is the import:
[DllImport("sidekick.dll", CallingConvention = CallingConvention.Winapi)]
public static extern int getValueByFunctionFromObject(int serial, int function, byte* debugCallString);
This is the actual function. Yes, it actually requires a static function pointer:
private const int FUNC_GetWeight = 0x004243D0;
private const string pinnedStringGetWeight = "getWeight";
unsafe public static int getWeight(int serial)
{
fixed (byte* pGetWeight = ASCIIEncoding.ASCII.GetBytes(pinnedStringGetWeight))
return Core.getValueByFunctionFromObject(serial, FUNC_GetWeight, pGetWeight);
}
Following is another method that I used when mocking my API, using a static struct, which I also hoped was pinned. I was hoping to find a way to simplify this.
public byte* getObjVarString(int serial, byte* varName)
{
string varname = StringPointerUtils.GetAsciiString(varName);
string value = MockObjVarAttachments.GetString(serial, varname);
if (value == null)
return null;
return bytePtrFactory.MakePointerToTempString(value);
}
static UnsafeBytePointerFactoryStruct bytePtrFactory = new UnsafeBytePointerFactoryStruct();
private unsafe struct UnsafeBytePointerFactoryStruct
{
fixed byte _InvalidScriptClass[255];
fixed byte _ItemNotFound[255];
fixed byte _MiscBuffer[255];
public byte* InvalidScriptClass
{
get
{
fixed (byte* p = _InvalidScriptClass)
{
CopyNullString(p, "Failed to get script class");
return p;
}
}
}
public byte* ItemNotFound
{
get
{
fixed (byte* p = _ItemNotFound)
{
CopyNullString(p, "Item not found");
return p;
}
}
}
public byte* MakePointerToTempString(string text)
{
fixed (byte* p = _ItemNotFound)
{
CopyNullString(p, text);
return p;
}
}
private static void CopyNullString(byte* ptrDest, string text)
{
byte[] textBytes = ASCIIEncoding.ASCII.GetBytes(text);
fixed (byte* p = textBytes)
{
int i = 0;
while (*(p + i) != 0 && i < 254 && i < textBytes.Length)
{
*(ptrDest + i) = *(p + i);
i++;
}
*(ptrDest + i) = 0;
}
}
}
How constants are allocated shouldn't matter in this case, because ASCIIEncoding.ASCII.GetBytes() returns a new byte array (it cannot return the constant's internal array, since it is encoded differently (edit: there is hopefully no way to get a pointer to a string's internal array, since strings are immutable)). However, the guarantee that the GC won't touch the array only lasts as long as the fixed scope does - in other words, when the function returns, the memory is no longer pinned.
Based on Kragans comment, I looked into the proper way to marshall my string to a byte pointer and am now using the following for the first example I used in my question:
[DllImport("sidekick.dll", CallingConvention = CallingConvention.Winapi)]
public static extern int getValueByFunctionFromObject(int serial, int function, [MarshalAs(UnmanagedType.LPStr)]string debugCallString);