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的函式宣告是:
但是為何不使用預設引數的方式來說明,還要讓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打斷你的測試:
這是因為VS2012的Debug exception設定為當發生exception時即中斷程式執行的關係,你可以透過取消上面的對話盒中的Break when this exception type is throw,或是從VS2012的[DEBUG] / [Exceptions…] (Ctrl-Alt-E)來取消程式被Platform::COMException^中斷。
不過我個人是比較希望能提供類似用#pragma的方式指定特定程式開(或關)中斷於特定的exception。
沒有留言:
張貼留言