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

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

ただいまの
回答率

90.34%

  • C++

    3764questions

    C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

  • Win32 API

    245questions

    Win32 APIはMicrosoft Windowsの32bitプロセッサのOSで動作するAPIです。

外部アプリケーションを子ウィンドウとして起動したい

解決済

回答 2

投稿

  • 評価
  • クリップ 1
  • VIEW 801

RyuL

score 3

現在愛用しているアプリケーションツールがあるのですが、そのツールは機能ごとの複数のウィンドウがバラバラに表示される物になっています。
個人的な使用感として、一つのウィンドウ内に各画面が分割されて並んでいる仕様(例としてVisual studio)の方が使いやすいので、それを実現しようと思いました。
そこで自前のアプリケーションを作成し、その中でそのツールを呼び出して実装できないか考えました。

CreateProcessを使えば外部アプリケーションを起動でき、また境界線付き子ウィンドウを複数作成し並べて、境界線が動かされた時にそれぞれの子ウィンドウのサイズをその動きに合わせて伸縮させれば分割ウィンドウが作成できるところまでは分かったのですが、この二つを組み合わせるところで行き詰ってしまいました。

ツールを子ウィンドウとして起動して、ツールの各ウィンドウのハンドルが取得出来れば実装出来そうなのですが、こういう事は可能なのでしょうか?

以上、よろしくお願いいたします。

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+2

こんな感じで別アプリのウィンドウを自分の作ったアプリの子ウィンドウにすることができます。

HWND hAppWnd = 対象アプリのウィンドウハンドル;
HWND hParent = 自分で作ったウィンドウのハンドル;
LONG_PTR ws = ::GetWindowLongPtr(hAppWnd, GWL_STYLE);
ws &= ~(WS_CAPTION | WS_SIZEBOX);    // キャプションとサイズ変更可能枠の除去
::SetWindowLongPtr(hAppWnd, GWL_STYLE, ws);
HWND res = ::SetParent(hAppWnd, hParent);
if(res == NULL)
{
    // 適宜エラー処理を入れてください。
}
RECT rc;
::GetClientRect(hParent, &rc);
int width = rc.right - rc.left;
int height = rc.bottom - rc.top;
::SetWindowPos(hAppWnd, NULL, 0, 0, width, height, SWP_NOZORDER);


サイズ変更等の追従は適宜ウィンドウメッセージを処理してください。また、軽く試してみたところ、なぜか表示が乱れる(再描画されない)ようなので、自分のウィンドウの再描画と合わせて別アプリ側のウィンドウも再描画させてやる必要があるかと思います。

それと、終了処理は注意が必要です。自分の作ったアプリを終了させると子ウィンドウにした別アプリも問答無用で破棄されるので、それが問題なら何らかの処理(WM_CLOSEを送るとか)をする必要があるかもしれません。

あと、CreateProcessで起動したアプリから確実にウィンドウハンドルが取得できるまで待つには、WaitForInputIdle関数が便利です。ウィンドウハンドルの取得はtakabosoftさんの方法が確実だと思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

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

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

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/06/28 10:12

    takabosoft様、catsforepaw様、お答えありがとうございます。
    お二人のアドバイスを参考にして作成したところ、別アプリのハンドルは取得出来のですが、その後子ウィンドウに出来ていません。
    SetParentの戻り値がNULLでは無いので、ここでエラーになっている訳でも無いと思うのですが…。

    ```c++
    // EnumWindowsProcのパラメータ構造体
    struct ENUM_WINDOWS_STR{
    PROCESS_INFORMATION* pi;
    HWND* hWnd;
    };

    // 起動している全プロセスから起動させたアプリケーションのハンドルを取得する
    BOOL CALLBACK EnumWindowsProc( HWND hWnd, LPARAM lParam )
    {
    // CreateProcess()で取得したPROCESS_INFORMATION構造体のポインタを取得
    ENUM_WINDOWS_STR* ep = (ENUM_WINDOWS_STR*)lParam;
    PROCESS_INFORMATION* pi = ep->pi;

    // ウインドウを作成したプロセスIDを取得
    DWORD lpdwProcessId = 0;
    ::GetWindowThreadProcessId( hWnd, &lpdwProcessId );

    // CreateProcessで起動したアプリのプロセスIDとメインウィンドウを作成したプロセスIDが同じ場合
    if( pi->dwProcessId == lpdwProcessId )
    {
    *(ep->hWnd) = hWnd;
    return FALSE;
    }

    return TRUE;
    }

    // ~中略~

    HWND hMainWnd = CreateWindowW( szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr );

    if( !hMainWnd )
    {
    return FALSE;
    }

    ShowWindow( hMainWnd, nCmdShow );
    UpdateWindow( hMainWnd );

    PROCESS_INFORMATION pi;
    STARTUPINFO si;

    ZeroMemory( &si, sizeof( si ) );
    si.cb = sizeof( si );

    CreateProcess( NULL, ( LPTSTR )"notepad", NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi );

    CloseHandle( pi.hThread );

    HWND hChildWnd = NULL;
    ENUM_WINDOWS_STR eNum_windows = { &pi, &hChildWnd };

    while( hChildWnd == NULL )
    {
    EnumWindows( EnumWindowsProc, (LPARAM)&eNum_windows );
    };

    HWND hAppWnd = hChildWnd;
    HWND hParent = hMainWnd;
    LONG_PTR ws = ::GetWindowLongPtr( hAppWnd, GWL_STYLE );
    ws &= ~( WS_CAPTION | WS_SIZEBOX ); // キャプションとサイズ変更可能枠の除去
    ::SetWindowLongPtr( hAppWnd, GWL_STYLE, ws );
    HWND res = ::SetParent( hAppWnd, hParent );
    if( res == NULL )
    {
    // 適宜エラー処理を入れてください。
    }
    RECT rc;
    ::GetClientRect( hParent, &rc );
    int width = rc.right - rc.left;
    int height = rc.bottom - rc.top;
    ::SetWindowPos( hAppWnd, NULL, 0, 0, width, height, SWP_NOZORDER );

    WaitForSingleObject( pi.hProcess,INFINITE );
    CloseHandle( pi.hProcess );

    // ~以下略~
    ```

    キャンセル

  • 2018/06/28 10:31 編集

    確証はないのですが、タイミングが問題なのかもしれません。
    EnumWindows関数の呼び出しを、プロセス起動直後からハンドルが取れるまでループするというやり方なので、別アプリ側がウィンドウを作ったら即座にハンドルを取得し、下の処理に移っています。そうなると、まだ初期化が済んでいない状態のウィンドウに対していろいろ操作を加えることになり、何らかの不都合が生じている可能性が考えられます。

    それを避けるのがWaitForInputIdle関数です。EnumWindows呼び出しの手前で実行してください。そうすることで、起動させたアプリの初期化が済むまで待ってくれます。また、その場合`while( hChildWnd == NULL )`は不要になります。

    キャンセル

  • 2018/06/29 09:45

    無事、別アプリが子ウィンドウになりました。

    再描画の件ですが、手動で親ウィンドウをアクティブにした時に再描画されたので、WM_PAINTのメッセージを受け取った時に、そこの処理の後にSetActiveWindowで親ウィンドウをアクティブにしたらとりあえず問題は無くなりました。

    takabosoft様、catsforepaw様、ありがとうございました。

    キャンセル

+1

やったことはありませんが、 
①まずCreateProcess API呼び出しついでにLPPROCESS_INFORMATIONからプロセスIDを取得しておきます。

次に、起動したプロセスのメインウィンドウハンドルを取得します。
これはおそらくEnumWindows API等で全トップレベルウィンドウを列挙し、GetWindowThreadProcessId APIで各ウィンドウのプロセスIDを取得して、①で取得したIDと一致するかどうかで特定できます。
ただ、起動直後にウィンドウが存在しているとは限らないため、Sleepやリトライの機構が必要になると予想されます。

あとは取得したウィンドウハンドルを`SetParentで親を差し替えればいけるのではないかと思います。

懸案事項として、SetParent APIの日本語のリファレンスには

新しい親ウィンドウと子ウィンドウは、同一アプリケーションに属していなければなりません。

などと書かれてるのですが、英語の方には何も書かれていないので、実際のところやっていいのか悪いのかはっきりしません・・・。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

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

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

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

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

  • ただいまの回答率 90.34%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

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

  • C++

    3764questions

    C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

  • Win32 API

    245questions

    Win32 APIはMicrosoft Windowsの32bitプロセッサのOSで動作するAPIです。