3

最近、2 つの Win32 API 呼び出し "PostMessage" と "SendNotifyMessage" の間に奇妙な違いがありました (少なくとも Win7 64 ビット SP1 で気付きました): 別のプロセスの所有されたトップレベル ウィンドウは、ブロードキャストされたメッセージ (HWND_BROADCAST) を "その WndProc で「SendNotifyMessage」でブロードキャストされたメッセージを受信して​​いる間、PostMessage」。

送信されたメッセージは、「RegisterWindowMessage」への呼び出しの助けを借りて登録されています。

Spy++ を使用しても、「PostMessage」を使用するとメッセージが到着するのを確認できません。さらに、「PostMessage」を使用して特定の HWND にメッセージを直接送信すると、期待どおりに到着することにも言及したいと思います。したがって、「PostMessage」のウィンドウ内部実装は、ブロードキャストの実行を反復するときにウィンドウをスキップするだけのようです。

それぞれの MSDN のドキュメントを読んでも、この違いに関する記述は見当たらず、これが PostMessage または SendNotifyMessage のバグなのかどうか、また Windows の将来のバージョンで引き続きこの動作を示すために SendNotifyMessage に依存できるかどうか疑問に思っています。

では、この状況で両方の機能がブロードキャストを異なる方法で処理する理由について、誰かがもっともらしい説明を持っていますか?

さらに、PostMessage を使用して、所有されているトップレベル ウィンドウにブロードキャストする方法があるかどうかを尋ねたいと思います。します)。

トップレベルの所有ウィンドウに到達したい理由に興味がある場合: WPF では、非表示の所有者ウィンドウを持つトップレベル ウィンドウを所有することにより、ウィンドウはタスクバー (Window.ShowInTaskbar プロパティ) から隠されます。

このトピックに関するアイデアやコメントをお寄せいただきありがとうございます。

添付ファイル: ここに動作を示すサンプルがあります...単純にビルドして 2 回開始します...2 番目のプロセスは、最初のプロセスにメッセージを表示する必要があります。ビルド EXE を含む完全なソリューションへのリンクもここにあります:完全な VS ソリューションへのリンク

#include <windows.h>
#include <stdio.h>

#include <string>
#include <vector>


HWND hwndMain = NULL;
HWND ownerHwnd = NULL;

std::vector<std::string> theOutput;
UINT MyRegisteredMessage1 = 0;


LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  PAINTSTRUCT ps;
  HDC hdc = NULL;

  if (message == MyRegisteredMessage1 && wParam != (WPARAM) hwndMain)
  {
    if (lParam == (LPARAM) 1)
      theOutput.push_back("Got a 'MyRegisteredMessage1' via PostMessage");
    if (lParam == (LPARAM) 2)
      theOutput.push_back("Got a 'MyRegisteredMessage1' via SendNotifyMessage");

    InvalidateRect(hwndMain, NULL, TRUE);
  }

  switch (message) 
  {
  case WM_PAINT:
    hdc = BeginPaint(hWnd, &ps);
    for(size_t i = 0, pos = 0; i < theOutput.size(); ++i, pos += 20)
      TextOutA(hdc, 0, pos, theOutput[i].c_str(), theOutput[i].size());
    EndPaint (hWnd, &ps);
    break;

  case WM_DESTROY:
    PostQuitMessage(0);
    break;

  default:
    return DefWindowProc(hWnd, message, wParam, lParam);
  }

  return 0;
}


LRESULT CALLBACK WndProcHidden(HWND hWnd, UINT message,
  WPARAM wParam, LPARAM lParam)
{
    return DefWindowProc(hWnd, message, wParam, lParam);
}


int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
  LPSTR lpszCmdLine, int nCmdShow) 
{
  MSG msg;
  BOOL bRet; 
  WNDCLASSA wc; 
  UNREFERENCED_PARAMETER(lpszCmdLine); 

  if (!hPrevInstance) 
  { 
    wc.style = 0; 
    wc.lpfnWndProc = (WNDPROC) WndProcHidden; 
    wc.cbClsExtra = 0; 
    wc.cbWndExtra = 0; 
    wc.hInstance = hInstance; 
    wc.hIcon = LoadIcon((HINSTANCE) NULL, IDI_APPLICATION); 
    wc.hCursor = LoadCursor((HINSTANCE) NULL, IDC_ARROW); 
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);;
    wc.lpszMenuName =  "MainMenu"; 
    wc.lpszClassName = "MyOwnerWindowClass"; 

    if (!RegisterClassA(&wc)) 
      return FALSE;

    wc.lpfnWndProc = (WNDPROC) WndProc; 
    wc.lpszClassName = "MyOwnedWindowClass"; 

    if (!RegisterClassA(&wc)) 
      return FALSE; 
  } 

  ownerHwnd = CreateWindowA("MyOwnerWindowClass", "OwnerWindow", 
    WS_OVERLAPPEDWINDOW, 0, 0, 800, 400, (HWND) NULL, 
    (HMENU) NULL, hInstance, (LPVOID) NULL); 

  hwndMain = CreateWindowA("MyOwnedWindowClass", "OwnedWindow", 
    WS_OVERLAPPEDWINDOW, 0, 0, 800, 400, ownerHwnd, 
    (HMENU) NULL, hInstance, (LPVOID) NULL); 

  // only show the "real" window
  ShowWindow(hwndMain, nCmdShow); 
  UpdateWindow(hwndMain); 

  MyRegisteredMessage1 = RegisterWindowMessageA("MyRegisteredMessage1");

  char infoText[256];
  _snprintf_s(infoText, 256,
    "HWND = %X, registered message code for 'MyRegisteredMessage1' = %d",
    hwndMain, MyRegisteredMessage1);
  theOutput.push_back(infoText);
  InvalidateRect(hwndMain, NULL, TRUE);

  PostMessage(HWND_BROADCAST, MyRegisteredMessage1, (WPARAM) hwndMain, (LPARAM) 1);
  Sleep(1000);
  SendNotifyMessageA(HWND_BROADCAST, MyRegisteredMessage1, (WPARAM) hwndMain, (LPARAM) 2);


  while( (bRet = ::GetMessage( &msg, NULL, 0, 0 )) != 0)
  {
      TranslateMessage(&msg); 
      DispatchMessage(&msg); 
  } 

  return msg.wParam; 
} 
4

3 に答える 3

1

を使用してメッセージを登録する必要がある場合があります-- このMSDN 記事RegisterWindowMessage()の備考セクションを参照してください

于 2013-04-13T11:11:47.047 に答える
1

情報のためにここにこれを追加するだけです..

アプリケーション レベルで IMessageFilter オブジェクトを登録することで、C# でこの問題を回避できました。このオブジェクトの PreFilterMessage はメッセージを受信し、そこから処理できます。

public class FooMessageFilter : IMessageFilter
{
    uint UM_FOO = 0;
    public event EventHandler OnFoo;
    
    public FooMessageFilter()
    {
        UM_FOO = Win32.RegisterWindowMessage("UM_FOO");
    }

    public bool PreFilterMessage(ref Message m)
    {
        if(m.Msg == UM_FOO)
        {
            if(OnFoo != null)
                OnFoo(this, new EventArgs());
                
            return true;
        }

        return false;
    }
}

次に、このメッセージ フィルターを、所有する最上位フォームのコンストラクターの Application コンテキストに追加しました。

public partial class Form1 : Form
{
    private FooMessageFilter fooFilter = new FooMessageFilter();
    public Form1()
    {
        InitializeComponent();
        
        // Register message filter
        Application.AddMessageFilter(fooFilter);
        
        // Subscribe to event
        fooFilter.OnFoo += HandleFoo;
    }
    
    private void HandleFoo(object o, EventArgs e)
    {
        MessageBox.Show("Foo!");
    }
}

そこからは、最上位ウィンドウのイベントをメッセージ フィルターに接続するだけでした。これは、現在のアーキテクチャに準拠する必要があり、メッセージがサード パーティのプロセスから発信されるために必要でした。

于 2015-02-09T17:09:58.823 に答える
0

PostMessage() のドキュメント ページには、整合性レベルの制限が適用されることが記載されています。

Windows Vista 以降、メッセージの投稿は UIPI の対象となります。プロセスのスレッドは、以下の整合性レベルのプロセス内のスレッドのメッセージ キューにのみメッセージを投稿できます。

SendNotifyMessage() に関するそのような制限については言及されていません。どちらの戻り値もチェックしないので、それに遭遇する可能性がありますが、それを知ることはできません。

于 2013-06-20T05:30:15.510 に答える