Привет, я пытаюсь преобразовать C / C ++ Strcut в C # и как заполнить элемент структуры с адресом другой структуры в C #?
C / C ++ Struct выглядит так:
typedef struct _NDISUIO_QUERY_OID
{
NDIS_OID Oid;
PTCHAR ptcDeviceName;
UCHAR Data[sizeof(ULONG)];
} NDISUIO_QUERY_OID, *PNDISUIO_QUERY_OID;
typedef struct My_Struct
{
//les have 2 variables...
UINT a;
UINT b;
}My_STATS, *PMy_STATS;
PNDISUIO_QUERY_OID pQueryOid = NULL;
pQueryOid = (PNDISUIO_QUERY_OID)malloc(sizeof(NDISUIO_QUERY_OID)+ sizeof(My_STATS)) ;
PMy_STATS Statistics;
pQueryOid->Oid = ulOIDCode;//Required OID
pQueryOid->ptcDeviceName = AUB_NAME;//REquired STRING
memcpy(pQueryOid->Data, Statistics, sizeof(My_STATS));
Мой C # Struct это:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct _NDISUIO_QUERY_OID
{
public uint Oid;
[MarshalAs(UnmanagedType.LPWStr)]
public string ptcDeviceName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = sizeof(uint))]
public string Data;
};
Проблема: Как скопировать структуру статистики в массив данных в C # ??
Спасибо 🙂
Вот моя реализация (к вашему сведению, SDF содержит весь этот код и многое другое)
internal class NDISQueryOid
{
protected const int NDISUIO_QUERY_OID_SIZE = 12;
protected byte[] m_data;
public int Size { get; private set; }
public NDISQueryOid(byte[] data)
{
int extrasize = data.Length;
Size = 8 + extrasize;
m_data = new byte[Size];
Buffer.BlockCopy(data, 0, m_data, DataOffset, data.Length);
}
public NDISQueryOid(int extrasize)
{
Size = NDISUIO_QUERY_OID_SIZE + extrasize;
m_data = new byte[Size];
}
protected const int OidOffset = 0;
public uint Oid
{
get { return BitConverter.ToUInt32(m_data, OidOffset); }
set
{
byte[] bytes = BitConverter.GetBytes(value);
Buffer.BlockCopy(bytes, 0, m_data, OidOffset, 4);
}
}
protected const int ptcDeviceNameOffset = OidOffset + 4;
public unsafe byte* ptcDeviceName
{
get
{
return (byte*)BitConverter.ToUInt32(m_data, ptcDeviceNameOffset);
}
set
{
byte[] bytes = BitConverter.GetBytes((UInt32)value);
Buffer.BlockCopy(bytes, 0, m_data, ptcDeviceNameOffset, 4);
}
}
protected const int DataOffset = ptcDeviceNameOffset + 4;
public byte[] Data
{
get
{
byte[] b = new byte[Size - DataOffset];
Array.Copy(m_data, DataOffset, b, 0, Size - DataOffset);
return b;
}
set
{
Size = 8 + value.Length;
m_data = new byte[Size];
Buffer.BlockCopy(value, 0, m_data, DataOffset, value.Length);
}
}
public byte[] getBytes()
{
return m_data;
}
public static implicit operator byte[](NDISQueryOid qoid)
{
return qoid.m_data;
}
}
Обратите внимание, что при моем использовании NDIS IOCT получает указатель (большая часть моей работы с NDIS выполняется небезопасно), поэтому вам придется выполнить некоторые настройки там.
Так что, если, например, вы запрашиваете BSSID, я знаю, что данные BSSID составляют 36 байтов, поэтому я бы создал что-то вроде этого:
var queryOID = new NDISQueryOid(36);
затем выделите имя и вызовите NDIS (производственный код имеет гораздо больше проверок, чем этот):
byte[] nameBytes = System.Text.Encoding.Unicode.GetBytes(adapterName + '\0');
fixed (byte* pName = &nameBytes[0])
{
queryOID.ptcDeviceName = pName;
queryOID.Oid = (uint)oid;
var bytes = queryOID.getBytes();
ndis.DeviceIoControl(IOCTL_NDISUIO_QUERY_OID_VALUE, bytes, bytes);
var result = new byte[queryOID.Data.Length];
Buffer.BlockCopy(queryOID.Data, 0, result, 0, result.Length);
}
РЕДАКТИРОВАТЬ
Итак result
член выше является байтовым массивом «результата» запроса. Что это означает и как вы интерпретируете это, зависит от того, какой OID вы запрашивали. Например, если вы запрашивали текущий подключенный SSID (т.е. NDIS_OID.SSID
), то возвращается 4-байтовая длина, за которой следует имя в кодировке ASCII, так что вы можете расшифровать его следующим образом:
int len = BitConverter.ToInt32(data, 0);
if (len > 0)
{
ssid = System.Text.Encoding.ASCII.GetString(data, 4, len);
}
Но опять же, это только для одного конкретного OID. Вы должны обрабатывать все возвратные случаи для каждого входящего OID, который вы решили поддержать.
Во-первых, у вас неправильный перевод кода на C ++: эквивалент C ++ для C ++ char[]
это не строка, это byte[]
, Как только вы это сделаете, вам просто нужно знать, как вообще скопировать структуру в байтовый массив. Вот скомпилированный пример:
using System;
using System.Runtime.InteropServices;struct Dest
{
public byte[] Data;
}
struct Src
{
public GCHandle StringHandle;
public long A;
public long B;
}
class Program
{
static void Main()
{
Copy();
}
static void Copy()
{
var str = "Hello";
var src = new Src {
A = 3,
B = 4,
StringHandle = GCHandle.Alloc(str, GCHandleType.Normal)
};
var dst = new Dest();
unsafe
{
Src* srcPtr = &src;
dst.Data = new byte[sizeof(Src)];
Marshal.Copy((IntPtr)srcPtr, dst.Data, 0, sizeof(Src));
}
// When you're sure no one can reference the string anymore
// (Including by accessing the data you put in dst.Data!)
src.StringHandle.Free();
}
РЕДАКТИРОВАТЬ: добавлен пример того, как работать с ссылочными типами, такими как строки.
Безопасно, вы не можете. .NET обеспечивает безопасность типов, что означает, что вы просто не можете заставить строку быть структурой. Тем не менее, вы можете посмотреть на данные вместо того, чтобы делать небезопасные приведения типов (почему вы храните два uint
в строке в первую очередь? И выложить это как Unicode?
Во-первых, вам придется сделать Data
байтовый массив. Может быть возможно сделать это и со строкой, но это только добавляет проблемы кодирования в смесь; если можешь, используй byte[]
вместо. Кроме того, если вам не нужно иметь различные виды данных внутри (кажется, что так), вы можете просто поместить два uint
поля прямо внутри структуры, и это должно работать нормально:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct _NDISUIO_QUERY_OID
{
public uint Oid;
[MarshalAs(UnmanagedType.LPWStr)]
public string ptcDeviceName;
public uint DataA;
public uint DataB;
};
Второй подход будет использовать байтовый массив размером с константу, достаточно длинный, чтобы содержать два uint:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct _NDISUIO_QUERY_OID
{
public uint Oid;
[MarshalAs(UnmanagedType.LPWStr)]
public string ptcDeviceName;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = sizeof(ulong))]
public byte[] Data;
};
Первые четыре байта будут первыми uint
, следующий будет вторым.
И, конечно, вы можете также использовать структуру .NET так же, как в исходном коде — просто убедитесь, что вы используете правильный тип данных в _NDISUIO_QUERY_OID
и это должно работать автоматически.
Однако следует отметить, что возвращаемые данные не обязательно имеют фиксированную длину. Это довольно сложно, и это в основном означает, что вам придется десериализовать структуру вручную на основе указателя и длины, которую вы получите.