在前幾篇文章中提到了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)。