ページ 1 / 1
開いているフォルダウィンドウのアドレスの取得について
Posted: 2007年2月11日(日) 02:39
by ゲスト
はじめまして。ActiveBasicでWIN32APIプログラミングを勉強させてもらっています。
件名の件について頭を悩ませているのですが、
開いているフォルダウィンドウから、そのURL(フォルダパス)を取得することは可能でしょうか?
私はよく、デスクトップにウィンドウを大量に開いて作業するので、
Explorerが落ちたときや一旦別の作業に取りかかるときに
簡単に以前のウィンドウを復旧できるような機能を求めています。
それには、
「現在のウィンドウ状態を保存する」
「記憶したウィンドウ状態を再現する(要するにフォルダを順に開く)」機能があれば実現できる訳です。
WSHではシェルの機能を利用して、WindowsオブジェクトのLocationURLとハンドルを取得できたのですが、
タスクバーボタンを入れ替えるソフトを利用している為か
タスクバーでの順番通りに取得できず(左から順に取得したいのだが、フォルダを開いた順になってしまう)、限界を感じています。
ログインし忘れました
Posted: 2007年2月11日(日) 02:44
by THEREMIN
間違えてゲストで投稿してしまいました。
上の投稿は私(THEREMIN)です。すみません。
Posted: 2007年2月11日(日) 03:25
by ゲスト
フォルダウインドウというのがエクスプローラーのウインドウでしたら
タイトルバーにフォルダのパスが全て表示されるのでエクスプローラーのウインドウハンドルを
取得後、そのウインドウハンドルを使ってタイトルバーの文字列を取得すれば
一応出来る気はしますが、根本的ではないですね
Posted: 2007年2月11日(日) 14:05
by THEREMIN
>ゲスト様
その場合では、フォルダ名しか取得できないんですよね・・・
フォルダオプションで「タイトルバーにフォルダのパス名を表示する」設定にしていれば
確かに取得はできるんですが、設定に依存するからいい方法ではない。
何かいい方法は無いんでしょうか?
Posted: 2007年2月11日(日) 22:39
by イグトランス
ABでやるとしても結局WindowsオブジェクトのLocationURLを読み取ることに変わりはありません。
ただしWSHでやるよりもかなり低水準になるので結構書くのが大変になると思います。
まず,タスクバーの内容を読み取る正式な方法はありません。
しかし,タスクバーのあのアプリケーションのボタンの部分はタブコントロールで出来ているため,
適切なメッセージを送ることにより,タスクバーのボタンの文字列を読み取ることまでは可能だと思います。
参考:
タスクバーとスタートメニューの歴史
その文字列を元にFindWindowを行えば対象となるウィンドウハンドルがわかります。
単純にウィンドウハンドルを取得したければ,ほかにもEnumWindows関数を使う手などがありますが,
タスクバーのボタンの並んでいる順にウィンドウハンドルが得られる保証はないと思います。
エクスプローラのフォルダ表示のウィンドウハンドルを元に,いくつか手順を踏むことでIWebBrowser2へのポインタを得られます。
これでWSH版に追いつきました。IWebBrowser2にはLocationURLがあり,これでようやくパスを得られます。
勿論WSHの手順を忠実に真似ていく方法もありますが,それではWSHから替える必要性がありませんね。
Posted: 2007年2月13日(火) 15:49
by THEREMIN
>>イグトランス様
返答が遅くなってすみません。
助言通りに作ってある程度感じが掴めてからレスしようかと思ってたもので・・・
>結局WindowsオブジェクトのLocationURLを読み取ること
要するにShellの機能を使うしか無い訳ですか
私の環境はWindowsXPなので、
Shell_TrayWnd> ReBarWindow32> MSTaskSwWClass> ToolbarWindow32クラスの
ウィンドウのハンドルを取得して、とりあえずそこにSendMessageを試しています。
コマンドがTB_系なので定数値が分からず、困ってますが。
>エクスプローラのフォルダ表示のウィンドウハンドル
これってCabinetWClassのことでしょうか?
それなら自分のやってることは間違ってるのかな・・・
Posted: 2007年2月13日(火) 17:35
by イグトランス
> これってCabinetWClassのことでしょうか?
それです。これがタスクバーのボタンにあるウィンドウタイトルと同じ名前のテキストを持っているため,
先程の手順で,FindWindowで検索するとおそらくこのクラスのウィンドウが該当することになると思います。
試したことがないのでわかりませんが,ほかのウィンドウハンドルでもできるのかもしれません。
Posted: 2007年2月13日(火) 19:15
by THEREMIN
>>イグトランス様
こんなに早く返事が来るとは思っていませんでした。
自分のつまらない報告に答えていただき、ありがとうございます。
>ほかのウィンドウハンドルでもできるのかもしれません
そうなんです、今まさにそれを試しているところなんですが・・
ただ、ウィンドウタイトルだけでは同じフォルダ名のウィンドウが開いたときに
判別できないので、やはり別の方法が必要だと思います。
自分の考えている方法は、タスクバーのボタンにはIDがあるようなので
それを左から順に取得できれば、最悪WSHとの合わせ技で実現できるかも、という感じです。
そこらへんは、タスクバーアイコンを移動することができるソフトのソースでもあればはっきり理解できると思うんですけどね・・・
できました!
Posted: 2007年2月22日(木) 21:34
by THEREMIN
ここのアドバイスや他の掲示版での情報も参考にして、なんとか
フォルダウィンドウのハンドルと ウィンドウの並んでいる順は取得することができました。
結構前に取得自体はできていたのですが、Unicodeテキストファイルの扱いで悪戦苦闘していて返事が遅れてしまいました。
イグトランス様のアドバイスを参考に、ToolbarWindow32クラスのウィンドウハンドルを取得して
そこからタブボタンの情報をSendMessage()で取得していったところ、
うまくフォルダウィンドウのハンドルを取得することができました。一応ソースを示しておきます。
[ここをクリックすると内容が表示されます] [ここをクリックすると非表示にします]コード: 全て選択
Type WindowInfo ' 取得したウィンドウ情報
Handle As HWND ' ウィンドウハンドル
ID As DWord ' ウィンドウID
Index As DWord ' ウィンドウが左から何番目にあるか
End Type
Sub GetWindowInfo()
' hTaskBarにToolbarWindow32クラスのウィンドウハンドルを取得
Dim hTaskBar As HWND
hTaskBar = FindWindow("Shell_TrayWnd",NULL)
hTaskBar = FindWindowExA(hTaskBar, NULL,"ReBarWindow32", NULL)
hTaskBar = FindWindowExA(hTaskBar, NULL,"MSTaskSwWClass", NULL)
hTaskBar = FindWindowExA(hTaskBar, NULL,"ToolbarWindow32",NULL)
' 共有メモリをオープン
Dim pSysShared As BytePtr ' ファイルがマップされた他のプロセスのビューの開始アドレス
Dim hProcess As HWND ' プロセスハンドル
Dim dw As DWord
Dim dwPID As DWord ' Process ID
Dim dwSize As DWord
dwSize = sizeof(TBBUTTON) + sizeof(TCHAR) * MAX_PATH '確保するサイズ
GetWindowThreadProcessId(hTaskBar, VarPtr(dwPID))
hProcess = OpenProcess(PROCESS_VM_OPERATION + PROCESS_VM_READ + PROCESS_VM_WRITE,FALSE, dwPID)
pSysShared = VirtualAllocEx(hProcess, NULL, dwSize, MEM_RESERVE + MEM_COMMIT, PAGE_READWRITE)
Dim tb As TBBUTTON
Dim hTaskBtn As HWND
Dim WinText[MAX_PATH] As Byte
Dim winID As DWord
Dim BtnNo As DWord
Dim nBtnIndex As DWord
Dim nBtnCount As DWord
' タブボタン(開いているフォルダウィンドウ)の数を取得
nBtnCount = SendMessage(hTaskBar,TB_BUTTONCOUNT,0,0)
Dim t_info As *WindowInfo ' タブボタンの情報
t_info = calloc(nBtnCount*SizeOf(WindowInfo))
/* TB_BUTTONCOUNT で得られる値は実際に表示されているボタンの数の2倍になっている
つまり、ウィンドウごとに2つのボタンが割り当てられている。その内奇数番号が有効なハンドル */
For nBtnIndex=1 To nBtnCount-1 Step 2
/* nBtnIndex 番目のボタンの情報を取得 */
SendMessage(hTaskBar, TB_GETBUTTON, nBtnIndex, pSysShared As LPARAM)
/* 取得した情報を自分のプロセス領域へコピー */
ReadProcessMemory(hProcess, pSysShared, VarPtr(tb), Len(tb), VarPtr(dw))
/* nBtnIndex 番目のボタンのIndexを取得 */
/* 指定するのはボタンの番号ではなくてコマンドID */
BtnNo = SendMessage(hTaskBar, TB_COMMANDTOINDEX, tb.idCommand, 0)
/* tb.dwDataの先頭メンバがウィンドウのハンドルになっているので、取得する */
ReadProcessMemory(hProcess,tb.dwData As VoidPtr,VarPtr(hTaskBtn),Len(hTaskBtn),VarPtr(dw))
/* そのウィンドウのテキストを普通に拾う */
GetWindowText(hTaskBtn,WinText,Len(WinText))
/* ウィンドウIDを取得することにより、一意のウィンドウを指定できる */
winID = GetWindowLong(hTaskBtn,GWL_ID)
/* ClassNameはちゃんと"CabinetWClass"になっている */
GetClassName(hTaskBtn, WINDOW_INFO, MAX_PATH)
/* 自作のWindowInfo構造体にタブボタンの情報を設定 */
t_info[nBtnIndex].Handle = hTaskBtn ' ウィンドウのハンドル
t_info[nBtnIndex].ID = winID ' ウィンドウのID(不要)
t_info[nBtnIndex].Index = BtnNo ' ボタン番号
End If
Next nBtnIndex
' 共有メモリをクローズ
VirtualFreeEx(hProcess, pSysShared, 0, MEM_RELEASE)
CloseHandle(hProcess)
End Sub
見にくくてすみません。
SendMessage()の他にも色々処理がいるようで、あんまり理解してはいないのですが、要するに
ToolBarコントロールは別プロセスなので、共有メモリをオープンし、
それを利用してメッセージの受け渡しをしなければならないようです。
http://techtips.belution.com/ja/vc/0001/
http://forums.belution.com/ja/vc/000/271/11s.shtml
http://hongliang.seesaa.net/article/7851680.html
http://www.atmarkit.co.jp/bbs/phpBB/vie ... forum=7&11
追記
Posted: 2007年2月22日(木) 21:36
by THEREMIN
*注記
上記のコードではウィンドウIDも取得していますが、ハンドル取得時にCabinetWClassのものを取得しているようなので
たぶん不要です。最初はウィンドウIDだけが一意なのかと思っていたので・・・
このように、ウィンドウの順番は取得できましたが、
フォルダウィンドウのアドレスだけはどうしてもCOMを使わないと取得できないようなので、
今の段階ではWSHとの合わせ技で、
ウィンドウの情報(LocationURLとウィンドウハンドル)を取得しファイルに保存する自作のWSHスクリプトを使って
ファイル経由でウィンドウの情報を受け取り、そのウィンドウハンドルと突き合わせながら
AB上で取得したフォルダウィンドウの順番を取得し、再度ファイルに書きこむという間接的なやり方で取得しています。
手順としては、
1. CreateProcess()でコマンドを渡すことでWSHスクリプトを起動
2. 終了するまで待って、終了コード(保存したファイル番号)を取得
3. 終了コードからファイルパスを作成してファイルを読み込む。読み込んだバッファからフォルダウィンドウの情報を取得する。
4. ABプログラム上で取得したタブボタンの情報を、取得したウィドウハンドル同士で突き合わせながらウィンドウの順番を設定する。
5. フォルダウィンドウの情報をウィンドウの並んでいる順にファイルに再書き込みする
このような感じでやっています。
ここまで作る上で副産物として、ユニコードテキストを読み込んで処理する関数ができました。
元の関数は、るっとパパ 様のLineInput #エミュレート ライブラリ「RPLineInput.sbp」を改変しています。
(上手く汎用化できたら、皆様に感謝の意も込めて実践コードモジュールに投稿したいと考えています。)