- PR -

System.Reflection.Assembly.LoadFileでファイルが開けなくなる

1
投稿者投稿内容
ブリンク
会議室デビュー日: 2009/01/21
投稿数: 16
投稿日時: 2009-02-04 17:42
連続質問ですみません。

exeファイルのマニフェスト情報を取得するために
System.Reflection.Assemblyをつかってファイルオープンしたのですが
プログラムが起動している間、オープンしたファイルがつかまれてしまいます。

clsGuid------------------------------------------------------------
'マニフェストGUID取得のためにファイルをオープン
Dim strGuid As String
strGuid = System.Reflection.Assembly.LoadFile(FileName).ManifestModule.ModuleVersionId.ToString

'↑この処理はクラスで行っている

FileWiteForm--------------------------------------------------------
'別画面で同じファイルをオープンしようとすると、エラーが発生します。
Dim Fs As New System.IO.FileStream(FileName, IO.FileMode.Create, IO.FileAccess.Write) '←エラー発生
------------------------------------------------------------

原因はSystem.Reflection.Assembly.LoadFileでファイルがつかまれているからですが、
処理を行っているクラスをNothingにしてもファイルはつかまれたままになっています。
(Nothigが適当なのかよくわかりませんが)

いろいろと調べたのですが
System.Reflection.Assembly.LoadFileで読み込んだファイルを解放する方法がわかりません。

「開発環境」
Microsoft Visual Studio 2008
Microsoft .NET Framework 3.5
Professional Edition
Microsoft Visual Basic 2008

どなたかご存知の方がいらっしゃいましたら宜しくお願いします。



[ メッセージ編集済み 編集者: ブリンク 編集日時 2009-02-04 17:42 ]

[ メッセージ編集済み 編集者: ブリンク 編集日時 2009-02-04 17:44 ]
じゃんぬねっと
ぬし
会議室デビュー日: 2004/12/22
投稿数: 7811
お住まい・勤務地: 愛知県名古屋市
投稿日時: 2009-02-04 18:07
引用:

ブリンクさんの書き込み (2009-02-04 17:42) より:

System.Reflection.Assemblyをつかってファイルオープンしたのですが
プログラムが起動している間、オープンしたファイルがつかまれてしまいます。


一度ロードしたアセンブリは、アプリケーション ドメインごとアンロードしないとダメだったかと思います (記憶あいまい)。

_________________
C# と VB.NET の入門サイト
じゃんぬねっと日誌
ブリンク
会議室デビュー日: 2009/01/21
投稿数: 16
投稿日時: 2009-02-04 19:16
じゃんぬねっとさん
さっそくのご報告ありがとうございます。

引用:

じゃんぬねっとさんの書き込み (2009-02-04 18:07) より:
一度ロードしたアセンブリは、アプリケーション ドメインごとアンロードしないとダメだったかと思います 。




参考になる情報ありがとうございます。
他のロード方法を試したり調べたりしているのですが、今のところ解決の糸口がつかめておりません。

やりたいことは、

指定したファイルのマニフェスト情報のModuleVersionId(MVID:モジュールバージョンID)の取得と更新

なのですが、
もうひとつの方向として考えているのは
System.Reflection.Assembly.LoadFileを使わずにMVIDを取得できないか
ということです。

最初にファイルをコピーして、そこからMVIDだけを読み取るとか
バイナリモードで直接ファイルを開き、
MVID直後に書かれている”D5 0A 3A 03 20 00 01 03”を検索してみようとか
くだらないアイデアは思いつくのですが。

今考えている候補はとりあえず以下のようなものです。
○System.Reflection.AssemblyをつかわずにMVIDを取得できないか?
○一度ファイル情報を自分の中に取り込み、ファイルではなく、
 データとしてSystem.Reflection.Assemblyから開くことはできないか?


逆アセンブリツールに[Reflector.exe]というものがあり
http://www.red-gate.com/products/reflector/

このツールは読み込んだファイルをつかまずにマニフェスト情報を取得しています。
一応、可能なことは可能なようなんです。(当然VB.Netでは作られていないと思います)

やじゅ
常連さん
会議室デビュー日: 2008/07/15
投稿数: 28
お住まい・勤務地: 静岡市
投稿日時: 2009-02-04 19:17
AppDomain.CreateDomainを使って
AppDomainを作成し、そこにEXEアセンブリをLoadする。
用が済んだら、作成したAppDomainをUnloadすると
いいです。
ブリンク
会議室デビュー日: 2009/01/21
投稿数: 16
投稿日時: 2009-02-04 19:55
やじゅさん

ご報告ありがとうございます。

引用:

やじゅさんの書き込み (2009-02-04 19:17) より:
AppDomain.CreateDomainを使って
AppDomainを作成し、そこにEXEアセンブリをLoadする。
用が済んだら、作成したAppDomainをUnloadすると
いいです。




AppDomainがよくわからず、サンプルをつかってみたのですが
申し訳ないのですが、処理イメージがつかめませんでした。
よくわからないのですが、

>>そこにEXEアセンブリをLoadする

ということはLoadするまえにLoad可能なアセンブリを取得するようなイメージしか思いつかなかったのですが。
起動されていないexeファイル名を指定してEXEアセンブリをロードすることは可能なのでしょうか。

おしえてちゃん、で申し訳ないのですが、
どなたか参考情報をご存知の方がいらっしゃいましたら宜しくお願いします。
まるく
大ベテラン
会議室デビュー日: 2004/01/09
投稿数: 181
投稿日時: 2009-02-05 12:25
これかな
http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?topic=46665&forum=7
ブリンク
会議室デビュー日: 2009/01/21
投稿数: 16
投稿日時: 2009-02-05 18:26
引用:

まるくさんの書き込み (2009-02-05 12:25) より:
これかな
http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?topic=46665&forum=7



まるくさん

ご報告ありがとうございます。まさにこちらの内容でした。
過去ログにあったようで、自分の調査が甘いためみなさんに手間をとらせてしまい
申し訳ありませんでした。

"DLL 解放"で調べたらすぐに検索できたようです。

じゃんぬねっとさん、やじゅさん、まるくさん
お手数をおかけして申し訳ありませんでした。ありがとうございました。



[ メッセージ編集済み 編集者: ブリンク 編集日時 2009-02-05 18:28 ]
ブリンク
会議室デビュー日: 2009/01/21
投稿数: 16
投稿日時: 2009-02-10 17:54
DLLを動的に使用することには成功したのですが、複雑という理由から実装しないことになってしまいました。
参考のため、まるくさんに教えて頂いた記事を参考にMVIDを動的に取得する方法をまとめてみました。
(まだ他に方法があるかもしれないのでもう少し調査します)

実行用DLLとインターフェース用のDLLを用意することと、各DLLの参照設定があるのがポイントです。
DLLのアセンブリ名やクラス名やファンクション名が入り乱れるので理解するのに時間がかかりました。

必要なもの
MVID取得を実行するDLL
インターフェース用DLL
実際に呼び出すEXE

*参照設定があるので、上から順につくってください

==インターフェース用DLL==
ソリューション(プロジェクト)名:TestInterface.sln(vbproj)(仮)
クラス名:clsInterface(仮)

--TestInterface.sln--
Public Class clsInterface
Public Interface PublicInterFace1
Function GetMVID(ByVal byFileName As String, ByRef MVIDbinary() As Byte) As Boolean
End Interface
End Class

→ビルドしてTestInterface.dllを作る

==MVID取得用DLL==
ソリューション(プロジェクト)名:GetMVID.sln(vbproj)(仮)
クラス名:clsGetMVID(仮)
TestInterface.dll(インターフェース用DLL)を参照設定に追加する

'↓シリアライズが必要
<Serializable()> _
Public Class clsGetMVID
Inherits MarshalByRefObject 'アプリケーションドメイン境界を超えてオブジェクトにアクセス
Implements MVIDInterface.clsInterface.PublicInterFace1 'インターフェースの継承

Public Function GetMVID(ByVal byFileName As String, ByRef MVIDbinary() As Byte) As Boolean Implements MVIDInterface.clsInterface.PublicInterFace1.GetMVID
Try
System.Reflection.Assembly.LoadFile(IO.Path.GetFullPath(byFileName)).ManifestModule.ModuleVersionId.ToByteArray.CopyTo(MVIDbinary, 0)
Catch ex As Exception
Return False
End Try

Return True
End Function

End Class

==実際に呼び出すEXE==
TestInterface.dllとTestInterface.dllを参照設定に追加する

Private Sub GetMVIDtest()

Dim hanlde As System.Runtime.Remoting.ObjectHandle
'ドメインを生成
Dim ad As AppDomain = AppDomain.CreateDomain("")
Dim MVIDBinary(15) As Byte
Dim FineName As String = "MVIDを取得したいexe(フルパス)"

'ハンドルにインスタンス生成 ("関数実行DLL名","関数実行DLL名.クラス名")
hanlde = ad.CreateInstance("GetMVID", "GetMVID.clsGetMVID")

'ラップされたオブジェクトをハンドルに取得し、インターフェースにキャスト
DirectCast(hanlde.Unwrap, MVIDInterface.clsInterface.PublicInterFace1).GetMVID(byFineName, MVIDBinary)

'ドメイン解放前に実行するとエラーとなる
'書き込みモードでファイルをオープンしてみる
'Dim Fs As New IO.FileStream(byFineName, IO.FileMode.Open, IO.FileAccess.Write)
'Fs.Close()

'ドメインを開放
AppDomain.Unload(ad)

'書き込みモードでファイルをオープンしてみる
Dim Fs As New IO.FileStream(byFineName, IO.FileMode.Open, IO.FileAccess.Write)
Fs.Close()

End

End Sub
====================

1

スキルアップ/キャリアアップ(JOB@IT)