visual tutorial studio plataforma development arma winapi windows-runtime uwp windows-10-universal

winapi - tutorial - ¿Cómo puedo obtener un MANUAL Win32 para un StorageFile o una StorageFolder en UWP?



uwp windows 7 (1)

Sé que puedo usar API de Win32 para acceder a archivos dentro de mi propia carpeta de datos local (por ejemplo, ver esta pregunta respondida ) pero necesito acceder a archivos fuera de mi aplicación (por ejemplo, desde la Biblioteca de Imágenes) y las bibliotecas que intento el uso se basa en el archivo HANDLE Win32 y / o se basan en el uso de nombres de archivo relativos.

Dado que la única forma de acceder a los archivos en la Biblioteca de imágenes (o recuperar archivos / carpetas devueltos por un selector) es mediante objetos StorageFile , ¿cómo puedo volver a utilizar mi código existente? ¿Debo volver a escribirlo para que sea asincrónico y confíe en las API de almacenamiento de WinRT?


A partir de la "Actualización de aniversario" (también conocida como "RS1" o compilación 10.0.14393), puede obtener un StorageItem Win32 desde un StorageItem (archivo o carpeta) y crear nuevos archivos con nombre (devolviendo un HANDLE ) desde una StorageFolder . Para ello, utilice las nuevas API IStorageFolderHandleAccess e IStorageItemHandleAccess .

Nota : Estas API se han colocado accidentalmente dentro de la partición WINAPI_PARTITION_DESKTOP (no son específicas del escritorio, están disponibles para los UWP). Esto se abordará en futuras actualizaciones de SDK.

Para utilizar una de estas nuevas interfaces COM, simplemente QI el StorageFile o StorageFolder para la interfaz. Si la interfaz no es compatible, significa que su aplicación se está ejecutando en un sistema operativo de nivel inferior (o tal vez el elemento de almacenamiento en realidad no está respaldado por un archivo real, sino que es más bien un pseudo archivo). Puede usar estas interfaces desde C ++ (C ++ / CX o WRL) o desde C #.

Aquí hay un ejemplo simple de usar un FolderPicker para que el usuario elija una ubicación en su disco (que devuelve un objeto StorageFolder ) y luego use las API de Win32 ReadFile y WriteFile para leer y escribir un archivo desde esa ubicación.

Como se indicó anteriormente, debemos copiar las declaraciones de la interfaz en nuestro propio código porque las versiones reales del SDK están en una partición API incorrecta. (Aconsejaría no modificar los archivos SDK para resolver el problema). Así que primero tenemos nuestro propio archivo de encabezado, por ejemplo, StorageHandleAccess.h , que copia las declaraciones del archivo 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

El siguiente es un uso simple de la API. Este ejemplo toma una StorageFolder , un nombre de archivo y un indicador de creación (abrir o crear) e intenta abrir (o crear) el archivo con nombre, lee (o escribe) algún texto de (a) el archivo y escribe alguna salida en el archivo. Consola de depuración.

El código no es particularmente útil en una configuración del mundo real, pero ilustra cómo usar la API. Esto se puede usar en un proyecto MainPage.xaml.cpp en blanco de C ++ para reemplazar el archivo MainPage.xaml.cpp (solo debe actualizar el espacio de nombres):

#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(); }