天天看點

mono嵌入式應用研究(四):Assemly的加載與執行

c#的Assemly一般有exe和dll兩種檔案字尾,其實都差不多,通過mono_domain_assembly_open這個API将Assemly加載進來,加載的時候如果給定的路徑不能直接加載,會通過設定好的Assemly查找路徑去查找。Assemly的加載需要給定一個AppDomain,加載進來後隻在這個AppDomain可用。

執行的話有兩種方式:

一種是使用int mono_jit_exec (MonoDomain *domain, MonoAssembly *assembly, int argc, char *argv[])直接運作,一般是針對exe,這個函數回去運作Assemly中的Main函數,argc和argv是傳遞給Main函數的參數。比較簡單,但對于嵌入式開發來說,這種方式不合适。

另外一種是Assemly中提取出類,然後用這個類建立對象,然後再調用對象的方法,我使用的也是這種方法。

使用這種方案前需要先關注一個API:mono_bool mono_domain_set (MonoDomain *domain, mono_bool force),作用是設定目前的AppDomain,mono的API有的需要提供AppDomain,有的則不需要,這時候他會使用目前的AppDomain,如果你使用了多個AppDomain,不設定好目前的AppDomain的話就會發生混亂。一般每次使用mono的API和調用c#函數前都需要設定下,看了下其内部實作,基本就是TLS變量的設定,應該可以被頻繁調用。

類的擷取可以使用API:MonoClass * mono_class_from_name(MonoImage *image, const char* name_space, const char *name),通過類名來獲得對應的MonoClass,需要提供類所在的命名空間,MonoImage可以通過API:MonoImage * mono_assembly_get_image  (MonoAssembly *assembly)從Assembly獲得。

寫到這裡要吐槽下mono的代碼品質真的不高,mono_class_from_name有個可以忽略大小寫的版本mono_class_from_name_case,看了下他的内部實作,居然有記憶體洩漏,mono似乎對一些常用的部分就有維護,不常用的部分就讓他去了,bug到處都是。我在研究過程中被深深的坑過。

類拿到手後就可以建立對象了,MonoObject * mono_object_new (MonoDomain *domain, MonoClass *klass),這個API可以用來建立對象,建立好的對象如果需要長期儲存的話需要使用mono_gchandle_new來添加gc引用,以免被gchuishou。

mono_object_new 隻會建立對象,并不會對對象進行初始化,是以我們還需要手動的調用構造函數。

使用API:MonoMethod * mono_class_get_method_from_name (MonoClass *klass, const char *name, int param_count),使用構造函數名“.ctor”來擷取構造函數,最後的參數指明了構造函數擁有的參數個數,以應付多态的情況,但如果參數數量相同隻是類型不同的話,這個API就無能為例了,需要使用周遊了。

使用API:MonoMethod * mono_class_get_methods (MonoClass* klass, gpointer *iter)可進行方法的周遊。

MonoObject * mono_runtime_invoke(MonoMethod *method, void *obj, void **params, MonoObject **exc)這個API可以用來執行擷取到的方法,params傳遞了方法的參數,是一個void*數組,長度要和方法的參數個數對應。string的話傳MonoString*,對象的話傳MonoClass*,數組的話傳MonoArray*,基礎值類型直接傳對應變量位址,結構的話就有點麻煩了,你需要在c裡面定義一個對應的結構,然後傳這個結構的位址。

最後一個參數會傳回調用時發生的exception,沒有的話傳回的是NULL,可以用過mono_object_to_string獲得具體的資訊描述。

對象初始化完成,就可以通路對象的成員,也可以調用對象的方法了,方法的調用和上面構造函數的調用是一樣的,下面我們說說成員的通路。

先要擷取MonoClassField,使用API:MonoClassField * mono_class_get_field_from_name (MonoClass *klass, const char *name)

然後使用API:void mono_field_get_value (MonoObject *obj, MonoClassField *field, void *value)擷取成員的值,通過最後一個參數傳入MonoString*,MonoClass*,MonoArray*,或者基礎類型變量的位址來獲得資料。