2014-03-24 4 views
1

Привет, я пытаюсь преобразовать C/C++ Strcut в C# и как заполнить элемент структуры адресом другой структуры в C#?Как сделать memcpy в C# .Net CF со следующей задачей

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# ??

Спасибо :)

+0

Можете привести более подробную информацию. Кажется, что на родной стороне у вас есть 4-байтовый буфер, Data, в который вы нажимаете 8 байтов. Как это может работать? Кто что выделяет? –

+0

@DavidHeffernan Привет SRY за поздний ответ :(Я буду обновлять Qn .. – arya2arya

+0

@DavidHeffernan Подобным же образом, как и в C++, Можно ли в C# ?? Я пытаюсь путем преобразования структуры в PTR, а затем используя Marshal.copy() – arya2arya

ответ

1

Безопасно, вы не можете. .NET обеспечивает безопасность типов, а это означает, что вы просто не можете заставить строку быть структурой. Тем не менее, вы можете посмотреть на данные, а не делать опасные забросы типа (почему вы хранения двух uint с в строку в первую очередь? И сортировочных как юникода?

Во-первых, вы должны сделать 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; 
}; 

Второй подход будет U зе константного размером байтового массива, достаточно долго, чтобы держать две uints:

[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, и он должен работать автоматически.

Следует отметить, однако, что возвращенные данные на самом деле не обязательно фиксированной длины. Это довольно сложно, и в основном это означает, что вам придется десериализовать структуру вручную на основе указателя и длины, которую вы получите.

+0

моя задача - скопировать структуру «Статистика» в «Данные» в структуре _NDISUIO_QUERY_OID. в байте [] как мы можем скопировать другой адрес структуры? – arya2arya

+0

@ arya2arya Вы не можете. , это не имеет большого значения, поскольку вы можете сериализовать структуру в ней - в основном, вместо того, чтобы делать небезопасную «memcpy», вы явно скажете: «Поместите первое поле в первые четыре байта, а второе в следующем четыре байта ». Чтение легко, вы можете использовать, например,« BitConverter.ToUInt32 (данные, 0); ', а для записи« BitConverter.GetBytes (fieldA) .CopyTo (Data, 0); 'будет делать все хорошо. Это не самый быстрый способ, но это безопасно. Просто прочитайте/напишите первое поле с 'startIndex = 0', а второе с' 4'. – Luaan

+0

Спасибо за ваш ответ .. :) , но мое требование - скопировать структуру в «данные» в структуру _NDISUIO_QUERY_OID :( – arya2arya

3

Вот моя реализация (FYI, СПФ содержит весь этот код и многое другое)

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); 
} 

EDIT

Так 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, который вы решите поддержать.

+0

Привет, сэр Мы можем использовать эту структуру в C#? ' [StructLayout (LayoutKind.Sequential, CharSet = CharSet.Unicode)] public struct _NDISUIO_QUERY_OID { public uint Oid; [MarshalAs (UnmanagedType.LPWStr)] public string ptcDeviceName; public IntPtr Data; (или) [MarshalAs (UnmanagedType.ByValArray, SizeConst = sizeof (uint))] public char [] Data; }; ' – arya2arya

+1

Это зависит от данных, которые вы передаете. Например, для BSSID нет, вы не можете, поскольку данные составляют 36 байт. Моя реализация позволяет использовать любую длину данных (именно поэтому я ее использую). – ctacke

+0

Привет, с приведенной выше реализацией я был немного смущен. Прошу прояснить меня Возможно ли скопировать вторую структуру в элемент данных 1-й структуры? – arya2arya

2

Сначала у вас неправильный перевод кода на 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(); 
     } 

EDIT: добавлен пример того, как иметь дело с ссылочных типов, таких как строки.

+0

Привет, сэр .. Это не позволяет мне создать указатель на структуру. Src * srcPtr = &src; Получение ошибки: Не можете взять адрес, получить размер или объявить указатель на управляемый тип – arya2arya

+1

Вы не можете взять адрес структуры, которая имеет членов ссылки типа в нем. Полагаю, вы попробовали добавить строку к Src. Я отредактировал пример, чтобы показать, как обращаться со ссылочными типами. – Asik

+0

@@ asik Но как вы это делаете? 'Src * srcPtr = &src;' – arya2arya