WINXP .NET MFCです
自作のアプリA、Bを同時に立ち上げておいて、AからBを操作したいのですが、方法が分かりません。
Aを前面に出しておいて、Aのボタンを押した時に、Bの側でも、IDC_BUTTON1 がクリックされOnBnClickedButton1()にコーディングされた処理が行われたり、Aで入力されている文字列をBに送ったりしたいのですが、可能でしょうか。
いろいろ調べてみたのですが、検索するにしても、何をキーワードにしたらヒットするのか見当がつきません。
SendMessageやcommandを使うらしいということは分かったのですが、実際にどのようにコーディングしたらいいのか分かりません。
ご指導をお願いいたします。
> Aのボタンを押した時に、Bの側でも、IDC_BUTTON1 がクリックされ
> OnBnClickedButton1()にコーディングされた処理が行われたり、
AとBとの間で送信するコマンドIDが一致しているか、そのボタンの
ハンドルが分かっていればSendMessageなりPostMessageなりで対処
できるでしょう。
> Aで入力されている文字列をBに送ったりしたい
プロセス間で文字列を受け渡すには、ATOMを使うと良いでしょう。
メッセージを文字列で渡すって方法もありますね。
場合によってはsocket使ったほうがツブシが効くんじゃないかと。
socketならマシンをまたいで通信できるしマルチ・クライアントできるし。
自分も名前付きパイプとかソケットがいいと思います。
ありがとうございます。
「ツブシがきく」ほうがいいのですが、socket自体が分かっていません。
このサイトの過去ログやネットでくぐってみたのですが、糸口も見つかりません。
初心者向きのサイトをご紹介いただくか、簡単であればコードを提示していただくことはできませんでしょうか。
あるいは、大きな書店で、1冊参考書を買って勉強するような大きな「ことがら」なのでしょうか。
また、SendMessage/PostMessageもあまり使ったことがないのですが、
ひとつのアプリケーションの中では、
GetParent()->SendMessage(WM_COMMAND, IDC_BUTTON1);
で、できると思うのです。
しかし、他のアプリケーションのボタンを操作する場合は、どうしたらよいのでしょうか。
GetParent()->の部分が変わってくると思うのですが、どのように別なアプリを指し示したらいいのか分かりません。
ATOMも分かっていないのですが、正直をいいまして、今、そこまで頭が回っていません。
初心者で恥ずかしいのですが、アドバイスを頂けませんでしょうか。
> 他のアプリケーションのボタンを操作する場合は、どうしたらよいのでしょうか。
EnumWindows(), EnumChildWindows() あたりを調べてみてください。
# socket(TCP/IP)は一度習得しておけば何かと便利です。本屋にごぉです。
> GetParent()->SendMessage(WM_COMMAND, IDC_BUTTON1);
> で、できると思うのです。
> しかし、他のアプリケーションのボタンを操作する場合は、どうしたらよいのでしょうか。
同じことです。他のアプリケーションだろが何だろが、WindowHandleさえわかっていればSendMessageできます。
裏返せば、SendMessage"される側"は"する側"に対し「私のWindowHandleはコレです」と"何らかの手段で"教えなくちゃあきません。
ファイルでもいいしbroadcast-SendMessageでもいいし共有メモリでもいいし、いろんなテがあります。
ありがとうございます。
socketについては、八重洲ブックセンターに行ってみます。
ただ、気長な話になってしまうので、とりあえずSendMessageを選択します。
問題は、そもそもWindowHandle が分かっていません。
GetDlgItemで、いわいるハンドルは取得できます。
WindowHandle=ハンドルでしょうか。
だとすると、その場合、
GetParent()->SendMessage(WM_COMMAND, IDC_BUTTON1);
の、IDC_BUTTON1を単純にハンドルに置き換えればいいのでしょうか。
MSDNを見ると、SendMessageの引数は3つになっていますが、それとの関係もよく分かりません。
SendMessageで文字列をやりとりすることは、できるのでしょうか。
ご指導をお願いいたします。
> GetDlgItemで、いわいるハンドルは取得できます。
> WindowHandle=ハンドルでしょうか。
「いわゆるハンドル」にもいろいろな種類があり、
その中でWindow に関するものがWindowHandle(型はHWND)です。
> の、IDC_BUTTON1を単純にハンドルに置き換えればいいのでしょうか。
否。
置き換えるべきは、この例ではGetParent()部分です。
# MFC に固執せず、直接 API の SendMessage を呼んだ方が楽だとは思います。
> MSDNを見ると、SendMessageの引数は3つになっていますが、それとの関係もよく分かりません。
API では、HWND も含めて引数は 4つです。
MFC では、HWND はクラスで覆い隠されているため、明示的に指定するのは3つまでです。
> SendMessageで文字列をやりとりすることは、できるのでしょうか。
WM_COPYDATA を使って可能です。
> 簡単であればコードを提示していただくことはできませんでしょうか。
MFCの助けを借りていいなら比較的簡単。
最も単純な例を示します:
--- server.cpp ---
--- cl -MD -D_AFXDLL -EHsc -DWINVER=0x0502 server.cpp
#include <afxsock.h>
#include <string.h>
#include <stdio.h>
int main() {
AfxSocketInit();
CSocket listenSocket;
listenSocket.Create(4444); // port-no.
listenSocket.Listen();
CSocket socket;
listenSocket.Accept(socket);
char buffer[64];
int len = socket.Receive(buffer,64);
buffer[len] = '\0';
printf("You say: [%s]\n", buffer);
strcpy(buffer, "さようなら");
printf("I say: [%s]\n", buffer);
socket.Send(buffer, strlen(buffer));
socket.Close();
listenSocket.Close();
return 0;
}
--- client.cpp ---
--- cl -MD -D_AFXDLL -EHsc -DWINVER=0x0502 client.cpp
#include <afxsock.h>
#include <string.h>
#include <stdio.h>
int main() {
AfxSocketInit();
CSocket socket;
socket.Create();
socket.Connect("localhost", 4444); // マシン内で接続するので"localhost"
char buffer[64];
strcpy(buffer, "こんにちは");
printf("I say: [%s]\n", buffer);
socket.Send(buffer, strlen(buffer));
int len = socket.Receive(buffer,64);
buffer[len] = '\0';
printf("You say: [%s]\n", buffer);
socket.Close();
return 0;
}
Banさん、ありがとうございます。
> 置き換えるべきは、この例ではGetParent()部分です。
> # MFC に固執せず、直接 API の SendMessage を呼んだ方が楽だとは思います。
申し訳ありません。
具体的に、GetParent()をどのように書き換えたらいいのでしょうか。
別アプリに用があるのであって、親を呼んでも意味はないというのは理解します。
ただ、初心者で、アロー演算子自体、よく分かっていません。
またAPI の SendMessage を呼ぶ場合、引数をどのように記述したらいいのかも分かりません。
分からないことばかりで、申し訳ありませんが、お願いいたします。
επιστημηさん、ありがとうございます。
MFCしか分からないので、それにあわせてフォームビューでアプリを作ってみたのですが、うまくいきません。
サンプルは、severアプリとclientアプリ相互でデータのやり取りをしていると思うので、送りと受けだけにしました。
(SendとReceiveをワンセットにする必要があるのかもと思いましたが、試してみると結果は同じでした)
インクルードは、いずれもview.cppの頭に書きました。
両方ともボタンをつくり、その中にコーディングしてみました。
severアプリ
void CseverView::OnBnClickedButton1()
{
AfxSocketInit();
CSocket listenSocket;
listenSocket.Create(4444); // port-no.
listenSocket.Listen();
CSocket socket;
listenSocket.Accept(socket);
char buffer[64];
strcpy(buffer, "さようなら");
printf("I say: [%s]\n", buffer);
socket.Send(buffer, strlen(buffer));
socket.Close();
listenSocket.Close();
}
これでボタンをクリックするとアプリが固まります。
また、リビルドする時、socket.Sendの行について、
'引数' : 'size_t' から 'int' に変換しました。データが失われているかもしれません。
という警告が出ます。
clientアプリ
(エディットボックスのメンバ変数valueをm_edit1にしました)
void CclientView::OnBnClickedButton1()
{
AfxSocketInit();
CSocket socket;
socket.Create();
socket.Connect("localhost", 4444); // マシン内で接続するので"localhost"
char buffer[64];
int len = socket.Receive(buffer,64);
buffer[len] = '\0';
m_edit1 = buffer;
UpdateData(false);
socket.Close();
}
実行すると、
Run-Time Check Failure #2 - Stack around the variable 'buffer' was corrupted.
というエラーになります。
アプリを両方立ち上げていても同じです。
以下、疑問点を、箇条書きします。
1.
--- server.cpp ---
--- cl -MD -D_AFXDLL -EHsc -DWINVER=0x0502 server.cpp(およびclient.cpp)
は、必要なのでしょうか。
リビルドでエラーにはなりませんが、書いても実行時に固まります。
2.通信する場合、受け手は待ちうけ状態になっている必要があるかと思いました。
で、PreTranslateMessageをハンドルしてみましたが、同じ結果になります。
if(pMsg->message == *****) {}
が必要かとも思いましたが、の*****の部分が分かりません。
双方ともOnBnClickedButtonでよろしいのでしょうか。
3.ふたつのアプリを同じフォルダに入れておく必要はありますでしょうか。
申し訳ありませんが、お願いいたします。
ぁぅぅぅ orz
僕が示したのはMFC-Socketのキモを掴むためのコンソール・アプリケーション。
「使ってください」ってシロモノではありませんし、
ましてや現コードに貼り付けるだけで動き出すよなものでは断じてありません。
GUIの中で使うとなればスレッドを起こすことになるか、
さもなくば非同期メッセージ駆動の CAsyncSocket で。
> --- server.cpp ---
> --- cl -MD -D_AFXDLL -EHsc -DWINVER=0x0502 server.cpp
> は、必要なのでしょうか。
ただのメモ書き。コードの一部ではありません。
"ファイル名"と"コマンドラインからのコンパイル法"です。
> 具体的に、GetParent()をどのように書き換えたらいいのでしょうか。
> またAPI の SendMessage を呼ぶ場合、引数をどのように記述したらいいのかも分かりません。
::SendMessage(
HWND hWnd, // 送信先ウィンドウのハンドル
UINT Msg, // メッセージ
WPARAM wParam, // メッセージの最初のパラメータ
LPARAM lParam // メッセージの 2 番目のパラメータ
);
投げる側は受ける側のハンドル(↑の第一引数)を知らないといけません。
メッセージはRegisterWindowMesage-APIで(他では使われていないユニークな)メッセージIDを取得するとよいでしょう。
このメッセージを受ける側は、メッセージ・マップに ON_REGISTERD_MESSAGE マクロを追加し、メッセージ・ハンドラを定義することになります。
> ただ、初心者で、アロー演算子自体、よく分かっていません。
そんな状態でこの問題に挑むのはどーかとも思う。
とどのつまり"おんぶにだっこ"になるんじゃないか?
> MFCしか分からないので、それにあわせてフォームビューでアプリを作ってみたのですが、うまくいきません。
「MFC さえ分かってないので」が正しいんじゃないかな。
Windows プログラミングにしろ、ソケットプログラミングにしろ、MFC は「はじめてのプログラミング」には向きませんよ。
まず Win32 API でできるようになってからだと思う。
> ただ、初心者で、アロー演算子自体、よく分かっていません。
APIを使う例だと(MFCを使っていてもこれは使えますが)、
特にアロー演算子を使う必要もありません。
C/C++ 言語レベルのことですので、MFC や Win32API 以前であり、
「自作の他のアプリの操作」とも別問題です。
言語の理解は前提として大事なので、よくわかるまでがんばってください。
# 人間相手に自然言語で話すなら片言でもなんとかなるかもしれませんが、
# コンピュータ相手に片言で命令すると、実は意図しない危険な
# 命令だったり....(特にC/C++では)。
> またAPI の SendMessage を呼ぶ場合、引数をどのように記述したらいいのかも分かりません。
// ハンドル取得は EnumWindow 等でも可。
// 以下は FindWindow を使う例(目的のWindowが取れているかのチェックは省略
// API の SendMessage を使う。
HWND window = ::FindWindow( ...ここで探すWindowを指定... );
if(window != NULL)
{
// WM_FOO は適当なメッセージ、
// wparam,lparam も適当な変数(使わない場合でも明示的に0を指定
LRESULT result = ::SendMessage(window, WM_FOO, wparam, lparam);
if(result ...ここはWM_FOO次第でエラーチェックなど...
}
// MFC の場合
CWnd* window = CWnd::FindWindow( ...ここで探すWindowを指定... );
if(window != NULL)
{
// MFCの場合、不要ならwparam,lparamを省略可(暗黙で0が指定される)
LRESULT result = window->SendMessage(WM_FOO, wparam, param);
if(result ...ここはWM_FOO次第でエラーチェックなど...
}
HWND_BROADCAST と RegisterWindowMessage と SendMesage を使って
ざくざくっと書いてみた(送り側/受け側ともにMFC-dialogアプリ:VC++7.1)。
つつがなく動いてくれやがってます。
んー、確かにビギナには辛いだろぉねぇ…
Banさん、ありがとうございます。
まだ分かっていません。
とりあえず、受け側として、ボタンを押すとエディットボックスに文字列が表示される単純なアプリを作り、
このボタンを送り側のアプリから押すことにしました。
ハンドルは、とりあえず問題を簡単にするため、ファイルに書き込み/読み出しします。
あれこれ目移りしていると、「ぐちゃぐちゃ」になってしまうので、MFCに絞ってやってみます。
CWnd* window = CWnd::FindWindow( ...ここで探すWindowを指定... );
この引数について、MSDNを見ると、
HWND FindWindow(
LPCTSTR lpClassName, // クラス名
LPCTSTR lpWindowName // ウィンドウ名
);
となっており、FindWindow(_T("MyNewClass"),NULL))という例がありました。
この"MyNewClass"にあたる名前を、まず受け手のアプリが起動する時に、なんらかの関数で指定する必要があるのではないでしょうか。
もし必要として、CreateMutexでアプリに名前をつけてやれば、それでよろしいのでしょうか。
ただ、この名前はアプリの名前で、クラスまで特定している訳ではないと思いました。
WM_FOOについても分かりません。
ひとつのアプリの中でボタンをクリックするのと同じように、SendMessage(WM_COMMAND, IDC_BUTTON1)でいいのでしょうか。
この場合、今回たびたび指摘されたIDC_BUTTON1のハンドルが登場しません。。。
なんだかすべてが手探りで、自分でも頭の悪いと思うのですが、なんとか解決したく、ご指導をお願いいたします。
僕が書いたサンプルコードでよければ差し上げます。メールくだせぃ。
# επιστημη さんのコードで解決かもしれませんが....。
> あれこれ目移りしていると、「ぐちゃぐちゃ」になってしまうので、MFCに絞ってやってみます。
正直な感想として、絞るところを間違えていると思います。
シャノン さんにも既に書かれていますが、まさに彼のいうとおりです。
MFCは、C++用のライブラリです。前提としてC++の知識が必要です。
MFCはまた、Win32の薄いラッパです。前提として、Win32の知識が必要です。
MFCで、スケルトン以上のちょっと凝ったことをやるには、
必ず両者の知識が必要とされることになります。
MFCってのは、基本的に「分かってる人」がちょっと楽をするためのものであって、
初心者が簡単に、安全に使えるものでは、実はないと思ってます。
> この"MyNewClass"にあたる名前を、まず受け手のアプリが起動する時に、
> なんらかの関数で指定する必要があるのではないでしょうか。
FindWindow を使うなら当然そうなります。
A から「Bに対して」何かをするには、まず B を特定する必要があるということです。
# (Bを含む)全てのWindowとかいうならまた話は別ですが。(broadcast)
> もし必要として、CreateMutexでアプリに名前をつけてやれば、それでよろしいのでしょうか。
駄目です。
Mutexはまったく関係がありません。
> ただ、この名前はアプリの名前で、クラスまで特定している訳ではないと思いました。
これも違います。
CreateMutexにつける名前は Mutexの名前であって、アプリの名などではありません。もちろん、クラスの名前でもありません。あくまで、単なるMutexの名前です。
また、FindWindow が要求する「クラス」と、C++のclassとは別物です。
# MFCで命名するような CFooBarDialog 等のことではありません。
Win32で使われる WNDCLASS の名前です。(RegisterClassEx API等で登録するもの)
通常、MFCではこの名前を意識しませんが、クラス名からWindowを検索する場合、
B で AfxRegisterClass等を用いて明示的に指定するのが一般的です。
いずれにしても、Win32/MFCの基礎を理解しないと、応用は難しいと思います。
# ちなみに、ウィンドウ名というのは、ウィンドウの上部にタイトルとして
# 表示されている文字列をさします。
# こちらから B を Find する方が簡単かも知れませんが、同名のWindowが
# 存在すると特定するには別の手段が必要になります。
> WM_FOOについても分かりません。
-- snip --
> ひとつのアプリの中でボタンをクリックするのと同じように、
> SendMessage(WM_COMMAND, IDC_BUTTON1)でいいのでしょうか。
よいと思いますが。
WM_COMMANDはWin32で定義されてますから、AでもBでも共通ですし、
引数さえ妥当なら B に正しく通じるでしょう。
A、B ともに自分の制御下にあるのなら、アプリ用の適当な独自 ID でも動作しますが、
他のアプリのことを考えたら、システムで登録済みの物を使うか、
もしくは επιστημηさんが書かれているように ID は登録しておくのが無難でしょう。
> この場合、今回たびたび指摘されたIDC_BUTTON1のハンドルが登場しません。。。
勘違いしているようですが、IDC_BUTTON1というボタンが押されたかのように
B に動作させる場合、
IDC_BUTTON1という名前のボタンのWindowHandleは必ずしも不要です。
WindowHandleが必要なのは、通常そのボタンの親(おそらくBのダイアログ?)の方です。
> なんだかすべてが手探りで、自分でも頭の悪いと思うのですが、
> なんとか解決したく、ご指導をお願いいたします。
頭のよしあしというより、手の抜きすぎでは?
地道や学習などの段取りを飛ばしすぎなだけではないでしょうか。
分かり易い例えかは自信がありませんが、
「ドイツ語(C++)をろくに話せず、法律(Win32)もよく知らない初心者が、
ドイツ語で書かれた法律書(MFC)を持ってきて、
これで裁判に勝つ方法を教えてくれと、ドイツ法のわかる弁護士に相談する」
どう思いますか。
少なくとも私は、「これをそのまま読み上げて」的なサンプルを書く気はありませんし、
といって、必要な知識は掲示板で説明して上げられる分量ではない、という...。
初心者でよく分からないのであれば、まずそれが理解できるくらいには
脱初心者する必要があるでしょうし、体系的に基礎を抑えるのが大事ではないかと思います。
# 説教くさい上に長っ...orz
ちなみに僕のサンプルでは、FindWindowを使いませんでした。
制御される側がRegisterWindowMessageで得たユニークなメッセージを
HWND_BROADCAST に SendMessage し、制御する側が受け取る
ってカラクリです。
なので制御される側が制御する側より"あと"に起動することになります。
# 定期的にSendMessageすればどっちからでも構わんけど。
ありがとうございます。
一応ボタンクリックを送ることは成功したのですが、まだ「半落ち」状態です。
タイトルはSetWindowTextで設定しました。
ボタンのハンドルを、ファイルを介して送り側アブリで受け取り、
LRESULT result = ::SendMessage(ボタンのハンドル, BM_CLICK,0 ,0);
で、うごきまし
手拍子で送信してしまったので、最初から書き直します。
ありがとうございます。
一応ボタンクリックを送ることは成功したのですが、まだ「半落ち」状態です。
タイトルはSetWindowTextで設定しました。
ボタンのハンドルを、ファイルを介して送り側アブリで受け取り、
LRESULT result = ::SendMessage(ボタンのハンドル, BM_CLICK,0 ,0);
で動きました。
「半落ち」なのは、「受け側」「送り側」の順でふたつのアプリを立ち上げた場合、「送り側」が選択状態で、「受け側」は淡色表示になっています。
「送り側」の最初のボタンクリックで、「受け側」も選択状態になり、二度目のボタンクリックで、初めて「受け側」のボタンクリックの処理が行われます。
1回のボタンクリックで、「受け側」のボタンがクリックされた状態にすることは可能でしょうか。
あるいは、「受け側」のアプリが後ろに隠れたまま処理だけが行われるというようなことは可能でしょうか。
それとも、他のアプリの操作というのは、本来、まず「受け側」のアプリを選択状態にしてから、というものなのでしょうか。
お手数ですが、なんとか納得できるところまで解決したく、ご指導をお願いいたします。
"どうしても"送り側が受け側の"ボタンを押"さにゃならんのですか?
受け側が適当なハンドラを用意し、そいつがボタンを押したのと同じ動きをすればよくないですか?
# 受け側が修正可能なら、僕なら迷わずそーする。
ありがとうございます。
ネットをくぐって調べ、ユーザー定義メッセージを自作して、「受け側」の
関数を作り、「受け側」単体では動くことを確認しました。
問題はアプリ間のメッセージのやり取りなのですが、
1.ユーザー定義メッセージ(WM_MYMESSAGEとしました)のハンドルの取得の仕方が分かりません。
GetDlgItem(m_hWnd, WM_MYMESSAGE)では、0 になってしまいます。
2.くぐったサイトの中に、WM_USERではなく、WM_APPから始めないとだめだと書いてあるものがありました。
試したところ、
#define WM_MYMESSAGE WM_USER+100
でも、
#define WM_MYMESSAGE WM_APP+100
でも、かわりばえしなかったのですが、WM_USERでいいのでしょうか。
3.「送り側」のアプリで、単純に、
::SendMessage(window, WM_MYMESSAGE,0 ,0);
と書くと、
'WM_MYMESSAGE' : 定義されていない識別子です。
というエラーになってしまいます。
対処法がわかりません。
お手数ですが、ご指導をお願いいたします。
だーから RegisterWindowMessage-API でユニークなメッセージIDを取得するのが無難だし安全だってば。
# 送ったサンプル"まったく"読んでないとミタ。
んーむ…
> 1.ユーザー定義メッセージ(WM_MYMESSAGEとしました)のハンドルの取得の仕方が分かりません。
> GetDlgItem(m_hWnd, WM_MYMESSAGE)では、0 になってしまいます。
メッセージにハンドルなんかありません。
ハンドルにメッセージを送るのです。
> 3.「送り側」のアプリで、単純に、
> ::SendMessage(window, WM_MYMESSAGE,0 ,0);
> と書くと、
> 'WM_MYMESSAGE' : 定義されていない識別子です。
> というエラーになってしまいます。
#define してないんじゃねぇですか?
ありがとうございます。
> # 送ったサンプル"まったく"読んでないとミタ。
包み隠さず白状して、よく分からないのです。
送り側のアプリを立ち上げても、受け側のアプリだけが開いてしまいます。
コードを睨んでいると、最初は頭の中が真っ白でも、次第にわかってくる、ということがよくあるのですが。。。
> メッセージにハンドルなんかありません。
> ハンドルにメッセージを送るのです。
では、何のハンドルにメッセージを送るのでしょうか。
もし、OnMyMessage(WPARAM wParam, LPARAM lParam ) であれば、MFCでそのハンドルを取得するには、どうしたらいいのでしょうか。
> #define してないんじゃねぇですか?
はい、ご指摘の通りでした。
申し訳ありませんが、ご指導をお願いいたします。
> 送り側のアプリを立ち上げても、受け側のアプリだけが開いてしまいます。
…動作確認したんだけどなー。
送り側(Talker)立ち上げて、受け側(Listener)立ち上げて、
送り側のボタン押せば受け側が反応するけど。
> では、何のハンドルにメッセージを送るのでしょうか。
そのメッセージを受け取るWindow。
ありがとうございます。
επιστημηさんから頂いたアプリは、そっくり「ひき写し」で新たにコーディングしなおし、作動を確認しました。
ただ、まだ、どう応用したらいいのか、見えてきません。
いろいろやってみます。
>> では、何のハンドルにメッセージを送るのでしょうか。
> そのメッセージを受け取るWindow。
「メッセージを受け取るWindow」がわかりません。
今、試しているのは、エディットボックスに文字を送るコードですが、そのエディットボックスのハンドルではダメでした。
実際のコーディングでは複数のコントロールにかかわる処理がある訳ですから、特定のコントロールのハンドルということはないと思いました。
またアプリ自体かとも思い、
hWND window = ::FindWindow( ...... );
の、windowを置いてみましたが、やはりダメでした。
他に window が思い当たりません。
申し訳ありませんが、ご示唆頂けませんでしょうか。
> επιστημηさんから頂いたアプリは、そっくり「ひき写し」で新たにコーディングしなおし、作動を確認しました。
> ただ、まだ、どう応用したらいいのか、見えてきません。
…うーん、答えは"すべて"その中にありますよ。
応用するもなにも、"そのものヅバリ"なハズなんですが。
異なるのは"受け側"のWindowHandleを"送り側"に知らしめる手段だけ。
# 送り/受け共に100行そこそこだし、しかも僕が追加したのは10行かそこらなんやけどなー
> 包み隠さず白状して、よく分からないのです。
…だからね。
まず MFC 使うのやめましょうよ。
今つまづいている所は、メッセージパッシングの仕組みといって、Windows プログラムの中で最も根幹となるところです。
ここを非 MFC プログラムでしっかり習得しておかないと、また後で絶対につまづきます。
その時、こういう掲示板で今回みたいに長々と付き合ってくれる相手はいないと思ってくださいね。
あ、epi さんが送ったソースは非 MFC なのかな。
だったら…これ
http://www.kumei.ne.jp/c_lang/index_sdk.html
を、最初から第15章くらいまで勉強してみたほうがいい気がする。
それで「Windows プログラミングの作法」をつかんでください。
> あ、epi さんが送ったソースは非 MFC なのかな。
いんや、いっちゃんシンプルな MFC-dialog アプリ。
送りも受けも100行ちょい。
ON_REGISTERED_MESSAGE マクロさえわかってりゃどってことねぇ。
なんなら公開しましょか。
http://www005.upp.so-net.ne.jp/episteme/Talker_Listener.zip
# 期間限定ほんの数日で撤収します。
↑初心者28号さんのに差し上げたやつをちょっとだけいじってあります。
送り/受け、どちらから立ち上げてもだいぢょぶになりました。
> お手数ですが、なんとか納得できるところまで解決したく、ご指導をお願いいたします。
きちんと納得したいなら、Windowsのメッセージ処理を勉強してください。
> 2.くぐったサイトの中に、WM_USERではなく、WM_APPから始めないと
> だめだと書いてあるものがありました。
その通りです。おそらく理由も書いてあったのでは?
意味を理解したいなら、Windowsのメッセージ処理を(以下略
> でも、かわりばえしなかったのですが、WM_USERでいいのでしょうか。
「やってみて代わり映えしない=OK」という考え方は改めた方がいいと思います。
「今のところたまたま動く」ということは、往々にしてあるものです。
> もし、OnMyMessage(WPARAM wParam, LPARAM lParam ) であれば、
> MFCでそのハンドルを取得するには、どうしたらいいのでしょうか。
Win32での主要なハンドル取得方法は既にこのスレに網羅されてます。
MFCを学んでいればWin32->MFCの応用は日常茶飯事だと思いますし、
私は、MFCの説明をしないことにしましたので、ご自身で応用してください。
> επιστημηさんから頂いたアプリは、そっくり「ひき写し」で
> 新たにコーディングしなおし、作動を確認しました。
> ただ、まだ、どう応用したらいいのか、見えてきません。
これがわかるくらいまで脱初心者しないと、「納得できるところまで解決」は
無理だと思います。
> 今、試しているのは、エディットボックスに文字を送るコードですが、
> そのエディットボックスのハンドルではダメでした。
どんなコードを書いたのかも知りませんが、エディットボックスが知らない
独自のメッセージをいきなり送りつけたのなら、当然無視されるだけでしょう。
もしくは、プロセス間で文字列をそのまま引数渡ししてるとか....。
> 実際のコーディングでは複数のコントロールにかかわる処理がある訳ですから、
> 特定のコントロールのハンドルということはないと思いました。
申し訳ありませんが、何を言わんとしているのか理解できませんでした。
何か錯誤に基づいている気はしますが、錯誤点も推測できませんでした。
# >επιστημη さん
# サンプル落とさせていただきました。
> # >επιστημη さん
> # サンプル落とさせていただきました。
ど、どぉでしょう? 決して難解なコードやとは思わんけども(ドチドチ
…なんでモタついてるか、わかったポ。
HWND_BROADCASTにせよFindWindowにせよ、その対象は親のいない最上位Windowであるな。
と・こ・ろ・が 受け側のハンドラをCViewに置いたとミタ。
CViewは最上位Windowじゃないから、いっくらメッセージ送っても受理されるワケがなーい。
僕のサンプル(CDialog-base)をCFormViewに"まんま"引き写したとすれば、これがもっとも有力。
私もシャノンさんの意見に賛成。
見ているとどうもC++言語も怪しいみたいだし。
(アロー演算子がどうこうと言う件)
Windowsのイベント駆動型の動作をきちんと理解しないと
表向き動く物が作れてもそれはコーディングパターンを覚えているだけで
理屈が理解できた事にはなりませんし、そのまま突き進むのは大変危険。
それこそハマリ込んで抜け出せなくなると思います。
Win32APIとC言語の世界できちんと基本的な仕組みを理解するところから
勉強しなおす事をお勧めします。
あと、MFCの使う前にC++言語の勉強も必要です。
MFCの勉強をしながらC++言語の勉強をしようと考えない事です。
混乱するだけで先に進むのが難しくなるだけです。
耳が痛いなー (^^
白状すれば僕はWin32APIほとんど知らないし、
CでWinアプリ書いたのはHelloWorldに毛が生えた程度のものっす。
MFCはWin32APIに被せた薄カワだし、MFCでWinアプリを書き出した頃
にはC++がわかってたから、MFCのソースを追いかけてWin32APIを勉強してます。
MFCからWin32APIが透けて見えるのね。
> CでWinアプリ書いたのはHelloWorldに毛が生えた程度のものっす。
初心者にはそれで十分でしょう。
メッセージループとウィンドウプロシージャを自力で書いてみないことにはどうにもなりませんて。
#俺が初めてそれらを書いたのは VB6 でしたがね。
#あれが脱 VB の契機だった。
> MFCからWin32APIが透けて見えるのね
それが善し悪しですね。
> 決して難解なコードやとは思わんけども(ドチドチ
HWND_BROADCAST や RegisterWindowMessage 使わないで、単純に定数決め打ちしたメッセージを送るだけでもいいんじゃないでしょうか?
ただし、MFC で FindWindow やろうとすると、クラス名の特定がまた難解ですからね…やっぱり最初は SDK でやるべきかと。
>> CでWinアプリ書いたのはHelloWorldに毛が生えた程度のものっす。
>
> 初心者にはそれで十分でしょう。
> メッセージループとウィンドウプロシージャを自力で書いてみないことにはどうにもなりませんて。
あー、うん。
とにかく「いっぺんやっとけ」と。
確かに、これやっとけばWindownのからくりとMFCのそれとの対応が付くわな。
>επιστημη さん
サンプル見ました。
どうせRegisterWindowMessageとHWND_BROADCASTを使うなら、
Listener側ではなく、Talker側でHWND_BROADCASTを使えば、
受け側を特定する必要すらないんじゃないかと思ったり
> Talker側でHWND_BROADCASTを使えば、
> 受け側を特定する必要すらないんじゃないかと思ったり
ぁぃ、僕もそう思ってる ^^;
いや、どうせ受理されることはないとわかってはいても、
そこにいる全員に意味もなく声かけるのはいかがなものかと遠慮した次第でして。
# ListenerがLRESULTを返すよなことがあればbroadcastできなくなるし。
それと、Listenerが「ココに投げて!」と申告するなら、
親のいない最上位Windowでなくてもメッセージがもらえる。
(CFrameWndでなく)CViewに受理させたいならコッチが便利かな、と。
たいへんありがとうございます。
まだ、解決していないのですが、一度中間報告をする必要があると思い、アップしました。
επιστημηさんから頂いたコードを元に勉強しています。
ダイアログベ−スをSDIに引き写そうとしたところ、
HWND_BROADCASTは最上位Windowでないと受け付けない、という問題にぶつかりました。
επιστημηさんから頂いたアドバイスで、MainFrmを使っているのですが、まだうまくいきません。
あと一歩だと思うので、もう少し「じたばた」します。
ここまで来たので、あとはなんとか自己解決したいのですが、最悪、煮詰まってしまった場合は、また質問するかもしれません。
いずれにしましても、数日中にまたアップします。
MainFrameなら最上位だからBROADCASTに反応できます。
CMainFrame::OnRegisteredMessage(WPARAM,LPARAM) から
CXXXDoc::なにか() を呼び、
CXXXDoc::なにか() から
UpdateAllViews(0) すれば
CXXXView::OnUpdate(...) に飛び込みます。
Document-Viewアーキテクチャに従うならそんな感じかしら。
ありがとうございます。
> CMainFrame::OnRegisteredMessage(WPARAM,LPARAM) から
> CXXXDoc::なにか() を呼び、
やはり、私が思ったほど単純ではないのたですね。
boolか
もちろんCMainFrame::OnRegisteredMessage()内に処理を直接記述
してもいいけど、それじゃなんのためにFrame/View/Docに分けたのやら。
なんだか、ばたばたっとキーボードを叩いていると、手拍子で、すぐ送信されてしまいます。
申し訳ありません。
> CMainFrame::OnRegisteredMessage(WPARAM,LPARAM) から
> CXXXDoc::なにか() を呼び、
やはり、私が思ったほど単純ではないのですね。
bool型の外部変数でフラグを立てて、viewのPreTranslateMessageで拾おうと思い、
「気の利かないやりかただなぁ」と自分で思っていたのですが、いろいろやってみて、結論としてはダメでした。
CXXXDoc::なにか() の「なにか」は、
OnNewDocument()でしょうか、シリアル化でしょうか。
両方とも試してみてダメでしたが、見落しがあるかもしれません。
また、「CXXXDoc::なにか() を呼び、」の 「呼び」のコードも分かりません。
他のクラスを簡単に「呼ぶ」ことはできるのでしょうか。
自己解決したかったのですが、無理だと思いました。
申し訳ありませんが、アドバイスいただけませんでしょうか。
> CXXXDoc::なにか() の「なにか」は、
> OnNewDocument()でしょうか、シリアル化でしょうか。
publicなメンバ関数をひとつ追加するだけ。
class CXXXDoc : public CDocument {
...
public:
void nanika();
};
> また、「CXXXDoc::なにか() を呼び、」の 「呼び」のコードも分かりません。
> 他のクラスを簡単に「呼ぶ」ことはできるのでしょうか。
呼べますともさ。CXXXDocのポインタ:doc さえ手に入れておけば:
LRESULT CMainFrame::OnRegisteredMessage(WPARAM,LPARAM) {
doc->nanika();
return 0;
}
ありがとうございます。
こちらのほうでもやっているのですが、やはり解決しません。
OnRegisteredMessageと
::SendMessage(HWND_BROADCAST, msg, 0, (LPARAM)GetSafeHwnd());
をMainFrameに移動させてブレークポイントを置いて調べると、そもそも
送り側のボタンを押しても、OnRegisteredMessageの中に入っていきません。
おなじところをぐるぐる回っているような気もするのですが。。。
> ::SendMessage(HWND_BROADCAST, msg, 0, (LPARAM)GetSafeHwnd());
> をMainFrameに移動させてブレークポイントを置いて調べると、
コレをどこに置いていますか?
ありがとうございます。
CMainFrame::PreCreateWindowと、OnCreateの両方で試しましたが、
いずれもダメでした。
PreCreateWindow(=WindowHandle生成前)でGetSafeHwnd(=WindowHandle)しても意味がないです。
まずは、メッセージについて(以下略
OnCreateはどーなんだっけ? '今から作るよ'であって'今できたばっかりよ'じゃないですよね。
とすると、'これ以降、HWNDばっちOK!'を知るメッセージとしては何捕まえるのがいいんでしょ?
> OnCreateはどーなんだっけ? '今から作るよ'であって'今できたばっかりよ'じゃないですよね。
大丈夫ですよ>OnCreateでGetSafeHwnd()
MFCの場合、OnCreateを呼ぶ前の時点で、HWNDをごにょごにょしてくれてます。
# Win32でも、引数の HWNDは有効。
> 大丈夫ですよ>OnCreateでGetSafeHwnd()
「Ban 2006/01/13(金) 10:37:10」書いた時点で試してみた結果。
> 大丈夫ですよ>OnCreateでGetSafeHwnd()
あらー? ぢゃー
> CMainFrame::PreCreateWindowと、OnCreateの両方で試しましたが、いずれもダメでした。
これは別の理由か?
念のため・・・GetSafeHwnd() の値は正しく取れていますか?
ぢっけんしました。 OnCreateで問題ありませんですね。
これで概ね解決だと思うが、まだなにか腑に落ちないとこがあるかしら?
たいへんありがとうございました。
解決しました。
SendMessageは、OnCreateでreturn 0;の前にボンと置いただけではエラーになりましたが、プロパティのオーバーライドを探して、ActivateFrameの中に
書き込んだところ、成功しました。
私が解決しただけでは過去ログとして意味をなさないので、επιστημηさんのお許しを得て、頂いたサンプルを公開します。
送り元(Talker)はダイアログベース、受け側(Listener)はフォームビューです。
Talkerを先に立ち上げます。
------------------------------------
Talker
ボタンを二つ、ダイアログに貼り付けます。(IDC_INCR, IDC_DECR)
●TalkerDlg.cpp
// TalkerDlg.cpp : 実装ファイル
(省略)
const UINT msg = ::RegisterWindowMessage("INTERNALUSE");//------追加
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
END_MESSAGE_MAP()
// CTalkerDlg ダイアログ
CTalkerDlg::CTalkerDlg(CWnd* pParent /*=NULL*/)
: CDialog(CTalkerDlg::IDD, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
listener = 0;//-------------------------------------------追加
}
(省略)
BEGIN_MESSAGE_MAP(CTalkerDlg, CDialog)
(省略)
ON_REGISTERED_MESSAGE(msg, OnRegisteredMessage)//----------追加
ON_BN_CLICKED(IDC_INCR, OnBnClickedIncr)//-----------------追加
ON_BN_CLICKED(IDC_DECR, OnBnClickedDecr)//-----------------追加
END_MESSAGE_MAP()
// CTalkerDlg メッセージ ハンドラ
(省略)
//以下追加
void CTalkerDlg::OnBnClickedIncr()
{
if ( listener ) {
::SendMessage(listener, msg, 1, 0);
}
}
void CTalkerDlg::OnBnClickedDecr()
{
if ( listener ) {
::SendMessage(listener, msg, 2, 0);
}
}
LRESULT CTalkerDlg::OnRegisteredMessage( WPARAM wparam, LPARAM lparam) {
if ( wparam == 0 ) {
listener = (HWND)lparam;
}
return 0;
}
●TalkerDlg.h : ヘッダー ファイル
(省略)
// 実装
protected:
(省略)
afx_msg LRESULT OnRegisteredMessage( WPARAM, LPARAM );//------------追加
DECLARE_MESSAGE_MAP()
//以下追加
private:
HWND listener;
public:
afx_msg void OnBnClickedIncr();
afx_msg void OnBnClickedDecr();
};
##長くなるので、一旦ここで送信します。
Listener
フォームにエディットボックスをひとつ(m_edit1)貼り付けます。
頂いたサンプルではスタティックテキストでしたが、ここは置換えました。
MainFrmからViewに値を送るのに、本来、ドキュメントクラス経由とすべきですが、
コードの短縮のため、直で送っています。
このサンプルを参考になさる方は、上段のεπιστημηさんの
ドキュメントクラスに関するご示唆を確認してください。
●MainFrm.cpp
(省略)
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
const UINT msg = ::RegisterWindowMessage("INTERNALUSE");//---------------追加
(省略)
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
ON_WM_CREATE()
ON_REGISTERED_MESSAGE(msg, OnRegisteredMessage)//--------------------追加
END_MESSAGE_MAP()
(省略)
// CMainFrame メッセージ ハンドラ
void CMainFrame::ActivateFrame(int nCmdShow)//プロパティのオーバーライドでハンドルします
{
::SendMessage(HWND_BROADCAST, msg, 0, (LPARAM)GetSafeHwnd());
CFrameWnd::ActivateFrame(nCmdShow);
}
LRESULT CMainFrame::OnRegisteredMessage( WPARAM wparam, LPARAM lparam) {//手書きです
switch ( wparam ) {
case 0 : count = 0; break;
case 1 : ++count ; break;
case 2 : --count ; break;
}
CFrameWnd* pFrame = (CFrameWnd*)AfxGetMainWnd();
CListenerView* pView = (CListenerView*)pFrame->GetActiveView();
pView->m_edit1=count;
pView->UpdateData(FALSE);
return 0;
}
●MainFrm.h
(省略)
// 属性
public:
int count;//------------------------------------------------------------------追加
(省略)
// 生成された、メッセージ割り当て関数
protected:
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg LRESULT OnRegisteredMessage( WPARAM, LPARAM );//----------------------追加
DECLARE_MESSAGE_MAP()
public:
virtual void ActivateFrame(int nCmdShow = -1);
};
> SendMessageは、OnCreateでreturn 0;の前にボンと置いただけではエラーになりましたが、
> プロパティのオーバーライドを探して、ActivateFrameの中に書き込んだところ、成功しました。
っかしーなー、僕はOnCreateに置いておっけーだったのに…
> LRESULT CMainFrame::OnRegisteredMessage( WPARAM wparam, LPARAM lparam) {//手書きです
> switch ( wparam ) {
> case 0 : count = 0; break;
> case 1 : ++count ; break;
> case 2 : --count ; break;
> }
> CFrameWnd* pFrame = (CFrameWnd*)AfxGetMainWnd();
> CListenerView* pView = (CListenerView*)pFrame->GetActiveView();
> pView->m_edit1=count;
> pView->UpdateData(FALSE);
> return 0;
> }
あくまで"動作検証のため"ですよね。
View::OnInitialUpdate() で ViewのHWNDをSendMessage(HWND_BROADCAST...)すればいいのに、
なんでこんなまだるっこいことするんだろ。
本体ロジックとは何の関係もないFrameにロジック書くわ
Viewのメンバを恥ずかしげもなく晒すわ
せっかくのDocumentは'すっからかん'だわ
…最悪。
こんなことならハナっからDialogベースでやればいい。
本番でこんなコード見せられたら窓から放り出してやる。
実験実装に過ぎないサンプルコードを丸呑みしたな。
επιστημηさん、ありがとうございます。
ドキュメントクラスで処理できるんですね。
ただ、プロセスを理解するために、またVC++への理解を進めるために、MainFrameであれこれ試行錯誤してよかったと思います。
「わからないことは、いつまでたってもわからない」というようなことになりがちで、
今回の勉強は、私にとっては、大きなブレークスルーでした。
繰り返しになりますが、たいへんありがとうございました。
> …最悪。
> こんなことならハナっからDialogベースでやればいい。
> 本番でこんなコード見せられたら窓から放り出してやる。
同感。
> ドキュメントクラスで処理できるんですね。
MFCが提供するドキュメント-ビュー・アーキテクチャの基礎です。
つまり、MFCさえ理解できてないという(以下略
> ただ、プロセスを理解するために、またVC++への理解を進めるために、
> MainFrameであれこれ試行錯誤してよかったと思います。
正しい基礎を抑えずにいきなり試行錯誤しちゃうと、変な癖がついちゃうだけです。
そこから正しい理解につなげるのは、初心者にはまず無理です。脱初心者のためにもただの遠回りの可能性大。
現にMainFrameの役割を知らないと、そこから逸脱してることにも気づけないわけで.....。
実践と勉強は車輪の両輪だと思います。
> 「わからないことは、いつまでたってもわからない」というようなことになりがちで、
> 今回の勉強は、私にとっては、大きなブレークスルーでした。
この姿勢があるのなら、方向性がずれてるのはもったいないと思います。
繰り返しになりますが、理解を深めるための試行錯誤は、まずWin32で
やることをお勧めします。皆さんもそう書かれていると思いますし、
少なくとも私は過去にそうしました。
結局、何事も基礎がまず大事という、当たり前のことなんですが。
まなびておもわざればすなわちくらし、
おもいてまなばざればすなわちあやうし
テカ
ツイート | ![]() |