UWP:使用简单快速的 PInvoke 调用 CreateFile 和 WriteFile 将文件写入磁盘

UWP: write file to disk using the simple and fast PInvoke calls CreateFile and WriteFile

我们有一个生成视频数据的非托管内存 C++ dll。用户界面在 UWP VB 中。 我需要将视频数据写入磁盘。用户从文件选择器中提供要写入的位置,并将此路径和文件名传递给 dll。使用 CreateFile 和 WriteFile 将数据写入磁盘。

这是VB代码

<DllImport("mycplus.dll", CallingConvention:=CallingConvention.StdCall)>
Public Function MainFrozen(ByVal EvF() As UInteger) As UInteger
End Function


Private Async Sub btnSaveAs_Click(sender As Object, e As RoutedEventArgs) Handles btnSaveAs.Click
    Dim savePicker = New Windows.Storage.Pickers.FileSavePicker()
    savePicker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.DocumentsLibrary
    savePicker.FileTypeChoices.Add("Video Files", New List(Of String)() From {".bin"})
    savePicker.SuggestedFileName = gVideoFileName
    Dim file As Windows.Storage.StorageFile = Await savePicker.PickSaveFileAsync()

    If file IsNot Nothing Then
        Dim fileName As String = file.Path
        Dim d(0 To 15) As UInt32

        d(0) = VIDEO_FILE_WRITE
        d(1) = System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(fileName)
        d(2) = System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(gAuthorName)
        d(3) = gReserved

        d(8) = MainFrozen(d)
    Else
        'txtDeubg.Text = "Operation cancelled."
    End If
End Sub

下面是C++代码

extern "C" __declspec (dllexport) ULONG __stdcall MainFrozen(PULONG ptrEv)
{
    ULONG eve = ptrEv[0];

    switch (eve)
    {
    case VIDEO_FILE_WRITE:
        SaveScanVideosToFile((LPCSTR)ptrEv[1], (LPCSTR)ptrEv[2], ptrEv[3]);
        break;
    }
    return 0;
}


ULONG SaveScanVideosToFile(LPCSTR   lpFileName, 
                            LPCSTR  lpAuthorName,
                            ULONG   reserved)
{

    HANDLE hFile;
    ULONG bytesWritten;
    ULONG n;

    hFile = CreateFile(lpFileName, GENERIC_WRITE, 0, NULL,
        CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

    for (n=0; n<gScanVideoCnt; n++)
    {
        WriteFile(hFile, ScanVideos[n].pImg->Bcine.pCineMem, SCAN_VIDEO_MEM_SIZ, &bytesWritten, NULL);
        WriteFile(hFile, ScanVideos[n].pImg, sizeof(IMG_TYPE), &bytesWritten, NULL);
    }
    CloseHandle(hFile);
    return 0;
}

问题是我无法写入除以下位置以外的任何位置:Windows.Storage.ApplicationData.Current.LocalFolder 即 C:\Users\ravi\AppData\Local\Packagesd6e339b-614b-4161-8c7d-3570f07fc01f_2ygbv2t9jdb9y\LocalState\

在其他位置,CreateFile 失败并拒绝访问。 但是使用.net 文件功能,我可以在最新的win10 creators 版本中将其他测试文件写入我PC 的几乎所有位置,这是非常受欢迎和令人鼓舞的。

问题是:应该设置哪些安全属性才能使 CreateFile 成功并像本机 .net 文件函数一样写入所有位置?

可能有解决方法,例如将整个数据编组到托管内存或将文件写入 ApplicationData.Current.LocalFolder,然后使用 vb.net 调用移动它。但没有什么比让 CreateFile 成功更重要的了。

PS:我们的应用程序是侧加载的,仅供企业使用,我们不介意它未通过 windows 存储测试。 (我们的客户从“windows NT”天开始就信任我们:-)

Microsoft 论坛的帮助让这个工作正常。click here for the full thread

基本上将文件对象转换为IStorageItemHandleAccess 并调用Create 来获取文件HANDLE。然后在非托管代码中使用 WriteFile 和 Close。

代码如下:

<ComImport>
<Guid("5CA296B2-2C25-4D22-B785-B885C8201E6A")>
<InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>
Friend Interface IStorageItemHandleAccess
    Function Create(ao As Int32, so As Int32, o As Int32, oploc As Int32,
                    ByRef ptrHandle As IntPtr) As IntPtr
End Interface

' 在您的函数中只需 2 行额外代码即可获得神奇的 HANDLE!

Dim storageHandleIface = DirectCast(file, IStorageItemHandleAccess)
Dim myfileHandle As IntPtr
Dim res = storageHandleIface.Create(&H120116, 2, 0, 0, myfileHandle)    ' WindowsStorageCom.h