【问题标题】:Pinvoke: passing a new struct pointer to a function which outs the size of the structPinvoke:将新的结构指针传递给超出结构大小的函数
【发布时间】:2019-09-24 17:00:14
【问题描述】:

我有以下函数的原型: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 结构 lpGroupInfo 的指针(我也已经定义/原型化了这个函数)。我知道其他所有内容是如何传入的,只是这个指针让我感到困惑。

函数声明指针如下:

lpGroupInfo - 指向接收请求信息的 INTERNET_CACHE_GROUP_INFO 结构的指针。

lpcbGroupInfo - 指向包含 lpGroupInfo 缓冲区大小的变量的指针。当函数返回时,该变量包含复制到缓冲区的字节数,或缓冲区所需的大小,以字节为单位。

我需要通过Marshal.AllocHGlobal 或其他方式分配内存吗?这似乎表明我只会在传入结构后才获得结构的大小,但是如果没有首先定义它,我怎么能传入呢?我完全不清楚如何创建初始指针,以及我应该如何 Marshal.PtrToStructure 它。

【问题讨论】:

  • 使用 lpGroupInfo 和 lpcbGroupInfo 调用函数一次为零和 0。这应该会给您返回 lpcbGroupInfo 中所需缓冲区的大小(应该保持参考)。然后按原样使用 AllocHGlobal 传递 lpcbGroupInfo 进行分配,再次调用该函数(并且不要忘记稍后调用 FreeHGlobal)。 PS你应该叫W版本,而不是A版本。
  • @SimonMourier 请原谅我的怀疑,我根本不知道 C 设计模式。您确定预期的方法是调用该函数两次吗?这是一种常见的模式吗?
  • 是的,这是一种非常常见的 Windows C API 模式。
  • 谢谢西蒙。如果您想为它添加一个,我会将您的答案标记为正确。

标签: c# pointers memory parameters pinvoke


【解决方案1】:

Simon 的评论回答了我的问题:

对于结构缓冲区指针作为参数传入的函数(旨在由函数修改),并且由于缓冲区大小不足而失败时它也超出了所需的缓冲区大小,正确的使用方法这些函数是将这些值初始化为默认值(IntPtr.Zero 和 0),然后当函数失败时(您可以确认错误是由于缓冲区大小引起的),它将设置所需的缓冲区大小,您可以给指针分配内存后再次调用函数。

这是一个示例 sn-p:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-05-15
    • 1970-01-01
    • 1970-01-01
    • 2021-07-17
    • 1970-01-01
    • 2021-11-02
    • 2019-05-03
    相关资源
    最近更新 更多