PInvoke FbwfFindFirst - FbwfCacheDetail проблемы - PullRequest
0 голосов
/ 28 августа 2018

Я пытаюсь создать PInvoke для FbwfFindFirst и борюсь со структурой FbwfCacheDetail.

Короче говоря, я не уверен, как выполнить маршал WCHAR fileName[1];, поскольку он имеет массив переменной длины и завершается ненулевым значением.

Любая помощь будет приветствоваться

Ответы [ 2 ]

0 голосов
/ 29 августа 2018

Ответ Саймона Мурье верен на 99%, и с обычными API это определенно сработало бы, но похоже, что этот конкретный API не следует «нормальным правилам», что бы это ни было;). Таким образом, мне нужно было изменить пару вещей, и вот что у меня сработало:

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

Забыл сказать, что pinvoke для FbwfFindFirst должен быть следующим, иначе он вернет ERROR_INVALID_PARAMETER:

[DllImport("fbwflib.dll")]
public static extern uint FbwfFindFirst(
    [MarshalAs(UnmanagedType.LPWStr)]    
    string volume,
    IntPtr cacheDetail,
    ref int size
);
0 голосов
/ 28 августа 2018

Поскольку вся структура имеет переменный размер, один из способов сделать это выглядит так (я не могу проверить это, потому что у меня нет этой DLL в моей системе):

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
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...