主程序
在大學課程里面,我對于模擬電路總是搞不清楚,直到現在也是這樣。我總覺得電路圖很奇怪,總會問“這部分電路是做什么用的”、“為什么會有這樣的效果”。在我的腦海里面,每部分的電路都應該有一定的用處,可是我總是看不明白。我媽媽說,我的思路被軟件所固化的太久了,看電路圖不應該總是一個個模塊的看,正確的方法應該是從電源的一極順著電路看,一直看到電源的另一極。我現在仍然不懂看電路圖,可是以我看代碼的經驗來說,我覺得分析源代碼按照這樣的思路來看會比較容易把脈絡理清楚。
在SharpDevelop的代碼中,由于很多的接口和插件的原因,很多代碼在看到某個地方會突然失去函數/方法調用的線索。例如看某個函數的實現的時候會跳到一個接口里面去,那是因為這部分功能在運行期才會給一個實現了這個接口的對象來進行具體的執行。從這個角度來說,設計模式也給我們研究代碼稍微帶來了一點小小的難度。在看Linux下源代碼的時候也經常遇到這種問題,在這個時候尋找代碼線索比較好的方法是用一個文本搜索工具來搜索相關的關鍵字。在Linux下我經常會用grep,Windows下面類似UltraEdit的“批量文件查找”功能會很好用(或者“Search And Replace”之類的工具)。這個是我讀代碼的一點小小的經驗,如果你知道有更好的方法,請告訴我讓我也學習一下 ? 。
我不想大段大段的貼代碼出來占地方(空間、帶寬,還有各位看官的注意力),在需要的地方我會貼上主要的代碼,因此最好能夠找代碼來對應著看。把代碼包解壓縮,我把它解到了“F:/SharpDevelop”(如果沒有說明,下文都是以此為代碼的根目錄了)。由于SharpDevelop本身對于察看代碼不是很方便,沒有“轉到定義”之類的功能,因此我建議你把它的代碼轉成VS的工程來看。不過很可惜,SharpDevelop的工程導出功能現在有問題,如果導出/src/SharpDevelop.cmbx 這個總的復合工程的話會失?。ㄎ矣浀肦C1版本是可以成功的,不知道為什么后來的版本反而會出問題),所以只能一個一個工程的導出。
好了,讓我們來看SharpDevelop的代碼吧。
1、起點
在主程序的起點在/src/Main/StartUp/SharpDevelopMain.cs,找到Main函數這就是整個程序的起點了。開始的部分是顯示封面窗體并加上命令行控制,其中SplashScreenForm 定義在/src/Main/Base/Gui/Dialogs/SplashScreen.cs文件中,這部分我就不多說了。之后是
Application.ThreadException += new ThreadExceptionEventHandler(ShowErrorBox);
SharpDevelop為了有效的進行錯誤報告,因此自己進行了異常的控制。系統出現異常的時候,SharpDevelop會攔截下來彈出它自己的異常提示報告對話框。這個代碼就是在這一行實現的。其中 ShowErrorBox 這個方法就在類SharpDevelopMain中,ExceptionBox 定義在/src/Main/StartUp/Dialogs/ExceptionBox.cs中。如果需要進行自己的異??刂?,可以學習一下這里的技巧。
2、充滿玄機的初始化
string [] addInDirs = ICSharpCode.SharpDevelop.AddInSettingsHandler.GetAddInDirectories( out ignoreDefaultPath );
AddInTreeSingleton.SetAddInDirectories(addInDirs, ignoreDefaultPath);
通過AddInSettingsHandler取得插件的目錄,并告知AddInTreeSingleton。AddInSettingsHandler定義在/src/Main/StartUp/Dialogs/AddInTreeSettingsHandler.cs中,它通過讀取系統配置(App.config)文件中的AddInDirectory節點的Path屬性來確定插件的目錄位置,或者你也可以通過自己定義的AddInDirectories節來指定插件目錄。如果你沒有做這些配置,默認的目錄在SharpDevelop運行目錄的。./Addins目錄下。
ServiceManager.Services.AddService( new MessageService());
ServiceManager.Services.AddService( new ResourceService());
ServiceManager.Services.AddService( new IconService());
通過ServiceManager(服務管理器)加入三個系統默認的服務,消息服務、資源服務、圖標服務。這三個服務中,消息服務是顯示各種信息提示,另外兩個是屬于系統的資源,SharpDevelop通過服務來進行統一調用和管理。
ServiceManager.Services.InitializeServicesSubsystem(“/Workspace/Services”);
初始化其他的服務。SharpDevelop把服務定義在插件樹的/Workspace/Services這個路徑中,凡是在這個路徑下的插件都被認為是服務,因此如果你自己定義了一個服務的話,也需要掛到這個路徑下(這里就是系統服務的擴展點了)。
注意!這一步中,在我們的眼皮子底下悄悄的進行了一個重要的初始化工作。各位看官請看,ServiceManager 定義在/src/Main/Core/Services/ ServiceManager.cs文件中,察看它的InitializeServicesSubsystem方法,我們發現這樣一行
AddServices((IService[]) AddInTreeSingleton.AddInTree.GetTreeNode(servicesPath).BuildChildItems(this).ToArray(typeof(IService)));
在這里,AddInTreeSingleton首次調用了AddInTree(插件樹)的實例。按照Singleton模式,只有在首次調用的時候才會初始化實例,這里也是同樣如此。整個系統的AddInTree是在這一步中進行了初始化工作,稍候我們將詳細介紹AddInTree如何進行初始化工作,先順便看看服務的初始化。在ServiceManager的InitializeServicesSubsystem方法中,通過AddInTree檢索服務插件路徑下的所有配置,并通過它來讀取、建立具體的對象,然后加入到服務列表中。之后通過一個循環,逐個的調用各個服務的InitializeService方法初始化服務。
AddInTree的初始化工作容我們稍候再看,先把主體的代碼看完。
commands = AddInTreeSingleton.AddInTree.GetTreeNode(“/Workspace/Autostart”).BuildChildItems(null);
for (int i = 0; i 《 commands.Count - 1; ++i)
{
((ICommand)commands[i]).Run();
}
/Workspace/Autostart是系統自動運行命令的擴展點路徑,定義在這個路徑下的插件會在系統啟動的時候自動運行。在這里,通過插件樹初始化建立處于這個路徑下的Command(命令),并逐一執行。BuildChildItems方法的功能是建立這個擴展點下的Command列表,我會在介紹AddTree的時候具體說明它的實現。
主程序代碼的最后,初始化完畢、關閉封面窗體,然后執行命令列表中最后一個命令(也就是系統的主界面)。在主界面退出的時候,系統卸載所有的服務。
在這部分代碼中,我們知道了兩個系統指定的擴展點路徑 /Workspace/Services 和 /Workspace/Autostart ,我們實現服務和指定系統自動運行命令的時候就可以掛到這兩個擴展點路徑下了。
托反射的福,ServiceManager.Services可以通過類型(接口)來查找具體的實例,也就是GetServices方法。但是ServiceManager的具體實現我們可以容后再看,這里已經不是最緊要的部分了。
接下來,我們來看看整個插件系統的核心-AddinTree的代碼,看看它是如何通過插件配置進行初始化并建立起整個系統的插件樹骨干。
編輯:hfy
-
Linux
+關注
關注
87文章
11341瀏覽量
210134 -
服務管理器
+關注
關注
0文章
2瀏覽量
477
發布評論請先 登錄
相關推薦
評論