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

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

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

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

C++

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

Q&A

解決済

4回答

2588閲覧

グローバル変数やstaticを使わずにウィンドウプロシージャからユーザー定義のクラスなどを利用する方法

tae256

総合スコア58

Win32 API

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

C++

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

0グッド

0クリップ

投稿2021/02/16 01:50

編集2021/02/16 05:59

ご閲覧ありがとうございます。

前提・実現したいこと

Win32APIとC++でソフトウェアを作ろうとしています。
今はグローバル変数やstatic修飾子を使わずに設計できないかを試しています。

発生している問題・疑問

グローバル変数やstatic修飾子を一切使わない場合、ウィンドウプロシージャからはC++やwin32API標準の識別子以外不可視であると思います。

試したこと

■ウィンドウプロシージャに引数を追加する
WNDCLASS構造体が必要とするウィンドウプロシージャの型WNDPROCと型違いになる。

■ウィンドウプロシージャそのものを何らかのクラスのメソッドにする(例えばMyWindowクラスを定義してそのメソッドとする)
static修飾子を付加しない場合WNDCLASS構造体にウィンドウプロシージャのメソッド名を代入する部分でC3867非標準の構文となりました。
仕組み的にもシステムが使うであろうウィンドウプロシージャがインスタンスありきでは不都合に思えます。
staticを付記すれば動作しますが、これしか方法はないのでしょうか。

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

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

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

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

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

y_waiwai

2021/02/16 02:20

質問は編集できます 追記修正しましょう
tae256

2021/02/16 02:33

大変失礼しました。 間違って投稿してしまいました。 ご丁寧にありがとうございます。
guest

回答4

0

※質問は閉じてしまいましたが、サンプルコードを書いていたので、せっかくなので放流させていただきます。

様々な方法があると思いますが、SetWindowLongPtrGetWindowLongPtrを利用し、インデックスGWLP_USERDATAの位置にウィンドウ固有のユーザーデータ(へのポインタ)を格納して取り扱う例です。ここに収め、必要なときに参照することでウィンドウが生きている間、永続的に利用することができます。つまり、グローバル変数の代わりとして使えます。尚、GWL_USERDATAではくGWLP_USERDATAを使うことで32ビット、64ビットどちらのビルドでも対応できます。(GWL_USERDATAは32ビットのみ)

以下のサンプルでは独自クラスUserClassのインスタンスへのポインタを収め、WM_PAINTでのウィンドウ描画時にメンバー関数Incrementで値を更新し、画面に表示します。このコード上ではC言語/C++でのグローバル変数は使っていません。簡単なコードですので、ビルドして試してみてください。ウィンドウのサイズを変えると都度、WM_PAINTが発生し、再描画されるはずです。

C++

1#include <Windows.h> 2 3#define WINDOWCLASSNAME "MyWindowClass" 4 5class UserClass { 6public: 7 UserClass() 8 : v_(0) { 9 } 10 11 int Increment() { 12 return ++v_; 13 } 14 15private: 16 int v_; 17}; 18 19LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); 20 21int APIENTRY wWinMain(_In_ HINSTANCE hInstance, 22 _In_opt_ HINSTANCE hPrevInstance, 23 _In_ LPWSTR lpCmdLine, 24 _In_ int nCmdShow) 25{ 26 UNREFERENCED_PARAMETER(hPrevInstance); 27 UNREFERENCED_PARAMETER(lpCmdLine); 28 29 WNDCLASSEXW wcex; 30 wcex.cbSize = sizeof(WNDCLASSEX); 31 wcex.style = CS_HREDRAW | CS_VREDRAW; 32 wcex.lpfnWndProc = WndProc; 33 wcex.cbClsExtra = 0; 34 wcex.cbWndExtra = 0; 35 wcex.hInstance = hInstance; 36 wcex.hIcon = NULL; 37 wcex.hCursor = LoadCursor(NULL, IDC_ARROW); 38 wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); 39 wcex.lpszMenuName = NULL; 40 wcex.lpszClassName = TEXT(WINDOWCLASSNAME); 41 wcex.hIconSm = NULL; 42 43 RegisterClassExW(&wcex); 44 45 HWND hWnd = CreateWindowW(TEXT(WINDOWCLASSNAME), L"Sample", WS_OVERLAPPEDWINDOW, 46 0, 0, 320, 240, NULL, NULL, hInstance, NULL); 47 48 if (!hWnd) 49 { 50 return FALSE; 51 } 52 53 UserClass* p = new UserClass(); 54 LONG_PTR ret = SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)p); 55 56 ShowWindow(hWnd, nCmdShow); 57 UpdateWindow(hWnd); 58 59 MSG msg; 60 61 while (GetMessage(&msg, NULL, 0, 0)) 62 { 63 TranslateMessage(&msg); 64 DispatchMessage(&msg); 65 } 66 67 return (int)msg.wParam; 68} 69 70LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 71{ 72 switch (message) { 73 case WM_PAINT: 74 { 75 PAINTSTRUCT ps; 76 WCHAR s[256]; 77 HDC hdc = BeginPaint(hWnd, &ps); 78 LONG_PTR p = GetWindowLongPtr(hWnd, GWLP_USERDATA); 79 if (p != NULL) { 80 UserClass* up = (UserClass*)(p); 81 int v = up->Increment(); 82 wsprintfW(s, L"v=%d", v); 83 } 84 else { 85 lstrcpyW(s, L"NONE"); 86 } 87 DrawTextW(hdc, s, -1, &ps.rcPaint, DT_LEFT); 88 EndPaint(hWnd, &ps); 89 } 90 break; 91 92 case WM_DESTROY: 93 { 94 LONG_PTR p = GetWindowLongPtr(hWnd, GWLP_USERDATA); 95 if (p != NULL) { 96 UserClass* up = (UserClass*)p; 97 delete up; 98 } 99 PostQuitMessage(0); 100 } 101 break; 102 103 default: 104 return DefWindowProc(hWnd, message, wParam, lParam); 105 } 106 107 return 0; 108}

投稿2021/02/16 07:03

編集2021/02/16 07:17
dodox86

総合スコア9183

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

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

dodox86

2021/02/16 07:08

サンプルコード中、ポインタのキャスト部分は少々雑だったかもしれません。製品に使うようなコードではC++での厳格なキャストを使うことを検討して良いと思います。
fana

2021/02/16 07:28

> Remarks > Certain window data is cached, so changes you make using SetWindowLongPtr will not take effect until you call the SetWindowPos function. という話がちょっとだけ気になりますね. GWLP_USERDATA が "Certain window data" に当てはまるのかどうか不明ですが.
dodox86

2021/02/16 07:36

SetWindowPos APIで影響せしめる部分は何となくウィンドウスタイル、GWL_EXSTYLEの部分でありそうですが、明言している訳でもなさそうで、確かに不明、ではありますね。
fana

2021/02/16 07:43

SetWindowPos側の説明: > If you have changed certain window data using SetWindowLong, you must call SetWindowPos for the changes to take effect. Use the following combination for uFlags: SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED. の,SWP_FRAMECHANGED の存在あたりから,スタイルあたりの話だろうとは推定できるのですが,こっちでも "certain window data" という記述で,何故か明言はしてくれてはいない様子.
fana

2021/02/16 07:46

それはそれとして,単品のポインタを扱うならば, SetProp/GetProp よりもこちらの方が(文字列をキーにして探してくるような処理がなさそうなので)良さそうですね.
tae256

2021/02/19 06:34

ご回答いただきありがとうございます! 気づかずにすみませんでした。 様々な方法があるのですね…! ご紹介いただいた関数はとても便利な方法ですね、早速試してみたいと思います。
fana

2021/02/19 07:41

> SetProp/GetProp よりもこちらの方が ...(略)... 良さそう という理由から,高評価を押しました. (質問者様におかれましても,試された結果としてこちらの方が良い感じであったならばベストアンサーの付け替えを成されるとよろしいかと)
guest

0

ベストアンサー

ウィンドウハンドルと何かを紐づける手段 として SetProp というAPIがあります.
これを使って何かしらのポインタ値を登録しておき,ウィンドウプロシージャ内で GetProp で取り出して使うという方法があります.

(ポインタ値を登録するのは,「HANDLE型のサイズ≧ポインタのサイズ」を暗黙的に前提としている形になるので,その点に微妙さはあるかも?)

投稿2021/02/16 02:47

fana

総合スコア11708

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

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

fana

2021/02/16 02:50

HANDLE型に何かしらの仕様的な保証みたいなのがあるのかどうかは知らないけども, HANDLE型の具体実装が void* であるうちはとりあえず大丈夫だろう,的な…
fana

2021/02/16 02:56

CreateWindow()の引数由来で WM_CREATE のパラメタから解決する手もあったと思うが, それだと,その値をウィンドウプロシージャが static 変数に覚えとく形になるだろうから,本件ではアウトなのかも(staticはダメ,ということなので).
tae256

2021/02/16 03:00

ご回答ありがとうございます。 その様な方法があるとは知りませんでした! 調べてやってみます、ありがとうございます。
tae256

2021/02/16 03:03

WM_CREATEのパラメタの件もはじめて知りました。こちらも別のプロジェクトで試してみます、ご丁寧にありがとうございます。
fana

2021/02/16 03:10

staticメンバを使っていいならば, > ウィンドウプロシージャそのものを何らかのクラスのメソッドにする のクラスのstaticメンバで ウィンドウハンドル→インスタンス を解決すればいい.
tae256

2021/02/16 05:57

重ね重ねありがとうございます! 疑問の解決とやりたい事はできました。 やっていて思ったのですが、staticやグローバル変数の無暗な多用が戒められるのであって、全く使わないというのも却って無駄な実装になりそうですね…。 「こうすればいいコード」という魔法のような答えはなくて、個々の事案についてちゃんと考える必要がある感じですしょうか…奥が深いです。
fana

2021/02/16 06:21

クラスのprivateなstaticメンバとかは,まさに本件のような場面で使うと良いものだと思います.
guest

0

https://sourceforge.net/projects/wtl/
WTLとかを参考にすると良いかも。

投稿2021/02/16 02:21

t_obara

総合スコア5488

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

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

tae256

2021/02/16 02:45

ご回答ありがとうございます。 全く知りませんでした、参考にしてみます。
guest

0

可能です。すべての変数を関数内で定義すればいい。

投稿2021/02/16 01:55

episteme

総合スコア16614

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

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

tae256

2021/02/16 02:46

ご回答ありがとうございます。 なるほど…。 おかげさまで自分の疑問がより具体的に言語化できます、ありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.46%

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

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

質問する

関連した質問