2013年5月2日 星期四

[W8App] Create folder

Windows 8 store app除了Metro style的UI外,和Desktop app最大的不同大概是對檔案的存取方式的差別,在W8App中檔案的讀取都必需使用非同步的方式來達成。

我透過下面的程式可在App已知的_strRoamingPath路徑下建立”Backup”子目錄:

   1: auto taskFolder = create_task(StorageFolder::GetFolderFromPathAsync(_strRoamingPath));
   2: taskFolder.then([](StorageFolder^ folder) {
   3:     return folder->CreateFolderAsync(L"Backup");
   4: }).then([](StorageFolder^ folder) {
   5:     return S_OK;
   6: }).then([](task<HRESULT> t) {
   7:     try
   8:     {
   9:         HRESULT hr = safe_cast<HRESULT>(t.get());
  10:     }
  11:     catch(Exception^ e)
  12:     {
  13:         switch(e->HResult)
  14:         {
  15:         case S_OK:
  16:         case E_ABORT:
  17:             break;
  18:         case 0x800700b7:    //    HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS)
  19:             break;
  20:         case ERROR_ALREADY_EXISTS:
  21:         default:
  22:             throw e;
  23:         }
  24:     }
  25: });

a. GetFolderFromPathAsyc


App是不能隨意存取檔案或目錄的。只能存取自已App的Local、Roaming與Temporary或是透過File Picker取得目錄的token,才能在目錄下新增檔案或子目錄。如果_strRoamingPath隨意指定一個目錄,通常會catch到E_ACCESSDENIED的exception。


此外,GetFolderFromPathAsync似乎不能進行等待,當我指定taskFolder進行.wait()等待所有程序完成時,在執行的過程中會task產生FAST_FAIL_INVALID_ARG exception。


W8App有另一個方式更較容易產生roaming folder。可透過ApplicationData來取得目前的RoamingFolder物件。



   1: StorageFolder^ folderRoaming = ApplicationData::Current->RoamingFolder;

b. CreateFolderAsync


我的例子如果執行第二次理論上會收到ERROR_ALREADY_EXISTS的exception。CreateFolderAsync其實要提供第二個參數讓我們選擇,如果要產生的目錄存在時相對應的處理方式。


GenerateUniqueName如果指定的目錄已存在,會自動加上編號,以我的例子會產生Backup (2)、Backup (3)…的目錄。
ReplaceExisting如果指定的目錄已存在,會產生新的目錄,也就是說目錄下原本的檔案會被清除。
FailIfExists應該是預設值,會發出ERROR_ALREADY_EXISTS的exception。
OpenIfExists可正常使用,無錯誤發生。

寫到這裡我忍不住要抱怨一下MSDN的說明,MSDN對CreateFolderAsync的函式宣告是:


CreateFolderAsync


CreateFolderAsync 2


但是為何不使用預設引數的方式來說明,還要讓programmer自己去猜第一種方式時預設的處理結果?


c. 處理例外


在PPL的架構下,當執行的過程中有exception或cancel的事件發生時,都會直接執行最後的一個.then區間的程式,透過catch(Exception^)來解讀exception的原因進行例外處理。


要注意的是雖然我程式中想要處理的例外目錄已存在時的錯誤ERROR_ALREADY_EXISTS,但程式不能直接以case ERROR_ALREADY_EXISTS來判定,而必需使用WIN32對應到HRESULT後的代碼才能對應到正確的case。可惜的是我們不能直接寫成case HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS):只好直接使用0x800700b7這樣意義不明的數字代碼。


在Debug的過程中,你可能會被下面的這個error message打斷你的測試:


CreateFolderAsync exception


這是因為VS2012的Debug exception設定為當發生exception時即中斷程式執行的關係,你可以透過取消上面的對話盒中的Break when this exception type is throw,或是從VS2012的[DEBUG] / [Exceptions…] (Ctrl-Alt-E)來取消程式被Platform::COMException^中斷。


comexception off


不過我個人是比較希望能提供類似用#pragma的方式指定特定程式開(或關)中斷於特定的exception。

沒有留言:

張貼留言