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

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

新規登録して質問してみよう
ただいま回答率
85.48%
Win32 API

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

C++

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

Q&A

解決済

2回答

13431閲覧

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

RyuL

総合スコア24

Win32 API

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

C++

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

0グッド

2クリップ

投稿2018/06/27 00:18

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

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

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

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

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

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

C++

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

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

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

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

投稿2018/06/27 04:01

catsforepaw

総合スコア5938

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

RyuL

2018/06/28 01: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 ); // ~以下略~ ```
catsforepaw

2018/06/28 01:32 編集

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

2018/06/29 00:45

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

0

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

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

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

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

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

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

投稿2018/06/27 01:34

編集2018/06/27 01:36
takabosoft

総合スコア8356

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問