Pinvoke: передача нового указателя структуры на функцию, которая превышает размер структуры - PullRequest
0 голосов
/ 24 сентября 2019

У меня есть прототип следующей функции: https://docs.microsoft.com/en-us/windows/win32/api/wininet/nf-wininet-GetUrlCacheGroupAttributeA

, которая выглядит следующим образом:

[DllImport("wininet", SetLastError = true, CharSet = CharSet.Auto, EntryPoint = "GetUrlCacheGroupAttributeA", CallingConvention = CallingConvention.StdCall)]
private static extern bool GetUrlCacheGroupAttribute(
        long gid,
        uint dwFlags,
        uint dwAttributes,
        IntPtr lpGroupInfo,
        ref uint lpcbGroupInfo,
        IntPtr lpReserved);

lpdwGroupInfo может иметь больше смысла как out вместо ref но в остальном я думаю, что это правильно.

Что я не понимаю, так это то, как я должен передать указатель на INTERNET_CACHE_GROUP_INFO struct lpGroupInfo (которую я также определил / создал прототип)наряду с этой функцией).Я знаю, как все остальное передается, только этот указатель смущает меня.

Функция для указателя сообщает следующее:

lpGroupInfo - Указатель на структуру INTERNET_CACHE_GROUP_INFO, которая получает запрошенныйинформация.

lpcbGroupInfo - Указатель на переменную, которая содержит размер буфера lpGroupInfo.Когда функция возвращается, переменная содержит количество байтов, скопированных в буфер, или требуемый размер буфера в байтах.

Нужно ли выделять память через Marshal.AllocHGlobal или что-то еще?Кажется, это говорит о том, что я получу размер структуры только после ее передачи, но как я могу передать ее, если она не определена впервые?Я не совсем понимаю, как создать начальный указатель, а затем, как меня ожидают Marshal.PtrToStructure.

1 Ответ

1 голос
/ 24 сентября 2019

Комментарий Саймона ответил на мой вопрос:

для функций, где указатель структурного буфера передается в качестве параметра (предназначенного для изменения функцией), и где он также превышает размер буфера, необходимый для негопроисходит сбой из-за недостаточного размера буфера, правильный способ использования этих функций заключается в передаче этих значений, инициализированных значениями по умолчанию (IntPtr.Zero и 0), затем при сбое функции (вы можете подтвердить, что ошибка произошла из-за размера буфера), он установит требуемый размер буфера, и вы можете снова вызвать функцию после выделения памяти для указателя.

Вот пример кода:

        private static void ClearUrlCacheGroups()
        {
            IntPtr enumHandle = FindFirstUrlCacheGroup(0, CACHEGROUP_SEARCH_ALL, IntPtr.Zero, 0, out long lpGroupId, IntPtr.Zero);

            if (enumHandle != IntPtr.Zero)
            {
                bool foundNextGroup;
                bool isDeleted;
                uint cacheGroupInfoBufferSize = 0;
                IntPtr cacheGroupInfoBuffer = Marshal.AllocHGlobal((IntPtr)cacheGroupInfoBufferSize);

                do
                {
                    bool getAttributeSucceeded = GetUrlCacheGroupAttribute(lpGroupId, 0, CACHEGROUP_ATTRIBUTE_GET_ALL, cacheGroupInfoBuffer, ref cacheGroupInfoBufferSize, IntPtr.Zero);

                    if (!getAttributeSucceeded && Marshal.GetLastWin32Error() == ERROR_INSUFFICIENT_BUFFER)
                    {
                        cacheGroupInfoBuffer = Marshal.ReAllocHGlobal(cacheGroupInfoBuffer, (IntPtr)cacheGroupInfoBufferSize);
                        getAttributeSucceeded = GetUrlCacheGroupAttribute(lpGroupId, 0, CACHEGROUP_ATTRIBUTE_GET_ALL, cacheGroupInfoBuffer, ref cacheGroupInfoBufferSize, IntPtr.Zero);
                    }

                    if (getAttributeSucceeded)
                    {
                        INTERNET_CACHE_GROUP_INFOA internetCacheEntry = (INTERNET_CACHE_GROUP_INFOA)Marshal.PtrToStructure(cacheGroupInfoBuffer, typeof(INTERNET_CACHE_GROUP_INFOA));
                    }

                    isDeleted = DeleteUrlCacheGroup(lpGroupId, CACHEGROUP_FLAG_FLUSHURL_ONDELETE, IntPtr.Zero);
                    foundNextGroup = FindNextUrlCacheGroup(enumHandle, ref lpGroupId, IntPtr.Zero);
                }
                while (foundNextGroup);

                Marshal.FreeHGlobal(cacheGroupInfoBuffer);
            }
        }
...