程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> GacUI Demo:PDB Viewer(分析pdb文件並獲取C++類聲明的詳細內容)

GacUI Demo:PDB Viewer(分析pdb文件並獲取C++類聲明的詳細內容)

編輯:C++入門知識

GacUI為了實現把界面序列化和反序列化到XML,必然要有類似反射一樣的功能。但是C++卻沒有反射,現在想到的方法就是,把編譯後的pdb文件拿出來。因為控件不是模板類,所以數據都可以直接獲取。pdb文件包含了所有函數的信息,還有被實例化後的模板類和模板函數的信息。因此只需要使用IDiaDataSource(Visual Studio提供的COM組件)讀取pdb的類聲明之後,把信息整理並輸出到一個xml裡面,然後就可以用C#編寫linq to xml的程序去分析並生成支持C++反射的一系列周邊代碼了。這樣就自動讓C++其中一部分必要的類獲得反射的功能,代價就是每一次修改完代碼之後,要記得非人肉地更新自動生成的代碼。

    不過為了更加形象的展示pdb的內容,我使用GacUI的帶Virtual Mode的TreeView打開pdb填充。這裡面有兩個view,第一個是pdb,第二個是整理後的class view。顯示pdb的GuiTreeView控件展示了如何通過提供一個數據源,從而實現“展開的時候再從pdb文件裡面讀取信息”的技術。而class view則是通過提供一個數據源來將一個文件中的xml讀取到內存並顯示出來,但是避免new那些暫時還不需要顯示出來的TreeViewNode對象。代碼放在Vczh Library++ 3.0http://vlpp.codeplex.com/ (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([=](GuiGraphicsComposition* sender, GuiEventArgs& arguments)
{
    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有關的對象支持反射的具體細節。元旦就先休息了,啊哈哈哈。
 


摘自  λ-calculus(驚愕到手了歐耶)
 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved