遅延バインディングでDLLエントリポイントの名前を変数で指定する方法
- 題名: 遅延バインディングでDLLエントリポイントの名前を変数で指定する方法
- 著者: 中野の徳竹
- 日時: 2013/03/05 9:35:53
- ID: 31373
- この記事の返信元:
- この記事への返信:
- ツリーを表示
- 題名: Re[1]: 遅延バインディングでDLLエントリポイントの名前を変数で指定する方法
- 著者: 管理人
- 日時: 2013/03/07 1:30:23
- ID: 31379
- この記事の返信元:
- この記事への返信:
- ツリーを表示
- 題名: Re[2]: 遅延バインディングでDLLエントリポイントの名前を変数で指定する方法
- 著者: 中野の徳竹
- 日時: 2013/03/09 20:51:30
- ID: 31388
- この記事の返信元:
- この記事への返信:
- ツリーを表示
分類:[.NET]
dobon.netはよくお世話になっています。管理人さんを始め関係者の皆さんには
お礼申し上げます。
さて、質問という訳ではないのですが、『こんなことも.netでできますよ』
レベルの投稿としてご覧ください。dobon.netの
http://dobon.net/vb/dotnet/links/extractarchive.htmlには下記の記載があります。
『Win32 APIのLoadLibrary関数とGetProcAddress関数により指定された
DLLの関数のアドレスは取得できますが、このアドレスで関数に
アクセスする方法が.NETにはありません。』
しかし、以下のような記述をすれば.netでも任意の関数を呼出すことが
できると思います(下記内容でUnlha32.dllを使用したテストでは問題なく
ファイルの解凍ができることは確認しました。ただ、さすがに動的に
パラメータまで指定するのは苦しいですが・・)。
ソースプログラムは上記URLに記載されているvb版の『遅延バインディングに
よる方法』をもとに変更しています。
確認環境:
Windows XP SP3
Visual Studio 2010 Pro
.NET Framework 4.0
Imports System.Runtime.InteropServices
Public Class ClassVB
'DLLモジュールをマップ
<DllImport("kernel32")> _
Private Shared Function LoadLibrary( _
ByVal lpLibFileName As String) As Integer
End Function
'マップを解除
<DllImport("kernel32")> _
Private Shared Function FreeLibrary( _
ByVal hLibModule As Integer) As Boolean
End Function
'関数のアドレスを取得
<DllImport("kernel32")> _
Private Shared Function GetProcAddress( _
ByVal hModule As Integer, ByVal lpProcName As String) As Integer
End Function
'以下使用するAPIのためのFunc
Private Delegate Function GetVersion( _
) As UInt16
Private Delegate Function GetRunning() As Boolean
Private Delegate Function CheckArchive( _
ByVal szFileName As String, _
ByVal iMode As Integer) As Boolean
Private Delegate Function sMain( _
ByVal hwnd As Integer, _
ByVal szCmdLine As String, _
ByVal szOutput As String, _
ByVal dwSize As Integer) As Integer
''' <summary>
''' 統合アーカイバ仕様のDLLで書庫を展開する
''' </summary>
''' <param name="dllName">DLLファイル名</param>
''' <param name="funcName">APIの頭に付く文字列</param>
''' <param name="command">展開のためのコマンド</param>
''' <param name="archiveFile">書庫ファイル名</param>
''' <param name="extractDir">展開先のフォルダ名</param>
Public Shared Sub ExtractArchive( _
ByVal dllName As String, _
ByVal funcName As String, _
ByVal command As String, _
ByVal archiveFile As String, _
ByVal extractDir As String)
'指定されたファイルがあるか調べる
If Not System.IO.File.Exists(archiveFile) Then
Throw New ApplicationException("ファイルが見つかりません")
End If
'DLLをロード
Dim hmod As Integer = LoadLibrary(dllName)
If hmod = 0 Then
Throw New ApplicationException( _
dllName + "のロードに失敗しました")
End If
Try
Dim funcaddr As IntPtr
'DLLのチェック
'関数のアドレスを取得
funcaddr = GetProcAddress(hmod, funcName + "GetVersion")
If funcaddr = 0 Then
Throw New ApplicationException( _
dllName + "がインストールされていません")
End If
Dim d As [Delegate] = Marshal.GetDelegateForFunctionPointer(funcaddr, GetType(GetVersion))
Dim getVersion As GetVersion = CType(d, GetVersion)
Dim ver As UInt16 = getVersion()
Debug.WriteLine("バージョン:{0}", ver)
'動作中かチェック
funcaddr = GetProcAddress(hmod, funcName + "GetRunning")
If funcaddr = 0 Then
Throw New ApplicationException( _
funcName + "GetRunningのアドレスが取得できませんでした")
End If
d = Marshal.GetDelegateForFunctionPointer(funcaddr, GetType(GetRunning))
Dim getRunning As GetRunning = CType(d, GetRunning)
If getRunning() Then
Throw New ApplicationException(dllName + "が動作中")
End If
'展開できるかチェック
funcaddr = GetProcAddress(hmod, funcName + "CheckArchive")
If funcaddr = 0 Then
Throw New ApplicationException( _
funcName + "CheckArchiveのアドレスが取得できませんでした")
End If
d = Marshal.GetDelegateForFunctionPointer(funcaddr, GetType(CheckArchive))
Dim checkArchive As CheckArchive = CType(d, CheckArchive)
If Not checkArchive(archiveFile, 0) Then
Throw New ApplicationException( _
archiveFile + "は対応書庫ではありません")
End If
'ファイル名とフォルダ名を修正する
If archiveFile.IndexOf(" "c) > 0 Then
archiveFile = """" + archiveFile + """"
End If
If Not extractDir.EndsWith("\") Then
extractDir += "\"
End If
If extractDir.IndexOf(" "c) > 0 Then
extractDir = """" + extractDir + """"
End If
'展開する
funcaddr = GetProcAddress(hmod, funcName)
If funcaddr = 0 Then
Throw New ApplicationException( _
funcName + "のアドレスが取得できませんでした")
End If
d = Marshal.GetDelegateForFunctionPointer(funcaddr, GetType(sMain))
Dim subMain As sMain = CType(d, sMain)
Dim ret As Integer = subMain(0, _
String.Format(command, archiveFile, extractDir), _
Nothing, 0)
'結果
If ret <> 0 Then
Throw New ApplicationException("書庫の展開に失敗しました")
End If
Catch ex As Exception
MsgBox(ex.Message)
Finally
'解放する
If hmod <> 0 Then
FreeLibrary(hmod)
End If
End Try
End Sub
End Class
また、これとは別に先のURLではRichard Birkbyさんが作成したInvoke.dllを
使用していますが、これはVisualStudio c/c++の下記インラインアセンブラで
代用できます。
Invoke.asmではecxとedxのレジスタを直接使用していますが、その後該当
レジスタの復旧をしていないので誤動作につながると困るなと思い、少し
ロジックを追加しています。これでもeaxレジスタは変更されますが、
コンパイラにより通常eaxには呼出し先関数の戻り値が設定されるので
問題はないと思います。
(下記内容で作成したCall.dllとUnlha32.dllを使用したテストでは問題なく
ファイルの解凍ができることは確認しました。)
// Call.cpp : コンソール アプリケーションのエントリ ポイントを定義します。
//
#include "stdafx.h"
struct stackImage {
void *svecx;
void *svebx;
void *ret;
void *ptr;
};
extern "C" void __declspec(naked) Invoke(void *ptr)
{
__asm{
push ebx
push ecx
mov ebx,esp
mov eax,[ebx].ptr ; get jump adress
mov ecx,[ebx].ret ; ret -> ptr
mov [ebx].ptr,ecx
mov ecx,[ebx].svebx ; svebx -> ret
mov [ebx].ret,ecx
mov ecx,[ebx].svecx ; svecx -> svebx
mov [ebx].svebx,ecx
pop ecx ; dummy pop
pop ecx ; restore ecx
pop ebx ; restore ebx
jmp eax ; jump real module
pop ecx ; save return address
pop edx ; Get function pointer
push ecx ; Restore return address
jmp edx ; Transfer control to the function pointer
}
}
(処理には関係しませんがアセンブラの最後4ステップはRichard Birkbyさんの
オリジナルです)