How can I get a Win32 HANDLE for a StorageFile or StorageFolder in UWP?

前端 未结 1 653
礼貌的吻别
礼貌的吻别 2020-12-29 13:36

I know I can use Win32 APIs for accessing files within my own local data folder (eg, see this answered question) but I need to access files outside of my app (eg, from the P

1条回答
  •  情话喂你
    2020-12-29 14:10

    Starting in the "Anniversary Update" (aka "RS1" or build 10.0.14393) you are able to get a Win32 HANDLE from a StorageItem (file or folder) and to create new named files (returning a HANDLE) from within a StorageFolder. You do this using the new IStorageFolderHandleAccess and IStorageItemHandleAccess APIs.

    Note: These APIs have been accidentally placed inside the WINAPI_PARTITION_DESKTOP partition (they're not desktop-specific; they're available to UWPs). This will be addressed in future SDK updates.

    To use one of these new COM interfaces, you simply QI the StorageFile or StorageFolder for the interface. If the interface isn't supported, it means your app is running on a down-level OS (or perhaps the Storage Item isn't actually backed by a real file, but is rather a pseudo-file). You can use these interfaces from C++ (C++/CX or WRL) or from C#.

    Here's a simple example of using a FolderPicker to have the user pick a location on their disk (which returns a brokered StorageFolder object) and then using Win32 APIs ReadFile and WriteFile to read and write a file from that location.

    As noted above, we have to copy the declarations for the interface into our own code because the real SDK versions are in the wrong API partition. (I would advise against modifying the SDK files to solve the problem). So first up is our own header file, eg StorageHandleAccess.h, that copies the declarations from the SDK file 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
    

    Next up is a simple usage of the API. This example takes a StorageFolder, a filename, and a creation flag (open or create) and tries to open (or create) the named file, reads (or writes) some text from (to) the file, and writes some output to the Debug console.

    The code isn't particularly useful in a real-world setting, but illustrates how to use the API. This can be used in a blank C++ XAML project to replace the MainPage.xaml.cpp file (you should only need to update the namespace):

    #include "pch.h"
    #include "MainPage.xaml.h"
    #include 
    
    // 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 
    #include "StorageHandleAccess.h"
    
    // For ComPtr<>
    #include 
    
    // For HandleT<>
    #include 
    
    __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 abiPointer(reinterpret_cast(folder));
      ComPtr handleAccess;
      HRESULT hr = abiPointer.As(&handleAccess);
      if (FAILED(hr))
        ThrowWithHRESULT(hr, L"Can't QI");
    
      // Standard RAII wrapper for HANDLEs that represent files
      HandleTwin32fileHandle;
    
      // 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();
    }
    

    0 讨论(0)
提交回复
热议问题