Reputation: 87
I have a prototype of the following function: https://learn.microsoft.com/en-us/windows/win32/api/wininet/nf-wininet-GetUrlCacheGroupAttributeA
Which looks like this:
[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);
The lpdwGroupInfo
might make more sense as out
instead of ref
but otherwise, I think this is correct.
What I don't understand is how I am expected to pass in a pointer for the INTERNET_CACHE_GROUP_INFO
struct lpGroupInfo
(which I have also defined/prototyped along with this function). I know how everything else is passed in, just this pointer confuses me.
The function states for the pointer the following:
lpGroupInfo - Pointer to an INTERNET_CACHE_GROUP_INFO structure that receives the requested information.
lpcbGroupInfo - Pointer to a variable that contains the size of the lpGroupInfo buffer. When the function returns, the variable contains the number of bytes copied to the buffer, or the required size of the buffer, in bytes.
Do I need to allocate memory via Marshal.AllocHGlobal
or something? This seems to suggest I will only get the size of the struct after passing it in but how can I pass it in if it isn't first defined? I'm not at all clear on how to create the initial pointer and then how I am expected to Marshal.PtrToStructure
it.
Upvotes: 0
Views: 93
Reputation: 87
Simon's comment answered my question:
for functions where a struct buffer pointer is passed in as a parameter(intended to be modified by the function) and where it also outs the size of the buffer needed when it fails due to insufficient buffer size, the proper way to use these functions is to pass in these values initialized to the default values(IntPtr.Zero and 0), then when the function fails(you can confirm the error was due to buffer size), it will have set the buffer size required and you can call the function again after allocating memory to the pointer.
Here is a sample snippet:
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);
}
}
Upvotes: 1