質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.83%
VBA

VBAはオブジェクト指向プログラミング言語のひとつで、マクロを作成によりExcelなどのOffice業務を自動化することができます。

Q&A

解決済

ハンドルを取得して表示済みの"名前を付けて保存"ダイアログの保存先を変更したい。

tetrisuehara
tetrisuehara

総合スコア20

VBA

VBAはオブジェクト指向プログラミング言語のひとつで、マクロを作成によりExcelなどのOffice業務を自動化することができます。

4回答

0グッド

0クリップ

3751閲覧

投稿2021/09/24 07:40

編集2021/10/01 01:13

前提・実現したいこと

お世話になっております。
seleniumを利用して名前を付けて保存ダイアログを出現させ、**FindWindow関数(またはFindWindowEx関数)**を利用してハンドルを取得させ、名前を付けて保存させようとしています。
**SPY++**を利用してハンドル名を取得して保存するデータの名前をつけてOKボタンをクリックし、保存する事はできたのですが、保存先のハンドルの取得と指定がうまくいきません。
試しに実行してもエラーは出ずにその部分に関してはスルーされているように見えます。

ご教授願います。

該当のソースコード

VBA

1Option Explicit 2 3Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long 4Declare Function SetForegroundWindow Lib "user32" (ByVal hwnd As Long) As Long 5Declare PtrSafe Function FindWindowEx Lib "user32" Alias "FindWindowExA" ( _ 6 ByVal hwndParent As LongPtr, _ 7 ByVal hwndChildAfter As LongPtr, _ 8 ByVal lpClassName As String, _ 9 ByVal lpWindowName As String) As Long 10Declare PtrSafe Function SendMessage Lib "user32" Alias "SendMessageA" ( _ 11 ByVal hwnd As Long, _ 12 ByVal wMsg As Long, _ 13 ByVal wParam As Long, _ 14 lParam As Any) As LongPtr 15 16Public Const VK_RETURN As Integer = &HD 17 18Sub test() 19 20 21中略 22 23'名前を付けて保存画面-------------------------- 24 25Dim hwnd As Long 26 ' ウィンドウハンドル取得 27 hwnd = FindWindow("#32770", "名前を付けて保存") ' ウィンドウタイトルで検索する場合 28 29 If hwnd <> 0 Then 30 ' 指定したハンドルのウィンドウを前面に表示 31 SetForegroundWindow hwnd 32 End If 33 34 Driver.Wait (5000) 35 36Dim hInputBox As Long 37Dim hbutton As Long 38 39'アドレスバーを選択する 40'hInputBox = FindWindowEx(hwnd親ハンドル,子のハンドル,ウインドウクラスネーム, "") 41 42hInputBox = FindWindowEx(hwnd, 0, "WorkerW", "") 43hInputBox = FindWindowEx(hInputBox, 0, "ReBarWindow32", "") 44hInputBox = FindWindowEx(hInputBox, 0, "Address Band Root", "") 45hInputBox = FindWindowEx(hInputBox, 0, "msctls_progress32", "") 46hInputBox = FindWindowEx(hInputBox, 0, "Breadcrumb Parent", "") 47hInputBox = FindWindowEx(hInputBox, 0, "ToolbarWindow32", "") 48 49SetForegroundWindow hInputBox 50'アドレスバーに入力場所を入れる 51Call SendMessage(hInputBox, &H6, 1, 0&) 'ToolbarWindow32をアクティブにする 52 53Call SendMessage(hInputBox, &HF5, 0, 0&)  'ボタンをクリックする(アドレス入力可能状態にする) 54 55Call SendMessage(hInputBox, &HC, 0, ByVal "C:\Users\uehara\Desktop") 'アドレス入力 56 57Call SendMessage(hInputBox, &HD, 0, 0&)  ' エンターを押す 58 59 60'名前を付けて保存する 61hInputBox = FindWindowEx(hwnd, 0, "DUIViewWndClassName", "") 62hInputBox = FindWindowEx(hInputBox, 0, "DirectUIHWND", "") 63hInputBox = FindWindowEx(hInputBox, 0, "FloatNotifySink", "") 64hInputBox = FindWindowEx(hInputBox, 0, "ComboBox", "") 65hInputBox = FindWindowEx(hInputBox, 0, "Edit", "") 66hbutton = FindWindowEx(hwnd, 0, "Button", "保存(&S)") 67Call SendMessage(hInputBox, &HC, 0, ByVal "テスト") 68Call SendMessage(hbutton, &H6, 1, 0&) 'ボタンをアクティブにする 69Call SendMessage(hbutton, &HF5, 0, 0&) 'ボタンをクリックする 70Driver.Wait (1000) 71End Sub 72

試したこと

Spy++
該当のハンドルはこちらで間違いないとは思います。
また、アドレス部分のハンドルに対して、SendMessageを利用してクリックをする(その部分を有効かできると考えて)ということも試しましたが、そちらも不発に終わりました。
(2021/10/01更新)
各種ご提案頂いた方法試した結果、ウィンドウハンドル自体は間違っていないようなので、アドレスバーへの指示式(指示方法?)が間違っているように思われます。

利用環境

excel2019 32bit版

気になる質問をクリップする

クリップした質問は、後からいつでもマイページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

下記のような質問は推奨されていません。

  • 質問になっていない投稿
  • スパムや攻撃的な表現を用いた投稿

適切な質問に修正を依頼しましょう。

takanaweb5

2021/09/28 03:02 編集

変数やDeclare宣言でハンドルはLongPtr型で宣言しなければならないはずです。 ハンドルの実態はポインタですから 32bit版のExcelをご使用であれば結果オーライですが
tetrisuehara

2021/09/28 03:59

すみません表記しておけばよかったですね。 利用環境は32ビット版になります。 質問文を編集しておきます。 ご指摘ありがとうございます。
tetrisuehara

2021/10/04 01:01

皆様ご回答頂きましてありがとうございました。 h.horikoshi様のご回答でベストアンサーとさせていただきますが、tyamzak_様、takanaweb5様共にお知恵をお貸しいただきまして誠にありがとうございました。 見ていただけているかはわかりませんが、コメントにてお礼申し上げます。

回答4

1

ベストアンサー

質問中で提示されたコード自体は動作していると思います。
※なお、各々のsendMessageの間にはDoEventsを入れてください。

当方で試してみましたが、ToolbarWindow32に値は入ります(プログラム実行後にSPYで
確認してみてください)
ただし、値は入っても入力フィールドの表示は変更されません。この入力フィールド
はいろいろなウィンドウが重なっていますので、対象にしているウィンドウが異なる
のではないかと思います。

...ところで、保存先のハンドルの取得と指定を試されているのは、ファイルの保存先ドライブや
フォルダを指定したいということなのでしょうか?
であれば、ファイル名フィールドにドライブを含めたフルパスのファイル名を指定すれば
よいかと思います。そうすれば保存先フィールドの値は関係なく指定したパスでファイルが
格納できます。ファイル名フィールドはEditクラスですので設定も簡単です。

投稿2021/10/01 13:09

h.horikoshi

総合スコア505

tetrisuehara👍を押しています

下記のような回答は推奨されていません。

  • 質問の回答になっていない投稿
  • スパムや攻撃的な表現を用いた投稿

このような回答には修正を依頼しましょう。

回答へのコメント

tetrisuehara

2021/10/04 00:59

遅ればせながら、ご回答ありがとうございます。 そのような理由で見た目で反映はされずに内部的には処理されているという状態だったんですね 私の方でも確認できました。 まさしく保存先及び名前をつけるまでをするというコードを書いておりました。 フルパスでファイル名のフィールで再現できるというのは思いもよりませんでした。 重ねてお礼申し上げます。

1

vb

1 hwnd = FindWindow("#32770", "名前を付けて保存") 2 Debug.Print WorksheetFunction.Dec2Hex(hwnd, 8) 3 4 hInputBox = FindWindowEx(hwnd, 0, "WorkerW", "") 5 Debug.Print WorksheetFunction.Dec2Hex(hInputBox, 8)

のようにして、たえずウインドウハンドルの変数の中身を確認し、spy上に表示されるウインドウハンドルの値と比較されてはどうでしょうか?

ふと思っただけで外しているかもしれませんが、EXCELは32bit、SPYも32bitでウインドウハンドルを取得したいアプリは64bitというのが原因とかないでしょうか?
32bitアプリでは、64bitのハンドル(ポインタ)を取得することは出来るのでしょうか?

投稿2021/09/28 11:43

編集2021/09/28 13:40
takanaweb5

総合スコア298

tetrisuehara👍を押しています

下記のような回答は推奨されていません。

  • 質問の回答になっていない投稿
  • スパムや攻撃的な表現を用いた投稿

このような回答には修正を依頼しましょう。

回答へのコメント

tetrisuehara

2021/09/29 06:08 編集

お世話になっております。 ご回答ありがとうございます。 ハンドルの値の比較までたどり着けていないのですが、先に32ビット・64ビットのご指摘についてですが、確かに原因の可能性は捨てきれないですね。 名前を付けて保存ダイアログのダイアログ自体はwindowsのシステムですので、私のPC自体が10Pro64ビット環境なので。 または、windowsシステムをアプリケーション側環境に準拠している場合、該当アプリケーションは32ビットなので、他に原因があるという事になりそうです。
tetrisuehara

2021/10/01 01:06

お世話になっております。 ウインドウハンドルの動きを見てみたのですが、やはりハンドル自体は変わっていませんでした。 よって、ハンドル取得後のハンドルへの命令式が間違っているように感じます。 ハンドルの命令式を見直してみます。 重ねてご提案いただきありがとうございました。

1

ですが、なぜ32より少ない場合呼び出しという形なのでしょうか?

エラーコードが32ビットであり、31が最上位なので、そうしています。

GetLaGetLastError

ウインドウハンドルは取得できていて、実際にsendmessageでアドレスを入れる時にハンドルが無効になっているということはどういうことでしょうか?

ウィンドウがトップにあると見なされていないからかもしれません。
そうであれば、SetForegroundWindow関数をSendMessageの直前に入れるとうまくいくことがあります。

エクセルVBAでWindows APIのSetForegroundWindow関数を利用する

いずれにせよ、操作したいウィンドウが送ろうとしているメッセージを受け付ける様に作られていれば操作できますが、そうでなければ操作できないと思います。
保存先の指定を手で行ってみて、想定されるメッセージのやり取りが作成されているか確認するのも良いかもしれません。

Spy++ (ウィンドウメッセージを調べる)の使い方

投稿2021/09/28 04:10

tyamzak_

総合スコア95

tetrisuehara👍を押しています

下記のような回答は推奨されていません。

  • 質問の回答になっていない投稿
  • スパムや攻撃的な表現を用いた投稿

このような回答には修正を依頼しましょう。

回答へのコメント

tetrisuehara

2021/09/28 04:47

お世話になっております。 ご回答頂きましてありがとうございます >いずれにせよ、操作したいウィンドウが送ろうとしているメッセージを受け付ける様に作られていれば操作できますが、そうでなければ操作できないと思います。 確かにアドレス入力に対する指示が違っているような気もしますのでこちらの見直しをしてみます。

1

昔触ったことがあります。
WindowsAPIはエラーで止まったりしなかったような記憶があります。
戻り値をチェックして確認してみてはいかがでしょうか。
例えばですが、

'アドレスバーを選択する
'アドレスバーに入力場所を入れる

の部分を、以下のようにすると、エラーが出ているのかどうかわかるかと思います。

VBA

1hInputBox = FindWindowEx(hwnd, 0, "WorkerW", "") 2If hInputBox < 32 Then 3  Call sGetErrMsg 4End If 5hInputBox = FindWindowEx(hInputBox, 0, "ReBarWindow32", "") 6If hInputBox < 32 Then 7  Call sGetErrMsg 8End If 9hInputBox = FindWindowEx(hInputBox, 0, "Address Band Root", "") 10If hInputBox < 32 Then 11  Call sGetErrMsg 12End If 13hInputBox = FindWindowEx(hInputBox, 0, "msctls_progress32", "") 14If hInputBox < 32 Then 15  Call sGetErrMsg 16End If 17hInputBox = FindWindowEx(hInputBox, 0, "Breadcrumb Parent", "") 18If hInputBox < 32 Then 19  Call sGetErrMsg 20End If 21hInputBox = FindWindowEx(hInputBox, 0, "ToolbarWindow32","") 22If hInputBox < 32 Then 23  Call sGetErrMsg 24End If 25 26Dim r as Long 27 28'アドレスバーに入力場所を入れる 29r = SendMessage(hInputBox, &HC, 0, ByVal "C:\Users\ユーザー名\Desktop") 30 31If r < 32 Then 32  Call sGetErrMsg 33End If 34 35Private Sub sGetErrMsg() 36'最後にDLLを呼び出したときのエラーを取得(GetLastError) 37  Dim lngResult As Long 38  Dim ErrorCode As Long 39  Dim ErrBuffer As String 40  'エラーメッセージを受け取るバッファーを確保 41  ErrBuffer = String$(256, vbNullChar) 42  '最後にDLLを呼び出したときのエラーコードを取得 43  ErrorCode = Err.LastDllError 44  'エラーコードからエラーメッセージを取得する 45  lngResult = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, ByVal 0&, ErrorCode, 0&, ErrBuffer, Len(ErrBuffer), 0&) 46  'バッファーからメッセージを取り出し 47  ErrBuffer = Left$(ErrBuffer, InStr(ErrBuffer, vbNullChar) - 1) 48  'エラーコードとエラーメッセージを表示 49  MsgBox "エラーコード: " & ErrorCode & vbLf & ErrBuffer, vbInformation, "GetLastError" 50End Sub

投稿2021/09/27 03:39

編集2021/09/27 03:40
tyamzak_

総合スコア95

tetrisuehara👍を押しています

下記のような回答は推奨されていません。

  • 質問の回答になっていない投稿
  • スパムや攻撃的な表現を用いた投稿

このような回答には修正を依頼しましょう。

回答へのコメント

tetrisuehara

2021/09/28 00:30

お世話になっております。ご回答頂きましてありがとうございます。 上記コードに変更して、宣言等してから動かしてみました。 結果としては ハンドルの掘り下げの部分では エラーコード:0 この操作を正しく終了しました。 と出ますので、正しくハンドルの取得はできているようです。 r = SendMessage(hInputBox, &HC, 0, ByVal "C:\Users\ユーザー名\Desktop") この部分でエラーを吐き出していて、 エラコード:1400 ウインドウハンドルが無効です。 と出ています。 質問としていくつかございまして、 まず1つ目 If hInputBox < 32 Then   Call sGetErrMsg ですが、なぜ32より少ない場合呼び出しという形なのでしょうか? この32の数字はハンドル番号を比較しているという認識でいいんでしょうか? 2つ目 ウインドウハンドルは取得できていて、実際にsendmessageでアドレスを入れる時にハンドルが無効になっているということはどういうことでしょうか? ご回答のほどよろしくお願いします。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.83%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問

同じタグがついた質問を見る

VBA

VBAはオブジェクト指向プログラミング言語のひとつで、マクロを作成によりExcelなどのOffice業務を自動化することができます。