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

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

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

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Win32 API

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

マルチスレッド

マルチスレッドは、どのように機能がコンピュータによって実行したのかを、(一般的にはスレッドとして参照される)実行の複合的な共同作用するストリームへ区分することが出来ます。

C++

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

Q&A

解決済

2回答

2350閲覧

WinAPI マルチスレッドにおける子ウィンドウの作成

Weapon

総合スコア106

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Win32 API

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

マルチスレッド

マルチスレッドは、どのように機能がコンピュータによって実行したのかを、(一般的にはスレッドとして参照される)実行の複合的な共同作用するストリームへ区分することが出来ます。

C++

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

0グッド

1クリップ

投稿2018/11/05 16:53

前提・実現したいこと

WinAPIを用いて別スレッドで子ウィンドウを作成しようと思いました.
とりあえず以下のようなコードを書きましたが別スレッドでのcWindowProc (子ウィンドウのプロシージャ)の未解決外部シンボルのエラーで止まります.
がエラーの問題以前にこのコーディングの方針自体が危ういような気がします.
1)別スレッドで子ウィンドウを作成してもいいものなのでしょうか?
2)別のスレッドではメインのメッセージループを取得できるものなのでしょうか?
3)別のスレッドではそのスレッドのメッセージループをかけると子ウィンドウとして安全ではなくなりますよね?どのような手段をとればいいですか?

コード概要

スレッドへ渡すデータはPassHandleで親のHWNDとHINSTANCE
親ウィンドウプロシージャWM_CREATEで子ウィンドウ作成のスレッド作成
そのスレッドでWNDCLASSEX登録,ウィンドウ作成,子ウィンドウのプロシージャ作成

該当のソースコード

C

1//definition of structure 2#include <windows.h> 3 4struct PassHandle { 5 HWND hwnd; 6 HINSTANCE hInst; 7}; 8 9const wchar_t CHILD_CLASS_NAME[] = L"Child Class Name";

C

1//main 2#define UNICODE 3 4#include <windows.h> 5#include <process.h> 6#include "structure.cpp" 7 8LRESULT CALLBACK WindowProc(HWND, UINT, WPARAM, LPARAM); 9 10DWORD WINAPI cwthread(LPVOID); 11 12const wchar_t CLASS_NAME[] = L"CLASS"; 13 14HINSTANCE hInst; 15 16int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR nCmdLine, int nCmdShow) { 17 UNREFERENCED_PARAMETER(hPrevInstance); 18 UNREFERENCED_PARAMETER(nCmdLine); 19 20 hInst = hInstance; 21 22 WNDCLASSEX wc = { 23 sizeof(WNDCLASSEX),CS_VREDRAW | CS_HREDRAW, WindowProc, 24 0, 0, hInstance, 25 NULL, NULL,(HBRUSH)GetStockObject(WHITE_BRUSH), 26 NULL, CLASS_NAME, NULL 27 }; 28 29 RegisterClassEx(&wc); 30 31 HWND hwnd = CreateWindowEx( 32 0, CLASS_NAME, L"Template", WS_OVERLAPPEDWINDOW, 33 50, 50, 960, 525, 34 NULL, NULL, hInstance, NULL 35 ); 36 37 ShowWindow(hwnd, nCmdShow); 38 UpdateWindow(hwnd); 39 40 MSG msg = {}; 41 42 while (GetMessage(&msg, NULL, 0, 0)) { 43 TranslateMessage(&msg); 44 DispatchMessage(&msg); 45 } 46 47 return (int)msg.wParam; 48} 49 50LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { 51 switch (uMsg) { 52 case WM_CREATE: 53 54 static PassHandle *mph; 55 mph->hwnd = hwnd; 56 mph->hInst = hInst; 57 58 _beginthreadex(NULL, 0, (_beginthreadex_proc_type)&cwthread, mph, 0, NULL); 59 break; 60 61 case WM_PAINT: 62 { 63 PAINTSTRUCT ps = {}; 64 HDC hdc = GetDC(hwnd); 65 66 BeginPaint(hwnd, &ps); 67 FillRect(hdc, &ps.rcPaint, (HBRUSH)GetStockObject(BLACK_BRUSH)); 68 EndPaint(hwnd, &ps); 69 } 70 break; 71 72 case WM_DESTROY: 73 PostQuitMessage(0); 74 return 0; 75 } 76 77 return DefWindowProc(hwnd, uMsg, wParam, lParam); 78}

C

1//definition of the other thread 2#ifndef UNICODE 3#define UNICODE 4#endif 5 6#include <windows.h> 7#include "structure.cpp" 8 9LRESULT CALLBACK cWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { 10 switch (uMsg) { 11 case WM_CREATE: 12 break; 13 14 case WM_PAINT: 15 { 16 PAINTSTRUCT ps = {}; 17 HDC hdc = GetDC(hwnd); 18 19 BeginPaint(hwnd, &ps); 20 FillRect(hdc, &ps.rcPaint, (HBRUSH)GetStockObject(GRAY_BRUSH)); 21 EndPaint(hwnd, &ps); 22 } 23 break; 24 25 case WM_DESTROY: 26 PostQuitMessage(0); 27 return 0; 28 } 29 30 return DefWindowProc(hwnd, uMsg, wParam, lParam); 31} 32 33DWORD WINAPI cwthread(LPVOID *lpvoid) { 34 PassHandle *pd; 35 pd = (PassHandle*)lpvoid; 36 HWND hwnd = pd->hwnd; 37 HINSTANCE hInst = pd->hInst; 38 39 WNDCLASSEX wc = { 40 sizeof(WNDCLASSEX),CS_VREDRAW | CS_HREDRAW, cWindowProc, 41 0, 0, hInst, 42 NULL, NULL,(HBRUSH)GetStockObject(WHITE_BRUSH), 43 NULL, CHILD_CLASS_NAME, NULL 44 }; 45 46 RegisterClassEx(&wc); 47 48 HWND chwnd = CreateWindowEx( 49 0, CHILD_CLASS_NAME, L"Child", WS_CHILD | WS_OVERLAPPEDWINDOW, 50 5, 5, 960, 540, 51 hwnd, 0, hInst, 0 52 ); 53 54 ShowWindow(chwnd, SW_SHOW); 55 UpdateWindow(chwnd); 56 57 return 0; 58}

補足情報(FW/ツールのバージョンなど)

Windows10
VisualStudio2017 Community

よろしくお願いします.

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

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

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

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

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

t_obara

2018/11/08 08:47

方針を問うのであれば、やりたいことをご提示することをおすすめします。なぜ別スレッドで子ウィンドウを作成しようとしたか、その理由が方針可否の判断理由になります。
Weapon

2018/11/08 09:18

反応遅れました.方針についての質問があるときはまた別に質問させていただきます.そのときはよろしくお願いします.
guest

回答2

0

ベストアンサー

GUI アプリケーションをマルチスレッド化する意義とは GUI の応答を担保するためにブロック処理(スレッドの実行を中段する処理)をバックグラウンドで動作させることにあります。そのため、同一の GUI に所属するウィンドウをマルチスレッドで動作させる意義はほとんどありません。
例外として OpenGL 等のようにレンダリングを担当するコンポーネントをマルチスレッドで動作させることはままあります。

上記を踏まえても回答のほとんどは Chironian さんが述べられている通りです。回答されていない部分を補足します。

1)別スレッドで子ウィンドウを作成してもいいものなのでしょうか?

可能です。異なるスレッド間で親子関係のあるウィンドウを生成することができます。

ただし、子ウィンドウが所属するスレッドは子ウィンドウが破棄されるまで持続する必要があります。そのため親ウィンドウの WM_NCDESTROY メッセージで子ウィンドウが所属するスレッドを破棄したりします。親ウィンドウの WM_DESTROY が終了した時点で子ウィンドウは破棄されているため、破棄タイミングを調整するのに WM_NCDESTROY を使用することになります。

親ウィンドウの所属するスレッドと子ウィンドウの所属するスレッドでフォーカスの授受がうまいこと行かないパターンがあるので、AttachThreadInput でスレッド間のメッセージキューの同期をとる必要があります。この制御を完璧にやろうとするとけっこうめんどうくさいです。
気のせいでした。AttachThreadInput が必要になるのはトップレベルウィンドウが異なる場合のみです。別スレッドの子ウィンドウには問題なくフォーカス移動できました。

他にアクセラレーターを処理する際、子ウィンドウの所属するスレッドからメインスレッド側に伝達する必要があったりします。子ウィンドウ側でアクセラレーターを処理する場合はその逆の処理が必要です。

なお、本題からはやや外れますがマルチスレッド(マルチプロセス)な ActiveX コントロール(正確には OLE オブジェクト)はアクセラレーターやフォーカスを処理するための手段を提供しています。

投稿2018/11/05 18:01

編集2018/11/06 17:10
atata0319

総合スコア881

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

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

0

こんにちは。

1)別スレッドで子ウィンドウを作成してもいいものなのでしょうか?

たぶんできると思いますが、ちょっと自信がありません。複数のスレッドでそれぞれがウィンドウを作ることは可能です。異なるスレッドで生成したウィンドウの間に親子関係をもたせることができるかどうかについてはできたような気もしますが、あまり自信がありません。

2)別のスレッドではメインのメッセージループを取得できるものなのでしょうか?

できません。そのスレッドでメッセージループを回す必要があります。

3)別のスレッドではそのスレッドのメッセージループをかけると子ウィンドウとして安全ではなくなりますよね?どのような手段をとればいいですか?

そんなことはないです。といいますか、Windowsのウィンドウはそれを生成した「スレッド」に属します。
そして、そのウィンドウにメッセージを分配するのは、作成したスレッドのメッセージループ(ポンプ)です。

なお、.NETのGUIは全く事情が異なります。.NETフレームワークはGUIスレッドのみがGUIをアクセスすることを前提に設計されているようです。なのでサブスレッドが.NETのGUIを触るのは危険です。.NETのGUIデータはWindowsのカーネルモードを通さずに、ユーザモードで直接アクセスするものもあるからだろうと思います。

投稿2018/11/05 17:29

編集2018/11/05 17:34
Chironian

総合スコア23272

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問