Accessが複数開かれている時にGetObject()する方法
はじめに
- 具体的には,CreateObject()やGetObject()といった関数で,各ソフトのApplicationオブジェクトに相当する参照を取得でき,例えば,ExcelからAccessにクエリを投げて,得られた結果をワークシートに転送する,といったマクロを作成可能である.
- いや,MSDN*2は最初の引数に使いたいファイルパスを指定すれば選択できそうな雰囲気ではあるが,試してみたところ,新しいインスタンスが開かれ,取得された参照が破棄されると同時にインスタンスも閉じられた.うまくやる方法があるのかもしれないが,ここでは操作するインスタンスを指定できないとしよう.
- そこで,ウィンドウタイトルから開いているファイルを推測し,そのウィンドウのハンドルを基にしてAccessibleObjectFromWindow()関数を呼び出し,目的のインスタンスの参照をつかむ方法が,首尾良く行きそうなので報告する.
AccessibleObjectFromWindow()関数
こいつでウィンドウハンドルから,インスタンスの参照を得られる.VBAでは引数の扱いに注意が必要な点がある.
- 第2引数は取得したいオブジェクトのIDを渡す.Office系の参照が欲しい場合はOBJID_NATIVEOMを指定する.
- 第3引数の REFIID riid はGUID渡すべきであるが,VBAのGUIDを指定すると怒られる.Declareな関数では使えないらしい?ので,同等の構造体をユーザー定義する.Office系の参照を得るためには,IDispatchに相当するGUIDを渡す.
Accessが複数開かれている場合に参照を取得する例
- Accessが複数開かれている場合を例にした実装を張る.
- Main()からEnumWindows()を呼び出し,目的のインスタンス("tesuto.accdb"なるファイルを開いたAccessインスタンス)が存在したら,フォーム"hoge"を開く.
- EnumWindows()のコールバック関数では,ウィンドウタイトルを調べ,目的のファイルを開いていそうだったら,Accessインスタンスの参照の取得を試みる.
Option Explicit Public AccessWndHandle As Long Public AccessApplication As Object Private Const OBJID_NATIVEOM = &HFFFFFFF0 Private Declare Function GetWindowText Lib "User32" _ Alias "GetWindowTextA" _ (ByVal Hwnd As Long, ByVal lpString As String, ByVal cch As Long) As Long Private Declare Function AccessibleObjectFromWindow Lib "oleacc" _ (ByVal Hwnd As Long, ByVal dwId As Long, _ ByRef riid As UUID, ByRef ppvObject As Object) As Long Private Declare Function EnumWindows Lib "User32" (ByVal lpEnumFunc As Long, ByVal lParam As Long) As Long Private Type UUID Data1 As Long Data2 As Integer Data3 As Integer Data4(7) As Byte End Type Public Function EnumWindowsCallBackFunc(ByVal Hwnd As Long, ByVal lParam As Long) As Boolean Dim title As String * 256 If False = GetWindowText(Hwnd, title, Len(title)) Then GoTo Continue End If If InStr(title, "tesuto") > 0 And InStr(title, "Access") > 0 Then Dim IID_IDispatch As UUID With IID_IDispatch .Data1 = &H20400 .Data4(0) = &HC0 .Data4(7) = &H46 End With If 0 <> AccessibleObjectFromWindow(Hwnd, OBJID_NATIVEOM, IID_IDispatch, AccessApplication) Then GoTo Continue End If AccessWndHandle = Hwnd EnumWindowsCallBackFunc = False Exit Function End If Continue: EnumWindowsCallBackFunc = True Exit Function End Function Public Sub Main() AccessWndHandle = 0 Call EnumWindows(AddressOf EnumWindowsCallBackFunc, 0) If AccessWndHandle = 0 Then Exit Sub End If AccessApplication.DoCmd.OpenForm "hoge" End Sub
参考
http://www.lw-tech.com/q1/base.htm
GDIplusCODE
Excel VBA - Get an Excel instance from the window handle