PInvoke FbwfFindFirst - FbwfCacheDetail problems

寵の児 提交于 2019-12-25 03:11:33

问题


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


回答1:


Since the whole structure is of variable size, one way to do this is like this (I can't test it because I don't have this dll on my system):

string volume = "";

int size = 0;
// ask for whole structure size
FbwfFindFirst(volume, IntPtr.Zero, ref size); // this call should return ERROR_MORE_DATA which is ok

// allocate for the structure
var ptr = Marshal.AllocHGlobal(size);
try
{
    FbwfFindFirst(volume, ptr, ref size); // should not return error

    // get the easy part 
    var detail = Marshal.PtrToStructure<FbwfCacheDetail>(ptr);

    // compute filename offset and get the string
    // file name length is in bytes, per documentation
    var fileName = Marshal.PtrToStringUni(ptr + Marshal.OffsetOf<FbwfCacheDetail>("fileName").ToInt32(), detail.fileNameLength / 2);
}
finally
{
    Marshal.FreeHGlobal(ptr);
}

[DllImport("fbwflib", CharSet = CharSet.Unicode)]
static extern int FbwfFindFirst(string volume, IntPtr cacheDetail, ref int size);

[StructLayout(LayoutKind.Sequential)]
struct FbwfCacheDetail
{
    public int cacheSize;
    public int openHandleCount;
    public int fileNameLength;
    byte fileName; // don't use this member
}



回答2:


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


来源:https://stackoverflow.com/questions/52058173/pinvoke-fbwffindfirst-fbwfcachedetail-problems

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!