[BlueLeaf1336]> PROBLEMS> ClickThereItIs!>

ClickThereItIs! - アルファ版完成

historyTOP

2004/02/01:作成

とりあえず動く奴TOP

2004/02/01

CAUTION!! ALPHA VERSION!! AT YOUR OWN LISK!!
とりあえずEXEとソース。20040131ClickHereItIs.zip(10,562bytes)
警告!! 開発版です!! 保証できません!!

動作確認は、Windows2000sp4 + IE6 + Full Windows-Updateで行っています。

さて、ちょっとだけ使い方を説明しておきます。何をしているのかはこれまでのページとこのページで一部を説明します。

まずダウンロードしたファイルを解凍するといくつかのファイルが入っていますが、実行に必要なのは

だけです。で、(当然ですが)EXEをダブルクリックすると次のようなヘナチョコな画面が開きます。

特に気にする必要はありません。ただ、常に最前面に居座るので邪魔です。適当に端っこに移動しておいて下さい。

次に、エディタを開きます。別にエディタでなくてもかまわないのですが、「ファイルを開く」あるいは「ファイルを保存」するダイアログが次の図のような奴でないと駄目です。

OKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOK

OKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOK

つまり、左側に便利なボタンのついているダイアログには対応できていません。次のような奴は駄目です。

NGNGNGNGNGNGNGNGNGNGNGNGNGNGNGNGNGNGNGNGNGNGNGNGNGNGNGNG

NGNGNGNGNGNGNGNGNGNGNGNGNGNGNGNGNGNGNGNGNGNGNGNGNGNGNGNG

で、「ファイルを開く」ダイアログあるいは「ファイルを保存する」ダイアログを開いて、

のいずれかをクリックしてみてください。今はエディタ(等の対象プログラム)が非アクティブになってしまいますが、それでもダイアログの示している場所が変化していることが確認できると思います。ただ、なんか変更反映タイミングがもうひとつです。やってもらえれば分かると思います。確かにクリックしているのに変更されない場合は、いろんなフォルダをクリックしたり、一旦エディタをアクティブにしたりしてみてください。特にタスクバーでアクティブにしたフォルダなんかはテキメンに反映されません。

でもまあ、先が見えてきました。

Open/SaveAsダイアログかどうかTOP

2004/02/01

現在次のコードで、CBTフックから飛んできた全種類ダイアログから「ファイルを開く」「ファイルを保存する」ダイアログを見分けています。多分だめだめですが、とりあえず動いているようです。

uses
    dlgs;

(*
    ===========================================================================
    Open/SaveAsダイアログかどうか(だいたい)
    ---------------------------------------------------------------------------
    http://www1.harenet.ne.jp/cgi-bin/cgiwrap/unaap/chtml/chtml.cgi?key=comdlg1
    ===========================================================================
*)
function    IsOpenSaveAsDialog(WH: HWND): Boolean;
var
    Check: HWND;
begin
    Result := false;

    //  以下に上げる標準と思われるコントロールを持ってればOK

    Check := GetDlgItem(WH, stc2);         //  ファイルの種類(&T)
    if (Check = 0) then Exit;
    Check := GetDlgItem(WH, stc3);         //  ファイル名(&N)
    if (Check = 0) then Exit;
    Check := GetDlgItem(WH, stc4);         //  ファイルの場所(&I)
    if (Check = 0) then Exit;
    Check := GetDlgItem(WH, cmb2);         //  ファイルの場所選択コンボ
    if (Check = 0) then Exit;
    Check := GetDlgItem(WH, lst1);         //  リストビュー
    if (Check = 0) then Exit;
    Check := GetDlgItem(WH, chx1);         //  読取専用ファイルとして開く(&R)チェック
    if (Check = 0) then Exit;
    Check := GetDlgItem(WH, cmb1);         //  ファイルの種類選択コンボ
    if (Check = 0) then Exit;
    Check := GetDlgItem(WH, edt1);         //  ファイル名入力エディット
    if (Check = 0) then Exit;
    Check := GetDlgItem(WH, IDOK);         //  開く(&O)ボタン
    if (Check = 0) then Exit;
    Check := GetDlgItem(WH, IDCANCEL);     //  キャンセルボタン
    if (Check = 0) then Exit;

    Result := true;
end;

クリックされた位置のパスを求めるTOP

2004/02/01

次に、現在次のコードで、「マウスクリックされた場所のフォルダパスの取得」を行っています。

uses
    ShlObj, ActiveX;

(*
    ===========================================================================
    デスクトップパスを取得する
    ---------------------------------------------------------------------------
    http://homepage1.nifty.com/MADIA/delphi/Win32API/ShFolder.htm
    http://homepage2.nifty.com/Mr_XRAY/Delphi/plFileDialog/B_BrowseFolder.htm
    ===========================================================================
*)
function    GetDeskTopPath():string;
var
    Root : PItemIDList;
    Buffer: PChar;
    Memory: IMalloc;
begin
    SHGetMalloc(Memory);
    Buffer := Memory.Alloc(MAX_PATH);
    //  特殊フォルダの位置を取得する
    SHGetSpecialFolderLocation(Application.MainForm.Handle, CSIDL_DESKTOP, Root);
    //  ItemIDList構造体からパス名を取り出す
    SHGetPathFromIDList(Root, Buffer);
    Result := Buffer;
    //  メモリの解放
    Memory.Free(Root);
    Memory.Free(Buffer);
end;

(*
    ===========================================================================
    マウスがデスクトップにある場合にデスクトップへのパスを返す
    マウスがフォルダ内にある場合にそのフォルダへのパスを返す
    マウスがエクスプローラにある場合にそのフォルダへのパスを返す
    マウスがよく分からない場所にある場合には空文字を返す
    ---------------------------------------------------------------------------
    http://hp.vector.co.jp/authors/VA020801/lnkpg/sample/getexp.txt
    http://hp.vector.co.jp/authors/VA009712/take/delphi/kabeapp.htm
    ===========================================================================
*)
function    GetFullPathOfMouse(): string;
var
    WndClassName: string;
    Buf: array[0..255] of Char;
    WH: HWND;
begin
    Result := '';
    //  マウス直下のウィンドウの一番天辺の親ウィンドウのハンドル
    WH := GetMostTopLevelWindow(WindowFromPoint(Mouse.CursorPos));
    //  そのウィンドウのクラス名
    WndClassName := GetClassNameStr(WH);
    //  デバッグ
    Application.MainForm.Caption := WndClassName;
    //  フォルダかエクスプローラ
    if (WndClassName = 'CabinetWClass') or (WndClassName = 'ExploreWClass') then
    begin
        //  WorkerW ウィンドウ検索
        WH := FindWindowEx(WH, 0, 'WorkerW', nil);
        if (WH <> 0) then
        begin
            //  ReBarWindow32 ウィンドウ検索  
            WH := FindWindowEx(WH, 0, 'ReBarWindow32', nil);
            if (WH <> 0) then
            begin
                //  ComboBoxEx32 ウィンドウ検索
                WH := FindWindowEx(WH, 0, 'ComboBoxEx32', nil);
                if (WH <> 0) then
                begin
                    //  そいつのテキストを取得
                    FillChar(Buf, SizeOf(Buf), #0);
                    SendMessage(WH, WM_GETTEXT, SizeOf(Buf), Integer(@Buf));
                    Result := Buf;
                end;
            end;
        end;
    end
    else
    //  デスクトップ
    if (WndClassName = 'Progman') then
    begin
        Result := GetDeskTopPath();
    end;
end;

いくつか登場しているクラス名が何なのか?については、コード中の参考URLを見てもらったり次の図を見てもらったりして判断してください。まず、適当なフォルダを開いた状態でそのフォルダウィンドウのウィンドウ階層図を取り出したものです。で、上記の見たこともないようなクラスは「アドレス(&D)」に辿り着くための階層だったわけです。

それからデスクトップを構成しているウィンドウの階層図です。「Progman」が天辺にあればデスクトップだろう...と。で、デスクトップならば、アドレスもくそもなく別の手段でデスクトップのフォルダパスを取得してやろうということです。

ただし、この方法で取得したデスクトップには「マイコンピュータ」や「マイネットワーク」が現れません。ご愁傷様です。

ターゲットのダイアログの表示中のパスを書き換えるTOP

2004/02/01

で、悩みに悩んでネットで見つけて「なるほどー」と納得したダイアログの表示パスの書き換えコードです。

(*
    ===========================================================================
    指定のダイアログにパス変更させてみる
    ---------------------------------------------------------------------------
    http://www.kumei.ne.jp/c_lang/sdk4/sdk_301.htm
    http://forum.nifty.com/fdelphi/samples/00908.html
    ===========================================================================
*)
procedure   SetDialogPath(WH: HWND; Path: string);
var
    EditWH, OkWH: HWND;
    Buf: array[0..255] of Char;
    PreText: string;
begin
    //  Editコントロール検索
    EditWH := GetDlgItem(WH, edt1);
    //  今の文字列をバックアップしておく
    FillChar(Buf, SizeOf(Buf), #0);
    SendMessage(EditWH, WM_GETTEXT, SizeOf(Buf), Integer(@Buf));
    PreText := Buf;
    //  書き込む
    SendMessage(EditWH, WM_SETTEXT, 0, Longint(PChar(Path)));
    //  OKボタン検索
    OkWH := GetDlgItem(WH, IDOK);
    //  OKボタン押す
    SendMessage(OkWH, BM_CLICK, 0, 0);
    //  元に戻す
    SendMessage(EditWH, WM_SETTEXT, 0, Longint(PChar(PreText)));
end;

この関数を呼ぶ前に、そのウィンドウが確かに本物の「ファイルを開く」「ファイルを保存する」ダイアログであることは確認しておけよ。というスタンスです。

フックTOP

2004/02/01

で、一番重要で動作が妙だと感じるフックですが、CBTフックを使用しています。で、ウィンドウが作られる瞬間とウィンドウが破棄される瞬間とアクティブなウィンドウが切り替わる瞬間に本体プログラムに対してメッセージを投げています。作成タイミングと破棄タイミングは完璧に予想通りの動作をしていると思いますが、アクティブなウィンドウが切り替わるタイミングが微妙で動作が妙だと感じる原因になっています。

道半ばです。

前ページで、6ページぐらいかも、と予想してましたが一気に進んだのでアレは嘘です。

EOFTOP