VB のたまご

作成日: 2015/12/21, 更新日: 2015/12/21


exe ファイルを起動したまま、参照プログラムを開放したい

  •  この記事を書く前に、アプリケーションドメインについて下調べした記事を書きました。
  • アプリケーションドメインについて詳しく知りたい方は、先にこちらをご覧ください。
  • 既に知ってるよという方、または先にこの記事を読まれたい方は、このままご覧ください。


スポンサーリンク


プログラムの動的読込ってどうやるの?

  •  先日、とある案件の技術調査をしていました。それは、プログラムの動的読込と動的開放についてです。 一般的に、外部プログラムの読み込みは2種類あり、参照設定に追加して読み込む静的読込と、 プログラム上で、実行ファイルを指定して読み込む動的読込があります。

  •  動的読込をするにはリフレクションを使います。 以下はそのサンプルです。WindowsApplication プロジェクトと、もう一つの WindowsApplication プロジェクトを準備します。 1つ目の WindowsApplication はメインプログラム、2つ目の WindowsApplication は外部プログラムという設定です。 普通は、ClassLibrary プロジェクトにするのかな?まぁ、今回は WindowsApplication プロジェクトでお願いします。

  • 外部プログラム側に以下のソースを追加します。
  • ' 2つ目の WindowsApplication/Class1.vb
    Public Class Class1
    
        Public Sub ShowMessage(msg As String)
            MsgBox(msg)
        End Sub
    
    End Class
    

  • 呼び出す側のプログラムは、以下の通りです。
  • Imports System.Reflection
    
    Dim dllFile As String = "WindowsApplication1.exe"
    Dim asm As Assembly = Assembly.LoadFrom(dllFile)
    Dim t As Type = asm.GetType("WindowsApplication1.Class1")
    Dim o As Object = Activator.CreateInstance(t)
    t.GetMethod("ShowMessage").Invoke(o, New Object() {"hello, world!"})
    
    t = asm.GetType("WindowsApplication1.Form1")
    o = Activator.CreateInstance(t)
    CType(o, Form).Show()
    CType(o, Form).Close()
    

  •  必ず実行する前に、読み込む側のメインプログラムと同じ場所に、外部プログラムを配置してください。 そうしないと、FileNotFoundException が発生してしまいます。 さて、メッセージボックスが表示されましたでしょうか。こんな感じです。 それでは、そのまま起動しておいて、再度、読み込む側のメインプログラムと同じ場所に、外部プログラムを配置してみてください。

  • イメージ

  •  はい、ファイルを置き換えます!

  • イメージ

  •  ええー!?

  •  と、こうなるんですね。「別のプログラムがこのフォルダーまたはファイルを開いているので、操作を完了できません。フォルダーまたはファイルを閉じてから実行してください」。 キャンセルするしかありません。処理が終わったら自動的に開放されるんじゃないのー!?って思っていたので、今夜は残業です。


スポンサーリンク


プログラムの動的解放ってどうやるの?

  •  んじゃ、なんかあるでしょ。Close メソッドとか Release メソッドとか。それ呼べばいいんでしょ。 あれ?インテリセンスにそれっぽいのが無い。見逃したか?もう一回見てもありません。上から下まで1つずつ見てもありませんでした。 目薬つけてもダメでした。

  •  あれこれ探していると、なにやらお作法があるみたいですね。先人たちによると、いくつかの対策方法があるみたいです。

  •  1.仲介プログラムという別の exe ファイルを用意して、仲介プログラムに動的読込と実行をさせて終了させる。それによって外部プログラムも解放される。 データの受け渡し(外部プログラムのフルパス)は、起動時の引数で渡すとか、ファイル経由で伝えるとかがあるよと。 なるほど。でも今回は自身だけで何とかやりたいので、NG です。

  •  2.一瞬だけなら終わってもいい場合は、アップデータープログラムという別の exe ファイルを用意して、外部プログラムの入れ替えとメインプログラムの再起動をやってあげる。 なるほど。でも今回は(

  •  3.アプリケーションドメインを別途作成して、その中で外部プログラムを動的読込と実行させる。 最後にアプリケーションドメインを削除することで、外部プログラムも解放される。 なるほど。これだー!で、アプリケーションドメインって何?

  •  アプリケーションドメインを理解するのがめっちゃ大変だったです。 命令だけ分かっても、実際どういうことなのかよく分からなくて、概念的なことを、空間とかのイメージで絵に書きながら理解していきました。 (・・・理解、したつもり、です)。
  •  簡単に言うと、アプリケーションドメインを新規作成して、その中で外部プログラムを動的読込とかなんやらして、 やりたいことが終わったら、作成したアプリケーションドメインを削除すればいいみたいです。

  •  それではサンプルです。
  • Imports System.Reflection
    
    ' 別のアプリケーションドメイン上でやり取りできるようにするため、
    ' MarshalByRefObject を継承することが大事なポイントになります。
    ' この中に、やりたい処理を書きます。
    Class TransparentProxy
        Inherits MarshalByRefObject
    
        Public Sub ShowInfo()
    
            Dim dllFile As String = "WindowsApplication1.exe"
            Dim asm As Assembly = Assembly.LoadFrom(dllFile)
            Dim t As Type = asm.GetType("WindowsApplication1.Class1")
            Dim o As Object = Activator.CreateInstance(t)
            t.GetMethod("ShowMessage").Invoke(o, New Object() {"hello, world!"})
    
            t = asm.GetType("WindowsApplication1.Form1")
            o = Activator.CreateInstance(t)
            CType(o, Form).Show()
            CType(o, Form).Close()
    
        End Sub
    
    End Class
    
    Imports System.Reflection
    
    Dim domain = AppDomain.CreateDomain("NewDomain1")
    Dim dt1 = domain.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName, GetType(TransparentProxy).FullName)
    Dim dt2 = CType(dt1, TransparentProxy)
    dt2.ShowInfo()
    AppDomain.Unload(domain)
    

  •  それでは実行してみます。動的に読み込みが終わり、実行もされました。 画面を終了させないで、外部プログラムを更新してみます。・・・。できたー!!!更新できるぜー! 無事に、外部プログラムが解放されている証拠ですね。いやー良かった良かった。では、帰りましょうか、家に。

  •  最後までこの記事を読んでいただき、ありがとうございました。


スポンサーリンク