Search code examples
c#.netpinvokemarshalling

PInvoke FbwfFindFirst - FbwfCacheDetail problems


I'm trying to create a PInvoke for FbwfFindFirst and am struggling with the struct FbwfCacheDetail.

In short, I'm not sure how to marshal WCHAR fileName[1]; seeing as it's a variable length array and a non-null terminated.

Any help would be welcomed


Solution

  • Simon Mourier's answer is 99% correct, and with normal APIs it would have definitely worked, but it appears as if this particular API doesn't follow "normal rules", whatever that might be ;). As such, I needed to modify a couple things and this is what worked for me:

    const int ERROR_INSUFFICIENT_BUFFER = 122;
    const int ERROR_MORE_DATA = 234;
    
    var volume = "C:";
    var fileName = string.Empty;
    var size = 0;
    
    while (true)
    {
        var ptr = Marshal.AllocHGlobal(size); // FbwfFindFirst fails if given IntPtr.Zero - regardless of what the value of size is.
    
        try
        {
            var result = FbwfFindFirst(volume, ptr, ref size);
    
            // Despite documentation saying otherwise, it can return either of these
            if (result == ERROR_MORE_DATA || result == ERROR_INSUFFICIENT_BUFFER)
            {
                continue;
            }
    
            if (result != 0)
            {
                throw new Exception($"Failed with {result}");
            }
    
            // get the easy part 
            var detail = (FbwfCacheDetail) Marshal.PtrToStructure(ptr, typeof(FbwfCacheDetail));
    
            // compute filename offset and get the string
            // file name length is in bytes, per documentation
            fileName = Marshal.PtrToStringUni(ptr + Marshal.OffsetOf(typeof(FbwfCacheDetail), "fileName").ToInt32(), detail.fileNameLength / 2);
            break;
        }
        finally
        {
            Marshal.FreeHGlobal(ptr);
        }
    }
    

    EDIT

    Forgot to say that the pinvoke for FbwfFindFirst needs to be as follows, else it will return ERROR_INVALID_PARAMETER:

    [DllImport("fbwflib.dll")]
    public static extern uint FbwfFindFirst(
        [MarshalAs(UnmanagedType.LPWStr)]    
        string volume,
        IntPtr cacheDetail,
        ref int size
    );