Windows 8 – Bypass Language Projection with Native C++/WRL

在前幾篇文章中提到了Windows 8中核心之一的Language Projection,從一個簡化的角度來看,Language Projection位於語言、

編譯器與Windows Runtime Classes的中間,負責將兩者透過Windows Metadata黏合起來,大略如圖1所示。

/黃忠成

 

The Language Projection

 

   在前幾篇文章中提到了Windows 8中核心之一的Language Projection,從一個簡化的角度來看,Language Projection位於語言、

編譯器與Windows Runtime Classes的中間,負責將兩者透過Windows Metadata黏合起來,大略如圖1所示。

圖1

需注意的一點是,Language Projection並沒有實質的規定要如何實作,Windows 8只提供了這樣的概念及可供完成Language Projection的

Windows Metadata檔案而已,因此,實際的Language Projection實作完全由語言環境及編譯器來決定。

 

  當然,也不一定一定要透過Language Projection才能使用Windows Runtime Classes,如果必要(通常是不會有這種情況,除非你用的語言已經不維護了,

否則未來提供Language Projection的機會很高),經由Windows 8 Developer Kit所提供的Include/Lib/Idl檔案就能在沒有Language Projection的狀況下使用Windows Runtime Classes。

 

  以Visual C++來說,只要安裝了Windows 8 Developer Kit(位於C:\Program Files (x86)\Windows Kits\8.0\)便可直接使用其提供的標頭檔(.h)及函式庫(.lib)來取用

Windows Runtime Classes,請注意,這並不是件容易的事,通常先決條件是你得熟悉C++、COM,也得知道要使用的Windwos Runtime Classes定義在哪個Lib中(否則Linker會失敗)。

 

  讓我們從一個簡單的C++ Metro Style App開始,直接透過Visual C++ 11來做可以讓我們跳過IUnknown、Iinspectable這些過於繁瑣的細節,直接穿越Language Projection來使

用Windows Runtime Classes。

這個應用程式很簡單,就只有包含一個Button,按下之後跳出一個訊息框,在標準的C++ Metro Style寫法中,大概是如下面這樣。

程式1


void App12::MainPage::Button_Click_1(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)

{

     auto dialog = ref new  Windows::UI::Popups::MessageDialog("Hello WinRT");

     dialog->ShowAsync();

}

在Language Projection的協助下,程式碼相當的簡潔且易懂,這時MessageDialog的型別及Intellisense資訊都來自Windows Metadata,編譯時編譯器也會透過Windows Metadata取得MessageDialog的類別名稱,然後透過RoGetActivationFactory來建立MessageDialog這個Windows Runtime Object。

如果沒有Language Projection,那麼程式碼會變成下面這樣。

程式2


#include roapi.h

#include windows.ui.popups.h

………………

void App12::MainPage::Button_Click_1(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)

{

     LPWSTR str = L"Windows.UI.Popups.MessageDialog";   

     LPWSTR strContent = L"Hello WinRT";   

     HSTRING hstr,hstrContent;  

     ABI::Windows::UI::Popups::IMessageDialogFactory*  pDialogFactory; 

     ABI::Windows::UI::Popups::IMessageDialog* pDialog;

    

     SUCCEEDED(WindowsCreateString( str, wcslen( str ), &hstr ));     

     SUCCEEDED(RoGetActivationFactory(hstr, __uuidof(ABI::Windows::UI::Popups::IMessageDialogFactory), ((void**)&pDialogFactory)));

     SUCCEEDED(WindowsCreateString( strContent, wcslen( strContent ), &hstrContent ));     

     SUCCEEDED(pDialogFactory->Create(hstrContent, &pDialog));


     ABI::Windows::Foundation::IAsyncOperation  * pcmdAsync;

     SUCCEEDED(pDialog->ShowAsync(&pcmdAsync));


     SUCCEEDED( WindowsDeleteString( hstr ));  

     SUCCEEDED( WindowsDeleteString( hstrContent ));  

}

 

兩者執行的結果是一樣的。

在Windows Runtime Classes中,所有的物件都是透過ActivationFactory建立的,你必須先透過RoGetActivationFactory傳入要建立的物件名稱(

此例為Windows.UI.Popups.MessageDialog),接著取得對應的Factory(此例為IMessageDialogFactory),最後透過這個Factory來建立起真正的物件。

  不用我提,大概所有人都覺得這個過程太麻煩了,因此Windows Team提供了WRL(Windows Runtime Library),透過WRL也能達到同樣的目的,

而且更簡潔(不要跟Language Projection後的比…..)。

程式3

 


#include roapi.h


#include windows.ui.popups.h

#include wrl/client.h

#include wrl/wrappers/corewrappers.h

…………..

void App12::MainPage::Button_Click_1(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)

{

     const wchar_t* msgBoxClassName = L"Windows.UI.Popups.MessageDialog";

     const wchar_t* helloWorldMessage = L"Hello WinRT!";

     HSTRING hString, hWelcomeString;

     auto hr = ::WindowsCreateString(msgBoxClassName,

         static_cast(::wcslen(msgBoxClassName)), &hString);

     if(SUCCEEDED(hr))

     {

         Microsoft::WRL::ComPtr  factory;

         hr = Windows::Foundation::GetActivationFactory(hString, &factory);

         if(SUCCEEDED(hr))

         {

              hr = ::WindowsCreateString(helloWorldMessage,static_cast(::wcslen(helloWorldMessage)), &hWelcomeString);

              if(SUCCEEDED(hr))

              {

                  Microsoft::WRL::ComPtr dialog;

                  hr = factory->Create(hWelcomeString, &dialog);

                  if(SUCCEEDED(hr))

                  {

                       Microsoft::WRL::ComPtr<__FIAsyncOperation_1_Windows__CUI__CPopups__CIUICommand> command;

                       hr = dialog->ShowAsync(&command);

                  }

                  ::WindowsDeleteString(hWelcomeString);

              }

         }

         ::WindowsDeleteString(hString);

     }

}

結果是一樣的。

 

WRL

 

  透過WRL的協助,C++編譯器在略為調整後就能穿過Language Projection來建立Metro Style的應用程式,雖然後續還有Application Package Building的課題要處理,

但相較於沒有WRL情況下簡單多了。

  那麼如果沒有WRL會有多麻煩呢?首先你得先實作Iunknown介面,然後實作IInspectable介面,接著實作IApplicationOverrides,然後取得IApplicationFactory物件,

進行Wrapper(inner,outer,其實就是模擬繼承)後建立出IApplication,最後取得IApplicationStatics後呼叫其Start函式,這時一個Win 32 Project的Metro Style應用程式就會執行起來,

大概是像以下的程式碼。

 


#include "stdafx.h"

#include windows.ui.xaml.h


class MyApplicationOverrides:public ABI::Windows::UI::Xaml::IApplicationOverrides

{

    private:

         ABI::Windows::UI::Xaml::IApplicationOverrides* _spBaseImplementation;

         LONGm_nRefCount;

     public:

         virtual HRESULT STDMETHODCALLTYPEQueryInterface(REFIIDriid ,void**ppObj);

         ULONG   STDMETHODCALLTYPE AddRef();
 
         ULONG   STDMETHODCALLTYPE Release();


         virtualHRESULTSTDMETHODCALLTYPE GetIids(

            /* [out] */__RPC__out ULONG *iidCount,

            /* [size_is][size_is][out] */__RPC__deref_out_ecount_full_opt(*iidCount) IID** iids);

       

        virtua lHRESULT STDMETHODCALLTYPE GetRuntimeClassName(

            /* [out] */__RPC__deref_out_opt HSTRING*className);

       

        virtual HRESULT STDMETHODCALLTYPE GetTrustLevel(

            /* [out] */__RPC__outTrustLevel* trustLevel);


         MyApplicationOverrides(void);

         ~MyApplicationOverrides(void);

        

         void SetBase(ABI::Windows::UI::Xaml::IApplicationOverrides* pBaseImplementation)

         {

              _spBaseImplementation = pBaseImplementation;

         }


         virtua lHRESULTSTDMETHODCALLTYPE OnActivated(

              /* [in] */__RPC__in_optABI::Windows::ApplicationModel::Activation::IActivatedEventArgs*args) = 0;

                       

         virtual HRESULTSTDMETHODCALLTYPE OnLaunched(

              /* [in] */__RPC__in_optABI::Windows::ApplicationModel::Activation::ILaunchActivatedEventArgs*args) = 0;

        

         virtual HRESULT STDMETHODCALLTYPE OnFileActivated(

              /* [in] */__RPC__in_optABI::Windows::ApplicationModel::Activation::IFileActivatedEventArgs*args) = 0;

           

         virtual HRESULT STDMETHODCALLTYPE OnSearchActivated(

              /* [in] */__RPC__in_optABI::Windows::ApplicationModel::Activation::ISearchActivatedEventArgs*args) = 0;

           

         virtual HRESULT STDMETHODCALLTYPE OnShareTargetActivated(

              /* [in] */__RPC__in_optABI::Windows::ApplicationModel::Activation::IShareTargetActivatedEventArgs*args) = 0;

           

         virtual HRESULT STDMETHODCALLTYPE OnFileOpenPickerActivated(

              /* [in] */__RPC__in_optABI::Windows::ApplicationModel::Activation::IFileOpenPickerActivatedEventArgs*args) = 0;

           

         virtual HRESULT STDMETHODCALLTYPE OnFileSavePickerActivated(

              /* [in] */__RPC__in_optABI::Windows::ApplicationModel::Activation::IFileSavePickerActivatedEventArgs*args) = 0;

           

         virtua lHRESULTSTDMETHOD CALLTYPE OnCachedFileUpdaterActivated(

              /* [in] */__RPC__in_optABI::Windows::ApplicationModel::Activation::ICachedFileUpdaterActivatedEventArgs*args) = 0;

};

有WRL後就不用這麼麻煩,可參考http://www.interact-sw.co.uk/iangblog/的文章(注意,已過時,新的架構已經無法使用同樣的手法來建立出Application)。