2011-07-31 14 views
8

AppDomainを別のベースディレクトリで作成しました。ただし、現在実行中のアセンブリをベースディレクトリにコピーせずに、現在実行中のアセンブリを他のAppDomainにロードすることはできません。私はバイトからロードしようとしました。現在のアセンブリを別のAppDomainにロード

私がロードしようとすると、私は例外を取得しないが、私は使用しようとすると:

domain.DoCallBack(new CrossAppDomainDelegate(... 

私が取得:

は、ファイルまたはアセンブリをロードできませんでした....... .... システムは、指定されたファイルを見つけることができません。

私のコードは次のとおりです。

private static void SaveAssemblies(Assembly ass, List<byte[]> assemblyByteList) 
{ 
    AssemblyName[] assNames = ass.GetReferencedAssemblies(); 
    foreach (AssemblyName assName in assNames) 
    { 
     Assembly referedAss = Assembly.Load(assName); 
     if (!referedAss.GlobalAssemblyCache) 
     { 
      SaveAssemblies(referedAss, assemblyByteList); 
     } 
    } 
    byte[] rawAssembly = File.ReadAllBytes(ass.Location); 
    assemblyByteList.Add(rawAssembly); 
} 

public static AppDomain CreateAppDomain(string dir, string name) 
{ 
    AppDomainSetup domainSetup = new AppDomainSetup(); 
    domainSetup.ApplicationBase = dir; 
    domainSetup.ApplicationName = Path.GetFileName(dir); 
    domainSetup.PrivateBinPath = Path.Combine(dir, "Libs"); 

    AppDomain domain = AppDomain.CreateDomain(name, null, domainSetup); 
    //Load system assemblies needed for the module 
    List<byte[]> assemblyByteList = new List<byte[]>(); 
    SaveAssemblies(Assembly.GetExecutingAssembly(), assemblyByteList); 

    foreach (byte[] rawAssembly in assemblyByteList) 
     domain.Load(rawAssembly); 

    domain.DoCallBack(new CrossAppDomainDelegate(SetupLogging)); 
    return domain; 
} 

アップデート:

アセンブリが、私は出力に見れば、私はこの

「TaskExecuter.Terminal.vshostを参照してくださいロードされているようです。 (Managed(v4.0.30319)):ロードされた 'NLog' 'TaskExecuter.Terminal.vshost.exe'(Managed(v4.0.30319)):ロードされた 'TaskExecuter'、シンボルが読み込まれました。

が、私はまだ例外を取得...私は=この

System.IO.FileNotFoundExceptionは、未処理のメッセージだった理解していない ファイルまたはアセンブリをロードできませんでした「TaskExecuter、バージョン= 1.0.4244.31921、 カルチャ=ニュートラル、PublicKeyToken =ヌル 'またはその依存関係の1つ。 システムは、指定されたファイルを見つけることができません。ソース= mscorlib
ファイル名= TaskExecuter、バージョン= 1.0.4244.31921、文化=中立、 なPublicKeyToken = nullをFusionLog ====事前バインド状態情報=== LOG:ユーザー=ピーター-PC \ピーターLOG:表示名=アプリケーションディレクトリ= ファイル:/// C:/ ProgramData/TaskExecuter/TaskLib/uTorrentTasksログ: Private:= C: \ ProgramData \ TaskExecuter \ TaskLib \ uTorrentTasks \ Libs 呼び出しアセンブリ:(不明)。 === LOG:このバインドは、デフォルトのロードコンテキストで開始します。ログ: アプリケーション構成ファイルを使用します。 2010 \ Projects \ TaskExecuter \ TaskExecuter.Terminal \ bin \ Release \ TaskExecuter.Terminal.vshost.exe.Config ログ:ホスト構成ファイルを使用します。 :LOG:マシン構成 C:\ Windows \ Microsoft.NET \ Framework \ v4.0.30319 \ config \ machine.configのファイルを使用します。 LOG:現時点でポリシーが参照に適用されていません(プライベート、 カスタム、部分、または場所ベースのアセンブリバインド)。ログ:新しいURLの をダウンロードしようとしています ファイル:/// C:/ProgramData/TaskExecuter/TaskLib/uTorrentTasks/TaskExecuter.DLL。 ログ:新しいURLのダウンロードを試みています ファイル:/// C:/ProgramData/TaskExecuter/TaskLib/uTorrentTasks/TaskExecuter/TaskExecuter.DLL。 ログ:新しいURLのダウンロードを試みています ファイル:/// C:/ProgramData/TaskExecuter/TaskLib/uTorrentTasks/Libs/TaskExecuter.DLL。 ログ:新しいURLのダウンロードを試みています ファイル:/// C:/ProgramData/TaskExecuter/TaskLib/uTorrentTasks/Libs/TaskExecuter/TaskExecuter.DLL。 ログ:新しいURLのダウンロードを試みています ファイル:/// C:/ProgramData/TaskExecuter/TaskLib/uTorrentTasks/TaskExecuter.EXE。 ログ:新しいURLのダウンロードを試みています ファイル:/// C:/ProgramData/TaskExecuter/TaskLib/uTorrentTasks/TaskExecuter/TaskExecuter.EXE。 ログ:新しいURLのダウンロードを試みています ファイル:/// C:/ProgramData/TaskExecuter/TaskLib/uTorrentTasks/Libs/TaskExecuter.EXE。 ログ:新しいURLのダウンロードを試みています ファイル:/// C:/ProgramData/TaskExecuter/TaskLib/uTorrentTasks/Libs/TaskExecuter/TaskExecuter.EXE。

のStackTrace:System.Reflection.RuntimeAssembly._nLoadで (のAssemblyName ファイル名、文字コードベース、証拠assemblySecurity、RuntimeAssembly locationHint、StackCrawlMark & stackMark、ブールthrowOnFileNotFound、 ブールforIntrospection、ブールsuppressSecurityChecks) するSystem.Reflectionました。 RuntimeAssembly.nLoad(のAssemblyName ファイル名、文字列のコードベース、証拠assemblySecurity、RuntimeAssembly locationHint、StackCrawlMark & stackMark、ブールthrowOnFileNotFound、 ブールforIntrospection、ブールsuppressSecurityChecks) System.Reflection.RuntimeAssembly.InternalLoad(文字列 assemblyString、証拠assemblySecurity、StackCrawlMark & stackMark、 でSystem.Reflection.RuntimeAssembly.InternalLoadAssemblyName(のAssemblyName assemblyRef、証拠assemblySecurity、StackCrawlMark & stackMark、 ブールforIntrospection、ブールsuppressSecurityChecks) でSystem.Reflection.Assembly.Load(文字列assemblyString) ブールforIntrospection) System.Runtime.Serialization.FormatterServices.LoadAssemblyFromString(文字列 のAssemblyName) System.Reflection.MemberInfoSerialiで でzationHolder..ctor dはTaskExecuter.AppDomainHelper.CreateAppDomain(文字列DIR、 文字列名)でSystem.AppDomain.DoCallBackで(SerializationInfoに 情報、StreamingContextコンテキスト) (CrossAppDomainDelegate callBackDelegate) :視覚的な\ \ユーザー\ピーター\ドキュメントプロジェクト\ TaskExecuter \ TaskExecuter \ TaskManagment \ Visual Studioの\ \ユーザー\ピーター\ドキュメント 2010: DでTaskExecuter.TaskManagment.TaskFinder.Probeでのライン50 ():プロジェクト\ TaskExecuter \ TaskExecuter \ AppDomainHelper.cs \スタジオ 2010 \ TaskFinder.cs:のd:\ users \ peter \ documents \ visual studioのTaskExecuter.TaskManagment.TaskManager.LoadTasks()の の Dでライン TaskExecuter.TaskManagment.TaskManager.Startで():プロジェクト\ TaskExecuter \ TaskExecuter \ TaskManagment \ TaskManager.cs \2010のVisual Studio \ \ユーザー\ピーター\ドキュメント 2010プロジェクト\ TaskExecuter \ TaskExecuter \ \ TaskManagment \ TaskManager.cs: d:\ users \ peter \ documents \ visualスタジオ 2010年\ Projects \ TaskExecuter \ TaskExecuter.Terminal \ ProgramにあるTaskExecuter.Terminal.Program.Main(String [] args)の の行。 .cs:行16 at System.AppDomain._nExecuteAssembly(RuntimeAssemblyアセンブリ、 String [] args) at System.AppDomain.ExecuteAssembly(String assemblyFile、 Evidence assemblySecurity、String [] args) ( )System.Threading.ThreadHelperのMicrosoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() にあります。ThreadStart_Context System.Threading.ExecutionContext.Runで(オブジェクト 状態) (のExecutionContext のExecutionContext、ContextCallbackコールバック、オブジェクト状態、ブール ignoreSyncCtx) System.Threading.ExecutionContext.Runで(のExecutionContext のExecutionContext、ContextCallbackコールバック、状態オブジェクト) System.Threading.ThreadHelper.ThreadStartで ()
のInnerException:

+3

アセンブリを単純な「お尻」に短縮しないことが良い方法です。このように呼び出されないと、よりうまく動作する可能性があります。 –

+0

@AlexeiLevenkov、あなたがアセンブリに関する情報を記録するためにstringbuilderを構築し始めるまで待ってください。その省略形で何を変数と呼ぶでしょうか? (pluginLog、domainLog、typeLog、___) – JoeBrockhaus

答えて

4

を私はarchive.orgを使用してポストをブログとも実用的なソリューションを考え出すにリンクを回復することができました。

私の目標は、exeを一時的な場所に動的にコンパイルし、そのexeシャドウに子のappdomainのすべてのメインdllをロードさせて、exeを生成したメインアプリケーションを簡単に更新できるようにすることでした。基本的なアプローチは、childAppDomain.CreateInstanceFromを使用して、コンストラクタでassembly resolveイベントハンドラをインストールする型を作成することです。私のコードは

var exportAppDomain = AppDomain.CreateDomain(
    runnerName, 
    null, 
    appDomainSetup, 
    new PermissionSet(PermissionState.Unrestricted)); 

exportAppDomain.CreateInstanceFrom(
    Assembly.GetExecutingAssembly().Location, 
    "ExportLauncher.AppDomainResolver", 
    true, 
    BindingFlags.Public | BindingFlags.Instance, 
    null, 
    new object[] { Assembly.GetExecutingAssembly().Location }, 
    null, 
    null); 

のように見えた。ここで必要に、AssemblyResolveハンドラを作成するタイプ(あなたが別のタイプを必要とする理由以下のブログ記事で説明)

class AppDomainResolver 
{ 
    string _sourceExeLocation; 

    public AppDomainResolver(string sourceExeLocation) 
    { 
     _sourceExeLocation = sourceExeLocation; 
     AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve; 
    } 

    Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) 
    { 
     if (args.Name.Contains("exporterLauncher")) // why does it not already know it has this assembly loaded? the seems to be required 
      return typeof(AppDomainResolver).Assembly; 
     else 
      return null; 
    } 
} 

は、元のブログ記事です:

アプリケーションドメインは難しいです...

.NETでApplication Domainを使って作業したことがありますか?最初はそれほど難しくはありませんあなたがそれらを知るようになると、あなたはすべての小さな困難を理解し始める。

ホストAppDomains.BaseDirectoryの外に移動しない限り、すべてうまく動作しますが、私たちの場合は、ホストアプリケーションの実行中にプラグインを「C:\ My Plug-ins」という場所に配置したかったのです「C:¥Program Files¥My App」で実行されます。これは、AppDomainからホストアセンブリのいくつかの問題が発生する可能性があるためです。

The Classic ここにいくつかの簡単なコードと最初の試みがあります。

1: string applicationBase = Path.GetDirectoryName(interOperabilityPackageType.AssemblyDescription.AssemblyPath); 
    2: AppDomainSetup setup = new AppDomainSetup 
    3: { 
    4:  ApplicationName = name, 
    5:  ApplicationBase = applicationBase, 
    6:  PrivateBinPath = AppDomain.CurrentDomain.BaseDirectory, 
    7:  PrivateBinPathProbe = AppDomain.CurrentDomain.BaseDirectory, 
    8:  ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile 
    9: }; 
    10: 
    11: Evidence evidence = new Evidence(AppDomain.CurrentDomain.Evidence); 
    12: AppDomain domain = AppDomain.CreateDomain(name, evidence, setup); 

は非常に単純なようだが、「ApplicationBaseは」「AppDomain.CurrentDomain.BaseDirectory」と異なっているので、我々は非常によく知っている例外であると思われるものに走りました。

System.IO.FileNotFoundException:ファイルまたはアセンブリ 'Host.Services、Version = 1.0.0.0、Culture = neutral、PublicKeyToken = null'またはその依存関係の1つを読み込めませんでした。システムは、指定されたファイルを見つけることができません。

どのような種類の動的ロードアセンブリで作業していたとしても、それはあなたにはよく知られています。そして、問題は、 "Host.Services"が "C:\ Program Files \ My App"に格納されているため、ホストアプリケーションドメイン内で認識されていて、それを探しているアプリケーションドメインが "C:\ My Plug-イン "。

まあ私たちは "AppDomain.CurrentDomain.BaseDirectory"を "C:\ Program Files \ My App"にも見せるように指示したと思ったが、そうではなかった。

AppDomain.AssemblyResolve to the rescue? これまで、これらの癖に取り組んでいたので、 "AppDomain"の使い方を知っていました。AssemblyResolve "を使用して、AppDomainが処理できなかったアセンブリを手動で解決します。

1: string applicationBase = Path.GetDirectoryName(interOperabilityPackageType.AssemblyDescription.AssemblyPath); 
    2: AppDomainSetup setup = new AppDomainSetup 
    3: { 
    4:  ApplicationName = name, 
    5:  ApplicationBase = applicationBase, 
    6:  PrivateBinPath = AppDomain.CurrentDomain.BaseDirectory, 
    7:  PrivateBinPathProbe = AppDomain.CurrentDomain.BaseDirectory, 
    8:  ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile 
    9: }; 
    10: 
    11: Evidence evidence = new Evidence(AppDomain.CurrentDomain.Evidence); 
    12: AppDomain domain = AppDomain.CreateDomain(name, evidence, setup); 
    13: domain.AssemblyResolve += Resolve; 

右動作するはずです、よく私たちはそう考えて、ものをもう一度、私たちは今何が起こることは、私たちが夢中にどこ代わりに、実際に限り、アプリケーションドメインを初期化し、それを使用するなど、取得のではなく、それが正しい失敗したということです、間違っていましたアセンブリを解決するためのイベントハンドラを作成します。

この例外も前述のように非常によく似ていますが、今回は、上記のスニペットの最後の行に設定した「解決」ハンドラを持つタイプのアセンブリを見つけることができません。

AppDomain.Load then! もちろん、イベントハンドラをフックアップするとき、アプリケーションドメインは、そのイベントを処理するオブジェクトの型を知る必要があります。実際には、アプリケーションドメインがそれを見つけることさえできない場合私たちは本当に何も処理できません。

次は何ですか?私たちの考えは、アプリケーションドメインに、GACで検出される可能性のある他の依存関係を持たない浅いアセンブリをロードし、イベントハンドラをフックするよう手動で指示することでした。

1: string applicationBase = Path.GetDirectoryName(interOperabilityPackageType.AssemblyDescription.AssemblyPath); 
    2: AppDomainSetup setup = new AppDomainSetup 
    3: { 
    4:  ApplicationName = name, 
    5:  ApplicationBase = applicationBase, 
    6:  PrivateBinPath = AppDomain.CurrentDomain.BaseDirectory, 
    7:  PrivateBinPathProbe = AppDomain.CurrentDomain.BaseDirectory, 
    8:  ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile 
    9: }; 
    10: 
    11: Evidence evidence = new Evidence(AppDomain.CurrentDomain.Evidence); 
    12: AppDomain domain = AppDomain.CreateDomain(name, evidence, setup); 
    13: domain.Load(File.ReadAllBytes(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Host.AssemblyLoader.dll"))); 
    14: domain.AssemblyResolve += new AssemblyLoader(AppDomain.CurrentDomain.BaseDirectory).Handle; 

次のような非常に単純なクラスを使用し、奇妙な解決動作は気にしないでください。

そうですか?いいえ...同じ問題は解決しません。

物事はずっと簡単です! 実際には、私たちがそれを機能させることができたとき、事実ははるかに単純になりました。

私は、.NETチームがこれがうまくいくと想定していたとは言えません。「PrivateBinPath」と「PrivateBinPathProbe」が使用されていた使用可能なものを実際に見つけることはできませんでした。さて、私たちは今それらを使用し、彼らが期待したとおりに動作させました!

1: [Serializable] 
    2: public class AssemblyLoader : MarshalByRefObject 
    3: { 
    4:  private string ApplicationBase { get; set; } 
    5: 
    6:  public AssemblyLoader() 
    7:  { 
    8:   ApplicationBase = AppDomain.CurrentDomain.SetupInformation.PrivateBinPath; 
    9:   AppDomain.CurrentDomain.AssemblyResolve += Resolve; 
    10:  } 
    11: 
    12:  private Assembly Resolve(object sender, ResolveEventArgs args) 
    13:  { 
    14:   AssemblyName assemblyName = new AssemblyName(args.Name); 
    15:   string fileName = string.Format("{0}.dll", assemblyName.Name); 
    16:   return Assembly.LoadFile(Path.Combine(ApplicationBase, fileName)); 
    17:  } 
    18: } 

そうではなく、我々はアプリケーションドメインを作成したイベントをフック、我々はクラスがそれ自身でそれをやらせる、と:だから我々は、代わりにこのように見えるように、「AssemblyLoader」クラスを変更

代わりに "CurrentDomain"に変更してください。

これは間違ったドメインにロードされているため、工場で作成する際に問題が発生するのですか?うまくいけば、ドメイン内のオブジェクトを外部から作成することができます。

1: string applicationBase = Path.GetDirectoryName(interOperabilityPackageType.AssemblyDescription.AssemblyPath); 
    2: AppDomainSetup setup = new AppDomainSetup 
    3: { 
    4:  ApplicationName = name, 
    5:  ApplicationBase = applicationBase, 
    6:  PrivateBinPath = AppDomain.CurrentDomain.BaseDirectory, 
    7:  PrivateBinPathProbe = AppDomain.CurrentDomain.BaseDirectory, 
    8:  ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile 
    9: }; 
    10: 
    11: Evidence evidence = new Evidence(AppDomain.CurrentDomain.Evidence); 
    12: AppDomain domain = AppDomain.CreateDomain(name, evidence, setup); 
    13: domain.CreateInstanceFrom(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Host.AssemblyLoader.dll"),"Host.AssemblyLoader"); 

私たちもそれはかなりにそれを自己をフックで生き続けなければならないので、「AssemblyLoader」への参照を維持するために気にしない。だから、次のようにドメインが今行われている作成

行事。

これは、同じ問題を偶然見つけたのを助けてくれることを願っています。プラグインを同じホストディレクトリにインストールし、必要な依存関係をすべてプラグインと共に展開するそれはプラグインがそれが依存していることを知っているものではありません。

以上のことは、私たちがホストアプリケーションベースから離れてプラグインをインストールすることを可能にしました。これはいいと思います。

誰かがこれを別の方法で解決した場合は、応答をしてください。いずれにしても賛否両論を見つけることができます。

質問がある、または上記をうまく動作させることができない場合は、お気軽にお問い合わせください。

著者:Jens Melgaard | posted @木曜日、7月1、2010 3:08 PM |フィードバック(0)

+2

PrivateBinPathProbeはディレクトリではなく、privatebinpathをプローブする必要があることを示す方法です。これはブールのように使うべきです。 http://msdn.microsoft.com/en-us/library/system.appdomainsetup.privatebinpathprobe%28v=vs.110%29.aspx – JoeBrockhaus

3

あなたは、元のアセンブリを使用しない理由理由はありますか?

外部のappdomainが元のアセンブリにアクセスできないような資格情報を使用していない限り、AppDomain.CreateInstanceFromAndUnwrapメソッドはそうすることができます。

私はこのようなクラスを使用して、あなたはのMarshalByRefObjectクラスであなたのリモートで実行されるコードを分離勧め:

public class MyRemoteClass : MarshalByRefObject 
{ 
    public void SetupLogging() 
    { 
     // ... 
    } 
} 

そして、このようにそれを使用する:これは、不要なトラブルを避けることができます

var assemblyPath = new Uri(typeof(MyRemoteClass).Assembly.CodeBase).LocalPath; 
var remote = (MyRemoteClass)domain.CreateInstanceFromAndUnwrap(assemblyPath, "NameSpace.MyRemoteClass"); 

remote.SetupLogging(); 

DoCallBackは値を返さないので、appdomain状態を介して戻り値を渡します。これにより、アプリケーションロジックにAppDomain配管コードが混在することもありません。

最後に、他の依存関係が正しくロードされるように、MyRemoteClass内のAppDomain.AssemblyResolveをインターセプトする必要があります。

+0

問題は、 "ApplicationBase"の下で定義されたアセンブリのみを読み込むことができ、2つのappdomainが完全に異なるApplicationBase値を持つことです。 – Peter

+0

これは別のメソッドを実行する正しい方法ですappdomain – Recep

0

アセンブリをロードする必要がある場合は、バイトからロードしないようにしてください...少なくとも、完全なアセンブリパスでロードすることをお勧めします。

"fusion log viewer"(http://www.bing.com/search?q=fussion+log+viewer)用のアセンブリを読み込む際の問題を調査し、コードを使用してアセンブリをロードしようとする場所を調べることが一般的です。

+0

問題は、 "ApplicationBase"の下で定義されたアセンブリのみを読み込むことができ、2つのappdomainsが完全に異なるApplicationBase値を持つことができることです。 – Peter

2

は(.GetName設定バイトからアセンブリをロードした後に解決策を見つけました).CodeBaseをnullに設定すると問題が解決しました...

私はthisページを見つけた後、より良い解決策を見つけました。

+0

どういうわけか、それを割り当てた後でさえもこれは元の値を持っています。 – Peter

+6

リンクはもはや機能しません。私はそれがリンクを残す以外に、ここにソリューションが貼り付けられることを推奨した理由だと思います。 –

0

私の推測では、エラーメッセージの重要な部分を逃したことである:

System.IO.FileNotFoundExceptionた未処理メッセージ=ファイルまたは アセンブリをロードできませんでした「TaskExecuter、バージョン= 1.0.4244.31921、文化=中立、PublicKeyToken = null ' またはその依存関係の1つ。システムは、指定されたファイルを見つけることができません。 Source = mscorlib

あなたのApplicationBase以外の場所からアセンブリをロードすることはできませんが、おそらく解決されてロードされる可能性がある依存アセンブリがありません。

ところで、バイトからロードを開始する場合は、ドメインにロードされたアセンブリを確認する必要があります。依存アセンブリは既にロードされている可能性がありますが、依存関係を自動的に解決することはできません。同じアセンブリを2回ロードした場合、その型は互換性がありません。 YourClassのオブジェクトをYourClassにキャストすることができないと言った、面白いCastExceptionsを取得します。

あなたのドメインにAssemblyResolveイベントハンドラを登録しようとすることはできますが、これで簡単に.dll hellの黒い魔法を思いついてしまいます。 他のすべてが失敗し、地獄を自分で.dllをするために行く場合は、ここで私を満たしています。 Need to hookup AssemblyResolve event when DisallowApplicationBaseProbing = true

関連する問題