问题
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