GacUI Demo:PDB Viewer(分析pdb檔案並獲取C++類宣告的詳細內容)
不過為了更加形象的展示pdb的內容,我使用GacUI的帶Virtual Mode的TreeView開啟pdb填充。這裡面有兩個view,第一個是pdb,第二個是整理後的class view。顯示pdb的GuiTreeView控制元件展示瞭如何通過提供一個數據源,從而實現“展開的時候再從pdb檔案裡面讀取資訊”的技術。而class view則是通過提供一個數據源來將一個檔案中的xml讀取到記憶體並顯示出來,但是避免new那些暫時還不需要顯示出來的TreeViewNode物件。程式碼放在Vczh Library++ 3.0(Candidate\GUI\GUIDemo\GUIDemo.sln)。現在先上圖:
解析PDB的關鍵程式碼在DumpPDB.cpp檔案中,大家只需要下載程式碼並閱讀即可。所有的內容都可以從MSDN搜尋IDiaDataSource獲得,但是執行的話則需要有這個COM元件,一般要求安裝Visual Studio。下面解釋一下一段C++程式碼。這是上面那個按鈕的回撥函式。這個回撥函式做了下面幾件事情
1、將Button和TagPage都Disable
2、利用執行緒池非同步將PDB的內容儲存到XML檔案中(一秒鐘)
3、第2步完成之後,發一個訊息回到GUI執行緒,自動顯示第二個TagPage
4、非同步將XML讀取到記憶體。在這裡我沒有使用延遲讀取技術,所以我直接建立了大約幾百萬個字串,需要五秒鐘
5、第4步完成之後,發一個訊息回到GUI執行緒嗎,將建立好的記憶體中的XML格式顯示在TreeView裡
這些非同步操作來往十分複雜,但是藉助C++0x就可以描述得十分清晰。GacUI的實現並沒有使用C++0x,但是仍然可以為使用C++0x的那部分使用者提供一些更加優化的介面。因此這些複雜的步驟最後就寫成了:
buttonDump->Clicked.AttachLambda
{
INativeController* controller=GetCurrentController();
tabControl->GetPages()[0]->GetContainer()->SetEnabled(false);
buttonDump->SetEnabled(false);
buttonDump->SetText(L"Dumping...");
buttonDump->GetRelatedControlHost()->GetBoundsComposition()->SetAssociatedCursor(controller->GetSystemCursor(INativeCursor::LargeWaiting));
ThreadPoolLite::QueueLambda([=]()
{
dumppdb::DumpPdbToXml(diaSymbol, L"..\\Debug\\GuiDemo.xml");
GetApplication()->InvokeLambdaInMainThread([=]()
{
tabControl->GetPages()[0]->GetContainer()->SetEnabled(true);
tabControl->SetSelectedPage(tabControl->GetPages()[1]);
buttonDump->SetText(L"Loading GuiDemo.xml in the class view...");
ThreadPoolLite::QueueLambda([=]()
{
FileStream fileStream(L"..\\Debug\\GuiDemo.xml", FileStream::ReadOnly);
CacheStream cacheStream(fileStream, 1048576);
BomDecoder decoder;
DecoderStream decoderStream(cacheStream, decoder);
StreamReader reader(decoderStream);
Ptr<TreeElement> xml=LoadXmlRawDocument(reader).Cast<TreeElement>();
GetApplication()->InvokeLambdaInMainThreadAndWait([=]()
{
buttonDump->SetText(L"GuiDemo.xml dumpped.");
buttonDump->GetRelatedControlHost()->GetBoundsComposition()->SetAssociatedCursor(controller->GetDefaultSystemCursor());
GuiTreeView* treeControl=new GuiTreeView(new win7::Win7TreeViewProvider, CreateProviderFromXml(xml));
treeControl->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0));
treeControl->SetVerticalAlwaysVisible(false);
treeControl->SetHorizontalAlwaysVisible(false);
tabControl->GetPages()[1]->GetContainer()->GetContainerComposition()->AddChild(treeControl->GetBoundsComposition());
});
});
});
});
});
buttonDump->Clicked.AttachLambda的意思是將一個滿足C++0x標準的lambda表示式當成一個事件處理程式繫結到一個時間上。ThreadPoolLite::QueueLambda則是將一個lambda表示式放進Windows核心實現的記憶體池進行非同步呼叫。GetApplication()->InvokeLambdaInMainThread(AndWait)則是在別的執行緒裡將一個lambda表示式放到GUI執行緒(一般是主執行緒)中執行。如果呼叫了Wait的版本,則這個函式會一直等到該lambda表示式在主執行緒執行完了才會返回。如果大家關心實現的話,可以去Candidate\GUI\GUI\NativeWindow\Windows\WinNativeWindow.cpp檔案裡檢視。
大家可以想象,在古老的不支援lambda表示式的C++版本里面,要實現這個過程,這個函式將被拆散成多少函式。為了傳遞很多複雜的物件,要寫多少個臨時的struct,new多少記憶體碎片才能將非同步回撥函式的引數做成Windows所希望的DWORD(__stdcall*)(void*)格式。為了把一部分事情放回到GUI執行緒做(我們都知道GUI庫不值得為了執行緒安全而做很多浪費效能的事情),得實現多少私有的Win32訊息,subclass多少東西才能最終做到。這一切在GacUI中都簡化了。
接下來將會研究如何利用pdb裡面的資訊讓跟GacUI有關的物件支援反射的具體細節。元旦就先休息了,啊哈哈哈。