Как безопасно вывести массив структур переменной длины из GetTokenInformation () для 32-битных и 64-битных систем?C # - PullRequest
4 голосов
/ 20 мая 2011

Я следую предоставленному коду пинвока здесь , но меня немного пугает маршаллинг массива переменной длины при размере = 1, а затем он проходит через него путем вычисления смещения вместо индексации в массив,Нет ли лучшего способа?И если нет, то как мне сделать это, чтобы сделать его безопасным для 32-разрядных и 64-разрядных?

    [StructLayout(LayoutKind.Sequential)]
    public struct SID_AND_ATTRIBUTES
    {
        public IntPtr Sid;
        public uint Attributes;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct TOKEN_GROUPS
    {
        public int GroupCount;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
        public SID_AND_ATTRIBUTES[] Groups;
    };


public void SomeMethod()
{
    IntPtr tokenInformation;

    // ... 

    string retVal = string.Empty;
    TOKEN_GROUPS groups = (TOKEN_GROUPS)Marshal.PtrToStructure(tokenInformation, typeof(TOKEN_GROUPS));
    int sidAndAttrSize = Marshal.SizeOf(new SID_AND_ATTRIBUTES());
    for (int i = 0; i < groups.GroupCount; i++)
    {
        // *** Scary line here: 
        SID_AND_ATTRIBUTES sidAndAttributes = (SID_AND_ATTRIBUTES)Marshal.PtrToStructure(
              new IntPtr(tokenInformation.ToInt64() + i * sidAndAttrSize + IntPtr.Size), 
              typeof(SID_AND_ATTRIBUTES));

    // ... 
}

Я вижу здесь другой подход - объявить длину массива намного большей, чем она может быть, но, похоже, у нее есть свои проблемы.

В качестве дополнительного вопроса: когда я выполняю вышеприведенный код в отладчике, я не могу оценить tokenInformation.ToInt64() или ToInt32().Я получаю ArgumentOutOfRangeException.Но строка кода выполняется просто отлично !?Что здесь происходит?

Ответы [ 2 ]

2 голосов
/ 20 мая 2011

Я думаю, что это выглядит хорошо - так же хорошо, как и всякие ковыряться в неуправляемых землях, в любом случае.

Однако мне интересно, почему начало tokenInformation.ToInt64() + IntPtr.Size, а не tokenInformation.ToInt64() + 4 (так как тип поля GroupCount - int, а не IntPtr). Это для упаковки / выравнивания структуры или просто что-то подозрительное? Я не знаю здесь.

Использование tokenInformation.ToInt64() важно, потому что на 64-битной машине взорвется (OverflowException), если значение IntPtr больше, чем то, которое может хранить int. Однако CLR отлично справится с long в обеих архитектурах и не изменит фактическое значение, извлеченное из IntPtr (и, следовательно, вернет его в new IntPtr(...)).

Представьте себе эту (непроверенную) функцию как вспомогательную оболочку:

// unpacks an array of structures from unmanaged memory
// arr.Length is the number of items to unpack. don't overrun.
void PtrToStructureArray<T>(T[] arr, IntPtr start, int stride) {
   long ptr = start.ToInt64();
   for (int i = 0; i < arr.Length; i++, ptr += stride) {
       arr[i] = (T)Marshal.PtrToStructure(new IntPtr(ptr), typeof(T));
   }
}

var attributes = new SID_AND_ATTRIBUTES[groups.GroupCount];
PtrToStructureArray(attributes, new IntPtr(tokenInformation.ToInt64() + IntPtr.Size), sidAndAttrSize);

Удачного кодирования.

0 голосов
/ 12 апреля 2012

Вместо того, чтобы угадывать, какое смещение, лучше использовать Marshal.OffsetOf(typeof(TOKEN_GROUPS), "Groups"), чтобы получить правильное смещение к началу массива.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...