首页 文章

如何在UWP中获取StorageFile或StorageFolder的Win32 HANDLE?

提问于
浏览
6

我知道我可以使用Win32 API来访问我自己的本地数据文件夹中的文件(例如,参见this answered question),但我需要访问我的应用程序之外的文件(例如,从图片库中),我正在尝试使用的库是所有这些都基于Win32文件 HANDLE 和/或它们依赖于使用相对文件名 .

由于获取图片库中文件的唯一方法(或获取从选择器返回的文件/文件夹)是通过 StorageFile 对象,如何重新使用我现有的代码?我是否必须将其重新编写为异步并依赖WinRT存储API?

1 回答

  • 15

    从"Anniversary Update"(又名"RS1"或build 10.0.14393)开始,您可以从 StorageItem (文件或文件夹)获取Win32 HANDLE 并从 StorageFolder 中创建新的命名文件(返回 HANDLE ) . 您可以使用新的IStorageFolderHandleAccessIStorageItemHandleAccess API执行此操作 .

    注意:这些API被意外放置在WINAPI_PARTITION_DESKTOP分区内(它们不是特定于桌面的;它们可供UWP使用) . 这将在未来的SDK更新中解决 .

    要使用这些新COM接口之一,您只需为接口确认 StorageFileStorageFolder . 如果接口实际上不是由真实文件支持,而是伪文件 . 您可以使用C(C / CX或WRL)或C#中的这些接口 .

    这是一个简单的例子,使用 FolderPicker 让用户在他们的磁盘上选择一个位置(返回一个代理的 StorageFolder 对象),然后使用Win32 API ReadFileWriteFile 从该位置读取和写入文件 .

    如上所述,我们必须将接口的声明复制到我们自己的代码中,因为真正的SDK版本位于错误的API分区中 . (我建议不要修改SDK文件来解决问题) . 首先是我们自己的头文件,例如 StorageHandleAccess.h ,它复制SDK文件 WindowsStorageCOM.h 中的声明:

    #pragma once
    
    // These are copied from WindowsStorageCOM.h
    // You can remove this header file once the real file has been updated
    // to fix the WINAPI_PARTITION_DESKTOP block
    
    typedef interface IOplockBreakingHandler IOplockBreakingHandler;
    typedef interface IStorageItemHandleAccess IStorageItemHandleAccess;
    typedef interface IStorageFolderHandleAccess IStorageFolderHandleAccess;
    
    #ifdef __cplusplus
    extern "C" {
    #endif 
    
      typedef /* [v1_enum] */
        enum HANDLE_OPTIONS
      {
        HO_NONE = 0,
        HO_OPEN_REQUIRING_OPLOCK = 0x40000,
        HO_DELETE_ON_CLOSE = 0x4000000,
        HO_SEQUENTIAL_SCAN = 0x8000000,
        HO_RANDOM_ACCESS = 0x10000000,
        HO_NO_BUFFERING = 0x20000000,
        HO_OVERLAPPED = 0x40000000,
        HO_WRITE_THROUGH = 0x80000000
      }     HANDLE_OPTIONS;
    
      DEFINE_ENUM_FLAG_OPERATORS(HANDLE_OPTIONS);
      typedef /* [v1_enum] */
        enum HANDLE_ACCESS_OPTIONS
      {
        HAO_NONE = 0,
        HAO_READ_ATTRIBUTES = 0x80,
        HAO_READ = 0x120089,
        HAO_WRITE = 0x120116,
        HAO_DELETE = 0x10000
      }     HANDLE_ACCESS_OPTIONS;
    
      DEFINE_ENUM_FLAG_OPERATORS(HANDLE_ACCESS_OPTIONS);
      typedef /* [v1_enum] */
        enum HANDLE_SHARING_OPTIONS
      {
        HSO_SHARE_NONE = 0,
        HSO_SHARE_READ = 0x1,
        HSO_SHARE_WRITE = 0x2,
        HSO_SHARE_DELETE = 0x4
      }     HANDLE_SHARING_OPTIONS;
    
      DEFINE_ENUM_FLAG_OPERATORS(HANDLE_SHARING_OPTIONS);
      typedef /* [v1_enum] */
        enum HANDLE_CREATION_OPTIONS
      {
        HCO_CREATE_NEW = 0x1,
        HCO_CREATE_ALWAYS = 0x2,
        HCO_OPEN_EXISTING = 0x3,
        HCO_OPEN_ALWAYS = 0x4,
        HCO_TRUNCATE_EXISTING = 0x5
      }     HANDLE_CREATION_OPTIONS;
    
    
      EXTERN_C const IID IID_IOplockBreakingHandler;
      MIDL_INTERFACE("826ABE3D-3ACD-47D3-84F2-88AAEDCF6304")
        IOplockBreakingHandler : public IUnknown
      {
      public:
          virtual HRESULT STDMETHODCALLTYPE OplockBreaking(void) = 0;
    
      };
    
      EXTERN_C const IID IID_IStorageItemHandleAccess;
      MIDL_INTERFACE("5CA296B2-2C25-4D22-B785-B885C8201E6A")
        IStorageItemHandleAccess : public IUnknown
      {
      public:
          virtual HRESULT STDMETHODCALLTYPE Create(
            /* [in] */ HANDLE_ACCESS_OPTIONS accessOptions,
            /* [in] */ HANDLE_SHARING_OPTIONS sharingOptions,
            /* [in] */ HANDLE_OPTIONS options,
            /* [optional][in] */ __RPC__in_opt IOplockBreakingHandler *oplockBreakingHandler,
            /* [system_handle][retval][out] */ __RPC__deref_out_opt HANDLE *interopHandle) = 0;
    
      };
    
      EXTERN_C const IID IID_IStorageFolderHandleAccess;
      MIDL_INTERFACE("DF19938F-5462-48A0-BE65-D2A3271A08D6")
        IStorageFolderHandleAccess : public IUnknown
      {
      public:
          virtual HRESULT STDMETHODCALLTYPE Create(
            /* [string][in] */ __RPC__in_string LPCWSTR fileName,
            /* [in] */ HANDLE_CREATION_OPTIONS creationOptions,
            /* [in] */ HANDLE_ACCESS_OPTIONS accessOptions,
            /* [in] */ HANDLE_SHARING_OPTIONS sharingOptions,
            /* [in] */ HANDLE_OPTIONS options,
            /* [optional][in] */ __RPC__in_opt IOplockBreakingHandler *oplockBreakingHandler,
            /* [system_handle][retval][out] */ __RPC__deref_out_opt HANDLE *interopHandle) = 0;
    
      };
    #ifdef __cplusplus
    }
    #endif
    

    接下来是API的简单用法 . 此示例采用 StorageFolder ,文件名和创建标志(打开或创建)并尝试打开(或创建)指定文件,从(到)文件读取(或写入)某些文本,并将一些输出写入调试控制台 .

    该代码在实际环境中不是特别有用,但说明了如何使用API . 这可以在空白C XAML项目中使用,以替换 MainPage.xaml.cpp 文件(您只需要更新命名空间):

    #include "pch.h"
    #include "MainPage.xaml.h"
    #include <ppltasks.h>
    
    // TODO: Replace with your namespace
    #error Replace this with your real namespace
    using namespace FileHandleFromStorageFolder;
    
    // Uncomment out this line and delete the next line once the SDK is fixed
    //#include <WindowsStorageCOM.h>
    #include "StorageHandleAccess.h"
    
    // For ComPtr<>
    #include <wrl\client.h>
    
    // For HandleT<>
    #include <wrl\wrappers\corewrappers.h>
    
    __declspec(noreturn) inline void ThrowWithHRESULT(HRESULT hr, const wchar_t* message)
    {
      using namespace Platform;
      throw ref new Exception(hr, ref new String(message));
    }
    
    __declspec(noreturn) inline void ThrowWithGetLastError(const wchar_t* message)
    {
      using namespace Platform;
      throw ref new Exception(HRESULT_FROM_WIN32(GetLastError()), ref new String(message));
    }
    
    // Test is a simple test function. Pass in one of the HANDLE_CREATION_OPTIONS values
    // (eg, HCO_CREATE_ALWAYS or HCO_OPEN_ALWAYS) and the function will try and either
    // write to the file (if it's empty) or read from it (if it's not).
    void Test(Windows::Storage::StorageFolder^ folder, const wchar_t* filename, HANDLE_CREATION_OPTIONS options)
    {
      using namespace Microsoft::WRL;
      using namespace Microsoft::WRL::Wrappers;
    
      // Get an IUnknown from the ref class, and then QI for IStorageFolderHandleAccess
      ComPtr<IUnknown> abiPointer(reinterpret_cast<IUnknown*>(folder));
      ComPtr<IStorageFolderHandleAccess> handleAccess;
      HRESULT hr = abiPointer.As(&handleAccess);
      if (FAILED(hr))
        ThrowWithHRESULT(hr, L"Can't QI");
    
      // Standard RAII wrapper for HANDLEs that represent files
      HandleT<HandleTraits::FileHandleTraits>win32fileHandle;
    
      // This is roughly equivalent of calling CreateFile2 
      hr = handleAccess->Create(filename, options,
        HANDLE_ACCESS_OPTIONS::HAO_WRITE | HANDLE_ACCESS_OPTIONS::HAO_READ,
        HANDLE_SHARING_OPTIONS::HSO_SHARE_NONE, HANDLE_OPTIONS::HO_NONE, nullptr,
        win32fileHandle.GetAddressOf());
      if (FAILED(hr))
        ThrowWithHRESULT(hr, L"Can't access file");
    
      // From here, it's standard Win32 code - nothing WinRT specific at all
      LARGE_INTEGER size{ 0 };
      if (FALSE == GetFileSizeEx(win32fileHandle.Get(), &size))
        ThrowWithGetLastError(L"Can't get file size");
    
      static const DWORD BUFFER_SIZE = 500;
      char buffer[BUFFER_SIZE];
      DWORD bytesUsed{ 0 };
    
      if (size.QuadPart == 0)
      {
        const static auto str = "Hello, world\r\n";
        if (FALSE == WriteFile(win32fileHandle.Get(), str, strlen(str), &bytesUsed, nullptr))
          ThrowWithGetLastError(L"Can't write to file");
    
        sprintf_s(buffer, ARRAYSIZE(buffer), "Wrote %d bytes to file\r\n", bytesUsed);
        OutputDebugStringA(buffer);
      }
      else
      {
        if (FALSE == ReadFile(win32fileHandle.Get(), buffer, ARRAYSIZE(buffer) - 1, &bytesUsed, nullptr))
          ThrowWithGetLastError(L"Can't read from file");
    
        buffer[bytesUsed] = 0;
        OutputDebugStringA(buffer);
      }
    }
    
    // Trivial driver that gets a StorageFolder and then creates a file
    // inside it, writes some text, then re-opens it to read text.
    void TestWrapper()
    {
      using namespace Windows::Storage;
      using namespace Windows::Storage::Pickers;
    
      auto picker = ref new FolderPicker();
      picker->FileTypeFilter->Append(L".txt");
      picker->SuggestedStartLocation = PickerLocationId::Desktop;
      concurrency::create_task(picker->PickSingleFolderAsync()).then([]
      (StorageFolder^ folder)
      {
        if (folder != nullptr)
        {
          // Create and then read back a simple file
          Test(folder, L"win32handletest.txt", HANDLE_CREATION_OPTIONS::HCO_CREATE_ALWAYS);
          Test(folder, L"win32handletest.txt", HANDLE_CREATION_OPTIONS::HCO_OPEN_ALWAYS);
        }
      }
      );
    }
    
    MainPage::MainPage()
    {
      InitializeComponent();
      TestWrapper();
    }
    

相关问题