ObjectFromLresultの使い方をお教えください

解決


プログラムはじめ  2010-04-24 14:50:57  No: 102351  IP: [192.*.*.*]

初めてご質問させていただきます。
分からないことが出てきたのでお教えください。
現在下記のページを見てObjectFromLresultの使い方の勉強をしています。
http://msdn.microsoft.com/en-us/library/dd373605(VS.85).aspx
実験的にExcelのVBAから自分自身(ワークブック)のウインドウハンドルを取得して、自分自身のウインドウオブジェクトを取得するというサンプルをつくりましたが、ObjectFromLresultの使い方が間違っているようでうまくいきません。だめな部分をご指摘していただけないでしょうか?

Public Const WM_GETOBJECT = &H3D&
Public Const OBJID_NATIVEOM = &HFFFFFFF0
Public Declare Function ObjectFromLresult Lib "oleacc" _
                        (ByVal lResult As Long, riid As Any, _
                         ByVal wParam As Long, ppvObject As Any) As Long
Public Declare Function SendMessage Lib "user32" _
                        Alias "SendMessageA" _
                        (ByVal hWnd As Long, ByVal Msg As Long, _
                         ByVal wParam As Long, lParam As Any) As Long
Public Declare Function FindWindowEx Lib "user32" _
        Alias "FindWindowExA" _
        (ByVal hwndParent As Long, ByVal hwndChildAfter As Long, _
        ByVal lpszClass As String, ByVal lpszWindow As String) As Long
Public Type GUID
    Data1 As Long
    Data2 As Integer
    Data3 As Integer
    Data4(0 To 7) As Byte
End Type

Option Explicit
Sub test()


Dim wkw As Excel.Window
Dim IID_IAccessible As GUID
Dim hwndApp As Long
Dim hwndClient As Long
Dim hwndBook As Long
Dim lngResult As Long
Dim lngRtnCode As Long
Dim strMsg As String

    With IID_IAccessible
        .Data1 = &H618736E0
        .Data2 = &H3C3D
        .Data3 = &H11CF
        .Data4(0) = &H81
        .Data4(1) = &HC
        .Data4(2) = &H0
        .Data4(3) = &HAA
        .Data4(4) = &H0
        .Data4(5) = &H38
        .Data4(6) = &H9B
        .Data4(7) = &H71
    End With
    hwndApp = FindWindowEx(0, hwndApp, "XLMAIN", vbNullString)
    hwndClient = FindWindowEx(hwndApp, 0, "XLDESK", vbNullString)
    hwndBook = FindWindowEx(hwndClient, 0, "EXCEL7", vbNullString)
    lngResult = SendMessage(hwndBook, WM_GETOBJECT, 0, ByVal OBJID_NATIVEOM)
    If lngResult Then
        lngRtnCode = ObjectFromLresult(lngResult, IID_IAccessible, 0, wkw)
    Else

        strMsg = "SendMessage Error "
        exit  sub
    End If
    If Not wkw Is Nothing Then
        strMsg = "確認できたよ " & wkw.Caption & vbTab 'OK
    Else
        strMsg = strMsg & "駄目でした"
    End If
    MsgBox strMsg

End Sub

編集 削除
K.J.K.  2010-04-26 10:24:08  No: 102352  IP: [192.*.*.*]

# 回答ではありません。

WM_GETOBJECT って、システムが送るメッセージであって、アプリ側が
直接使うものとは想定されていないメッセージだったような。

Accessible なオブジェクトを取得するのならば、AccessibleObjectFromPoint、
AccessibleObjectFromEvent、AccessibleObjectFromWindow
がなどがあります。特に今回は最後のを使うべきではないかと。

編集 削除
熊谷隆史  2010-04-26 10:51:23  No: 102353  IP: [192.*.*.*]

> Option Explicit
が妙な位置にあるのはご愛嬌として。


> ObjectFromLresult
は何の戻り値を返しますか?
変数に受けてそれを評価しないのでは意味がありませんね。

で、今回はE_NOINTERFACE(0x80004002)を返しています。
継承関係の無いiidを渡しているのが間違いということに気付くでしょう。
IID_IDispatch等を指定してください。

# 他所で以前、同様の質問をしたはずなのに。

編集 削除
プログラムはじめ  2010-04-26 23:10:33  No: 102354  IP: [192.*.*.*]

K.J.K.様ごかいとうありがとうございます。
accessibleobjectfromwindowについては、
http://msdn.microsoft.com/en-us/library/dd317978(VS.85).aspx
を読んで以下のようにすればうまくオブジェクトがとれました。


Option Explicit
Public Const OBJID_NATIVEOM = &HFFFFFFF0
Private Declare Function AccessibleObjectFromWindow Lib "oleacc" _
        (ByVal hWnd As Long, ByVal dwId As Long, _
        riid As Any, ppvObject As Any) As Long
Public Declare Function FindWindowEx Lib "user32" _
        Alias "FindWindowExA" _
        (ByVal hwndParent As Long, ByVal hwndChildAfter As Long, _
        ByVal lpszClass As String, ByVal lpszWindow As String) As Long
Public Type GUID
    Data1 As Long
    Data2 As Integer
    Data3 As Integer
    Data4(0 To 7) As Byte
End Type


Sub test2()

Dim wkw As Excel.Window
Dim IID_Idispatch As GUID
Dim hwndApp As Long
Dim hwndClient As Long
Dim hwndBook As Long
Dim lngResult As Long
Dim lngRtnCode As Long
Dim strMsg As String

    With IID_Idispatch
        .Data1 = &H20400
        .Data4(0) = &HC0
        .Data4(7) = &H46
    End With
    hwndApp = FindWindowEx(0, hwndApp, "XLMAIN", vbNullString)
    hwndClient = FindWindowEx(hwndApp, 0, "XLDESK", vbNullString)
    hwndBook = FindWindowEx(hwndClient, 0, "EXCEL7", vbNullString)
    AccessibleObjectFromWindow hwndBook, OBJID_NATIVEOM, _
                                           IID_Idispatch, wkw

    If Not wkw Is Nothing Then
        strMsg = "確認できたよ " & wkw.Caption & vbTab 'OK
    Else
        strMsg = strMsg & "駄目でした"
    End If
    MsgBox strMsg

End Sub

ただリンク先を読む限りIID_Iaccessibleを引き渡してもうまくいくと思いましたがうまくいきませんでした。私の誤訳でしょうか?

編集 削除
プログラムはじめ  2010-04-26 23:33:20  No: 102355  IP: [192.*.*.*]

熊谷隆史様いつもありがとうございます。

># 他所で以前、同様の質問をしたはずなのに。

以前ご提示頂いたサンプルは以下のような感じだったと思いますが、

Public Const WM_GETOBJECT = &H3D&
Public Const OBJID_NATIVEOM = &HFFFFFFF0
Public Declare Function ObjectFromLresult Lib "oleacc" _
                        (ByVal lResult As Long, riid As Any, _
                         ByVal wParam As Long, ppvObject As Any) As Long
Public Declare Function SendMessage Lib "user32" _
                        Alias "SendMessageA" _
                        (ByVal hWnd As Long, ByVal Msg As Long, _
                         ByVal wParam As Long, lParam As Any) As Long
Public Declare Function IIDFromString Lib "ole32" _
                        (lpsz As Any, lpiid As Any) As Long
Public Declare Function FindWindowEx Lib "user32" _
        Alias "FindWindowExA" _
        (ByVal hwndParent As Long, ByVal hwndChildAfter As Long, _
        ByVal lpszClass As String, ByVal lpszWindow As String) As Long
Public Const IID_IDispatch = "{00020400-0000-0000-C000-000000000046}"

Option Explicit
Sub test3()


Dim wkw As Excel.Window
Dim hwndApp As Long
Dim hwndClient As Long
Dim hwndBook As Long
Dim lngResult As Long
Dim lngRtnCode As Long
Dim strMsg As String
Dim bytID() As Byte
Dim IID(0 To 3) As Byte
    hwndApp = FindWindowEx(0, hwndApp, "XLMAIN", vbNullString)
    hwndClient = FindWindowEx(hwndApp, 0, "XLDESK", vbNullString)
    hwndBook = FindWindowEx(hwndClient, 0, "EXCEL7", vbNullString)
    lngResult = SendMessage(hwndBook, WM_GETOBJECT, 0, ByVal OBJID_NATIVEOM)
    If lngResult Then
        bytID = IID_IDispatch & vbNullChar
        IIDFromString bytID(0), IID(0)
        lngRtnCode = ObjectFromLresult(lngResult, IID(0), 0, wkw)
    Else

        strMsg = "SendMessage Error "
    End If
    If Not wkw Is Nothing Then
        strMsg = "確認できたよ " & wkw.Caption & vbTab 'OK
    Else
        strMsg = strMsg & "駄目でした"
    End If
    MsgBox strMsg

End Sub

    
iidfromstringというAPIがmsdnを読んでもどうしても理解できなかったので、自分なりにサンプルを書き換え実験をして今回の質問にいたりました。

熊谷隆史様がおっしゃるとおりIID_IaccessibleをIID_IDispatchにかえると私が始めに提示したtestもうまく動作しましたが、http://msdn.microsoft.com/en-us/library/dd373605(VS.85).aspx
にはIID_Iaccessibleを引き渡してくださいと書いてあるように見えますがこれも私の誤訳でしょうか?

編集 削除
K.J.K.  2010-04-27 11:53:28  No: 102356  IP: [192.*.*.*]

> Dim wkw As Excel.Window
> lngRtnCode = ObjectFromLresult(lngResult, IID(0), 0, wkw)

ですので、IAccessible型の変数でないのでは。
タイプライブラリを用意して、
Dim oAccessible As IAccessible
lngRtnCode = AccessibleObjectFromWindow(hwndBook, OBJID_NATIVEOM, _
                                           IID_IAccessible, oAccessible)
とでもすべきなのでは。

編集 削除
プログラムはじめ  2010-04-27 23:22:05  No: 102357  IP: [192.*.*.*]

K.J.K様お付き合いくださって本当にありがとうございます。
アドバイスに従い以下のようなコードを組みましたが、うまく取れませんでした。

Option Explicit
Public Const OBJID_NATIVEOM = &HFFFFFFF0
Private Declare Function AccessibleObjectFromWindow Lib "oleacc" _
        (ByVal hWnd As Long, ByVal dwId As Long, _
        riid As Any, ppvObject As Any) As Long
Public Declare Function FindWindowEx Lib "user32" _
        Alias "FindWindowExA" _
        (ByVal hwndParent As Long, ByVal hwndChildAfter As Long, _
        ByVal lpszClass As String, ByVal lpszWindow As String) As Long
Public Type GUID
    Data1 As Long
    Data2 As Integer
    Data3 As Integer
    Data4(0 To 7) As Byte
End Type


Sub test4()

Dim oAccessible As IAccessible
Dim IID_IAccessible As GUID
Dim hwndApp As Long
Dim hwndClient As Long
Dim hwndBook As Long
Dim lngResult As Long
Dim lngRtnCode As Long
Dim strMsg As String

    With IID_IAccessible
        .Data1 = &H618736E0
        .Data2 = &H3C3D
        .Data3 = &H11CF
        .Data4(0) = &H81
        .Data4(1) = &HC
        .Data4(2) = &H0
        .Data4(3) = &HAA
        .Data4(4) = &H0
        .Data4(5) = &H38
        .Data4(6) = &H9B
        .Data4(7) = &H71
    End With
    hwndApp = FindWindowEx(0, hwndApp, "XLMAIN", vbNullString)
    hwndClient = FindWindowEx(hwndApp, 0, "XLDESK", vbNullString)
    hwndBook = FindWindowEx(hwndClient, 0, "EXCEL7", vbNullString)
    AccessibleObjectFromWindow hwndBook, OBJID_NATIVEOM, _
                                           IID_IAccessible, oAccessible

    If Not oAccessible Is Nothing Then
        strMsg = "確認できたよ " & oAccessible.Caption & vbTab 'OK
    Else
        strMsg = strMsg & "駄目でした"
    End If
    MsgBox strMsg

End Sub

ものによってはIID_IAccessibleを引き渡すとうまくいくので、取る対象によってIdispatchを引き渡したり、Iaccessibleを引き渡したり選ばないといけないということなのでしょうか。msdnを見る限りどちらでもよさそうですが、なにか法則があると理解しました。

編集 削除
Abyss  2010-04-28 16:31:38  No: 102358  IP: [192.*.*.*]

お邪魔します。

>ものによってはIID_IAccessibleを引き渡すとうまくいくので、
>取る対象によってIdispatchを引き渡したり、Iaccessible を
>引き渡したり選ばないといけないということなのでしょうか。
>msdnを見る限りどちらでもよさそうですが、なにか法則が
>あると理解しました。

リンク先MSDNに下記のような記述があります。

To obtain an IDispatch interface pointer to a class 
supported by the native object model, specify OBJID_NATIVEOM
in dwObjectID.

なのでご提示のコードを最小限修正するのなら、OBJID_NATIVEOMを
OBJID_WINDOWもしくはOBJID_CLIENTに変更すればよいと思います。

しかし、
Dim oAccessible As IAccessible
の宣言で
oAccessible.Captionは実行時エラーになりますよ。

編集 削除
プログラムはじめ  2010-04-29 01:19:38  No: 102359  IP: [192.*.*.*]

Abyss様いつもお世話になっております。
まさかこの板でご回答をいただけると思っていませんでした。

>OBJID_WINDOWもしくはOBJID_CLIENT

なるほど確かにAccExplorer32.exeで調べるとワークブック(Excel7)には、role  textがウィンドウのものとクライアントのものしかないので、IID_Iaccessibleを引き渡す場合、OBJID_WINDOW、OBJID_CLIENTのどちらかを
設定するのが正しいということですね。
Excel.window型のオブジェクトを得る場合はmsdnに書いてあるようにIID_IdispatchとOBJID_NATIVEOMを引き渡すとよいということですか。
かなりすっきりしました。
最後に一つだけご教示ください。
最初に私が提示したtestのようにObjectFromLresultにIID_IAccessibleを引き渡すのはまちがいでしょうか?(dim wkw as objectとか色々変更したけどうまく取れませんでした。IID_Iaccessibleを引き渡すというmsdnの記述がよく分かりません。)

編集 削除
Abyss  2010-05-01 03:18:04  No: 102360  IP: [192.*.*.*]

>最初に私が提示したtestのようにObjectFromLresultに
>IID_IAccessibleを引き渡すのはまちがいでしょうか?

上級者からのレスがないですが、K.J.Kさんからのレス同様
WM_GETOBJECTメッセージはクライアント側で弄れる
メッセージではないです。そもそも、wParam値がなにに
なるかも分かりませんし。
クライアント側よりAccessibleObjectFromWindowなどの要求があったら、
システムのWindowProcにてWM_GETOBJECTメッセージ処理の結果、
IDispatchなりIAccessibleインターフェースポインタが
帰ってくるのでは?

編集 削除
プログラムはじめ  2010-05-01 10:12:15  No: 102361  IP: [192.*.*.*]

分かりました。今回色々教えていただいたことでまえより随分理解が進みました。
K.J.K様、熊谷隆史様、Abyss様本当にありがとうございました。
今後ともよろしくお願いします。

編集 削除
プログラムはじめ  2010-05-01 10:12:55  No: 102362  IP: [192.*.*.*]

チェックを忘れました

編集 削除
熊谷隆史  2010-05-01 12:20:41  No: 102363  IP: [192.*.*.*]

# 解決済みですが。
MSDNに明記されてないから腑に落ちないのでしょうけど。
そういうものだと思って頂くしかないかなと。

逆にObjectFromLresultの第一引数に指定するLRESULT値を
どうやって得るのかを考えてみてください。
    lngLresult = LresultFromObject(IID_IAccessible, 0, ThisWorkbook.Windows(1))
では、同様にE_NOINTERFACEが返りますね。

    lngLresult = LresultFromObject(IID_IDispatch, 0, ThisWorkbook.Windows(1))
では、きちんとLresult値が得られます。
(必ずしもLresultFromObjectが使われるわけではありませんが)

これは異プロセス/異スレッド間で使われる間接的なオブジェクトのマーシャリングです。
こちらのリンク先にあるのが直接的な手法ですね。
http://madia.world.coocan.jp/cgi-bin/VBBBS2/wwwlng.cgi?print+200703/07030025.txt

編集 削除
プログラムはじめ  2010-05-04 21:30:34  No: 102364  IP: [192.*.*.*]

熊谷隆史様、ありがとうございます。
解決後に書き込めることを知りませんでした。
マーシャリングという言葉が検索してもよく分かりませんでした。(IIDFromStringが私が理解できない原因もそこにあります。)もう少し勉強してみます。

編集 削除