C++windows消息通信

概述

  • 消息队列是存储在内核中的
  • GUI线程才有消息队列
  • 一开始创建的都是非GUI线程,指向的是SSDT表(Thread.ServiceTable-> KeServiceDescriptorTable)
  • 当线程第一次调用Win32k.sys时,会调用一个函数:PsConvertToGuiThread,我们知道在3环进0环的过程中会取得一个调用号,当调用号在100以下的时候,在ntosknl.exe里面,当调用号大于100则是图形处理函数,调用Win32k.sys
    如果是一个GUI线程,win32Thread指向的就是THREADINFO结构,如果是普通线程,这里就是一个空指针
    底层实现主要看ETHREAD->ServiceTable是指向SSDT还是Shadow SSDT,详细介绍
  • 0x0000-0x03ff(WM_USER-1)为系统定义消息,用户自定义消息范围为0x0400(WM_USER)-0x7fff
  • 进程间消息通信,一般会通过CWnd::FindWindow()找到接收进程的窗口句柄,然后通过PostMessage()发送,接收的UI线程就会按消息进行处理

windows消息机制

消息机制

重要结构体和API说明

内核层对象KTHREAD

typedef struct _KTHREAD
{
     DISPATCHER_HEADER Header;	//说明该对象是一个分发器对象,可以被等待。线程结束时,等待被满足
     UINT64 CycleTime;	
     ULONG HighCycleTime;
     UINT64 QuantumTarget;
     PVOID InitialStack;	//原始栈位置(高地址)
     PVOID StackLimit;		//栈低地址
     PVOID KernelStack;		//内核调用栈开始位置
     ULONG ThreadLock;
     union
     {
          KAPC_STATE ApcState;
          UCHAR ApcStateFill[23];
     };
     CHAR Priority;
     WORD NextProcessor;
     WORD DeferredProcessor;
     ULONG ApcQueueLock;
     ULONG ContextSwitches;
     UCHAR State;
     UCHAR NpxState;
     UCHAR WaitIrql;
     CHAR WaitMode;
     LONG WaitStatus;
     union
     {
          PKWAIT_BLOCK WaitBlockList;
          PKGATE GateObject;
     };
     union
     {
          ULONG KernelStackResident: 1;
          ULONG ReadyTransition: 1;
          ULONG ProcessReadyQueue: 1;
          ULONG WaitNext: 1;
          ULONG SystemAffinityActive: 1;
          ULONG Alertable: 1;
          ULONG GdiFlushActive: 1;
          ULONG Reserved: 25;
          LONG MiscFlags;
     };
     UCHAR WaitReason;
     UCHAR SwapBusy;
     UCHAR Alerted[2];
     union
     {
          LIST_ENTRY WaitListEntry;
          SINGLE_LIST_ENTRY SwapListEntry;
     };
     PKQUEUE Queue;
     ULONG WaitTime;
     union
     {
          struct
          {
               SHORT KernelApcDisable;
               SHORT SpecialApcDisable;
          };
          ULONG CombinedApcDisable;
     };
     PVOID Teb;
     union
     {
          KTIMER Timer;
          UCHAR TimerFill[40];
     };
     union
     {
          ULONG AutoAlignment: 1;
          ULONG DisableBoost: 1;
          ULONG EtwStackTraceApc1Inserted: 1;
          ULONG EtwStackTraceApc2Inserted: 1;
          ULONG CycleChargePending: 1;
          ULONG CalloutActive: 1;
          ULONG ApcQueueable: 1;
          ULONG EnableStackSwap: 1;
          ULONG GuiThread: 1;
          ULONG ReservedFlags: 23;
          LONG ThreadFlags;
     };
     union
     {
          KWAIT_BLOCK WaitBlock[4];
          struct
          {
               UCHAR WaitBlockFill0[23];
               UCHAR IdealProcessor;
          };
          struct
          {
               UCHAR WaitBlockFill1[47];
               CHAR PreviousMode;
          };
          struct
          {
               UCHAR WaitBlockFill2[71];
               UCHAR ResourceIndex;
          };
          UCHAR WaitBlockFill3[95];
     };
     UCHAR LargeStack;
     LIST_ENTRY QueueListEntry;
     PKTRAP_FRAME TrapFrame;
     PVOID FirstArgument;
     union
     {
          PVOID CallbackStack;
          ULONG CallbackDepth;
     };
     PVOID ServiceTable;	/*****************/
     UCHAR ApcStateIndex;
     CHAR BasePriority;
     CHAR PriorityDecrement;
     UCHAR Preempted;
     UCHAR AdjustReason;
     CHAR AdjustIncrement;
     UCHAR Spare01;
     CHAR Saturation;
     ULONG SystemCallNumber;
     ULONG Spare02;
     ULONG UserAffinity;
     PKPROCESS Process;
     ULONG Affinity;
     PKAPC_STATE ApcStatePointer[2];
     union
     {
          KAPC_STATE SavedApcState;
          UCHAR SavedApcStateFill[23];
     };
     CHAR FreezeCount;
     CHAR SuspendCount;
     UCHAR UserIdealProcessor;
     UCHAR Spare03;
     UCHAR Iopl;
     PVOID Win32Thread;		/*****************/
     PVOID StackBase;
     union
     {
          KAPC SuspendApc;
          struct
          {
               UCHAR SuspendApcFill0[1];
               CHAR Spare04;
          };
          struct
          {
               UCHAR SuspendApcFill1[3];
               UCHAR QuantumReset;
          };
          struct
          {
               UCHAR SuspendApcFill2[4];
               ULONG KernelTime;
          };
          struct
          {
               UCHAR SuspendApcFill3[36];
               PKPRCB WaitPrcb;
          };
          struct
          {
               UCHAR SuspendApcFill4[40];
               PVOID LegoData;
          };
          UCHAR SuspendApcFill5[47];
     };
     UCHAR PowerState;
     ULONG UserTime;
     union
     {
          KSEMAPHORE SuspendSemaphore;
          UCHAR SuspendSemaphorefill[20];
     };
     ULONG SListFaultCount;
     LIST_ENTRY ThreadListEntry;
     LIST_ENTRY MutantListHead;
     PVOID SListFaultAddress;
     PVOID MdlForLockedTeb;
} KTHREAD, *PKTHREAD;

/*
Header:说明该对象是一个分发器对象,可以被等待。线程结束时,等待被满足。

MutantListHead:指向一个链表头。链表中包含所有属于该线程的突变体对象(mutant,对应互斥体对象)。

InitialStack:原始栈位置(高地址)

StackLimit:栈低地址

KernelStack:内核调用栈开始位置

StackBase:当前栈的基地址。

ThreadLock:自旋锁,用于保护线程数据成员。

ApcState:KAPC_STATE结构,指定线程的APC信息,包括APC链表,是否有APC正在等待,是否正在处理APC。

ApcQueueable:是否可插入APC

NextProcessor:关于处理器调度的选择。

DeferredProcessor:关于处理器调度的选择。

AdjustReason:优先级调整原因

AdjustIncrement:优先级调整调整量

ApcQueueLock:保护APC队列的自旋锁。

ContextSwitches:记录线程进行了多少次切换。

State:线程当前状态。

NpxState:浮点处理器状态。

Alertable:线程是否可以被唤醒。

WaitNext:

WaitIrql:原先的IRQL。

WaitReason:等待原因

WaitMode:线程等待时的处理器模式,内核or用户

WaitStatus:等待的结果状态。

WaitBlockList:KWAIT_BLOCK为元素的链表,记录线程所有等待的分发器对象。每个分发器对象也有一个KWAIT_BLOCK组成的链表,记录所有等待在该对象的线程。

GateObject:等待的门对象,等待门对象和等待分发器对象不会同时发生。

Priority:动态优先级。

BasePriority:基本优先级。

PriorityDecrement:优先级动态调整过程中的递减值。

Saturation:线程基本优先级调整相对于进程基本优先级是否超过了区间的一半。

EnableStackSwap:内核栈是否准许被换出。

SwapBusy:当前是否正在进程上下文切换。

Alerted:线程在警告模式下是否可以被唤醒。

WaitListEntry:双向链表节点,等待被执行时,作为节点加入某链表

SwapListEntry:单链表节点,内核栈需要被换出时,加入KiStackInSwapListHead为头的链表。另外,线程处于DeferredReady状态时加入DeferredReadyListHead为头的链表。

Queue:队列分发器对象,线程正在处理此队列中的项。

WaitTime:线程进入等待时刻的时间点。

KernelApcDisable/SpecialApcDisable:内核APC和特殊内核APC是否被禁止。

TEB:进程地址空间的一个TEB域

Timer:定时器。

AutoAlignment:与KPROCESS相同

DisableBoost:与KPROCESS相同

WaitBlock:4个KWAIT_BLOCK成员的数组,线程等待的分发器少于4个时,使用这里的空间,不分配新空间。

QueueListEntry:线程处理一个队列项时,加入到队列对象的线程链表中的地址。

TrapFrame:指向KTRAP_FRAME类型的指针。用户保存执行现场。

CallbackStack:线程的回调栈地址,在从内核模式返回用户模式时用。

ServiceTable: 指向系统使用的系统服务表,非GUI线程为KeServiceDescriptorTable,GUI线程为KeServiceDescriptorTableShadow。

IdealProcess:理想处理器

Preempted:是否被高优先级线程抢占了。

ProcessReadyQueue:是否在进程对象的ReadyListHead列表中。

KernelStackResident:线程的内核栈是否驻留在线程中。

Affinity:处理器亲和性,为线程指定的处理器集合必须是该集合的子集。

UserAffinity:线程的用户亲和性。

Process:执行线程的进程对象。

ApcStateIndex:指明当前APC状态在ApcStatePointer域中的索引。

Win32Thread:指向Windows子系统管理的区域的指针。

SuspendApc/SuspendSemaphore:用于支持线程挂起的域。

ThreadListEntry:双链表的节点,线程被创建时,加入到进程的ThreadListHead链表中。

SListFaultAddress:上一次用户模式互锁单链表POP操作发生页面错误的地址。

SuspendSemaphore:与上面有关。
*/

THREADINFO结构体,该结构体拥有4个消息队列,以及一些标志

  1. Sent Message Queue 发送消息队列,储存SendMessage()不同线程时发送来的消息,是一个指针数组
  2. Posted Message Queue 登记消息队列,储存PostMessage()发送来的消息(同线程也进队列),是一个指针数组
  3. Virtualized Input Queue 输入消息队列,接收键盘等虚拟输入信息和系统内部消息队列,是一个指针数组
  4. Reply Message Queue 响应消息队列,不同线程时发送线程使用SendMessage函数发送信息后线程被挂起,接收线程处理完后返回的信息存放于此,发送线程被唤醒,发送线程从该队列取出返回信息,是一个指针数组
  5. nExitCode:确定线程退出状态,是一个int型,不同数值说明线程处于不同状态
  6. 唤醒标志:判断是否处于唤醒状态

MSG结构

typedef struct tagMSG {
    HWND        hwnd;        //该消息所属的窗口句柄
    UINT        message;    //指定消息的类型
    WPARAM      wParam;        //用于指定消息的附加信息,根据消息不同,代表不同意思
    LPARAM      lParam;        //用于指定消息的附加信息,根据消息不同,代表不同意思
    DWORD       time;        //该消息投递到消息列队当中的时间
    POINT       pt;            //该消息投递到消息列队当时,鼠标的当前位置
} MSG, *PMSG, NEAR *NPMSG, FAR *LPMSG;

API

函数原型:LRESULT SendMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM IParam);
hWnd:指定接收消息的窗口;HWND_BROADCAST,则消息将被发送到系统中所有顶层窗口,包括无效或不可见的非自身拥有的窗口、被覆盖的窗口和弹出式窗口,但消息不被发送到子窗口;NULL直接返回
Msg:发送的消息
wParam:消息wParam参数,附加参1
IParam:消息lParam参数,附加参2

/*接收线程与SendMessage调用线程属于同一线程,阻塞至处理返回
  接收线程与SendMessage调用线程不属于同一线程,消息被投放到send message queue,然后返回*/
LRESULT SendMessageTimeout(
  HWND hWnd,           // 同SendMessage
  UINT Msg,            // 同SendMessage
  WPARAM wParam,       // 同SendMessage
  LPARAM lParam,       // 同SendMessage
  UINT fuFlags,        // 消息发送方式
  /*
  SMTO_ABORTIFHUNG    如果接受进程处于挂起状态,那么此函数将直接返回,超时值无效。
  SMTO_BLOCK    以阻塞方式运行
  SMTO_NORMAL    等待函数返回请安,线程可以正常处理其他请求
  SMTO_NOTIMEOUTIFNOTHUNG    如果接受现成没有挂起,知道超时值,函数才返回
  */
  UINT uTimeout,       // 消息超时值,hWnd为HWND_BROADCAST时,在每个窗口超时之前,该函数不会返回。因此,总等待时间可以高达 uTimeout * 顶级窗口数
  LPDWORD lpdwResult   // 消息处理的结果。 此参数的值取决于指定的消息
);

简单demo

server读:

// Win32Server.cpp : 定义应用程序的入口点。
//

#include "stdafx.h"
#include "Win32Server.h"

#define MAX_LOADSTRING 100
#define MSG_CUSTOM_INFO WM_USER+1


// 全局变量: 
HINSTANCE hInst;                                // 当前实例
WCHAR szTitle[MAX_LOADSTRING];                  // 标题栏文本
WCHAR szWindowClass[MAX_LOADSTRING];            // 主窗口类名

// 此代码模块中包含的函数的前向声明: 
ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    // TODO: 在此放置代码。
	OutputDebugStringW(_T("------start------"));
    // 初始化全局字符串
    LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadStringW(hInstance, IDC_WIN32SERVER, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);

    // 执行应用程序初始化: 
    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }

    HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_WIN32SERVER));

    MSG msg;

    // 主消息循环: 
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return (int) msg.wParam;
}



//
//  函数: MyRegisterClass()
//
//  目的: 注册窗口类。
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEXW wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WndProc;
    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;
    wcex.hInstance      = hInstance;
    wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WIN32SERVER));
    wcex.hCursor        = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = MAKEINTRESOURCEW(IDC_WIN32SERVER);
    wcex.lpszClassName  = szWindowClass;
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    return RegisterClassExW(&wcex);
}

//
//   函数: InitInstance(HINSTANCE, int)
//
//   目的: 保存实例句柄并创建主窗口
//
//   注释: 
//
//        在此函数中,我们在全局变量中保存实例句柄并
//        创建和显示主程序窗口。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   hInst = hInstance; // 将实例句柄存储在全局变量中

   HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);

   if (!hWnd)
   {
      return FALSE;
   }

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}

//
//  函数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  目的:    处理主窗口的消息。
//
//  WM_COMMAND  - 处理应用程序菜单
//  WM_PAINT    - 绘制主窗口
//  WM_DESTROY  - 发送退出消息并返回
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_COMMAND:
        {
            int wmId = LOWORD(wParam);
            // 分析菜单选择: 
            switch (wmId)
            {
            case IDM_ABOUT:
                DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
                break;
            case IDM_EXIT:
                DestroyWindow(hWnd);
                break;
            default:
                return DefWindowProc(hWnd, message, wParam, lParam);
            }
        }
        break;
    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            // TODO: 在此处添加使用 hdc 的任何绘图代码...
            EndPaint(hWnd, &ps);
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
	case MSG_CUSTOM_INFO:
		OutputDebugStringW(_T("new message"));
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

// “关于”框的消息处理程序。
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    UNREFERENCED_PARAMETER(lParam);
    switch (message)
    {
    case WM_INITDIALOG:
        return (INT_PTR)TRUE;

    case WM_COMMAND:
        if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
        {
            EndDialog(hDlg, LOWORD(wParam));
            return (INT_PTR)TRUE;
        }
        break;
    }
    return (INT_PTR)FALSE;
}

client写:

// Win32Client.cpp : 定义应用程序的入口点。
//

#include "stdafx.h"
#include "Win32Client.h"

#define MAX_LOADSTRING 100
#define IDC_BUTTON_CLICK 150
#define MSG_CUSTOM_INFO WM_USER+1
// 全局变量: 
HINSTANCE hInst;                                // 当前实例
WCHAR szTitle[MAX_LOADSTRING];                  // 标题栏文本
WCHAR szWindowClass[MAX_LOADSTRING];            // 主窗口类名

// 此代码模块中包含的函数的前向声明: 
ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    // TODO: 在此放置代码。

    // 初始化全局字符串
    LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadStringW(hInstance, IDC_WIN32CLIENT, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);

    // 执行应用程序初始化: 
    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }

    HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_WIN32CLIENT));

    MSG msg;

    // 主消息循环: 
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return (int) msg.wParam;
}


void OnButtonClick()
{
	HWND hWnd = FindWindowW(_T("WIN32SERVER"), _T("Win32Server"));
	PostMessageW(hWnd, MSG_CUSTOM_INFO, 123L, 0);	//post是放入到接收线程的队列,这里123以值传递的方式,因此没问题,如果是该进程的堆空间(new出来的),则会导致问题
	::MessageBox(NULL, L"已发送123456给另一个进程", L"提示", MB_OK);
}

//
//  函数: MyRegisterClass()
//
//  目的: 注册窗口类。
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEXW wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WndProc;
    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;
    wcex.hInstance      = hInstance;
    wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WIN32CLIENT));
    wcex.hCursor        = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = MAKEINTRESOURCEW(IDC_WIN32CLIENT);
    wcex.lpszClassName  = szWindowClass;
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    return RegisterClassExW(&wcex);
}

//
//   函数: InitInstance(HINSTANCE, int)
//
//   目的: 保存实例句柄并创建主窗口
//
//   注释: 
//
//        在此函数中,我们在全局变量中保存实例句柄并
//        创建和显示主程序窗口。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   hInst = hInstance; // 将实例句柄存储在全局变量中

   HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);

   if (!hWnd)
   {
      return FALSE;
   }
   CreateWindowW(_T("BUTTON"), _T("Click Me"), WS_CHILD | WS_VISIBLE, 10, 40, 80, 20, hWnd, (HMENU)IDC_BUTTON_CLICK, NULL, NULL);

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}

//
//  函数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  目的:    处理主窗口的消息。
//
//  WM_COMMAND  - 处理应用程序菜单
//  WM_PAINT    - 绘制主窗口
//  WM_DESTROY  - 发送退出消息并返回
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_COMMAND:
        {
            int wmId = LOWORD(wParam);
            // 分析菜单选择: 
            switch (wmId)
            {
            case IDM_ABOUT:
                DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
                break;
            case IDM_EXIT:
                DestroyWindow(hWnd);
                break;
			case IDC_BUTTON_CLICK:
				//进入按钮点击事件
				OnButtonClick();
            default:
                return DefWindowProc(hWnd, message, wParam, lParam);
            }
        }
        break;
    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            // TODO: 在此处添加使用 hdc 的任何绘图代码...
            EndPaint(hWnd, &ps);
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

// “关于”框的消息处理程序。
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    UNREFERENCED_PARAMETER(lParam);
    switch (message)
    {
    case WM_INITDIALOG:
        return (INT_PTR)TRUE;

    case WM_COMMAND:
        if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
        {
            EndDialog(hDlg, LOWORD(wParam));
            return (INT_PTR)TRUE;
        }
        break;
    }
    return (INT_PTR)FALSE;
}

就是两个win32空的窗口程序,server端跟进程通信相关的有

#define MSG_CUSTOM_INFO WM_USER+1	//通信消息ID
.......
case MSG_CUSTOM_INFO:	//是通信消息ID,输出控制台输出
OutputDebugStringW(_T("new message"));

client端跟进程通信相关的有

void OnButtonClick()
{
	HWND hWnd = FindWindowW(_T("WIN32SERVER"), _T("Win32Server"));	//找到窗口句柄
	PostMessageW(hWnd, MSG_CUSTOM_INFO, 123L, 0);	//post通信消息
	::MessageBox(NULL, L"已发送123456给另一个进程", L"提示", MB_OK);
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值