radianの備忘録

プログラミングやPCに関する事をメモ代わりに残していきます

ネイティブDLLやCOMを使う

.NET環境に移行しても、昔からあるネイティブDLLやCOMを利用したくなる事が時々ありますが、その場合に知っていたほうが良いこと等を書いていきます。

(1) 文字列の受け渡し
CharSetを適切に指定すれば、String型でやりとり可能です。WinAPIでAnsi版、Unicode版両方がある場合でも自動的に解釈してくれます。(紛らわしいので、明示的に指定するに越したことはないのですが。)

'この例だとUnicode版のLoadLibraryWを呼び出してくれる
Public Shared Declare Unicode Function LoadLibrary Lib "kernel32" (lpFileName As String) As IntPtr


(2) 構造体のアドレスを渡す場合
LayoutKind.Sequentialで宣言した構造体の参照を渡してあげれば、勝手にマーシャリングしてくれます。2byte以下のメンバを含む構造体はPack値に注意しましょう。

Imports System
Imports System.Runtime.CompilerServices
Imports System.Runtime.InteropServices

Public Class Form1

    <StructLayout(LayoutKind.Sequential, Pack:=4)>
    Private Structure RECT
        Public left As Integer
        Public top As Integer
        Public right As Integer
        Public bottom As Integer
    End Structure

    Private Declare Auto Function GetDesktopWindow Lib "user32.dll" () As IntPtr
    Private Declare Auto Function GetWindowRect Lib "user32.dll" (ByVal hwnd As IntPtr, <Out()> ByRef rc As RECT) As Integer

    Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
        Dim rc As New RECT
        GetWindowRect(GetDesktopWindow(), rc)
        MsgBox(rc.right & " " & rc.bottom)
    End Sub

End Class


(3) COMインターフェースのポインタを受け取る場合
こちらも、COMインターフェースの宣言をきちんとしてあげれば、インターフェースの参照を渡してあげればOKです。Object型で受けて後からキャストすることも可能です。下は、IMallocでのメモリ確保&解放の例です。

Imports System
Imports System.Runtime.CompilerServices
Imports System.Runtime.InteropServices

Public Class Form1

    <ComImport()> _
    <InterfaceType(ComInterfaceType.InterfaceIsIUnknown)> _
    <Guid("00000002-0000-0000-C000-000000000046")> _
    Private Interface IMalloc
        <MethodImpl(MethodImplOptions.PreserveSig)>
        Function Alloc(ByVal cb As UInt32) As IntPtr
        <MethodImpl(MethodImplOptions.PreserveSig)>
        Function Realloc(ByVal pv As IntPtr, ByVal cb As UInt32) As IntPtr
        <MethodImpl(MethodImplOptions.PreserveSig)>
        Sub Free(ByVal pv As IntPtr)
        <MethodImpl(MethodImplOptions.PreserveSig)>
        Function GetSize(ByVal pv As IntPtr) As UInt32
        <MethodImpl(MethodImplOptions.PreserveSig)>
        Function DidAlloc(ByVal pv As IntPtr) As UInt32
        <MethodImpl(MethodImplOptions.PreserveSig)>
        Sub HeapMinimize()
    End Interface

    Private Declare Sub CoGetMalloc Lib "ole32.dll" (ByVal dwMemContext As Integer, <Out()> ByRef malloc As IMalloc)

    Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
        Dim malloc As IMalloc = Nothing
        CoGetMalloc(1, malloc)
        Dim ptr As IntPtr = malloc.Alloc(1000)
        Dim size As UInt32 = malloc.GetSize(ptr)
        MsgBox("PtrAddress:0x" & Hex(ptr.ToInt64) & " PtrSize:" & size)
        malloc.Free(ptr)
        size = malloc.GetSize(ptr)
        Marshal.ReleaseComObject(malloc)
        malloc = Nothing
    End Sub

End Class


(4) コールバック関数(関数ポインタ)を指定する場合
コールバック関数と同じ引数を持つDelegateを作成して渡せばOKです。Marshal.GetFunctionPointerForDelegateを使えば、関数ポインタも取得出来ます。下は、EnumChildWindowsの使用例です。

Imports System.Collections.Generic
Imports System.Runtime.InteropServices

Public Class EnumWindowSample

    Private Delegate Function EnumWindowsDelegate(hWnd As IntPtr, lparam As IntPtr) As Boolean

    Private Declare Function EnumChildWindows Lib "user32.dll" (ByVal hWnd As IntPtr, ByVal lpEnumFunc As EnumWindowsDelegate, ByVal lparam As IntPtr) As Integer
    Private Declare Function EnumChildWindows Lib "user32.dll" (ByVal hWnd As IntPtr, ByVal lpEnumFunc As IntPtr, ByVal lparam As IntPtr) As Integer
    Private _ListHWnd As New List(Of IntPtr)

    'Delegateを渡す
    Public Function GetChildWindows1(ByVal hWndOwner As IntPtr) As List(Of IntPtr)
        _ListHWnd.Clear()
        EnumChildWindows(hWndOwner, New EnumWindowsDelegate(AddressOf EnumWindowsProc), IntPtr.Zero)
        Return _ListHWnd
    End Function

    '関数ポインタを取得して渡す
    Public Function GetChildWindows2(ByVal hWndOwner As IntPtr) As List(Of IntPtr)
        _ListHWnd.Clear()
        Dim pEnumWindowsProc As IntPtr = Marshal.GetFunctionPointerForDelegate(New EnumWindowsDelegate(AddressOf EnumWindowsProc))
        EnumChildWindows(hWndOwner, pEnumWindowsProc, IntPtr.Zero)
        Return _ListHWnd
    End Function

    'ウィンドウ列挙コールバック関数
    Private Function EnumWindowsProc(hWnd As IntPtr, lParam As IntPtr) As Boolean
        _ListHWnd.Add(hWnd)
        Return True
    End Function

End Class