- 締切済み
VBAでコントロールのハンドルを取得したい
Excel2000のVBAでFormに配置したコントロールのハンドルを取得するにはどうしたらよいのでしょうか? 目的は、Winsock API(ws2_32.dll)の WSAAsyncSelect関数の2番目の引数にセットする値を取得することです。USER32.dll の GetDlgItem が使用できそうなことがわかったのですが、こちらもその2番目の引数にセットするIDがわかりません。
- みんなの回答 (4)
- 専門家の回答
みんなの回答
すみません、説明がVBベースでした・・・。 メッセージのキャッチ目的なら、KenKen_SPさんの 仰るとおりで完璧だと思います。 もしUserFormをサブクラスせず別オブジェクトに専属させたいのであれば、 RegisterClass,CreateWindow API関数で別の親ウインドウを作成して クラスモジュールでメッセージハンドラ用プロシージャを 用意して当てればモジュールがすっきりします。 スモールアイコン、拡張スタイルは無関係なので Ex いらずです。 互換プログラミングスタイルで私はVB、VBAでも極力APIで埋め込みますが、 小規模ならKenKen_SPさんのやり方を適用するべきです。
- KenKen_SP
- ベストアンサー率62% (785/1258)
> ウインドウメッセージを捕らえたいのではなく、(特殊な方法で) > ウインドウメッセージを送りたいのです。 WinSock から通知されてくるメッセージ、例えば FD_READ を捕らえ たら、recv() をコール...するといった処理をさせるわけではない.. ということですね? ダミーで良いなら、通知先は Userform で良いのでは? #2 のコードで得られたハンドルで良いと思います。 Frame などは下記のようなコードで取れるは取れるのですけど、確実に 「この Frame のハンドル」とは取れないのです。 例えば、Form 内に Frame が 2つ配置されていたり、ListBox も配置 されていたら、その内のどれかのハンドル...といった具合です。 したがって、「取れる・取れない」というレベルでは、「取れる」なの ですが、実用に使えるものではありません。 # これが Excel VBA の仕様なのでどうしようもないんです... Private Declare Function FindWindow Lib "user32.dll" Alias "FindWindowA" ( _ ByVal lpClassName As String, _ ByVal lpWindowName As String) As Long Private Declare Function FindWindowEx Lib "user32.dll" Alias "FindWindowExA" ( _ ByVal hWnd1 As Long, _ ByVal hWnd2 As Long, _ ByVal lpsz1 As String, _ ByVal lpsz2 As String) As Long Private Sub CommandButton1_Click() Dim hWnd As Long hWnd = FindWindow("ThunderDFrame", Me.Caption) hWnd = FindWindowEx(hWnd, 0&, "F3 Server 60000000", vbNullString) hWnd = FindWindowEx(hWnd, 0&, "F3 Server 60000000", vbNullString) MsgBox hWnd End Sub 興味本位で恐縮ですが、メッセージを受け取らないのに、なぜ通知先 が必要なんでしょう? だとしたら、WSAAsyncSelect の意図がよく 分かりません。 あまりご期待に沿う回答ができず、すみません。
お礼
ご丁寧に教えていただきありがとうございました。
補足
> WinSock から通知されてくるメッセージ、例えば FD_READ を捕らえ > たら、recv() をコール...するといった処理をさせるわけではない.. > ということですね? そういう処理をしたいのですが、そういう処理をダミーとして配置したコントロールのイベントとして記述しようとしているのです。 > 興味本位で恐縮ですが、メッセージを受け取らないのに、なぜ通知先 > が必要なんでしょう? だとしたら、WSAAsyncSelect の意図がよく > 分かりません。 メッセージは受け取りたいのです。メッセージを受けるコントロールがダミーなだけで、メッセージを受けても処理しないわけではありません。 考えていた方法では困難そうですので、 方向転換して、UserFormをサブクラス化する方向で少し検討してみませす。 ありがとうございました。
- KenKen_SP
- ベストアンサー率62% (785/1258)
VBA の標準コントロール(Microsoft Forms2 Object Library)は、 一部( ListBox や Frame )を除いてウインドウハンドルを取得 できません。SPY++ で調べるとわかります(^^; # テキストボックスなどはハンドルが無いので、FindWindowEx # を使っても取得できません。 これは、VB6.0 との差別化のための仕様だと思いますが... WSAAsyncSelect について詳しくはありませんが、ウインドウ メッセージを捕らえるなら、別にコントロールでなくとも Userform のウインドウハンドルが取得できれば良いのでは ありませんか? Userform のハンドル取得方法は FindWindow API を使用します。 注意点としては、Excel97 と Excel2000以降のバージョンで クラス名が変更になっている点です。 クラス名: "ThunderXFrame" ' Excel 97 クラス名: "ThunderDFrame" ' Excel 2000 Later もし Excel97 での動作も視野に入れるなら下記のようなコード で条件分岐させれば良いでしょう。 sClass = IIf(Val(Application.Version) <= 9, _ "ThunderXFrame", _ "ThunderDFrame") hWnd = FindWindow(sClass, UserForm1.Caption) あとはこのハンドルを使って、Userform をサブクラス化すれば メッセージをフックできると思います。
補足
丁寧な回答ありがとうございます。 ListBox や Frameのハンドルが取得できるのであれば、目的は達成できるのですが、これはどのようにすれば取得できるのでしょうか? > WSAAsyncSelect について詳しくはありませんが、ウインドウ > メッセージを捕らえるなら、別にコントロールでなくとも > Userform のウインドウハンドルが取得できれば良いのでは > ありませんか? ウインドウメッセージを捕らえたいのではなく、 (特殊な方法で)ウインドウメッセージを送りたいのです。(実際に送るのはシステム?がします) そのために、送り先のコントロールのハンドルが必要になっています。 WSAAsyncSelectは、その為の設定をします。 [使用イメージ] WSAAsyncSelect(ソケット, 送り先のハンドル, 送信メッセージ, イベント) as エラーコード メッセージを受けるコントロールはダミーなので、ListBox でも問題ありません。
コントロールハンドルでしたらやはり ≫コントロール名.hwnd(Button1.hwnd のように) で取得できますが取得実体がなければ、 1.EnumChildWindows 関数で列挙 2.GetClassName 関数でクラス名を照合させて洗い出す 後者は動的コントロール作成でも使えます。 (VBプログラマはほとんど使わないですけど・・・)
補足
早速の回答ありがとうございます。 しかし、、、 Button1.hWnd のようにしてみましたが、「コンパイルエラー メソッド又はデータメンバーが見つかりません」となります。(入力補完でもでてきませんが、なぜか"w"は自動的に大文字になります。Button1.hWnd()ともしてみましたが、同じでした) > 1.EnumChildWindows 関数で列挙 > 2.GetClassName 関数でクラス名を照合させて洗い出す これについては、使用方法がわかりませんが、もう少し調べてみます。 ありがとうございました。
お礼
ありがとうございました。 別ウインドウを作成する方法もやってみます。