调用ActivateApplication启动Windows商店应用可能导致商店应用崩溃

最近有开发者反映从Windows商店下载下来的某些商店应用如果使用IApplicationActivationManager:: ActivateApplication启动的话会有问题。一个典型的例子就是Bubble Birds 1.0。如果你用这个函数启动Bubble Birds程序的话,在某些时候会报出以下异常:

 

 IApplicationActivationManager:: ActivateApplication可以在当前会话通过通用启动合同(Windows.Launch)来激活指定的Windows商店应用。由于该函数只能用于桌面应用,通常我们会使用它来做自动化测试。下面是一个调用该函数的示例代码:

int _tmain(int argc, _TCHAR* argv[])

{

       LPCWSTR appId = L"<Use your app id>";

       CoInitialize(NULL);

       IApplicationActivationManager* paam = NULL;

       HRESULT hr = S_OK;

       hr = CoCreateInstance(CLSID_ApplicationActivationManager, NULL, CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&paam));

       if (SUCCEEDED(hr))

       {

              hr = CoAllowSetForegroundWindow(paam, NULL);

              if (SUCCEEDED(hr))

              {

                     DWORD pid = 0;

                     hr = paam->ActivateApplication(appId, nullptr, AO_NONE, &pid);

              }

       }

       CoUninitialize();

        return hr;

}

如果使用上面的代码启动Windows商店应用Bubble Birds,在商店应用第一次启动的时候是完全没有问题的。但是如果商店应用已经启动过了,在内存中有了一个实例,并且是处于Suspended状态,那么用ActivateApplication再次启动该应用的时候就会出现黑屏然后报上面的异常信息。这是由于使用ActivateApplication启动商店应用和从开始界面点击磁贴启动应用的行为是有所不同的。

 

当用户点击磁贴来启动一个已经处于Suspended状态的应用时,该应用会直接从内存中被唤醒而不必调用OnLaunched函数。但是如果用户使用ActivateApplication函数激活已经处于Suspended状态的应用时,该应用会被唤醒并且会调用OnLaunched函数。如果该Windows商店应用在启动代码中没有考虑过这一点话,那么就会导致一些不可预知的问题。就像Bubble Birds这样,它产生了黑屏和其后的异常错误。由于我并没有Bubble Birds的代码,没有办法进行深入的调试。但是从反编译出来的代码来看,黑屏是由于应用程序不正确的设置MainPage中的控件的Visibility属性导致的。而异常是由于访问配置文件“datastore.dat”时产生共享冲突引起的。当使用ActivateApplication重新启动该应用程序时,事件响应函数 Current_VisibilityChanged被激活了两次。而Current_VisibilityChanged 函数会将配置信息存贮到datastore.dat 文件中。下面就是反编译出来的相关代码:

private async void Current_VisibilityChanged(object sender, VisibilityChangedEventArgs e)
{
if (!e.get_Visible())
{
if (App._TheGame != null)
{
App._TheGame.Paused = true;
}
await this.SaveDataAsync();
}
}

public async Task SaveDataAsync()
{
StorageFolder localFolder = ApplicationData.get_Current().get_LocalFolder();
StorageFile storageFile = await localFolder.CreateFileAsync("datastore.dat", 1);
await FileIO.WriteTextAsync(storageFile, JsonConvert.SerializeObject(App._TheGame.Serialize()));
storageFile = await localFolder.CreateFileAsync("scoreboard.dat", 1);
await FileIO.WriteTextAsync(storageFile, JsonConvert.SerializeObject(App._Scoreboard));
ApplicationDataContainer localSettings = ApplicationData.get_Current().get_LocalSettings();
localSettings.get_Values()["GameSettings"] = JsonConvert.SerializeObject(App._TheSettings);
}

如果Current_VisibilityChanged 几乎同时被激活的话,由于前一个调用需要对datastore.dat文件进行写操作独占了文件句柄,后一个调用就会在打开该文件的时候就会产生ShareViolation异常。如果你使用Process Monitor观察出问题时候的文件读写记录就能清楚的看到这一点:

11:54:55.9410468 AM  XIMAD.BubbleBirds8.exe     7016   CreateFile   C:\Users\hanxia\AppData\Local\Packages\XIMADINC.BubbleBirds_np8fj6akx2czy\LocalState\datastore.dat       SUCCESS       Desired Access: Generic Read/Write, Disposition: Open, Options: Non-Directory File, Disallow Exclusive, Attributes: n/a, ShareMode: Read, Delete, AllocationSize: n/a, OpenResult: Opened

11:54:55.9416659 AM  XIMAD.BubbleBirds8.exe     7016   CreateFile   C:\Users\hanxia\AppData\Local\Packages\XIMADINC.BubbleBirds_np8fj6akx2czy\LocalState\datastore.dat       SHARING VIOLATION    Desired Access: Generic Read/Write, Disposition: Open, Options: Non-Directory File, Disallow Exclusive, Attributes: n/a, ShareMode: Read, Delete, AllocationSize: n/a

11:54:55.9426178 AM  XIMAD.BubbleBirds8.exe     7016   CloseFile   C:\Users\hanxia\AppData\Local\Packages\XIMADINC.BubbleBirds_np8fj6akx2czy\LocalState\datastore.dat       SUCCESS      

由于目前我们并没有提供任何应用接口来唤醒一个商店应用,对于这个问题,目前规避的办法就只能是在调用ActivateApplication函数之前先检查一下当前环境中是否存在需要启动Window商店应用的实例,如果有的话,那么就先将该实例关闭。