third party dll aus pfad in seperate domäne laden und instanzieren
-
Hey ho,..
ich würde gerne ne third party dll aus einem pfad in eine sperate domäne laden und die klasse instanzieren. Dazu habe ich mir eine kleine klasse gebastelt:
//############################################################################## // Smart Module Loader //############################################################################## ref class SmartModuleLoader { AppDomain ^domain; Assembly ^assembly; Object^_classInstance; Type^_class; public: SmartModuleLoader() { domain=AppDomain::CreateDomain(AppDomain::CurrentDomain->FriendlyName); }; ~SmartModuleLoader() { AppDomain::Unload(domain); }; Assembly^ Load(String^string, Exception^&error) { try { error=nullptr; this->assembly = Assembly::LoadFrom(string); } catch(Exception ^e) { //Type^ e_1 = Type::GetType("System.IO.FileLoadException"); Type^ e_2 = Type::GetType("System.IO.FileNotFoundException"); error=e; if(e->GetType()->FullName ==e_2->FullName) { System::Windows::Forms::OpenFileDialog^ openFileDialog1 = gcnew System::Windows::Forms::OpenFileDialog; openFileDialog1->InitialDirectory = "c:\\"; openFileDialog1->Filter = "dll files (*.dll)|*.dll|All files (*.*)|*.*"; openFileDialog1->DefaultExt = "dll"; openFileDialog1->FileName = string; openFileDialog1->CheckFileExists = true; openFileDialog1->CheckPathExists = true; openFileDialog1->FilterIndex = 1; openFileDialog1->RestoreDirectory = true; openFileDialog1->Multiselect = false; openFileDialog1->Title = "Please look for the File: "+ string; if ( openFileDialog1->ShowDialog() == System::Windows::Forms::DialogResult::OK ) { return this->Load(openFileDialog1->FileName,error); } else { return Assembly::GetExecutingAssembly(); };//end if Dialog };//end if compare exception FileNotFoundException };//end catch failure return this->assembly; };//end method Load Object^ InvokeMethod(String^string, cli::array<Object^,1> ^parameters,cli::array<Type^> ^typeArray, Exception ^&error) { Object^ret; try { error=nullptr; //MethodInfo^mi = this->_class->GetMethod(string); MethodInfo^mi; if(typeArray == nullptr) { mi=this->_class->GetMethod(string); } else { mi=this->_class->GetMethod(string,typeArray); } ret= mi->ReturnType; ret= mi->Invoke(this->_classInstance,parameters); } catch(Exception ^e) { error=e; }; return ret; }; Object^ InstanciateClass(String ^string,Exception^&error) { try { error=nullptr; this->_class = this->assembly->GetType(string); cli::array<System::Type^> ^types=this->assembly->GetTypes(); this->_classInstance = domain->CreateInstance(this->assembly->FullName,this->_class->FullName); return this->_classInstance; } catch(Exception ^e) { error=e; }; return false; }; };
eine typischer aufruf sieht z.b. so aus:
//init return value int iRet=std_ret; //init SmartModuleLoader SmartModuleLoader ^loader = gcnew SmartModuleLoader(); //init exception Exception ^e=nullptr; //load assembly Assembly^ assembly = loader->Load(DLL_LOCATION,e); UNREFERENCED_PARAMETER(assembly); //check for exception if(e!=nullptr) return module_load_error; //instanciate class Object^objClass = loader->InstanciateClass(DLL_EXAMPLE_CLASS,e); UNREFERENCED_PARAMETER(objClass); if(e!=nullptr) return class_load_error; //connect System::Object^ returnvalue = loader->InvokeMethod(DLL_EXAMPLE_METHOD_1,nullptr,nullptr,e); //check for failure if( e!=nullptr ) return error_method_1; //try to call the method String^strTemp =(String^)loader->InvokeMethod( DLL_EXAMPLE_METHOD, gcnew cli::array<Object^,1>{gcnew String(strBar), gcnew String(strProgN),gcnew String(strBlub)}, gcnew cli::array<Type^,1>{String::typeid,String::typeid,String::typeid}, e); //check for error if( e!=nullptr ) return error_method_2; //and cast iRet=MarshalNetToStdString(strTemp,strFoo,iSize); return iRet; };
Das ding ist, wenn die dll nicht im pfad der executable ist, findet er die klasse nicht....
könnt ihr mir da evtl helfen?
Greetz
-
Ok problem ist gelöst,...
//############################################################################## // Smart Module Loader //############################################################################## ref class SmartModuleLoader { AppDomain ^domain; Assembly ^assembly; Object^_classInstance; Type^_class; public: SmartModuleLoader() { }; ~SmartModuleLoader() { AppDomain::Unload(domain); }; Assembly^ Load(String^string, Exception^&error) { try { error=nullptr; //first load the assembly this->assembly = Assembly::LoadFrom(string); //now create the domain with specificly dependency paths System::AppDomainSetup ^setup =gcnew System::AppDomainSetup; //get the base directory String^location=this->assembly->Location; //cpy it to an char array to use st-library for method find_last_of char path[280]; memset(&path,0,280); MarshalNetToStdString(location,(char*)&path,280); //get last postion std::string strLocal; strLocal.append(path,strlen(path)); size_t iPos=strLocal.find_last_of("/\\"); if(iPos!=std::string::npos) { strLocal=strLocal.substr(0,iPos); location = gcnew String(strLocal.c_str()); //String^base = AppDomain::CurrentDomain->BaseDirectory; setup->ApplicationName =AppDomain::CurrentDomain->FriendlyName; setup->ApplicationBase = AppDomain::CurrentDomain->BaseDirectory; setup->PrivateBinPath = location; setup->CachePath = location; setup->DynamicBase = location; }; //System::Evidence this->domain=AppDomain::CreateDomain(AppDomain::CurrentDomain->FriendlyName,AppDomain::CurrentDomain->Evidence,setup);// + strDateTime } catch(Exception ^e) { //Type^ e_1 = Type::GetType("System.IO.FileLoadException"); Type^ e_2 = Type::GetType("System.IO.FileNotFoundException"); error=e; if(e->GetType()->FullName ==e_2->FullName) { System::Windows::Forms::OpenFileDialog^ openFileDialog1 = gcnew System::Windows::Forms::OpenFileDialog; openFileDialog1->InitialDirectory = "c:\\"; openFileDialog1->Filter = "dll files (*.dll)|*.dll|All files (*.*)|*.*"; openFileDialog1->DefaultExt = "dll"; openFileDialog1->FileName = string; openFileDialog1->CheckFileExists = true; openFileDialog1->CheckPathExists = true; openFileDialog1->FilterIndex = 1; openFileDialog1->RestoreDirectory = true; openFileDialog1->Multiselect = false; openFileDialog1->Title = "Please look for the File: "+ string; if ( openFileDialog1->ShowDialog() == System::Windows::Forms::DialogResult::OK ) { return this->Load(openFileDialog1->FileName,error); } else { return Assembly::GetExecutingAssembly(); };//end if Dialog };//end if compare exception FileNotFoundException };//end catch failure return this->assembly; };//end method Load Object^ InvokeMethod(String^string, cli::array<Object^,1> ^parameters,cli::array<Type^> ^typeArray, Exception ^&error) { Object^ret; try { error=nullptr; //MethodInfo^mi = this->_class->GetMethod(string); MethodInfo^mi; if(typeArray == nullptr) { mi=this->_class->GetMethod(string); } else { mi=this->_class->GetMethod(string,typeArray); } //ret= mi->ReturnType; ret= mi->Invoke(this->_classInstance,parameters); } catch(Exception ^e) { error=e; }; return ret; }; Object^ InstanciateClass(String ^string,Exception^&error) { try { error=nullptr; this->_classInstance = domain->CreateInstance(this->assembly->FullName,this->_class->FullName); return this->_classInstance; } catch(Exception ^e) { error=e; }; return false; }; };
-
Du kannst keine Klasse in einer AppDomain instanziieren und in einer anderen Verwenden!!!
Das geht nur via Remoting...Und wenn Du die AppDomain wieder entlädst, dann ist auch diese Klasse wieder weg!!!
-
Hi,
also das die klasse weg ist wenn ich die domäne entlade ist klar.
Ich musste jetzt in der Praxis feststellen das nicht alle methoden richtig geladen werden und mittels GetMethods() werden mir nur manche methoden angezeigt.
Remoting.... hast Du da ein beispiel?
grüüüße und dank
------------------------------------------------------------------------------
Edit:
Die Dll soll aber nicht registriert sein und in einem anderen Verzeichniss als die Hauptapplikation liegen...
-
Wo die DLL liegt ist egal.
Du kannst von einer Klasse in einer anderen AppDomain keine Methoden aufrufen, wenn das Objekt nicht von "MarshalByRefObject" abgeleitet wurde oder als "Serializable" markiert wurde...Siehe:
http://stackoverflow.com/questions/599731/use-the-serializable-attribute-or-subclassing-from-marshalbyrefobject
http://msdn.microsoft.com/en-us/library/system.marshalbyrefobject
-
Hm,..
wie schon gesagt handelt es sich dabei um eine third party dll und wüsste jetzt nicht wie ich dies Marshalen sollte.
Wo die DLL liegt ist egal.
Du kannst von einer Klasse in einer anderen AppDomain keine Methoden aufrufen, wenn das Objekt nicht von "MarshalByRefObject" abgeleitet wurde oder als "Serializable" markiert wurde...Naja... der source funktioniert ja aber...,
Also ich lade die assembly mit LoadFrom,
Erzeuge eine Domäne mit dem Pfaden der Assembly,
Hole mir den typen der klasse aus der assembly,
und erzeuge eine Instance der klasse (der ctor wird dann auch aufgerufen) aus der creierten domäne heraus. (scheint ja zu funktionieren)Die aufrufe bekannter Methoden funtioniert, und die dll wird auch richtig entladen wenn ich die domäne entlade.
Es wird das MethodenInfo array auch mit der richtigen anzahl der methoden geliefert. Es wird auch der Name der Methoden gesetzt, aber GetMethod findet nicht die Überladene Methode mit der richtigen anzahl an parametern zurück, geschweige manchmal einen nullptr.
Wenn ich hingegen nach den Namen suche und die Indizes ausprobiere (Wegen der Überladung) finde ich die richtige Methode und kann sie aufrufen.
Nun nochmal ein paar fragen:
a) Läd mein Smartloader die Methoden denn nicht in der neuen domäne ?
b) Wie kann ich automatisiert und (vor allem richtig) die Methodeninformationen laden..hab dank
-
1. Du kannst nur Klassen verwenden die als [Serializable] markiert sind oder von MarshalAsRefObject abgeleitet sind. Ist dies der Fall?????
2. Ansonsten kannst Du nur "simple" Methoden aufrufen (POD-Typen); das ist aber auch trickreich... und nicht empfohlen...Alles andere führt nicht zu einem zuverlässigen Ergebnis.