Win32核心编程-程序员秘密

技术标签: C++  c++  Linux  win32  linux  

Win32核心编程

Win32核心编程

在这里插入图片描述

Windows 编程基础

在这里插入图片描述

创建一个空白解决方案

在这里插入图片描述

创建控制台程序

在解决方案中添加一个控制台程序–》空项目
在这里插入图片描述

添加源代码
在这里插入图片描述
在这里插入图片描述

添加代码并编译执行
在这里插入图片描述

测试:成功打印
在这里插入图片描述



创建一个窗口程序

在解决方案中添加一个项目
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

测试:制作窗口成功
在这里插入图片描述

创建静态库程序

在解决方案中添加一个新项目 -》静态库程序
在这里插入图片描述

创建完后设置为启动项目

在这里插入图片描述
运行测试:无法运行
在这里插入图片描述

有入口 --> 可执行 --> 程序的最终文件可以进内存
没有入口 --> 无法执行

创建动态库程序

在解决方案中添加一个新项目 -》动态库程序

在这里插入图片描述

在这里插入图片描述

测试:动态库不能独立运行,需要依赖其他进程
在这里插入图片描述

三种应用程序的对比
在这里插入图片描述

windows开发环境

VC的编译工具

CL.EXE 和 LINK.EXE 可执行程序 在安装的路径下的 VC下bin
在这里插入图片描述

Windows动态库

(kernel32.dll, user32.dll, gdi32.dll 三个动态库的路径) 在 C:\Windows\System32 下
在这里插入图片描述

头文件

(几个重要的头文件所在的路径) C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Include
在这里插入图片描述

一个简单的窗口程序

WinMain函数

窗口程序入口函数
句柄:
是一个用来找到内存的东西,但绝对不是指针

在这里插入图片描述

MessageBox函数

提示框

在这里插入图片描述
测试代码:hello.cpp

#include <windows.h>

int WinMain(HINSTANCE hIns, HINSTANCE hPreIns, LPSTR lpCmdLine, int nCmdShow)
{
    
	int ret = MessageBox(NULL, "HELLO WORLD", "INFORMATION", MB_ABORTRETRYIGNORE | MB_ICONERROR);
	if (ret == IDABORT) {
    
       	
	}
	else if (ret == IDRETRY) {
    
	
	}
	else {
    
	}
	return 0;
}

完整的编译链接的过程:

           CL.EXE
 .c/.cpp -----------> .obj|
                                      |  LINK.EXE
                                      |---------------------------> .exe
         RC.EXE                |
 .rc   -------------> .res|

在这里插入图片描述
脚本文件:hello.rc 图片名称为small.ico

100 ICON small.ico

操作步骤:
cl.exe -c hello.c -->Hello.c
rc.exe Hello.rc --> Hello.res
link.exe Hello.obj Hello.res user32.lib --> Hello.exe

在这里插入图片描述
编写窗口程序的步骤

  1. 定义WinMain函数
  2. 定义窗口处理函数(自定义,处理消息)
  3. 注册窗口类(向操作系统内核写入数据)
  4. 创建窗口(内存中创建窗口)
  5. 显示窗口(根据窗口数据在屏幕上绘制图像)
  6. 消息循环(提取消息/翻译消息/派发消息)
  7. 消息处理

参考代码:

#include <windows.h>

//窗口处理函数(自定义,处理消息)
LRESULT CALLBACK WndProc(HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam) {
     //回调函数
	return DefWindowProc(hWnd, msgID, wParam, lParam);  //给各种消息做默认处理
}

//入口函数
int CALLBACK WinMain(HINSTANCE hIns, HINSTANCE hPreIns, LPSTR lpCmdLine, int nCmdShow) {
    
	
	//注册窗口类(系统内核中写入数据)
	WNDCLASS wc = {
     0 };
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
	wc.hCursor = NULL;
	wc.hIcon = NULL;
	wc.lpfnWndProc = WndProc;
	wc.lpszClassName = "Main";
	wc.lpszMenuName = NULL;
	wc.style = CS_HREDRAW | CS_VREDRAW;
	RegisterClass(&wc);  //将以上所有赋值全部写入操作系统内核
	
	//内存中创建窗口
	HWND hWnd = CreateWindowEx(0,"Main","windows",WS_OVERLAPPEDWINDOW,100,100,700,500,NULL,NULL,hIns,NULL);

	//显示窗口
	ShowWindow(hWnd, SW_SHOW);	

	//消息循环
	MSG nMsg = {
     0 };
	while (GetMessage(&nMsg, NULL, 0, 0)) {
    
		TranslateMessage(&nMsg);
		DispatchMessage(&nMsg);//将消息交给窗口处理函数(自己定义的那个函数)
	}
	return 0;
}

DBCS和UNICODE编码

DBCS 与UNICODE

在这里插入图片描述

ASC - 7位代表一个字符 128个字符
ASCII - 美国国家信息标准交换码(扩展ASC码) 8位代码一个字符,256个字符
MBCS - 多字节字符编码
DBCS - 单双字节混合编码(没有统一标准) 例 A 01 我 0203 是 0405 程 0607 序 0809 源0A0B
UNICODE -万国码
utf-8 : unix 系统
utf-16: 所有字符统统按照两个字节来编码

wchar_t

在这里插入图片描述
在这里插入图片描述
参考代码:

//#define UNICODE
#include <windows.h>
#include <stdio.h>
#define WIDECHAR

void T_char() {
    
	const TCHAR* pszText = __TEXT("hello");
	//wchar_t * pszText = L"hello";
#ifdef UNICODE
	wprintf(L"%s\n",pszText);
#else
    printf("单:%s\n",pszText);
#endif
/*
#ifdef WIDECHAR
	wchar_t* pszText = L"hello";
	wprintf(L"%s\n", pszText);
#else
	const char* pszText = "hello";
	printf("单:%s\n", pszText);
#endif
*/
}


void C_char() {
    
	const char* pszText = "hello char";
	printf("%s\n", pszText);
}

void W_char() {
    
    const wchar_t* pszText = L"hello wcahr";  //宽字节字符串
	int nlen = wcslen(pszText);  
	wprintf(L"%s %d\n", pszText, nlen);
}

void PrintUnicode() {
    
	const wchar_t* pszText = L"反倒是房价快速拉升国家";
	//wprintf(L"%s\n", pszText); //对汉字打印支持不完善
	HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
	WriteConsole(hOut, pszText, wcslen(pszText), NULL, NULL);
}

int main(void) {
    
	//T_char();
	//C_char();
	//W_char();
	PrintUnicode();
	return 0;
}

项目属性 :
Unicode字符集(IDE开发工具,增加UNICODE宏的定义)
多字节字符集(IDE开发工具,不增加UNICODE宏的定义)

<winnt.h> 中的代码

#ifdef  UNICODE                     
  typedef wchar_t TCHAR;
  #define __TEXT(quote) L##quote     
#else             
  typedef char TCHAR;
  #define __TEXT(quote) quote         
#endif   

涉及字符集的集中类型:
TCHAR char/wchar_t

LPSTR char*
LPCSTR const char*

LPWSTR wchar_t*
LPCWSTR const wchar_t*

LPTSTR TCHAR* (char*/wchar_t*)
LPCTSTR const TCHAR*

打印字符到控制台

BOOL WINAPI WriteConsole(
   HANDLE hConsoleOutput,         // 标准输出句柄
   const VOID *lpBuffer,          // 准备输出内容的buffer
   DWORD nNumberOfCharsToWrite,   // 准备输出字符串的长度
   LPDWORD lpNumberOfCharsWritten,// 返回实际输出字符串的长度
   LPVOID lpReserved              // 备用参数 NULL
);

三个标准句柄:
标准输入句柄、标准输出句柄、标准错误句柄
HANDLE WINAPI GetStdHandle(
DWORD nStdHandle
);

nStdHandle的取值
STD_INPUT_HANDLE
STD_OUTPUT_HANDLE
STD_ERROR_HANDLE

窗口的注册

在这里插入图片描述

系统窗口类的注册

在这里插入图片描述
参考代码:BUTTON

#include <windows.h>

HINSTANCE g_hIns = 0;  //接收当前程序实例句柄
void SysReg() {
    
  //不需要注册,系统已经注册
	HWND hWnd = CreateWindowEx(0, "BUTTON", "0K", WS_OVERLAPPEDWINDOW, 100, 100, 500, 500, NULL, NULL, g_hIns,NULL);
	ShowWindow(hWnd, SW_SHOW);
}


int CALLBACK WinMain(HINSTANCE hIns, HINSTANCE hPreIns, LPSTR lpCmdLine, int nCmdShow) {
    
	SysReg();
	return 0;
	
}

应用程序窗口类的注册

在这里插入图片描述

在这里插入图片描述

窗口类型风格

在这里插入图片描述

窗口的创建

CreateWindowEx (推荐使用)
在这里插入图片描述
在这里插入图片描述

参考代码:

#include <windows.h>
HINSTANCE g_hInstance = 0;
void SysReg()
{
    
	// 系统窗口类由系统注册,应用程序可以直接使用
	// 创建窗口
	HWND hWnd = CreateWindow("BUTTON","OK",WS_OVERLAPPEDWINDOW,
		100,100,700,500,NULL,NULL,g_hInstance,NULL);
	// 显示窗口
	ShowWindow(hWnd,SW_SHOW);
	// 消息循环
	MSG nMsg = {
    0};
	while(GetMessage(&nMsg,NULL,0,0))
	{
    
		TranslateMessage(&nMsg);
		DispatchMessage(&nMsg);
	}
}
void AppReg()
{
    
	WNDCLASS wcs = {
    0};
	wcs.cbClsExtra = 0;
	wcs.cbWndExtra = 0;
	wcs.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
	wcs.hCursor = NULL;
	wcs.hIcon = NULL;
	wcs.hInstance = g_hInstance; /**************/
	wcs.lpfnWndProc = DefWindowProc;
	wcs.lpszClassName = "Main";  /**************/
	wcs.lpszMenuName = NULL;
	wcs.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
	RegisterClass(&wcs);

	CreateWindow("Main","Window",WS_OVERLAPPEDWINDOW,
		100,100,700,500,NULL,NULL,g_hInstance,NULL);
}
int CALLBACK WinMain(HINSTANCE hInstance,HINSTANCE hPrevIns,
	LPSTR lpCmdLine,int nCmdShow)
{
    
	g_hInstance = hInstance;
	SysReg();
	return 0;
}

子窗口的创建

在这里插入图片描述
参考代码1:主窗口关闭后进程也关闭

#include <windows.h>
#include <stdio.h>
HINSTANCE g_hIns = 0; //保存当前程序实例句柄
//窗口处理函数(自定义,处理消息)
LRESULT CALLBACK WndProc(HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam) {
    
	switch(msgID) {
    
	case WM_DESTROY:
		PostQuitMessage(0);   //能够是GetMessage函数返回0  
		break;
	}
	return DefWindowProc(hWnd, msgID, wParam, lParam);  //给各种消息做默认处理
}

//注册窗口类
void Register(LPSTR lpClassName, WNDPROC wndproc) {
    
	WNDCLASS wc = {
     0 };
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
	wc.hCursor = NULL;
	wc.hIcon = NULL;
	wc.hInstance = g_hIns;
	wc.lpfnWndProc = WndProc;
	wc.lpszClassName = lpClassName;
	wc.lpszClassName = "Main";
	wc.lpszMenuName = NULL;
	wc.style = CS_HREDRAW | CS_VREDRAW;
	RegisterClass(&wc);  //将以上所有赋值全部写入操作系统内核
}

//创建主窗口
HWND CreateMain(LPSTR lpClassName, LPSTR lpWndName) {
    
	HWND hWnd = CreateWindowEx(0, lpClassName, lpWndName, WS_OVERLAPPEDWINDOW, 100, 100, 700, 500, NULL, NULL, g_hIns, NULL);
	return hWnd;
}

//显示窗口
void Display(HWND hWnd){
    
	ShowWindow(hWnd, SW_SHOW);
	UpdateWindow(hWnd);  //刷新窗口
}

//消息循环
void Message() {
    
	MSG nMsg = {
     0 };
	while (GetMessage(&nMsg, NULL, 0, 0)) {
    
		TranslateMessage(&nMsg);
		DispatchMessage(&nMsg);  //将消息交给窗口处理函数(自定义)
	}
}

//入口函数
int CALLBACK WinMain(HINSTANCE hIns, HINSTANCE hPreIns, LPSTR lpCmdLine, int nCmdShow) {
    
	g_hIns = hIns;
	Register("Main", WndProc);
	HWND hWnd = CreateMain("Main", "window");
	Display(hWnd);
    Message();
	return 0;
}

参考代码2:

#include <windows.h>
#include <stdio.h>
HINSTANCE g_hIns = 0; //保存当前程序实例句柄
//窗口处理函数(自定义,处理消息)
LRESULT CALLBACK WndProc(HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam) {
    
	switch(msgID) {
    
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	}
	return DefWindowProc(hWnd, msgID, wParam, lParam);  //给各种消息做默认处理
}

//注册窗口类
void Register(LPSTR lpClassName, WNDPROC wndproc) {
    
	WNDCLASS wc = {
     0 };
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
	wc.hCursor = NULL;
	wc.hIcon = NULL;
	wc.hInstance = g_hIns;
	wc.lpfnWndProc = WndProc;
	wc.lpszClassName = lpClassName;
	wc.lpszClassName = "Main";
	wc.lpszMenuName = NULL;
	wc.style = CS_HREDRAW | CS_VREDRAW;
	RegisterClass(&wc);  //将以上所有赋值全部写入操作系统内核
}

//创建主窗口
HWND CreateMain(LPSTR lpClassName, LPSTR lpWndName) {
    
	HWND hWnd = CreateWindowEx(0, lpClassName, lpWndName, WS_OVERLAPPEDWINDOW, 100, 100, 700, 500, NULL, NULL, g_hIns, NULL);
	return hWnd;
}

//创建子窗口
HWND CreateChild(LPSTR lpClassName, LPSTR lpWndName, HWND hParent) {
    
	HWND hChild = CreateWindowEx(0, lpClassName, lpWndName, WS_CHILD | WS_VISIBLE | WS_OVERLAPPEDWINDOW, 100, 100, 200, 200, hParent, NULL, g_hIns, NULL);
	return hChild;
}

//显示窗口
void Display(HWND hWnd){
    
	ShowWindow(hWnd, SW_SHOW);
	UpdateWindow(hWnd);  //刷新窗口
}

//消息循环
void Message() {
    
	MSG nMsg = {
     0 };
	while (GetMessage(&nMsg, NULL, 0, 0)) {
    
		TranslateMessage(&nMsg);
		DispatchMessage(&nMsg);  //将消息交给窗口处理函数(自定义)
	}
}


//入口函数
int CALLBACK WinMain(HINSTANCE hIns, HINSTANCE hPreIns, LPSTR lpCmdLine, int nCmdShow) {
    
	g_hIns = hIns;
	Register("Main", WndProc);
	HWND hWnd = CreateMain("Main", "window");
	Display(hWnd);
	Register("Child",DefWindowProc);
	HWND hChild1 = CreateChild("Child", "c1", hWnd);
	HWND hChild2 = CreateChild("Child", "c2", hWnd);
	MoveWindow(hChild1, 300, 100, 200, 200, FALSE);
	MoveWindow(hChild2, 300, 100, 200, 200, FALSE);
	Message();
	return 0;
}

消息机制

在这里插入图片描述

窗口处理函数和消息

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

windows常用消息

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

参考代码: winMsg.cpp

// 定义自定义消息
#define WM_MYMESSAGE WM_USER + 1001
#include <windows.h>
#include <stdio.h>
HINSTANCE g_hInstance = 0;// 当前程序实例句柄
HANDLE g_hOutput = 0;  // 接收标准输出句柄
HWND g_hEdit = 0;
/*void OnCreate(HWND hWnd,LPARAM lParam)
{
	CREATESTRUCT* pcs = (CREATESTRUCT*)lParam;
	char* pszText = (char*)pcs->lpCreateParams;
	MessageBox(hWnd,pszText,"Infor",MB_OK);
}*/
void OnCreate(HWND hWnd)
{
    
	g_hEdit = CreateWindowEx(0,"EDIT","",WS_CHILD|WS_VISIBLE|WS_BORDER,
		0,0,200,200,hWnd,NULL,g_hInstance,NULL);
	// 自定义消息的发送位置任意
	// 既可以使用SendMessage,也可以使用PostMessage
//	SendMessage(hWnd,WM_MYMESSAGE,100,200);
	PostMessage(hWnd,WM_MYMESSAGE,100,200);
}
void OnMyMessage(HWND hWnd,WPARAM wParam,LPARAM lParam)
{
    
	char szText[256] = {
    0};
	sprintf(szText,"自定义消息被处理 - wParam:%d,lParam:%d\n",
		wParam,lParam);
	MessageBox(hWnd,szText,"Infor",MB_OK);
}
void OnSize(HWND hWnd,LPARAM lParam)
{
    
	int nWidth = LOWORD(lParam);
	int nHeight = HIWORD(lParam);
	char szText[256] = {
    0};
	sprintf(szText,"WM_SIZE--宽:%d,高:%d\n",nWidth,nHeight);
	WriteConsole(g_hOutput,szText,strlen(szText),NULL,NULL);
	MoveWindow(g_hEdit,0,0,nWidth,nHeight,TRUE);
}
// 2. 窗口处理函数
LRESULT CALLBACK WndProc(HWND hWnd,UINT msgID,
	WPARAM wParam,LPARAM lParam)
{
    
	switch(msgID){
    
	case WM_MYMESSAGE:
		OnMyMessage(hWnd,wParam,lParam);
		break;
	case WM_SIZE:
		OnSize(hWnd,lParam);
		break;
	case WM_CREATE:
//		OnCreate(hWnd,lParam);
		OnCreate(hWnd);
		break;
	case WM_SYSCOMMAND:
//		return 0;
//		MessageBox(hWnd,"WM_SYSCOMMAND","Infor",MB_OK);
		if(wParam==SC_CLOSE){
    
			int nRet = MessageBox(hWnd,"是否退出?","提示",MB_YESNO);
			if(nRet==IDYES){
    
				// 什么也不写
			}else{
    
				return 0;
			}
		}
		break;
	case WM_DESTROY:
//		PostQuitMessage(0);  // GetMessage返回0?
//		SendMessage(hWnd,WM_QUIT,0,0);
		PostMessage(hWnd,WM_QUIT,0,0);
		break;
	}
	return DefWindowProc(hWnd,msgID,wParam,lParam);
}
// 3. 注册窗口类
void Register(LPCSTR lpClassName,WNDPROC wndProc)
{
    
	WNDCLASS wcs  ={
    0};
	wcs.cbClsExtra = 0;				// 窗口类附加数据缓冲区
	wcs.cbWndExtra = 0;				// 窗口附加数据缓冲区
	wcs.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);// 背景色
	wcs.hCursor = NULL;				// 光标 NULL--系统默认
	wcs.hIcon = NULL;				// 图标 NULL--系统默认
	wcs.hInstance = g_hInstance;	// 当前程序实例句柄
	wcs.lpfnWndProc = wndProc;		// 窗口处理函数
	wcs.lpszClassName = lpClassName;// 窗口类名称
	wcs.lpszMenuName = NULL;		// 菜单 NULL-没有菜单
	wcs.style = CS_HREDRAW | CS_VREDRAW;// 一般风格
	RegisterClass(&wcs);// 将以上赋值写入操作系统
}
// 4. 创建窗口
HWND CreateMain(LPCSTR lpClassName,LPCSTR lpWindowName)
{
    
	char* pszText = "Data";
	HWND hWnd = CreateWindowEx(0,lpClassName,lpWindowName,
		WS_OVERLAPPEDWINDOW,100,100,700,500,NULL,NULL,
		g_hInstance,pszText);
	return hWnd;
}
// 5. 显示窗口
void Display(HWND hWnd)
{
    
	ShowWindow(hWnd,SW_SHOW);
	UpdateWindow(hWnd);
}
// 6. 消息循环
void Message()
{
    
	MSG nMsg = {
    0};
	while(GetMessage(&nMsg,NULL,0,0))
	{
    
		TranslateMessage(&nMsg);
		DispatchMessage(&nMsg);
	}

/*	while(1){
		if(PeekMessage(&nMsg,NULL,0,0,PM_NOREMOVE))
		{
			// 有消息
			if(GetMessage(&nMsg,NULL,0,0))
			{ // 不是WM_QUIT
				TranslateMessage(&nMsg);
				DispatchMessage(&nMsg);
			}else{ // WM_QUIT
				return;
			}
		}
		else{
			// 没有消息  做空闲处理
			WriteConsole(g_hOutput,"OnIdle",6,NULL,NULL);
		}
	}*/
}
// 1. WinMain函数
int CALLBACK WinMain(HINSTANCE hInstance,HINSTANCE hPrevIns,
	LPSTR lpCmdLine,int nCmdShow)
{
    
	AllocConsole();  // 增加一个DOS窗口
	g_hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
	g_hInstance = hInstance;
	Register("Main",WndProc);
	HWND hWnd = CreateMain("Main","Window");
	Display(hWnd);
	Message();
	return 0;
}

添加DOS窗口

HANDLE g_hOutput = 0; //保存标准输出句柄

AllocConsole();
g_hOutput = GetStdHandle(STD_OUTPUT_HANDLE);

消息获取

在这里插入图片描述
参考代码:

//消息循环
void Message() {
    
	MSG nMsg = {
     0 };
	/*
	while (GetMessage(&nMsg, NULL, 0, 0)) {
		TranslateMessage(&nMsg);
		DispatchMessage(&nMsg);  //将消息交给窗口处理函数(自定义)
	}
	*/
	while (1) {
    
	  if(	PeekMessage(&nMsg, NULL, 0, 0, PM_NOREMOVE)){
    
         //有消息	
		  if (GetMessage(&nMsg, NULL, 0, 0)) {
    
			  TranslateMessage(&nMsg);
			  DispatchMessage(&nMsg);
		  }
		  else {
    
			  return;
		  }
	  }
	  else {
    
	      //空闲处理 
		  WriteConsole(g_hOutput, "OnIdld", 6, NULL, NULL);
	  }
	}
}

消息的发送

在这里插入图片描述

消息分类

在这里插入图片描述
参考代码:

void OnMyMessage(HWND hWnd, WPARAM wParam, LPARAM lParam) {
    
	char szText[256] = {
     0 };
	sprintf(szText, "自定义消息被处理:wParam=%d,1Param=%d", wParam, lParam);
	MessageBox(hWnd, szText, "Infor", MB_OK);
}

消息队列

在这里插入图片描述

在这里插入图片描述

绘图消息

在这里插入图片描述
在这里插入图片描述

参考代码:

#include <windows.h>
#include <stdio.h>
HINSTANCE g_hInstance = 0;// 当前程序实例句柄
HANDLE g_hOutput = 0;
int g_xPos = 100,g_yPos = 100;
void OnKeyDown(HWND hWnd,WPARAM wParam)
{
    
	switch(wParam)
	{
    
	case 0X25:
		g_xPos-=5;
		break;
	case 0X26:
		g_yPos-=5;
		break;
	case 0X27:
		g_xPos+=5;
		break;
	case 0X28:
		g_yPos+=5;
		break;
	}
	InvalidateRect(hWnd,NULL,TRUE); 
	char szText[256] = {
    0};
	sprintf(szText,"WM_KEYDOWN : %08X\n",wParam);
	WriteConsole(g_hOutput,szText,strlen(szText),NULL,NULL);
	
}
void OnKeyUp(HWND hWnd,WPARAM wParam)
{
    
	char szText[256] = {
    0};
	sprintf(szText,"WM_KEYUP : %08X\n",wParam);
	WriteConsole(g_hOutput,szText,strlen(szText),NULL,NULL);
}
void OnPaint(HWND hWnd)
{
    
	PAINTSTRUCT ps = {
    0};
	HDC hdc = BeginPaint(hWnd,&ps);
	TextOut(hdc,g_xPos,g_yPos,"Hello",5);
	EndPaint(hWnd,&ps);
}
void OnChar(HWND hWnd,WPARAM wParam)
{
    
	char szText[256] = {
    0};
	sprintf(szText,"WM_CHAR: %08X\n",wParam);
	WriteConsole(g_hOutput,szText,strlen(szText),NULL,NULL);
}
// 2. 窗口处理函数
LRESULT CALLBACK WndProc(HWND hWnd,UINT msgID,
	WPARAM wParam,LPARAM lParam)
{
    
	switch(msgID)
	{
    
	case WM_CHAR:
		OnChar(hWnd,wParam);
		break;
	case WM_PAINT:
		OnPaint(hWnd);
		break;
	case WM_KEYUP:
		OnKeyUp(hWnd,wParam);
		break;
	case WM_KEYDOWN:
		OnKeyDown(hWnd,wParam);
		break;
	case WM_DESTROY:
		PostQuitMessage(0);  // GetMessage返回0?
		break;
	}
	return DefWindowProc(hWnd,msgID,wParam,lParam);
}
// 3. 注册窗口类
void Register(LPCSTR lpClassName,WNDPROC wndProc)
{
    
	WNDCLASS wcs  ={
    0};
	wcs.cbClsExtra = 0;				// 窗口类附加数据缓冲区
	wcs.cbWndExtra = 0;				// 窗口附加数据缓冲区
	wcs.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);// 背景色
	wcs.hCursor = NULL;				// 光标 NULL--系统默认
	wcs.hIcon = NULL;				// 图标 NULL--系统默认
	wcs.hInstance = g_hInstance;	// 当前程序实例句柄
	wcs.lpfnWndProc = wndProc;		// 窗口处理函数
	wcs.lpszClassName = lpClassName;// 窗口类名称
	wcs.lpszMenuName = NULL;		// 菜单 NULL-没有菜单
	wcs.style = CS_HREDRAW | CS_VREDRAW;// 一般风格
	RegisterClass(&wcs);// 将以上赋值写入操作系统
}
// 4. 创建窗口
HWND CreateMain(LPCSTR lpClassName,LPCSTR lpWindowName)
{
    
	HWND hWnd = CreateWindowEx(0,lpClassName,lpWindowName,
		WS_OVERLAPPEDWINDOW,100,100,700,500,NULL,NULL,
		g_hInstance,NULL);
	return hWnd;
}
// 5. 显示窗口
void Display(HWND hWnd)
{
    
	ShowWindow(hWnd,SW_SHOW);
	UpdateWindow(hWnd);
}
// 6. 消息循环
void Message()
{
    
	MSG nMsg = {
    0};
	while(GetMessage(&nMsg,NULL,0,0))
	{
    
		TranslateMessage(&nMsg);
		DispatchMessage(&nMsg);
	}
}
// 1. WinMain函数
int CALLBACK WinMain(HINSTANCE hInstance,HINSTANCE hPrevIns,
	LPSTR lpCmdLine,int nCmdShow)
{
    
	AllocConsole();
	g_hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
	g_hInstance = hInstance;
	Register("Main",WndProc);
	HWND hWnd = CreateMain("Main","Window");
	Display(hWnd);
	Message();
	return 0;
}

键盘消息

在这里插入图片描述
在这里插入图片描述

参考代码:


void OnKeyDown(HWND hWnd,WPARAM wParam)
{
    
	switch(wParam)
	{
    
	case 0X25:
		g_xPos-=5;
		break;
	case 0X26:
		g_yPos-=5;
		break;
	case 0X27:
		g_xPos+=5;
		break;
	case 0X28:
		g_yPos+=5;
		break;
	}
	InvalidateRect(hWnd,NULL,TRUE); 
	char szText[256] = {
    0};
	sprintf(szText,"WM_KEYDOWN : %08X\n",wParam);
	WriteConsole(g_hOutput,szText,strlen(szText),NULL,NULL);
	
}

// 2. 窗口处理函数
LRESULT CALLBACK WndProc(HWND hWnd,UINT msgID,
	WPARAM wParam,LPARAM lParam)
{
    
	switch(msgID)
	{
    
	case WM_CHAR:
		OnChar(hWnd,wParam);
		break;
	case WM_PAINT:
		OnPaint(hWnd);
		break;
	case WM_KEYUP:
		OnKeyUp(hWnd,wParam);
		break;
	case WM_KEYDOWN:
		OnKeyDown(hWnd,wParam);
		break;
	case WM_DESTROY:
		PostQuitMessage(0);  // GetMessage返回0?
		break;
	}
	return DefWindowProc(hWnd,msgID,wParam,lParam);
}

练习:使用键盘的方向键控制字符串在窗口客户区移动。(15分钟)

// 假设按下 A
TranslateMessage(&nMsg)
{
    
    if(nMsg.message != WM_KEYDOWN )
       return ...;
    (是WM_KEYDOWN)
    根据nMsg.wParam(按键的虚拟键值),可以获取按下的按键
    if(不是可见字符键)
      return ...;
    if(是可见字符键){
    
      判断CapsLock键的状态
      if(打开)
        PostMessage(nMsg.hwnd,WM_CHAR,0X41,...);// A:0X41
      else
        PostMessage(nMsg.hWnd,WM_CHAR,0X61,...);// a:0X61
    }
    return ...;   
}

练习:实现鼠标移动时字符串的跟随

WM_PAINT
WM_MOUSEMOVE
InvalidateRect()函数

// 定时器处理函数  
VOID CALLBACK TimerProc(
  __in  HWND hwnd,			// 窗口句柄
  __in  UINT uMsg,			// 消息ID (只能是WM_TIMER)
  __in  UINT_PTR idEvent,	// 定时器ID
  __in  DWORD dwTime			// 当前计算机的系统时间
);

鼠标消息

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

参考代码:

#include <windows.h>
#include <stdio.h>
HINSTANCE g_hInstance = 0;// 当前程序实例句柄
HANDLE g_hOutput = 0;
int g_xPos = 0,g_yPos = 0;
void OnLButtonDown(HWND hWnd,WPARAM wParam,LPARAM lParam)
{
    
	char szText[256] = {
    0};
	sprintf(szText,"WM_LBUTTONDOWN-- 按键状态:%08X,鼠标坐标(%d,%d)\n",
		wParam,LOWORD(lParam),HIWORD(lParam));
	WriteConsole(g_hOutput,szText,strlen(szText),NULL,NULL);
}
void OnLButtonUp(HWND hWnd,WPARAM wParam,LPARAM lParam)
{
    
	char szText[256] = {
    0};
	sprintf(szText,"WM_LBUTTONUP-- 按键状态:%08X,鼠标坐标(%d,%d)\n",
		wParam,LOWORD(lParam),HIWORD(lParam));
	WriteConsole(g_hOutput,szText,strlen(szText),NULL,NULL);
}
void OnPaint(HWND hWnd)
{
    
	PAINTSTRUCT ps = {
    0};
	HDC hdc = BeginPaint(hWnd,&ps);
	TextOut(hdc,g_xPos,g_yPos,"Hello",5);
	EndPaint(hWnd,&ps);
}
void OnMouseMove(HWND hWnd,LPARAM lParam)
{
    
	g_xPos = LOWORD(lParam);
	g_yPos = HIWORD(lParam);
	InvalidateRect(hWnd,NULL,FALSE);
}
void OnLButtonDblClk(HWND hWnd,WPARAM wParam,LPARAM lParam)
{
    
	char szText[256] = {
    0};
	sprintf(szText,"WM_LBUTTONDBLCLK -- 按键状态:%08X,鼠标坐标(%d,%d)\n",
		wParam,LOWORD(lParam),HIWORD(lParam));
	WriteConsole(g_hOutput,szText,strlen(szText),NULL,NULL);
}
void OnMouseWheel(HWND hWnd,WPARAM wParam)
{
    
	 short nDelta = HIWORD(wParam);
	 char szText[256] = {
    0};
	 sprintf(szText,"WM_MOUSEWHEEL: %d\n",nDelta);
	 WriteConsole(g_hOutput,szText,strlen(szText),NULL,NULL);
}
// 2. 窗口处理函数
LRESULT CALLBACK WndProc(HWND hWnd,UINT msgID,
	WPARAM wParam,LPARAM lParam)
{
    
	switch(msgID)
	{
    
	case WM_MOUSEWHEEL:
		OnMouseWheel(hWnd,wParam);
		break;
	case WM_LBUTTONDBLCLK:
		OnLButtonDblClk(hWnd,wParam,lParam);
		break;
	case WM_MOUSEMOVE:
//		OnMouseMove(hWnd,lParam);
		break;
	case WM_PAINT:
		OnPaint(hWnd);
		break;
	case WM_LBUTTONUP:
		OnLButtonUp(hWnd,wParam,lParam);
		break;
	case WM_LBUTTONDOWN:
		OnLButtonDown(hWnd,wParam,lParam);
		break;
	case WM_DESTROY:
		PostQuitMessage(0);  // GetMessage返回0?
		break;
	}
	return DefWindowProc(hWnd,msgID,wParam,lParam);
}
// 3. 注册窗口类
void Register(LPCSTR lpClassName,WNDPROC wndProc)
{
    
	WNDCLASS wcs  ={
    0};
	wcs.cbClsExtra = 0;				// 窗口类附加数据缓冲区
	wcs.cbWndExtra = 0;				// 窗口附加数据缓冲区
	wcs.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);// 背景色
	wcs.hCursor = NULL;				// 光标 NULL--系统默认
	wcs.hIcon = NULL;				// 图标 NULL--系统默认
	wcs.hInstance = g_hInstance;	// 当前程序实例句柄
	wcs.lpfnWndProc = wndProc;		// 窗口处理函数
	wcs.lpszClassName = lpClassName;// 窗口类名称
	wcs.lpszMenuName = NULL;		// 菜单 NULL-没有菜单
	wcs.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;// 一般风格
	RegisterClass(&wcs);// 将以上赋值写入操作系统
}
// 4. 创建窗口
HWND CreateMain(LPCSTR lpClassName,LPCSTR lpWindowName)
{
    
	HWND hWnd = CreateWindowEx(0,lpClassName,lpWindowName,
		WS_OVERLAPPEDWINDOW,100,100,700,500,NULL,NULL,
		g_hInstance,NULL);
	return hWnd;
}
// 5. 显示窗口
void Display(HWND hWnd)
{
    
	ShowWindow(hWnd,SW_SHOW);
	UpdateWindow(hWnd);
}
// 6. 消息循环
void Message()
{
    
	MSG nMsg = {
    0};
	while(GetMessage(&nMsg,NULL,0,0))
	{
    
		TranslateMessage(&nMsg);
		DispatchMessage(&nMsg);
	}
}
// 1. WinMain函数
int CALLBACK WinMain(HINSTANCE hInstance,HINSTANCE hPrevIns,
	LPSTR lpCmdLine,int nCmdShow)
{
    
	AllocConsole();
	g_hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
	g_hInstance = hInstance;
	Register("Main",WndProc);
	HWND hWnd = CreateMain("Main","Window");
	Display(hWnd);
	Message();
	return 0;
}

定时器消息

在这里插入图片描述
在这里插入图片描述

参考代码:

#include <windows.h>
#include <stdio.h>
HINSTANCE g_hInstance = 0;// 当前程序实例句柄
HANDLE g_hOutput = 0;
void OnTimer(HWND hWnd,WPARAM wParam)
{
    
	char szText[256] = {
    0};
	switch(wParam){
    
	case 1001:
		sprintf(szText,"窗口处理函数处理了定时器:%d\n",wParam);
		WriteConsole(g_hOutput,szText,strlen(szText),NULL,NULL);
		break;
	case 1002:
		sprintf(szText,"窗口处理函数处理了定时器:%d\n",wParam);
		WriteConsole(g_hOutput,szText,strlen(szText),NULL,NULL);
		break;
	}
}
void CALLBACK TimerProc(HWND hwnd,UINT uMsg,UINT idEvent,DWORD dwTime)
{
    
	char szText[256] = {
    0};
	switch(idEvent){
    
	case 1001:
		sprintf(szText,"定时器处理函数处理了定时器:%d\n",idEvent);
		WriteConsole(g_hOutput,szText,strlen(szText),NULL,NULL);
		break;
	case 1002:
		sprintf(szText,"定时器处理函数处理了定时器:%d\n",idEvent);
		WriteConsole(g_hOutput,szText,strlen(szText),NULL,NULL);
		break;
	}
}
// 2. 窗口处理函数
LRESULT CALLBACK WndProc(HWND hWnd,UINT msgID,
	WPARAM wParam,LPARAM lParam)
{
    
	switch(msgID)
	{
    
	case WM_LBUTTONDOWN:
		KillTimer(hWnd,1001);
		KillTimer(hWnd,1002);
		break;
	case WM_TIMER:
		OnTimer(hWnd,wParam);
		break;
	case WM_CREATE:
		SetTimer(hWnd,1001,1000,NULL);
		SetTimer(hWnd,1002,2000,TimerProc);
		break;
	case WM_DESTROY:
		PostQuitMessage(0);  // GetMessage返回0?
		break;
	}
	return DefWindowProc(hWnd,msgID,wParam,lParam);
}
// 3. 注册窗口类
void Register(LPCSTR lpClassName,WNDPROC wndProc)
{
    
	WNDCLASS wcs  ={
    0};
	wcs.cbClsExtra = 0;				// 窗口类附加数据缓冲区
	wcs.cbWndExtra = 0;				// 窗口附加数据缓冲区
	wcs.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);// 背景色
	wcs.hCursor = NULL;				// 光标 NULL--系统默认
	wcs.hIcon = NULL;				// 图标 NULL--系统默认
	wcs.hInstance = g_hInstance;	// 当前程序实例句柄
	wcs.lpfnWndProc = wndProc;		// 窗口处理函数
	wcs.lpszClassName = lpClassName;// 窗口类名称
	wcs.lpszMenuName = NULL;		// 菜单 NULL-没有菜单
	wcs.style = CS_HREDRAW | CS_VREDRAW;// 一般风格
	RegisterClass(&wcs);// 将以上赋值写入操作系统
}
// 4. 创建窗口
HWND CreateMain(LPCSTR lpClassName,LPCSTR lpWindowName)
{
    
	HWND hWnd = CreateWindowEx(0,lpClassName,lpWindowName,
		WS_OVERLAPPEDWINDOW,100,100,700,500,NULL,NULL,
		g_hInstance,NULL);
	return hWnd;
}
// 5. 显示窗口
void Display(HWND hWnd)
{
    
	ShowWindow(hWnd,SW_SHOW);
	UpdateWindow(hWnd);
}
// 6. 消息循环
void Message()
{
    
	MSG nMsg = {
    0};
	while(GetMessage(&nMsg,NULL,0,0))
	{
    
		TranslateMessage(&nMsg);
		DispatchMessage(&nMsg);
	}
}
// 1. WinMain函数
int CALLBACK WinMain(HINSTANCE hInstance,HINSTANCE hPrevIns,
	LPSTR lpCmdLine,int nCmdShow)
{
    
	AllocConsole();
	g_hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
	g_hInstance = hInstance;
	Register("Main",WndProc);
	HWND hWnd = CreateMain("Main","Window");
	Display(hWnd);
	Message();
	return 0;
}

资源的使用

在这里插入图片描述

菜单的分类使用

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

创建 WinRes项目 添加资源文件—>添加菜单资源
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

选中WinRes.rc ,添加资源
在这里插入图片描述

添加加菜单资源
在这里插入图片描述

**编辑第一个菜单下拉选项 – 新建 id_new**在这里插入图片描述

编辑第二个菜单选项 退出 id_exit
在这里插入图片描述

在这里插入图片描述
菜单项分三种
被点击后唯一功能就是 弹出下拉菜单(Popup风格)
被点击后能够发出消息
为分隔线(Separator)

新建和退出直接做一条分割线
在这里插入图片描述
在这里插入图片描述

右键菜单

在这里插入图片描述
在这里插入图片描述

参考代码:WinRes.cpp

#include <windows.h>
#include "resource.h"
HINSTANCE g_hInstance = 0;// 当前程序实例句柄
void OnPaint(HWND hWnd)
{
    
	PAINTSTRUCT ps = {
    0};
	HDC hdc = BeginPaint(hWnd,&ps);
	HICON hIcon = LoadIcon(g_hInstance,(LPCSTR)IDI_ICON1);
	DrawIcon(hdc,200,200,hIcon);
	EndPaint(hWnd,&ps);
}
void OnCommand(HWND hWnd,WPARAM wParam)
{
    
	switch(LOWORD(wParam))
	{
    
	case ID_NEW:
		if(HIWORD(wParam)){
    
           MessageBox(hWnd,"Ctrl+N被点击","Infor",MB_OK);
		}else
		   MessageBox(hWnd,"新建菜单项被点击","Infor",MB_OK);
		break;
	case ID_EXIT:
		PostQuitMessage(0);
		break;
	case ID_SET:
		MessageBox(hWnd,"Ctrl+S被点击","Infor",MB_OK);
		break;
	}
}
// 2. 窗口处理函数
LRESULT CALLBACK WndProc(HWND hWnd,UINT msgID,
	WPARAM wParam,LPARAM lParam)
{
    
	switch(msgID)
	{
    
	case WM_COMMAND:
		OnCommand(hWnd,wParam);
		break;
	case WM_SETCURSOR:
		if(LOWORD(lParam)==HTCLIENT){
    
			POINT pt = {
    0};
			GetCursorPos(&pt);  // 屏幕坐标系下的光标坐标
			ScreenToClient(hWnd,&pt); // 屏幕坐标系-->客户区坐标系
			RECT rcClient = {
    0};
			GetClientRect(hWnd,&rcClient); // 客户区坐标系下的客户区大小
			if(pt.x<rcClient.right/2)
			{
    
				HCURSOR hCur = LoadCursor(g_hInstance,(LPCSTR)IDC_CURSOR2);
				SetCursor(hCur);
			}else{
    
				HCURSOR hCur = LoadCursor(g_hInstance,(LPCSTR)IDC_CURSOR1);
				SetCursor(hCur);
			}
			return 0;
		}
		break;
	case WM_PAINT:
		OnPaint(hWnd);
		break;
	case WM_DESTROY:
		PostQuitMessage(0);  // GetMessage返回0?
		break;
	}
	return DefWindowProc(hWnd,msgID,wParam,lParam);
}
// 3. 注册窗口类
void Register(LPCSTR lpClassName,WNDPROC wndProc)
{
    
	WNDCLASS wcs  ={
    0};
	wcs.cbClsExtra = 0;				// 窗口类附加数据缓冲区
	wcs.cbWndExtra = 0;				// 窗口附加数据缓冲区
	wcs.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);// 背景色
	wcs.hCursor = LoadCursor(g_hInstance,(LPCSTR)IDC_CURSOR1);// 光标 NULL--系统默认
	wcs.hIcon = LoadIcon(g_hInstance,(LPCSTR)IDI_ICON1);// 图标 NULL--系统默认
	wcs.hInstance = g_hInstance;	// 当前程序实例句柄
	wcs.lpfnWndProc = wndProc;		// 窗口处理函数
	wcs.lpszClassName = lpClassName;// 窗口类名称
	wcs.lpszMenuName = (LPCSTR)IDR_MENU1;// 菜单 NULL-没有菜单
	wcs.style = CS_HREDRAW | CS_VREDRAW;// 一般风格
	RegisterClass(&wcs);// 将以上赋值写入操作系统
}
// 4. 创建窗口
HWND CreateMain(LPCSTR lpClassName,LPCSTR lpWindowName)
{
    
	HWND hWnd = CreateWindowEx(0,lpClassName,lpWindowName,
		WS_OVERLAPPEDWINDOW,100,100,700,500,NULL,NULL,
		g_hInstance,NULL);
	return hWnd;
}
// 5. 显示窗口
void Display(HWND hWnd)
{
    
	ShowWindow(hWnd,SW_SHOW);
	UpdateWindow(hWnd);
}
// 6. 消息循环
void Message()
{
    
	MSG nMsg = {
    0};
	HACCEL hAccel = LoadAccelerators(g_hInstance,(LPCSTR)IDR_ACCELERATOR1);
	while(GetMessage(&nMsg,NULL,0,0))
	{
    
		if(!TranslateAccelerator(nMsg.hwnd,hAccel,&nMsg))
		{
    
			TranslateMessage(&nMsg);
			DispatchMessage(&nMsg);
		}		
	}
}
// 1. WinMain函数
int CALLBACK WinMain(HINSTANCE hInstance,HINSTANCE hPrevIns,
	LPSTR lpCmdLine,int nCmdShow)
{
    
	g_hInstance = hInstance;
	Register("Main",WndProc);
	char szTitle[256] = {
    0};
	LoadString(g_hInstance,IDS_TITLE,szTitle,256);
	HWND hWnd = CreateMain("Main",szTitle);
	Display(hWnd);
	Message();
	return 0;
}

图标资源的使用

在这里插入图片描述
添加图标
在这里插入图片描述

在这里插入图片描述

光标

在这里插入图片描述
在这里插入图片描述

字符串资源

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

参考代码:

#include <windows.h>
#include <stdio.h>
#include "resource.h"
HANDLE g_hOutput = 0;
HINSTANCE g_hInstance = 0;// 当前程序实例句柄
HMENU g_hFile = 0; 
BOOL flag = FALSE; // 标志勾选状态
void OnCommand(HWND hWnd,WPARAM wParam)
{
    
	switch(LOWORD(wParam)) // 被点击菜单项的ID
	{
    
	case ID_POPUP_COPY:
		MessageBox(hWnd,"窗口处理函数处理了复制菜单项被点击",
			"Infor",MB_OK);
		break;
	case ID_NEW:
//		MessageBox(hWnd,"新建菜单项被点击","Infor",MB_OK);
		if(!flag)
			CheckMenuItem(g_hFile,ID_NEW,MF_BYCOMMAND | MF_UNCHECKED);
		else
			CheckMenuItem(g_hFile,0,MF_BYPOSITION | MF_CHECKED);
		flag = !flag;
		break;
	case ID_EXIT:
		PostQuitMessage(0);
		break;
	case ID_ABOUT:
		MessageBox(hWnd,"关于菜单项被点击","Infor",MB_OK);
		break;
	}
}
void OnInitMenuPopup(HWND hWnd,WPARAM wParam,LPARAM lParam)
{
    
	char szText[256] = {
    0};
	sprintf(szText,"即将显示的菜单的句柄:%d,"
"被点击菜单项索引:%d,即将显示的菜单是否是窗口菜单:%d\n",
		wParam,LOWORD(lParam),HIWORD(lParam));
	WriteConsole(g_hOutput,szText,strlen(szText),NULL,NULL);
}
void OnRButtonUp(HWND hWnd,LPARAM lParam)
{
    
	// 加载菜单
	HMENU hMenu = LoadMenu(g_hInstance,(LPCSTR)IDR_MENU2);
	// 获取指定菜单项下的子菜单
	HMENU hPopup = GetSubMenu(hMenu,0);
	POINT pt = {
    0};
	pt.x = LOWORD(lParam);
	pt.y = HIWORD(lParam);
	ClientToScreen(hWnd,&pt); // 客户区坐标系-->屏幕坐标系
	// 显示右键菜单
	TrackPopupMenu(hPopup,TPM_CENTERALIGN | TPM_VCENTERALIGN,
		pt.x,pt.y,0,hWnd,NULL);
}
void OnContextMenu(HWND hWnd,LPARAM lParam)
{
    
	// 加载菜单
	HMENU hMenu = LoadMenu(g_hInstance,(LPCSTR)IDR_MENU2);
	// 获取指定菜单项下的子菜单
	HMENU hPopup = GetSubMenu(hMenu,0);
	POINT pt = {
    0};
	pt.x = LOWORD(lParam);
	pt.y = HIWORD(lParam);
//	ClientToScreen(hWnd,&pt); // 客户区坐标系-->屏幕坐标系
	// 显示右键菜单
	BOOL nRet = TrackPopupMenu(hPopup,
		TPM_CENTERALIGN | TPM_VCENTERALIGN | TPM_RETURNCMD,
		pt.x,pt.y,0,hWnd,NULL);
	switch(nRet)
	{
    
	case ID_POPUP_COPY:
		MessageBox(hWnd,"没有发出WM_COMMAND消息,处理了复制菜单项被点击",
			"Infor",MB_OK);
		break;
	}
}
// 2. 窗口处理函数
LRESULT CALLBACK WndProc(HWND hWnd,UINT msgID,
	WPARAM wParam,LPARAM lParam)
{
    
	switch(msgID)
	{
    
	case WM_CONTEXTMENU:
		OnContextMenu(hWnd,lParam);
		break;
	case WM_RBUTTONUP:
//		OnRButtonUp(hWnd,lParam);
		break;
	case WM_INITMENUPOPUP:
		OnInitMenuPopup(hWnd,wParam,lParam);
		break;
	case WM_COMMAND:
		OnCommand(hWnd,wParam);
		break;
	case WM_DESTROY:
		PostQuitMessage(0);  // GetMessage返回0?
		break;
	}
	return DefWindowProc(hWnd,msgID,wParam,lParam);
}
// 3. 注册窗口类
void Register(LPCSTR lpClassName,WNDPROC wndProc)
{
    
	WNDCLASS wcs  ={
    0};
	wcs.cbClsExtra = 0;				// 窗口类附加数据缓冲区
	wcs.cbWndExtra = 0;				// 窗口附加数据缓冲区
	wcs.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);// 背景色
	wcs.hCursor = NULL;				// 光标 NULL--系统默认
	wcs.hIcon = NULL;				// 图标 NULL--系统默认
	wcs.hInstance = g_hInstance;	// 当前程序实例句柄
	wcs.lpfnWndProc = wndProc;		// 窗口处理函数
	wcs.lpszClassName = lpClassName;// 窗口类名称
	wcs.lpszMenuName = NULL;//(LPCSTR)IDR_MENU1;// 菜单 NULL-没有菜单
	wcs.style = CS_HREDRAW | CS_VREDRAW;// 一般风格
	RegisterClass(&wcs);// 将以上赋值写入操作系统
}
// 4. 创建窗口
HWND CreateMain(LPCSTR lpClassName,LPCSTR lpWindowName)
{
    
	HMENU hMenu = LoadMenu(g_hInstance,(LPCSTR)IDR_MENU1);
	g_hFile = GetSubMenu(hMenu,0);
	HWND hWnd = CreateWindowEx(0,lpClassName,lpWindowName,
		WS_OVERLAPPEDWINDOW,100,100,700,500,NULL,hMenu,
		g_hInstance,NULL);
//	SetMenu(hWnd,hMenu);
	return hWnd;
}
// 5. 显示窗口
void Display(HWND hWnd)
{
    
	ShowWindow(hWnd,SW_SHOW);
	UpdateWindow(hWnd);
}
// 6. 消息循环
void Message()
{
    
	MSG nMsg = {
    0};
	while(GetMessage(&nMsg,NULL,0,0))
	{
    
		TranslateMessage(&nMsg);
		DispatchMessage(&nMsg);
	}
}
// 1. WinMain函数
int CALLBACK WinMain(HINSTANCE hInstance,HINSTANCE hPrevIns,
	LPSTR lpCmdLine,int nCmdShow)
{
    
	AllocConsole();
	g_hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
	g_hInstance = hInstance;
	Register("Main",WndProc);
	HWND hWnd = CreateMain("Main","Window");
	Display(hWnd);
	Message();
	return 0;
}

加速键资源的使用

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

测试按 Crtl +M和crtl +P
在这里插入图片描述
// 假设按下 Ctrl + N

TranslateAccelerator(hWnd,hAccel,&nMsg)
{
    
   if(nMsg.message != WM_KEYDOWN &&
         nMsg.message != WM_SYSKEYDOWN)
   {
    
      return 0;  // 没有键盘按键被按下,不可能是加速键
   }
   根据 nMsg.wParam(虚拟键值),可以获知哪些按键被按下(Ctrl+N)
   根据加速键句柄,到加速键表中查找是否有匹配的加速键
   if(不是)
      return 0; // 不是加速键
   else
   {
    
      SendMessage(hWnd,WM_COMMAND,ID_NEW///1,0);
      return 1;
   }
}

参考代码:

#include <windows.h>
#include <stdio.h>
#include "../WinRes/resource.h"
HINSTANCE g_hIns = 0; //保存当前程序实例句柄

void OnCommand(HWND hWnd, WPARAM wParam) {
    
	switch (LOWORD(wParam))   //被点击菜单项的ID
	{
    
	case ID_NEW:
		MessageBox(hWnd, "新建菜单项被点击", "Infor", MB_OK);
		break;
	}
}


//窗口处理函数(自定义,处理消息)
LRESULT CALLBACK WndProc(HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam) {
    
	switch (msgID) {
    
	case WM_COMMAND:
		OnCommand(hWnd, wParam);
		break;
	case WM_DESTROY:
		PostQuitMessage(0);   //能够是GetMessage函数返回0  
		break;
	}
	return DefWindowProc(hWnd, msgID, wParam, lParam);  //给各种消息做默认处理
}

//注册窗口类
void Register(LPSTR lpClassName, WNDPROC wndproc) {
    
	WNDCLASS wc = {
     0 };
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
	wc.hCursor = NULL;
	wc.hIcon = NULL;
	wc.hInstance = g_hIns;
	wc.lpfnWndProc = WndProc;
	wc.lpszClassName = lpClassName;
	wc.lpszClassName = "Main";
	wc.lpszMenuName = (CHAR *)IDR_MENU1;
	wc.style = CS_HREDRAW | CS_VREDRAW;
	RegisterClass(&wc);  //将以上所有赋值全部写入操作系统内核
}

//创建主窗口
HWND CreateMain(LPSTR lpClassName, LPSTR lpWndName) {
    
	HWND hWnd = CreateWindowEx(0, lpClassName, lpWndName, WS_OVERLAPPEDWINDOW, 100, 100, 700, 500, NULL, NULL, g_hIns, NULL);
	return hWnd;
}

//显示窗口
void Display(HWND hWnd) {
    
	ShowWindow(hWnd, SW_SHOW);
	UpdateWindow(hWnd);  //刷新窗口
}

//消息循环
void Message() {
    
	MSG nMsg = {
     0 };
	HACCEL hAccel = LoadAccelerators(g_hIns, (CHAR*)RT_ACCELERATOR);
	while (GetMessage(&nMsg, NULL, 0, 0)) {
    
		if (! TranslateAccelerator(nMsg.hwnd, hAccel, &nMsg)) {
    
			TranslateMessage(&nMsg);
			DispatchMessage(&nMsg);  //将消息交给窗口处理函数(自定义)
		}
	}
}

//入口函数
int CALLBACK WinMain(HINSTANCE hIns, HINSTANCE hPreIns, LPSTR lpCmdLine, int nCmdShow) {
    
	g_hIns = hIns;
	Register("Main", WndProc);
	HWND hWnd = CreateMain("Main", "window");
	Display(hWnd);
	Message();
	return 0;
}

Windows绘图

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
获取绘图对象的信息

int GetObject(
  __in   HGDIOBJ hgdiobj,   // 绘图对象的句柄
  __in   int cbBuffer,      // 缓冲区大小
  __out  LPVOID lpvObject   // 缓冲区首地址
);

参考代码:

#include <windows.h>
HINSTANCE g_hInstance = 0;// 当前程序实例句柄
void DrawPixel(HDC hdc)
{
    
//	SetPixel(hdc,100,100,RGB(255,0,0));
	for(int i=0;i<256;i++){
    
		for(int j=0;j<256;j++){
    
			SetPixel(hdc,100+i,100+j,RGB(255,i,j));
		}
	}
}
void DrawLine(HDC hdc)
{
    
	MoveToEx(hdc,100,100,NULL);
	LineTo(hdc,300,300);
	LineTo(hdc,100,300);
	LineTo(hdc,100,100);
}
void DrawRect(HDC hdc)
{
    
	Rectangle(hdc,100,100,300,300);
	RoundRect(hdc,300,100,600,300,50,50);
}
void DrawEll(HDC hdc)
{
    
	Ellipse(hdc,100,100,300,300);
	Ellipse(hdc,300,100,600,300);
}
void DrawArc(HDC hdc)
{
    
//	Rectangle(hdc,100,100,300,300);
	Arc(hdc,100,100,300,300,200,50,50,200);
}
void OnPaint(HWND hWnd)
{
    
	PAINTSTRUCT ps = {
    0};
	HDC hdc = BeginPaint(hWnd,&ps);

	// 1. 创建画笔
	HPEN hPen = CreatePen(PS_SOLID,5,RGB(150,220,220));
	// 2. 将画笔应用到DC中
	HGDIOBJ hOldPen = SelectObject(hdc,hPen);

	// 创建画刷
//	HBRUSH hBrush = CreateSolidBrush(RGB(200,220,220));
//	HBRUSH hBrush = CreateHatchBrush(HS_DIAGCROSS,RGB(180,220,220));
	HGDIOBJ hBrush = GetStockObject(NULL_BRUSH);
	// 将画刷应用到DC中
	HGDIOBJ hOldBrush = SelectObject(hdc,hBrush);
	// 3. 绘图
//	DrawPixel(hdc);   // 设置一个点的颜色值
//	DrawLine(hdc);    // 绘制直线
	DrawRect(hdc);    // 绘制矩形
//	DrawEll(hdc);     // 绘制椭圆
//	DrawArc(hdc);     // 绘制弧线
	// 将画刷从DC中取出
	SelectObject(hdc,hOldBrush);
	// 释放画刷
//	DeleteObject(hBrush);
	// 4. 从DC中取出画笔
	SelectObject(hdc,hOldPen);
	// 5. 释放画笔
	DeleteObject(hPen);
	EndPaint(hWnd,&ps);
}
// 2. 窗口处理函数
LRESULT CALLBACK WndProc(HWND hWnd,UINT msgID,
	WPARAM wParam,LPARAM lParam)
{
    
	switch(msgID)
	{
    
	case WM_PAINT:
		OnPaint(hWnd);
		break;
	case WM_DESTROY:
		PostQuitMessage(0);  // GetMessage返回0?
		break;
	}
	return DefWindowProc(hWnd,msgID,wParam,lParam);
}
// 3. 注册窗口类
void Register(LPCSTR lpClassName,WNDPROC wndProc)
{
    
	WNDCLASS wcs  ={
    0};
	wcs.cbClsExtra = 0;				// 窗口类附加数据缓冲区
	wcs.cbWndExtra = 0;				// 窗口附加数据缓冲区
	wcs.hbrBackground = CreateSolidBrush(RGB(200,220,220));// 背景色
	wcs.hCursor = NULL;				// 光标 NULL--系统默认
	wcs.hIcon = NULL;				// 图标 NULL--系统默认
	wcs.hInstance = g_hInstance;	// 当前程序实例句柄
	wcs.lpfnWndProc = wndProc;		// 窗口处理函数
	wcs.lpszClassName = lpClassName;// 窗口类名称
	wcs.lpszMenuName = NULL;		// 菜单 NULL-没有菜单
	wcs.style = CS_HREDRAW | CS_VREDRAW;// 一般风格
	RegisterClass(&wcs);// 将以上赋值写入操作系统
}
// 4. 创建窗口
HWND CreateMain(LPCSTR lpClassName,LPCSTR lpWindowName)
{
    
	HWND hWnd = CreateWindowEx(0,lpClassName,lpWindowName,
		WS_OVERLAPPEDWINDOW,100,100,700,500,NULL,NULL,
		g_hInstance,NULL);
	return hWnd;
}
// 5. 显示窗口
void Display(HWND hWnd)
{
    
	ShowWindow(hWnd,SW_SHOW);
	UpdateWindow(hWnd);
}
// 6. 消息循环
void Message()
{
    
	MSG nMsg = {
    0};
	while(GetMessage(&nMsg,NULL,0,0))
	{
    
		TranslateMessage(&nMsg);
		DispatchMessage(&nMsg);
	}
}
// 1. WinMain函数
int CALLBACK WinMain(HINSTANCE hInstance,HINSTANCE hPrevIns,
	LPSTR lpCmdLine,int nCmdShow)
{
    
	g_hInstance = hInstance;
	Register("Main",WndProc);
	HWND hWnd = CreateMain("Main","Window");
	Display(hWnd);
	Message();
	return 0;
}

GDI绘图对象-画笔

在这里插入图片描述

在这里插入图片描述

GDI绘图对象- 画刷

在这里插入图片描述
在这里插入图片描述

参考代码

void OnPaint(HWND hWnd)
{
    
	PAINTSTRUCT ps = {
    0};
	HDC hdc = BeginPaint(hWnd,&ps);

	// 1. 创建画笔
	HPEN hPen = CreatePen(PS_SOLID,5,RGB(150,220,220));
	// 2. 将画笔应用到DC中
	HGDIOBJ hOldPen = SelectObject(hdc,hPen);

	// 创建画刷
//	HBRUSH hBrush = CreateSolidBrush(RGB(200,220,220));
//	HBRUSH hBrush = CreateHatchBrush(HS_DIAGCROSS,RGB(180,220,220));
	HGDIOBJ hBrush = GetStockObject(NULL_BRUSH);
	// 将画刷应用到DC中
	HGDIOBJ hOldBrush = SelectObject(hdc,hBrush);
	// 3. 绘图
//	DrawPixel(hdc);   // 设置一个点的颜色值
//	DrawLine(hdc);    // 绘制直线
	DrawRect(hdc);    // 绘制矩形
//	DrawEll(hdc);     // 绘制椭圆
//	DrawArc(hdc);     // 绘制弧线
	// 将画刷从DC中取出
	SelectObject(hdc,hOldBrush);
	// 释放画刷
//	DeleteObject(hBrush);
	// 4. 从DC中取出画笔
	SelectObject(hdc,hOldPen);
	// 5. 释放画笔
	DeleteObject(hPen);
	EndPaint(hWnd,&ps);
}

GDI绘图对象-位图

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

参考代码:

#include <windows.h>
#include "resource.h"
HINSTANCE g_hInstance = 0;// 当前程序实例句柄
void DrawBmp(HDC hdc)
{
    
	// 1. 添加位图资源(不需要写代码)
	// 2. 加载位图
	HBITMAP hBmp = LoadBitmap(g_hInstance,(LPCSTR)IDB_BITMAP2);
	// 3. 创建一个和当前DC相匹配的内存DC
	// 同时在内存中构建一个虚拟区域,内存DC对应这块虚拟区域
	/*
	   创建一个内存DC的内核结构,同时申请一块内存,内存DC维护这块内存
	   申请的内存空间和当前DC维护的显存空间大小相同
	   当前DC维护的显存空间大小由窗口大小决定
	*/
	HDC hMemDC = CreateCompatibleDC(hdc);
	// 4. 将位图数据放入内存DC中,内存DC立即在虚拟区域绘制位图的图像
	/*
	   将位图的数据存入内存DC维护的那块内存中
	*/
	HGDIOBJ hOldBmp = SelectObject(hMemDC,hBmp);
	// 5. 成像(绘制位图)
	/*
	   将内存DC维护的内存中的数据,以拷贝内存的方式一次性放入当前DC维护的显存中
	*/

	BITMAP bmpInfo = {
    0};
	GetObject(hBmp,sizeof(bmpInfo),&bmpInfo);

	BitBlt(hdc,50,50,bmpInfo.bmWidth,bmpInfo.bmHeight,hMemDC,0,0,NOTSRCCOPY);

	StretchBlt(hdc,300,50,bmpInfo.bmWidth*2,bmpInfo.bmHeight*2,
		hMemDC,0,0,bmpInfo.bmWidth,bmpInfo.bmHeight,SRCCOPY);

	// 6. 从内存DC中取出位图
	SelectObject(hMemDC,hOldBmp);
	// 7. 释放位图
	DeleteObject(hBmp);
	// 8. 释放内存DC
	DeleteDC(hMemDC);
}
void OnPaint(HWND hWnd)
{
    
	PAINTSTRUCT ps = {
    0};
	HDC hdc = BeginPaint(hWnd,&ps);
	// 绘制位图
	DrawBmp(hdc);
	EndPaint(hWnd,&ps);
}
// 2. 窗口处理函数
LRESULT CALLBACK WndProc(HWND hWnd,UINT msgID,
	WPARAM wParam,LPARAM lParam)
{
    
	switch(msgID)
	{
    
	case WM_PAINT:
		OnPaint(hWnd);
		break;
	case WM_DESTROY:
		PostQuitMessage(0);  // GetMessage返回0?
		break;
	}
	return DefWindowProc(hWnd,msgID,wParam,lParam);
}
// 3. 注册窗口类
void Register(LPCSTR lpClassName,WNDPROC wndProc)
{
    
	WNDCLASS wcs  ={
    0};
	wcs.cbClsExtra = 0;				// 窗口类附加数据缓冲区
	wcs.cbWndExtra = 0;				// 窗口附加数据缓冲区
	wcs.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);// 背景色
	wcs.hCursor = NULL;				// 光标 NULL--系统默认
	wcs.hIcon = NULL;				// 图标 NULL--系统默认
	wcs.hInstance = g_hInstance;	// 当前程序实例句柄
	wcs.lpfnWndProc = wndProc;		// 窗口处理函数
	wcs.lpszClassName = lpClassName;// 窗口类名称
	wcs.lpszMenuName = NULL;		// 菜单 NULL-没有菜单
	wcs.style = CS_HREDRAW | CS_VREDRAW;// 一般风格
	RegisterClass(&wcs);// 将以上赋值写入操作系统
}
// 4. 创建窗口
HWND CreateMain(LPCSTR lpClassName,LPCSTR lpWindowName)
{
    
	HWND hWnd = CreateWindowEx(0,lpClassName,lpWindowName,
		WS_OVERLAPPEDWINDOW,100,100,700,500,NULL,NULL,
		g_hInstance,NULL);
	return hWnd;
}
// 5. 显示窗口
void Display(HWND hWnd)
{
    
	ShowWindow(hWnd,SW_SHOW);
	UpdateWindow(hWnd);
}
// 6. 消息循环
void Message()
{
    
	MSG nMsg = {
    0};
	while(GetMessage(&nMsg,NULL,0,0))
	{
    
		TranslateMessage(&nMsg);
		DispatchMessage(&nMsg);
	}
}
// 1. WinMain函数
int CALLBACK WinMain(HINSTANCE hInstance,HINSTANCE hPrevIns,
	LPSTR lpCmdLine,int nCmdShow)
{
    
	g_hInstance = hInstance;
	Register("Main",WndProc);
	HWND hWnd = CreateMain("Main","Window");
	Display(hWnd);
	Message();
	return 0;
}

文字和字体

文字的绘制

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

参考代码:WinText.cpp

#include <windows.h>
HINSTANCE g_hInstance = 0;// 当前程序实例句柄
void OnPaint(HWND hWnd)
{
    
	PAINTSTRUCT ps = {
    0};
	HDC hdc = BeginPaint(hWnd,&ps);
	// 设置文字颜色
	SetTextColor(hdc,RGB(255,0,0));
	// 设置文字背景颜色
//	SetBkColor(hdc,RGB(250,165,165));
	// 设置文字背景模式
	SetBkMode(hdc,TRANSPARENT);

	// 创建字体
	HFONT hFont = CreateFont(30,0,100,0,FW_THIN,0,1,0,
		     GB2312_CHARSET,0,0,0,0,"LiSu");
	// 将字体应用到DC中
	HGDIOBJ hOldFont = SelectObject(hdc,hFont);
	char szText[] = "达内科技";
	// 绘制字符串
	TextOut(hdc,100,100,szText,strlen(szText));
	RECT rc = {
    0};
	rc.left = 100;
	rc.top = 200;
	rc.right = 400;
	rc.bottom = 400;
//	Rectangle(hdc,100,150,200,200);
	DrawText(hdc,szText,strlen(szText),&rc,
		DT_LEFT | DT_VCENTER | DT_SINGLELINE);
	// 从DC中取出字体
	SelectObject(hdc,hOldFont);
	// 释放字体
	DeleteObject(hFont);
	EndPaint(hWnd,&ps);
}
// 2. 窗口处理函数
LRESULT CALLBACK WndProc(HWND hWnd,UINT msgID,
	WPARAM wParam,LPARAM lParam)
{
    
	switch(msgID)
	{
    
	case WM_PAINT:
		OnPaint(hWnd);
		break;
	case WM_DESTROY:
		PostQuitMessage(0);  // GetMessage返回0?
		break;
	}
	return DefWindowProc(hWnd,msgID,wParam,lParam);
}
// 3. 注册窗口类
void Register(LPCSTR lpClassName,WNDPROC wndProc)
{
    
	WNDCLASS wcs  ={
    0};
	wcs.cbClsExtra = 0;				// 窗口类附加数据缓冲区
	wcs.cbWndExtra = 0;				// 窗口附加数据缓冲区
	wcs.hbrBackground = CreateSolidBrush(RGB(220,200,220));// 背景色
	wcs.hCursor = NULL;				// 光标 NULL--系统默认
	wcs.hIcon = NULL;				// 图标 NULL--系统默认
	wcs.hInstance = g_hInstance;	// 当前程序实例句柄
	wcs.lpfnWndProc = wndProc;		// 窗口处理函数
	wcs.lpszClassName = lpClassName;// 窗口类名称
	wcs.lpszMenuName = NULL;		// 菜单 NULL-没有菜单
	wcs.style = CS_HREDRAW | CS_VREDRAW;// 一般风格
	RegisterClass(&wcs);// 将以上赋值写入操作系统
}
// 4. 创建窗口
HWND CreateMain(LPCSTR lpClassName,LPCSTR lpWindowName)
{
    
	HWND hWnd = CreateWindowEx(0,lpClassName,lpWindowName,
		WS_OVERLAPPEDWINDOW,100,100,700,500,NULL,NULL,
		g_hInstance,NULL);
	return hWnd;
}
// 5. 显示窗口
void Display(HWND hWnd)
{
    
	ShowWindow(hWnd,SW_SHOW);
	UpdateWindow(hWnd);
}
// 6. 消息循环
void Message()
{
    
	MSG nMsg = {
    0};
	while(GetMessage(&nMsg,NULL,0,0))
	{
    
		TranslateMessage(&nMsg);
		DispatchMessage(&nMsg);
	}
}
// 1. WinMain函数
int CALLBACK WinMain(HINSTANCE hInstance,HINSTANCE hPrevIns,
	LPSTR lpCmdLine,int nCmdShow)
{
    
	g_hInstance = hInstance;
	Register("Main",WndProc);
	HWND hWnd = CreateMain("Main","Window");
	Display(hWnd);
	Message();
	return 0;
}

对话框窗口

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

参考代码1:WinNDlg

#include <windows.h>
#include "resource.h"
HINSTANCE g_hInstance = 0;// 当前程序实例句柄
// 对话框处理函数
INT CALLBACK DlgProc(HWND hWndDlg,UINT msgID,WPARAM wParam,LPARAM lParam)
{
    
	switch(msgID)
	{
    
	case WM_DESTROY:
		MessageBox(hWndDlg,"对话框窗口即将被销毁","提示",MB_OK);
		break;
	case WM_SYSCOMMAND:
		if(wParam == SC_CLOSE)
			DestroyWindow(hWndDlg);
	//		EndDialog(hWndDlg,1001);
		break;
	}
	return FALSE;
}
void OnCommand(HWND hWnd,WPARAM wParam)
{
    
	switch(LOWORD(wParam))
	{
    
	case ID_MODELESS:
		HWND hWndDlg = CreateDialog(g_hInstance,(LPCSTR)IDD_DIALOG1,hWnd,DlgProc);
		ShowWindow(hWndDlg,SW_SHOW);
		break;
	}
}
// 2. 窗口处理函数
LRESULT CALLBACK WndProc(HWND hWnd,UINT msgID,
	WPARAM wParam,LPARAM lParam)
{
    
	switch(msgID)
	{
    
	case WM_COMMAND:
		OnCommand(hWnd,wParam);
		break;
	case WM_DESTROY:
		PostQuitMessage(0);  // GetMessage返回0?
		break;
	}
	return DefWindowProc(hWnd,msgID,wParam,lParam);
}
// 3. 注册窗口类
void Register(LPCSTR lpClassName,WNDPROC wndProc)
{
    
	WNDCLASS wcs  ={
    0};
	wcs.cbClsExtra = 0;				// 窗口类附加数据缓冲区
	wcs.cbWndExtra = 0;				// 窗口附加数据缓冲区
	wcs.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);// 背景色
	wcs.hCursor = NULL;				// 光标 NULL--系统默认
	wcs.hIcon = NULL;				// 图标 NULL--系统默认
	wcs.hInstance = g_hInstance;	// 当前程序实例句柄
	wcs.lpfnWndProc = wndProc;		// 窗口处理函数
	wcs.lpszClassName = lpClassName;// 窗口类名称
	wcs.lpszMenuName = (LPCSTR)IDR_MENU1;// 菜单 NULL-没有菜单
	wcs.style = CS_HREDRAW | CS_VREDRAW;// 一般风格
	RegisterClass(&wcs);// 将以上赋值写入操作系统
}
// 4. 创建窗口
HWND CreateMain(LPCSTR lpClassName,LPCSTR lpWindowName)
{
    
	HWND hWnd = CreateWindowEx(0,lpClassName,lpWindowName,
		WS_OVERLAPPEDWINDOW,100,100,700,500,NULL,NULL,
		g_hInstance,NULL);
	return hWnd;
}
// 5. 显示窗口
void Display(HWND hWnd)
{
    
	ShowWindow(hWnd,SW_SHOW);
	UpdateWindow(hWnd);
}
// 6. 消息循环
void Message()
{
    
	MSG nMsg = {
    0};
	while(GetMessage(&nMsg,NULL,0,0))
	{
    
		TranslateMessage(&nMsg);
		DispatchMessage(&nMsg);
	}
}
// 1. WinMain函数
int CALLBACK WinMain(HINSTANCE hInstance,HINSTANCE hPrevIns,
	LPSTR lpCmdLine,int nCmdShow)
{
    
	g_hInstance = hInstance;
	Register("Main",WndProc);
	HWND hWnd = CreateMain("Main","Window");
	Display(hWnd);
	Message();
	return 0;
}

参考代码2:WinMDlg.cpp

#include <windows.h>
#include "resource.h"
HINSTANCE g_hInstance = 0;// 当前程序实例句柄
// 对话框处理函数
INT CALLBACK DlgProc(HWND hWndDlg,UINT msgID,WPARAM wParam,LPARAM lParam)
{
    
	switch(msgID)
	{
    
	case WM_INITDIALOG:
		MessageBox(hWndDlg,"WM_INITDIALOG","Infor",MB_OK);
		break;
/*	case WM_CREATE:
		MessageBox(hWndDlg,"WM_CREATE","Infor",MB_OK);
		break;*/
	case WM_SYSCOMMAND:
		if(wParam == SC_CLOSE)
			EndDialog(hWndDlg,1001);
//			DestroyWindow(hWndDlg);
		break;
	}
	return FALSE; // 将消息交给真正的对话框窗口处理函数的后续代码处理
}
void OnCommand(HWND hWnd,WPARAM wParam)
{
    
	switch(LOWORD(wParam))// 菜单项ID
	{
    
	case ID_MODAL:
		int nRet = DialogBox(g_hInstance,(LPCSTR)IDD_DIALOG1,hWnd,DlgProc);
		if(nRet==1001)
		{
    
			MessageBox(hWnd,"successful!","Infor",MB_OK);
		}
		break;
	}
}
// 2. 窗口处理函数
LRESULT CALLBACK WndProc(HWND hWnd,UINT msgID,
	WPARAM wParam,LPARAM lParam)
{
    
	switch(msgID)
	{
    
	case WM_COMMAND:
		OnCommand(hWnd,wParam);
		break;
	case WM_DESTROY:
		PostQuitMessage(0);  // GetMessage返回0?
		break;
	}
	return DefWindowProc(hWnd,msgID,wParam,lParam);
}
// 3. 注册窗口类
void Register(LPCSTR lpClassName,WNDPROC wndProc)
{
    
	WNDCLASS wcs  ={
    0};
	wcs.cbClsExtra = 0;				// 窗口类附加数据缓冲区
	wcs.cbWndExtra = 0;				// 窗口附加数据缓冲区
	wcs.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);// 背景色
	wcs.hCursor = NULL;				// 光标 NULL--系统默认
	wcs.hIcon = NULL;				// 图标 NULL--系统默认
	wcs.hInstance = g_hInstance;	// 当前程序实例句柄
	wcs.lpfnWndProc = wndProc;		// 窗口处理函数
	wcs.lpszClassName = lpClassName;// 窗口类名称
	wcs.lpszMenuName = (LPCSTR)IDR_MENU1;// 菜单 NULL-没有菜单
	wcs.style = CS_HREDRAW | CS_VREDRAW;// 一般风格
	RegisterClass(&wcs);// 将以上赋值写入操作系统
}
// 4. 创建窗口
HWND CreateMain(LPCSTR lpClassName,LPCSTR lpWindowName)
{
    
	HWND hWnd = CreateWindowEx(0,lpClassName,lpWindowName,
		WS_OVERLAPPEDWINDOW,100,100,700,500,NULL,NULL,
		g_hInstance,NULL);
	return hWnd;
}
// 5. 显示窗口
void Display(HWND hWnd)
{
    
	ShowWindow(hWnd,SW_SHOW);
	UpdateWindow(hWnd);
}
// 6. 消息循环
void Message()
{
    
	MSG nMsg = {
    0};
	while(GetMessage(&nMsg,NULL,0,0))
	{
    
		TranslateMessage(&nMsg);
		DispatchMessage(&nMsg);
	}
}
// 1. WinMain函数
int CALLBACK WinMain(HINSTANCE hInstance,HINSTANCE hPrevIns,
	LPSTR lpCmdLine,int nCmdShow)
{
    
	g_hInstance = hInstance;
	Register("Main",WndProc);
	HWND hWnd = CreateMain("Main","Window");
	Display(hWnd);
	Message();
	return 0;
}

子控件

在这里插入图片描述

按钮

在这里插入图片描述

下拉按钮

在这里插入图片描述

复选框

在这里插入图片描述

参考代码:

#include <windows.h>
#include <stdio.h>
HINSTANCE g_hIns = 0; //保存当前程序实例句柄
void OnCreate(HWND hWnd) {
    
	CreateWindowEx(0, "BUTTON", "OK", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON|BS_NOTIFY, 50, 50, 100, 40, hWnd, (HMENU)1001, g_hIns, NULL);
	CreateWindowEx(0, "BUTTON", "GroupButton", WS_CHILD | WS_VISIBLE |BS_GROUPBOX, 30, 30, 300, 400, hWnd, (HMENU)1002, g_hIns, NULL);
	CreateWindowEx(0, "BUTTON", "CheckBox", WS_CHILD | WS_VISIBLE |BS_CHECKBOX, 50, 180, 200, 200, hWnd, (HMENU)1003, g_hIns, NULL);
	CreateWindowEx(0, "BUTTON", "CheckBox", WS_CHILD | WS_VISIBLE |BS_AUTOCHECKBOX, 50, 220, 200, 30, hWnd, (HMENU)1004, g_hIns, NULL);

	CreateWindowEx(0, "BUTTON", "霸天虎", WS_CHILD | WS_VISIBLE |BS_AUTOCHECKBOX|WS_GROUP , 50, 180, 200, 20, hWnd, (HMENU)1005, g_hIns, NULL);
	CreateWindowEx(0, "BUTTON", "汽车人", WS_CHILD | WS_VISIBLE |BS_AUTOCHECKBOX, 50, 180, 210, 20, hWnd, (HMENU)1006, g_hIns, NULL);
	
	CreateWindowEx(0, "BUTTON", "喜洋洋", WS_CHILD | WS_VISIBLE |BS_AUTOCHECKBOX |WS_GROUP, 50, 180, 240, 20, hWnd, (HMENU)1007, g_hIns, NULL);
	CreateWindowEx(0, "BUTTON", "灰太狼", WS_CHILD | WS_VISIBLE |BS_AUTOCHECKBOX, 50, 180, 270, 20, hWnd, (HMENU)1008, g_hIns, NULL);
}

void OnCommand(HWND hWnd, WPARAM wParam) {
    
	switch (LOWORD(wParam))
	{
    
	case 1003:
		if (HIWORD(wParam) == BN_CLICKED) {
    
			HWND hCheck = GetDlgItem(hWnd, 1003); //通过控件ID获取控件句柄
			LRESULT nState =  SendMessage(hCheck, BM_GETCHECK, 0, 0);
			if (nState == BST_CHECKED) 
				SendMessage(hCheck, BM_SETCHECK, BST_UNCHECKED, 0);
			else
				SendMessage(hCheck, BM_SETCHECK, BST_CHECKED, 0);
		}
		break;
	case 1001:
		if (HIWORD(wParam) == BN_CLICKED) {
    
			MessageBox(hWnd, "单击了按钮", "Infor", MB_OK);
		}
		else if (HIWORD(wParam) == BN_CLICKED) {
    
			MessageBox(hWnd, "双击了按钮", "Infor", MB_OK);
		}
		break;
	}
}


//窗口处理函数(自定义,处理消息)
LRESULT CALLBACK WndProc(HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam) {
    
	switch (msgID) {
    
	case WM_COMMAND:
		OnCommand(hWnd, wParam);
		break;
	case WM_CREATE:
		OnCreate(hWnd);
		break;
	case WM_DESTROY:
		PostQuitMessage(0);   //能够是GetMessage函数返回0  
		break;
	}
	return DefWindowProc(hWnd, msgID, wParam, lParam);  //给各种消息做默认处理
}

//注册窗口类
void Register(LPSTR lpClassName, WNDPROC wndproc) {
    
	WNDCLASS wc = {
     0 };
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
	wc.hCursor = NULL;
	wc.hIcon = NULL;
	wc.hInstance = g_hIns;
	wc.lpfnWndProc = WndProc;
	wc.lpszClassName = lpClassName;
	wc.lpszClassName = "Main";
	wc.lpszMenuName = NULL;
	wc.style = CS_HREDRAW | CS_VREDRAW;
	RegisterClass(&wc);  //将以上所有赋值全部写入操作系统内核
}

//创建主窗口
HWND CreateMain(LPSTR lpClassName, LPSTR lpWndName) {
    
	HWND hWnd = CreateWindowEx(0, lpClassName, lpWndName, WS_OVERLAPPEDWINDOW, 100, 100, 700, 500, NULL, NULL, g_hIns, NULL);
	return hWnd;
}

//显示窗口
void Display(HWND hWnd) {
    
	ShowWindow(hWnd, SW_SHOW);
	UpdateWindow(hWnd);  //刷新窗口
}

//消息循环
void Message() {
    
	MSG nMsg = {
     0 };
	while (GetMessage(&nMsg, NULL, 0, 0)) {
    
		TranslateMessage(&nMsg);
		DispatchMessage(&nMsg);  //将消息交给窗口处理函数(自定义)
	}
}

//入口函数
int CALLBACK WinMain(HINSTANCE hIns, HINSTANCE hPreIns, LPSTR lpCmdLine, int nCmdShow) {
    
	g_hIns = hIns;
	Register("Main", WndProc);
	HWND hWnd = CreateMain("Main", "window");
	Display(hWnd);
	Message();
	return 0;
}

编辑框

在这里插入图片描述
参考代码:

#include <windows.h>
#include <stdio.h>
HINSTANCE g_hIns = 0; //保存当前程序实例句柄

void OnCreate(HWND hWnd) {
    
	CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", "hello",
		WS_CHILD | WS_VISIBLE|WS_BORDER|ES_AUTOHSCROLL|ES_MULTILINE|ES_AUTOHSCROLL|WS_HSCROLL|WS_VSCROLL,
		0, 0, 200, 200, hWnd, (HMENU)1001, g_hIns, NULL); //WS_EX_CLIENTEDGE阴影边框  ES_PASSWORD --只适合单行编辑框和ES_MULTILINE冲突
}

void OnCommand(HWND hWnd, WPARAM wParam) {
    
	switch (LOWORD(wParam))  //菜单项ID 或控件ID 
	{
    
	case 1001:
		if (HIWORD(wParam) == EN_CHANGE) {
    
			MessageBox(hWnd, "内容被修改", "Infor", MB_OK);
		}
		break;
	case ID_GET: {
    
		HWND hEdit = GetDlgCtrlID(hWnd, 1001);
		CHAR szText[256] = {
     0 };
		SendMessage(hEdit, WM_GETTEXT, 256, (LPARAM)szText);
		MessageBox(hWnd, szText, "Infor", MB_OK);
	    }
		break;
	case ID_SET:
	{
    
		HWND hEdit = GetDlgItem(hWnd, 1001);
		CHAR szText[] = "asdfdasrewafdsaf";
		SendMessage(hEdit, WM_SETTEXT, 0, (LPARAM)szText);
	}
		break;
	}
}

//窗口处理函数(自定义,处理消息)
LRESULT CALLBACK WndProc(HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam) {
    
	switch (msgID) {
    
	case WM_COMMAND:
			OnCommand(hWnd, wParam);
			break;
	case WM_CREATE:
		OnCreate(hWnd);
		break;
	case WM_DESTROY:
		PostQuitMessage(0);   //能够是GetMessage函数返回0  
		break;
	}
	return DefWindowProc(hWnd, msgID, wParam, lParam);  //给各种消息做默认处理
}

//注册窗口类
void Register(LPSTR lpClassName, WNDPROC wndproc) {
    
	WNDCLASS wc = {
     0 };
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
	wc.hCursor = NULL;
	wc.hIcon = NULL;
	wc.hInstance = g_hIns;
	wc.lpfnWndProc = WndProc;
	wc.lpszClassName = lpClassName;
	wc.lpszClassName = "Main";
	wc.lpszMenuName = NULL;
	wc.style = CS_HREDRAW | CS_VREDRAW;
	RegisterClass(&wc);  //将以上所有赋值全部写入操作系统内核
}

//创建主窗口
HWND CreateMain(LPSTR lpClassName, LPSTR lpWndName) {
    
	HWND hWnd = CreateWindowEx(0, lpClassName, lpWndName, WS_OVERLAPPEDWINDOW, 100, 100, 700, 500, NULL, NULL, g_hIns, NULL);
	return hWnd;
}

//显示窗口
void Display(HWND hWnd) {
    
	ShowWindow(hWnd, SW_SHOW);
	UpdateWindow(hWnd);  //刷新窗口
}

//消息循环
void Message() {
    
	MSG nMsg = {
     0 };
	while (GetMessage(&nMsg, NULL, 0, 0)) {
    
		TranslateMessage(&nMsg);
		DispatchMessage(&nMsg);  //将消息交给窗口处理函数(自定义)
	}
}

//入口函数
int CALLBACK WinMain(HINSTANCE hIns, HINSTANCE hPreIns, LPSTR lpCmdLine, int nCmdShow) {
    
	g_hIns = hIns;
	Register("Main", WndProc);
	HWND hWnd = CreateMain("Main", "window");
	Display(hWnd);
	Message();
	return 0;
}



Windows库程序

在这里插入图片描述

完成的项目的提纲

静态库:

  1. 创建C静态库 ,调用C静态库
  2. 创建C++静态库,调用C++静态库(调用C静态库)

动态库:

  1. 创建C静态库 ,调用C静态库
  2. 创建C++静态库,调用C++静态库(调用C静态库)
  3. 创建C++动态库(类库),调用C++动态库(类库)

静态库和动态库的区别?

  1. 静态库在编译时,又链接器将库中的代码拷贝到可执行程序中。exe程序。
  2. 动态库在运行时,由可执行程序根据函数地址调用动态库中的代码。
    在这里插入图片描述
    在这里插入图片描述

静态库程序

在现有的解决方案下生成静态库

生成静态库(C语言)

在这里插入图片描述

第二 选择桌面向导在这里插入图片描述
在这里插入图片描述

选择静态库.LIB 空项目
在这里插入图片描述
添加点 C 文件
在这里插入图片描述

写入代码
在这里插入图片描述

生成:
在这里插入图片描述
在这里插入图片描述

调用静态库(C语言)

在这里插入图片描述
添加一个可执行控制台程序
在这里插入图片描述

设置为启动程序
在这里插入图片描述
添加一个点c的文件
在这里插入图片描述
查看静态库文件并调用
查看clib.lib 所在的路径
在这里插入图片描述

写入代码:

#include <stdio.h>
//C编译器在函数调用前,不需要前向声明
//2.告诉链接器去哪儿抓取加减法函数的源代码

#pragma comment(lib,"../debug/clib.lib")

int main(void) {
    
	int sum = clib_add(5, 3);
	int sub = clib_sub(5, 3);
	printf("%d,%d\n",sum,sub);
	getchar();
	return 0;
} 

测试
在这里插入图片描述

在这里插入图片描述

生成静态库(C++语言)

在这里插入图片描述
在这里插入图片描述
添加cpp文件
在这里插入图片描述
在这里插入图片描述
写入代码:

int cpplib_add(int a, int b) {
    
	return a + b;
}

int cpplib_sub(int a, int b) {
    
	return a - b;
}

生成静态库
在这里插入图片描述
在这里插入图片描述

调用静态库(C++语言)

创建控制台程序
在这里插入图片描述在这里插入图片描述
设置为启动项
在这里插入图片描述

添加源文件**在这里插入图片描述**写入代码 usecpplib.cpp

//1.前向声明(头文件)
//2.链接器使用
//3.功能代码
#include <stdio.h>
int cpplib_add(int , int );
int cpplib_sub(int , int );

//告诉链接器从哪儿抓取加减法的源代码
#pragma comment(lib,"../debug/cpplib.lib")

//功能代码
int  main(void) {
    
	int sum = cpplib_add(5, 3);
	int sub = cpplib_sub(5, 3);
	printf("%d,%d\n",sum,sub);
	getchar();
	return 0;
}

测试:

在这里插入图片描述

C++程序调用C静态库

写入代码:

//1.前向声明(头文件)
//2.链接器使用
//3.功能代码
#include <stdio.h>
int cpplib_add(int , int );
int cpplib_sub(int , int );

//c库函数的声明
int clib_add(int, int);
int clib_sub(int, int);

//告诉链接器从哪儿抓取加减法的源代码
#pragma comment(lib,"../debug/cpplib.lib")
#pragma comment(lib,"../debug/clib.lib") //链接C库

//功能代码
int  main(void) {
    
	int sum = cpplib_add(5, 3);
	int sub = cpplib_sub(5, 3);
	printf("%d,%d\n",sum,sub);

	sum = clib_add(5, 3);
	sub = clib_sub(5, 3);
	printf("%d,%d\n",sum,sub);

	getchar();
	return 0;
}

测试: 报错
C++程序在编译时对函数有换名操作
在这里插入图片描述

**解决办法:**前面加extern “C”
在这里插入图片描述
修改代码为:

//1.前向声明(头文件)
//2.链接器使用
//3.功能代码
#include <stdio.h>
int cpplib_add(int , int );
int cpplib_sub(int , int );

//c库函数的声明
extern "C" int clib_add(int, int);
extern "C" int clib_sub(int, int);

//告诉链接器从哪儿抓取加减法的源代码
#pragma comment(lib,"../debug/cpplib.lib")
#pragma comment(lib,"../debug/clib.lib") //链接C库

//功能代码
int  main(void) {
    
	int sum = cpplib_add(5, 3);
	int sub = cpplib_sub(5, 3);
	printf("%d,%d\n",sum,sub);

	sum = clib_add(5, 3);
	sub = clib_sub(5, 3);
	printf("%d,%d\n",sum,sub);

	getchar();
	return 0;
}

测试: 成功执行
在这里插入图片描述

动态库程序

生成动态库(C语言)

在这里插入图片描述
在这里插入图片描述
写入代码:
在这里插入图片描述
生成动态库
在这里插入图片描述
在这里插入图片描述

设置dumpbin.exe 程序导出
C:\Program Files(x86)\Microsoft Visual Studio 19.0\VC\bin
使用该程序查看dll 的导出函数的。
如果运行不成功,提示缺少.dll 文件,解决步骤如下:
1> 切换到C:\Program Files(x86)\Microsoft Visual Studio 19.0\VC 目录,执行批处理文件vcvarsall.bat,双击即可。
2> 设置path 环境变量,增加如下路径 ;C:\Program Files(x86)\Microsoft Visual Studio 19.0\Common7\IDE\

需要在导出函数中前面添加 _declspec(dllexport)
在这里插入图片描述

在这里插入图片描述在这里插入图片描述
在这里插入图片描述

注意:
当生成动态库时,如果有导出函数,生成dll文件的同时生成配套的lib 文件。
该lib 文件与原来的静态库的lib 文件是不同的。原来的静态库lib文件中存储的是函数的源代码。
而与dll 配套的lib文件存储的是函数的偏移地址。源代码在dll文件中。

调用动态库(c语言)

新建调用项目
在这里插入图片描述
设置为启动项,并添加代码测试
在这里插入图片描述
写入代码:

//1.头文件包含,前向声明
#include <stdio.h>

//2.告诉链接器去哪儿抓取加减法函数的地址
#pragma comment(lib,"../debug/cdll.lib")

//3.功能代码
int main(void) {
    
	int sum = cdll_add(5, 3);
	int sub = cdll_sub(5, 3);
	printf("%d,%d\n",sum,sub);
	getchar();
	return 0;
}

测试成功
在这里插入图片描述
与对静态库调用在代码的编写上时类似的,但是本质上是不同的!
这种调用方式(隐式调用),需要的文件:usecdll.exe +cdll.lib(函数的偏移地址) +cdll.dll (函数的源代码)

在这里插入图片描述

生成动态库(C++语言)

在这里插入图片描述

添加文件并写入代码生成:

_declspec(dllexport) int cppdll_add(int a, int b) {
    
	return a + b;
}
_declspec(dllexport) int cppdll_sub(int a, int b) {
    
	return a + b;
}

_declspec(dllexport) int cppdll_mul(int a, int b) {
    
	return a * b;
}

在这里插入图片描述

在这里插入图片描述

调用动态库(C++语言)

创建项目
在这里插入图片描述

添加cpp文件
在这里插入图片描述
写入代码

//前向声明
#include <stdio.h>
int cppdll_add(int, int);
int cppdll_sub(int, int);

//链接信息,告诉链接器从哪儿抓取函数的地址
#pragma comment(lib,"../debug/cppdll.lib")

//功能代码
int main(void) {
    
	int sum = cppdll_add(5, 3);
	int sub = cppdll_sub(5, 3);
	printf("sum=%d,sub=%d\n",sum,sub);
	getchar();
	return 0;
}

官方推荐写法:

//前向声明
#include <stdio.h>

//增加导入声明(官方文档推荐)

_declspec(dllimport) int cppdll_add(int, int);
_declspec(dllimport) int cppdll_sub(int, int);

//链接信息,告诉链接器从哪儿抓取函数的地址
#pragma comment(lib,"../debug/cppdll.lib")

//功能代码
int main(void) {
    
	int sum = cppdll_add(5, 3);
	int sub = cppdll_sub(5, 3);
	printf("sum=%d,sub=%d\n", sum, sub);
	getchar();
	return 0;
}

测试:
在这里插入图片描述

C++调用C动态库

生成C动态库
在这里插入图片描述
使用之前代码,添加调用C库函数

//前向声明
#include <stdio.h>

//增加导入声明(官方文档推荐)

_declspec(dllimport) int cppdll_add(int, int);
_declspec(dllimport) int cppdll_sub(int, int);

//调用C库函数,需要增加extern “C ”
extern "C" int cdll_add(int, int);
extern "C" int cdll_sub(int, int);

//链接信息,告诉链接器从哪儿抓取函数的地址
#pragma comment(lib,"../debug/cppdll.lib")
#pragma comment(lib,"../debug/cdll.lib")

//功能代码
int main(void) {
    
	int sum = cppdll_add(5, 3);
	int sub = cppdll_sub(5, 3);
	printf("sum=%d,sub=%d\n", sum, sub);

	sum = cdll_add(5, 3);
	sub = cdll_sub(5, 3);
	printf("sum=%d,sub=%d\n", sum, sub);
	getchar();
	return 0;
}

测试:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

生成动态库时:两种导出方式

  1. 声明导出添加 _declspec(dllexport) 导出的是函数换名后的对应的相对地址

  2. 模块定义文件导出 在库程序中添加.def文件
    导出的可以是不做换名的函数对应的相对地址,也可以是换名的函数对应的相对地址

调用动态库:两种链接方式

  1. 隐式链接
    程序执行前,操作系统负责加载动态库(读取.exe文件head部分依赖的动态库的名字)
    操作系统根据函数的编号到dll的head部分查找对应的相对地址进而获取函数的绝对地址
    程序结束时,操作系统负责卸载已经加载的动态库

  2. 显式链接
    程序员调用LoadLibrary函数,负责使动态库执行(加载动态库)
    程序员调用GetProcAddress函数,获取函数的绝对地址
    程序员调用FreeLibrary函数,卸载动态库
    在这里插入图片描述
    参考代码:

//包含头文件
#include <stdio.h>
#include <Windows.h>

//2.显示链接时。不需要配套的lib文件

//功能代码
//1.定义函数指针类型
typedef int(*Func)(int, int);

int main() {
    
	//加载dll
	HMODULE hDll = LoadLibrary("cppdll.dll");

	//获取函数的绝对地址
	Func add = (Func)GetProcAddress(hDll,"?cppdll_add@@[email protected]");

	//调用函数
	int sum = add(5, 3);
	printf("sum=%d\n",sum);

	//卸载dll
	FreeLibrary(hDll);
	getchar();
} 

为了解决 函数名称麻烦,使用导出方式2 :在库程序中添加.def文件
在这里插入图片描述
添加代码:并重新生成

LIBRARY cppdll
EXPORTS 
  cppdll_add @1
  cppdll_sub @2

在这里插入图片描述
重新编写代码:

//包含头文件
#include <stdio.h>
#include <Windows.h>

//2.显示链接时。不需要配套的lib文件

//功能代码
//1.定义函数指针类型
typedef int(*Func)(int, int);

int main() {
    
	//加载dll
	HMODULE hDll = LoadLibrary("cppdll.dll");

	//获取函数的绝对地址
	//Func add =(Func)GetProcAddress(hDll,"?cppdll_add@@[email protected]");
	//加法
	Func add =(Func)GetProcAddress(hDll,"cppdll_add");

	//调用函数
	int sum = add(5, 3);
	printf("sum=%d\n",sum);

	//减法
	Func sub = (Func)GetProcAddress(hDll,"cppdll_sub");
	int sub2 = sub(5, 3);
	printf("sub=%d\n",sub2);

	
	Func mul = (Func)GetProcAddress(hDll,"?cppdll_mul@@[email protected]");
	int mul2 = mul(5, 3);
	printf("sub=%d\n",mul2);


	//卸载dll
	FreeLibrary(hDll);
	getchar();
} 

测试:
在这里插入图片描述

创建C++动态库(类库)

创建项目
在这里插入图片描述

添加头文件
在这里插入图片描述
写入代码:math.h

#pragma once
//或者使用ifndef。。。endif

//math.h在创建类库时(导出类),和调用类库时(导入类)都需要
#ifndef DLL_EXT_CLASS 
#define EXT_CLASS _declspec(dllexport)  //创建dll
#else
#define EXT_CLASS _declspec(dllimport)  //调用dll
#endif // !


class EXT_CLASS CMath {
    
public:
	int add(int a, int b);
	int sub(int a, int b);
};

添加源文件
在这里插入图片描述
写入代码:math.cpp

#define DLL_EXT_CLASS
#include "math.h"

int CMath::add(int a, int b) {
    
	return a + b;
}

int CMath::sub(int a, int b) {
    
	return a - b;
}

生成:
在这里插入图片描述在这里插入图片描述

调用类库

在这里插入图片描述

创建项目并设置为启动项
在这里插入图片描述

添加源文件
在这里插入图片描述
写入代码:

#include <stdio.h>
#include "../classdll/math.h"

#pragma comment(lib,"../debug/classdll.lib");

int main(void) {
    
	CMath math;
	printf("sum=%d\n",math.add(5,3));
	printf("sub=%d\n",math.sub(5,3));
	return 0;
}

测试:
在这里插入图片描述

等候单个句柄有信号

#include <Windows.h>
DWORD WINAPI WaitForSingleObject(
  __in  HANDLE hHandle,       // 句柄
  __in  DWORD dwMilliseconds  // 等候时间 INFINITE
);

参考代码: math.cpp

#define DLL_EXT_CLASS
#include "math.h"
#include <Windows.h>

//dll的入口函数
BOOL WINAPI DllMain(HANDLE hinstDLL, DWORD dwReason, LPVOID lpvReserved) {
    
	switch (dwReason)
	{
    
	case DLL_PROCESS_ATTACH:
		MessageBox(NULL, "进程加载时的初始化", "提示", MB_OK);
		break;
	case DLL_THREAD_ATTACH:
		break;
	case DLL_THREAD_DETACH:
		break;
	case DLL_PROCESS_DETACH:
		MessageBox(NULL, "进程卸载时清理工作", "提示", MB_OK);
		break;
	}
	return TRUE;
}

int CMath::add(int a, int b) {
    
	return a + b;
}

int CMath::sub(int a, int b) {
    
	return a - b;
}

测试:在这里插入图片描述
在这里插入图片描述

字符集问题

在这里插入图片描述
解决办法:

方法1: 项目-》属性-》更改字符集为多字节字符集在这里插入图片描述
方法2:字符串串前面加大写 L




基于windows 多线程编程

在这里插入图片描述

线程的使用

定义线程处理函数

DWORD WINAPI ThreadProc(
    LPVOID lpParameter //创建线程时,传递给线程的参数
);

创建线程

HANDLE CreateThread(
    LPSECURITY_ATTRIBUTES lpThreadAttributes,//安全属性 (一般NULL)
    SIZE_T dwStackSize,                        //线程栈的大小 (一般0)
    LPTHREAD_START_ROUTINE lpStartAddress, //线程处理函数的函数地址
    LPVOID lpParameter,                         //传递给线程处理函数的参数
    DWORD dwCreationFlags,                //线程的创建方式,
    LPDWORD lpThreadId                       //创建成功,返回线程的ID
);   
//创建成功,返回线程句柄

dwCreationFlags
取值:
0 - 创建之后线程立刻执行
CREATE_SUSPENDED - 创建之后线程处于挂起状态

结束线程

结束指定线程

	BOOL TerminateThread(
    HANDLE hThread,    // handle to thread
    DWORD dwExitCode   // exit code
);

结束函数所在的线程

VOID ExitThread(
    DWORD dwExitCode   // exit code for this thread
);

关闭线程句柄

CloseHandle

线程的挂起和执行

挂起
DWORD SuspendThread(
HANDLE hThread // handle to thread
);

DWORD ResumeThread(
HANDLE hThread // handle to thread
);

线程的信息

获取当前线程的ID
GetCurrentThreadId

获取当前线程的句柄
GetCurrentThread

打开指定ID的线程,获取其句柄

HANDLE OpenThread(
     DWORD dwDesiredAccess,  // access right
     BOOL bInheritHandle,           // handle inheritance option
     DWORD dwThreadId            // thread identifier
);

参考代码:

#include <stdio.h>
#include <Windows.h>

//线程函数1
DWORD WINAPI ThreadProc(LPVOID lpParameter /*创建线程时,传递给线程的参数 */) {
    	
	//DWORD id = GetCurrentThreadId();
	//printf("%d\n", id);
	while (true)
	{
    
		for (int i = 0; i < 20; i++) {
    
			printf("*");
		}
		printf("\n");
		Sleep(1000);
	}
	return 0;
}

//线程函数2
DWORD WINAPI ThreadProc2(LPVOID lpParameter /*创建线程时,传递给线程的参数 */) {
    
	while (true)
	{
    
		for (int i = 0; i < 20; i++) {
    
			printf("-");
		}
		printf("\n");
		Sleep(1000);
	}
	return 0;
}


int main(void) {
    
	//1. 创建子线程
	DWORD nID = 0;
	HANDLE hThread = CreateThread(NULL, 0, ThreadProc, NULL, 0, &nID);
	//printf("nID=%d", nID);
	DWORD nID2 = 0;
	HANDLE hThread2 = CreateThread(NULL, 0, ThreadProc2, NULL,CREATE_SUSPENDED, &nID2);  //CREATE_SUSPENDED 挂起

	getchar();	
	//切换线程状态:子线程1的状态由执行-> 挂起:子线程2的状态由挂起->执行
	SuspendThread(hThread);
	ResumeThread(hThread2);
	getchar();

	//不再使用线程句柄时,需要关闭句柄,否则有句柄泄露
	CloseHandle(hThread);
	CloseHandle(hThread2);
	return 0;
}

等候函数

WaitForSingleObject - 等候单个句柄有信号

DWORD WaitForSingleObject(
HANDLE hHandle,    //等候对象的句柄
DWORD dwMilliseconds //等候时间
);

其中:
dwMilliseconds 等候时间 毫秒为单位
INFINITE 表示无限期等候

WaitForMultipleObjects - 等候多个句柄有信号

DWORD WaitForMultipleObjects(
    DWORD  nCount,                     //句柄数量
    CONST HANDLE *lpHandles,  //句柄BUFF的地址
    BOOL bWaitAll,              //等候方式
    DWORD dwMilliseconds       // 等候时间 INFINITE
);

其中
bWaitAll - 等候方式
TRUE - 表示所有句柄都有信号,才结束等候
FASLE- 表示句柄中只要有1个有信号,就结束等候。

多线程的问题
线程A -> 线程B -> 线程A 。。。。。
当线程A执行printf输出时,如果线程A的执行时间结束,系统会将线程A的相关信息(栈、寄存器)压栈保护,同时将线程B相关信息恢复,然后执行线程B,线程B继续输出字符。由于线程A正输出字符,线程B会继续输出,画面字符会产生混乱。

线程同步技术

原子锁
临界区(段)
互斥

事件
信号量
可等候定时器

原子锁

相关问题
多个线程对同一个数据进行原子操作,会产生结果丢失
比如执行++运算时, 当线程A执行g_nValue++时,如果线程切换时间 正好是在线程A将值保存到g_nValue之前, 线程B继续执行g_nValue++,那么当线程A再次被切换回来之后,会将原来线程A保存的值保存到g_nValue上,线程B进行的加法操作被覆盖。

原子锁的使用

原子锁-对单条指令的操作。
API
InterlockedIncrement
InterlockedDecrement
InterlockedCompareExchange
InterlockedExchange

原子锁的实现
直接对数据所在的内存操作,并且在任何一个瞬间只能有一个线程访问。

参考代码:WinInterlock.cpp

#include <stdio.h>
#include <Windows.h>

//1.定义一个全局变量
long gValue = 0;

//2.添加两个子线程函数
DWORD WINAPI ThreadProc(LPVOID pParam) {
    
	for (int i = 0; i < 1e8; i++) {
    
		//gValue++;
		InterlockedIncrement(&gValue);   //原子锁
	}
	return 0;
}

DWORD WINAPI ThreadProc2(LPVOID pParam) {
    
	for (int i = 0; i < 1e8; i++) {
    
		//gValue++;
		InterlockedIncrement(&gValue);   //原子锁
	}
	return 0;
}


int main(void) {
    
	//1. 创建两个子线程
	DWORD nID;
	HANDLE hThread[2] = {
     0 };
	hThread[0] = CreateThread(NULL, 0, ThreadProc, NULL, 0, &nID);
	hThread[1] = CreateThread(NULL, 0, ThreadProc, NULL, 0, &nID);

	//等待两个子线程完成计算后,再打印结果
	//线程句柄何时有信号何时无信号呢?线程正在执行时无信号,线程执行完成,有信号。
	WaitForMultipleObjects(2, hThread, TRUE, INFINITE);


	//打印计算结果;
	printf("%d\n", gValue);

	CloseHandle(hThread[0]);
	CloseHandle(hThread[1]);
	getchar();
	return 0;
}
 

互斥 Mutex

相关的问题
多线程下代码或资源的共享使用。

互斥的使用
创建互斥

HANDLE CreateMutex(
    LPSECURITY_ATTRIBUTES lpMutexAttributes, //安全属性
    BOOL bInitialOwner,             //初始的拥有者 TRUE/FALSE
    LPCTSTR lpName                //命名
); 
//创建成功返回互斥句柄

bInitialOwner - 初始的拥有者
TRUE - 调用CreateMutex的线程拥有互斥
FALSE - 创建的时没有线程拥有互斥

等候互斥

WaitForXXX
互斥的等候遵循谁先等候谁先获取

释放互斥

BOOL ReleaseMutex(
     HANDLE hMutex      // handle to mutex
);

关闭互斥句柄
CloseHandle

参考代码:WinMutex.cpp

#include <stdio.h>
#include <Windows.h>

//全局逇互斥句柄
HANDLE hMutex = NULL;

//子线程函数1
DWORD WINAPI PrintProc(LPVOID pParam) {
    
	while (true)
	{
    
		//哪些线程先等待,就会先得到互斥。互斥变成无信号
		WaitForSingleObject(hMutex, INFINITE);
		//通过之后,被线程1拥有,互斥变成无信号
		for (int i = 0; i < 20; i++) {
    
			printf("*");
		}
		printf("\n");
		//释放互斥,互斥不被任何线程拥有,变成有信号
		ReleaseMutex(hMutex);
		Sleep(1000);
	}
	return 0;
}

//子线程函数2
DWORD WINAPI PrintProc2(LPVOID pParam) {
    
	while (true)
	{
    
		//等待互斥有信号
		WaitForSingleObject(hMutex, INFINITE);
		for (int i = 0; i < 20; i++) {
    
			printf("-");
		}
		printf("\n");
		ReleaseMutex(hMutex);
		Sleep(1000);
	}
	return 0;
}

int main(void) {
    
	//创建互斥,FALSE意味着主线程不拥有互斥。目前是有信号。
	//当互斥不被任何线程拥有时,有信号:被线程拥有时,无信号。	
	hMutex = CreateMutex(NULL, FALSE, "myMutex");


	//1. 创建线程
	DWORD nID, nID2;
	HANDLE hThread, hThread2;
	hThread = CreateThread(NULL, 0, PrintProc, NULL, 0, &nID);
	hThread2 = CreateThread(NULL, 0, PrintProc2, NULL, 0, &nID2);


	getchar();
	//2.关闭线程句柄
	CloseHandle(hThread);
	CloseHandle(hThread2);
	CloseHandle(hMutex);
	return 0;
}

事件

相关问题
程序之间的通知的问题。
事件的使用

创建事件

HANDLE CreateEvent(
      LPSECURITY_ATTRIBUTES lpEventAttributes, //安全属性
      BOOL bManualReset,  //事件重置(复位)方式  TRUE手动,FALSE自动
      BOOL bInitialState,      //事件初始状态,TRUE有信号
      LPCTSTR lpName       //事件命名
); 
//创建成功返回 事件句柄

等候事件
WaitForSingleObject/ WaitForMultipleObjects

触发事件
将事件设置成有信号状态 – 触发事件

BOOL SetEvent(
      HANDLE hEvent   // handle to event
);

将事件设置成无信号状态-- 复位事件

BOOL ResetEvent(
     HANDLE hEvent   // handle to event
);

关闭事件
CloseHandle

注意
小心事件的死锁。

参考代码:WinEvent.cpp

#include <stdio.h>
#include <Windows.h>

//1.全局事件句柄
HANDLE hEvent = NULL;

//两个线程函数,一个线程用于打印,另一个线程用于控制打印时间间隔
DWORD WINAPI PrintProc(LPVOID pParam) {
    
	while (true)
	{
    
		WaitForSingleObject(hEvent, INFINITE);
		//有信后,通过后, 自动重置,表示事件自动变成无信号
		printf("**************\n");
	}
	return 0;
}

DWORD WINAPI CtrlProc(LPVOID pParam) {
    
	while (true)
	{
    
		Sleep(1000);
		SetEvent(hEvent);
	}
	return 0;
}

int main(void) {
    

	//FALSE:自动重置;FALSE:初始无信号
	hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
	DWORD nID;
	HANDLE hThread = CreateThread(NULL, 0, PrintProc, NULL, 0, &nID);
	HANDLE hThread2 =CreateThread(NULL, 0, CtrlProc, NULL, 0, &nID);
	
	getchar();
	CloseHandle(hThread);
	CloseHandle(hThread2);

	return 0;

}


信号量

相关的问题
类似于事件,解决通知的相关问题。但是可以提供一个计数器,可以设置次数。

信号量的使用
创建信号量

HANDLE CreateSemaphore(
    LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, //安全属性
     LONG lInitialCount,              //初始化信号量数量 3
     LONG lMaximumCount,       //信号量的最大值 10
     LPCTSTR lpName               //命名
); 
//创建成功返回信号量句柄

等候信号量
WaitFor…
每等候通过一次,信号量的信号减1,直到为0阻塞

给信号量指定计数值

BOOL ReleaseSemaphore(
     HANDLE hSemaphore,        //信号量句柄
     LONG lReleaseCount,         //释放数量
     LPLONG lpPreviousCount   // 释放前原来信号量的数量
                                                  // 可以为NULL
 );

关闭句柄
CloseHandle

参考代码:WinSema.cpp

#include <stdio.h>
#include <Windows.h>

HANDLE hSema = NULL; 

DWORD WINAPI PrintProc(LPVOID pParam) {
    
	while (true)
	{
    
		WaitForSingleObject(hSema, INFINITE);
		printf("*************\n");
		Sleep(1000);
	}
	return 0;
}

int main(void) {
    

	hSema = CreateSemaphore(NULL, 3, 10, NULL);
	DWORD nID;
	HANDLE hThread = CreateThread(NULL, 0, PrintProc, NULL, 0, &nID);
	getchar();

	//释放5个信号
	ReleaseSemaphore(hSema,5,NULL);
	CloseHandle(hSema);
	CloseHandle(hThread);
	return 0;
}


版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_41560737/article/details/123709825

智能推荐

oracle SGA_stevenfeng的博客-程序员秘密

文章来源:[url]http://blog.sina.com.cn/s/blog_5421dfd20100ffn8.html[/url]3.2 SGA系统全局区--System global area(SGA) 是一组共享的内存结构,它里面存储了oracle数据库实例(instance)的数据和控制文件信息。如果有多个用户同时连接到数据库,他们会共享这一区域,因此SGA也称之为Shar...

理解Kestone中的四种Token_程序员小熊的博客-程序员秘密

Token 是什么通俗的讲,token 是用户的一种凭证,需拿正确的用户名/密码向 Keystone 申请才能得到。如果用户每次都采用用户名/密码访问 OpenStack API,容易泄露用户信息,带来安全隐患。所以 OpenStack 要求用户访问其 API 前,必须先获取 token,然后用 token 作为用户凭据访问 OpenStack API。 四种 Token 的由来D

任务的同步与通信_江枫渔火_huang的博客-程序员秘密

一个完善的多任务操作系统,必须具有完备的同步和通信机制。任务的同步和事件1 任务间的同步直接制约关系:源于任务之间的合作间接制约关系:源于对资源的共享多任务合作过程中,处理的问题:各任务间应该具有一种互斥关系,即对于某个共享资源,如果一个任务正在使用,则其他任务只能等待,等待该任务释放该资源后,等待的任务才能使用。相关的任务在执行中要有先后次序,一个任务要等其伙伴发来通

【OCC学习5】记录最新版本emcc编译occ的bug:opencascade-7.6.0/src/Standard/Standard_Time.hxx:29:25: error: redefinit_loveoobaby的博客-程序员秘密

在研究OCC与Webassembly结合使用,编译的时候遇到以下问题:原来是最新版本的emcc导致的,版本回退到3.1.10就可以了。

第四篇:SpringBoot 整合JPA_吴杰JAVA的博客-程序员秘密

JPA是什么?JPA是Java Persistence API的简称,中文名Java持久层API,是JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。 Sun引入新的JPA ORM规范出于两个原因:其一,简化现有Java EE和Java SE应用开发工作;其二,Sun希望整合ORM技术,实现天下归一。想了解更多请自行百度百科。JPAJPA的...

从零开始学架构——架构基础_从0开始学架构_无良的奋斗少年的博客-程序员秘密

本文章的内容是根据书籍《从零开始学架构》整理的资料,供作者以及读者查阅。1. 架构的定义软件架构指软件系统的顶层架构。详细阐述如下:a. “系统由一群关联个体组成”,个体分别是:系统、子系统、模块、组件等。架构需要明确系统包含哪些“个体”。b. 系统中的个体需要“按照某种规则”运作,架构需要明确个体运作和协作的规则。2. 架构设计的目的2.1. 软件架构的历史背景(...

随便推点

CentOS使用Nginx反向代理Tomcat部署Java Web项目_雷丘最可爱的博客-程序员秘密

CentOS使用Nginx反向代理Tomcat部署Java Web项目今天我舍友问我:CSDN是什么意思?床上等你吗?文章目录CentOS使用Nginx反向代理Tomcat部署Java Web项目一、安装Tomcat9.01.配置Java环境2.安装Tomcat二、安装Nginx1. 编译工具及库文件2.安装PCRE3.安装Nginx4.编辑Nginx配置文件三.小结一、安装Tomcat9.01.配置Java环境先卸载系统自带的恶心人的openjdk,方法的话比较暴力,使用rpm找到所有

Python微信检测你的颜值高低_github 颜值测试_github_38231611的博客-程序员秘密

Python微信检测你的颜值高低需要准备的工具功能实现原因代码Face.py![颜值检测功能实现的代码](https://img-blog.csdnimg.cn/20190228165826502.png)WechatRobot.py使用教程1丶运行WechatRobot.py文件2丶添加好友或群组开启功能运行结果需要准备的工具Python 2.7itchat模块功能实现原因当初有段...

关于windows 10 安装_fly-iot的博客-程序员秘密

1,Windows 10微软福利啊,免费升级windows 10,赶紧升级下自己的电脑。 微软的升级是有期限的,是7月底。还有 1个多月的时间。 https://www.microsoft.com/zh-cn/windows/windows-10-upgrade 下载一个 exe直接进行安装就好。2,安装过程 下载到了c盘的隐藏文件夹。 3个多G。 不知道拷贝到别的电脑上面能不能使

android 来电拒接_Android学习小记-----拦截电话/拒接电话,规避拒接电话前响一声或者两声的问题..._肖潇潇洒洒的博客-程序员秘密

1 importandroid.app.Service;2 importandroid.content.Context;3 importandroid.content.Intent;4 importandroid.media.AudioManager;5 importandroid.os.Handler;6 importandroid.os.IBinder;7 importandroid.os.M...

JNI第一天 JNI基本数据类型_Rnwater的博客-程序员秘密

一、JNI基本数据类型字符对应"()V" "(II)V" "(Ljava/lang/String;Ljava/lang/String;)V" ()中的字符表示参数,后面的则代表返回值"()V"就代表 void func()"(II)V" 表示 void func(int,int)上述是单字符的表示,下面来看下数组的表示方法数组是以"["开始用两个字符表示 ...

2.4g和5g要不要合并_无线路由2.4g和5g要同时开吗_神经现实的博客-程序员秘密

展开全部可以同时开,但是没有必要。单频无e68a84e8a2ad62616964757a686964616f31333366303065线路由器一般工作在2.4GHz频段,这个频段是免费的公共频段,其它的电子产品,如空调、微波炉、无线鼠标、无线音箱、蓝牙、无绳电话等同样在使用这个频段,这就很容易造成设备间的无线信号干扰,从而导致无线网络不稳定。由于双频无线路由器有2.4GHz和5GHz两个频段可选...

推荐文章

热门文章

相关标签