Windows

进程间通信

发送消息

一般是自定义消息,使用SendMessage(需要有两个窗口)

同样的先创建一个进程A 用来发送消息 再创建一个进程B 接收消息

将二者放在同一个解决方案下

image-20240709151352321

可以先创建A再添加B

A:发送消息

1
2
3
4
5
6
7
#define MS_TEST WM_USER+1
void CADlg::OnBnClickedButton1()
{
HWND hWndB = ::FindWindow(NULL,"B");//获取句柄
::SendMessage(hWndB,MS_TEST,0x12456789,0x98745612);//发送消息 有两个API这里使用的是sendmessage
//sendmessage直到消息被处理后才会返回 会阻塞
}

B:接收消息

现在B这添加一个类导向

image-20240709152000364

image-20240709152343548

1
2
3
4
5
6
7
afx_msg LRESULT CBDlg::OnMsTest(WPARAM wParam, LPARAM lParam)
{
CString strFmt;
strFmt.Format("w:%08X,l:%08X", wParam, lParam);
AfxMessageBox(strFmt);
return 0;
}

同时将自定义的消息扔到类视图的CBDlg中

img

然后运行 打开A 和B 的exe

img

这里点击发送消息 结果发现没有接收到消息 因为窗口没有标题 findwindows无法捕捉 于是没有获取到句柄

在类视图中添加显示窗口名 这样B就有了

img

img

然后点击发送消息 B即可响应了

img

WM_COPYDATA

可以携带少量数据,效率低(发生两次拷贝)

消息的传输:A进程的数据先拷到内核高2G的数据区 再从内核高2G的数据区拷到B进程(内核高2G的数据区是所有进程共用的)

img

参数1:目标窗口的句柄

参数3:本身窗口的句柄

参数2:

img

参数1:要传递给接收应用程序的数据类型

参数2: lpData 成员指向的数据的大小(以字节为单位)

参数3:要传递给接收应用程序的数据(可以是NULL)

在A中添加一个按钮为copydata

1
2
3
4
5
6
7
8
9
10
11
12
void CADlg::OnBnClickedButton2()
{
HWND hWndB = ::FindWindow(NULL, "B");
COPYDATASTRUCT cps;
cps.dwData = 0x12345678;
cps.lpData = "你好, copydata";
cps.cbData = strlen((char *)cps.lpData);
::SendMessage(hWndB,
WM_COPYDATA,
(WPARAM)GetSafeHwnd(),
(LPARAM) & cps);
}

跟上次一样在B中添加类向导

img

1
2
3
4
5
6
7
8
9
BOOL CBDlg::OnCopyData(CWnd* pWnd, COPYDATASTRUCT* pCopyDataStruct)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值

CString strFmt;
strFmt.Format("dw:%08X,lp:%s", pCopyDataStruct->dwData, pCopyDataStruct->lpData);
AfxMessageBox(strFmt);
return CDialogEx::OnCopyData(pWnd, pCopyDataStruct);
}

img

A可以将缓冲区中的数据发给B 数据的传输时单向的 B无法修改A中的数据

dll共享段

步骤:

1.定义共享段,并定义导出变量 导出变量需要初始化

1
2
3
#pragma data_seg("CR40SharedSection")//开始
_declspec(dllexport)DWORD g_dwVal = 0;
#pragma data_seg()//结束的位置

2.将此共享段声明为可共享

img

img

1
#pragma comment(linker,"/SECTION:CR40SharedSection,RWS")//可读可写可共享

用dependency walker 查看导出结果

img

多个进程加载同一个dll 就可以通过dll共享段进行数据间的交互

在dll共享段中添加一个Use进程

img

img

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#pragma comment(lib,"../x64/debug/dll.lib")
extern _declspec(dllimport) DWORD g_dwVal;
void CUseDlg::OnBnClickedButton1()
{
// TODO: 在此添加控件通知处理程序代码
SetDlgItemInt(EDT_READ,g_dwVal);//EDT_READ 为读对应的编辑框的ID
}


void CUseDlg::OnBnClickedButton2()
{
// TODO: 在此添加控件通知处理程序代码
g_dwVal = GetDlgItemInt(EDT_WRITE);//EDT_WRITE 为写对应的编辑框的ID
}

由于不能直接运行dll所以得将Use设为运行项目

运行结果:

img

dll共享段中也可以为数组

dll:

1
_declspec(dllexport)char g_sz[MAXBYTE] = {};//初始化 初始化后才会分配内存 才可以让多个进程交互数据

Use:

1
2
3
4
5
6
7
8
9
10
11
12
13
extern _declspec(dllimport)char g_sz[MAXBYTE];
void CUseDlg::OnBnClickedButton1()
{
// TODO: 在此添加控件通知处理程序代码
SetDlgItemText(EDT_READ, g_sz);
}


void CUseDlg::OnBnClickedButton2()
{
// TODO: 在此添加控件通知处理程序代码
GetDlgItemText(EDT_WRITE,g_sz,sizeof(g_sz));
}

img

文件映射

1.文件操作相关API

1
2
3
4
5
6
打开: CREATEFILE
关闭: CLOSEHANDLE
读: READFILE
写: WRITEFILE
文件指针: SETFILEPOINTER
文件大小: GETFILESIZE

img

参数1:路径

参数2:打开文件的访问权限(读还是写)

参数3:共享模式 A打开该文件 B同样可以对这个文件进行操作(删、读、写)若为0 则独占该文件 其他的无法打开

img

参数5:打开标志

参数6 :文件属性

img

参数4:成功读取的文件字节数

img

img

参数2指向低32位 参数3指向高32位

img

img

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <iostream>
#include<Windows.h>
int main()
{
HANDLE hFile = CreateFile(
"C:\\vs\\Dll共享段\\x64\\Debug\\Use.exe",
GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ,//共享读
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hFile == INVALID_HANDLE_VALUE) {
std::cout << "打开文件失败" << std::endl;
}
BYTE aryBuff[MAXBYTE] = {};
DWORD dwByteReaded = 0;
BOOL bRet = ReadFile(hFile, aryBuff, sizeof(aryBuff), &dwByteReaded, NULL);;
if (!bRet) {
CloseHandle(hFile);
std::cout << "读取文件失败" << std::endl;
return 0;
}
//移动文件指针
DWORD dwNewFilePos = SetFilePointer(hFile, 0x1000, NULL, FILE_BEGIN);
if ( dwNewFilePos == INVALID_SET_FILE_POINTER) {
CloseHandle(hFile);
std::cout << "移动文件指针失败" << std::endl;
return 0;
}

char szBuff[] = { "hello file!" };
DWORD dwByteWrited = 0;
bRet = WriteFile(hFile, szBuff, sizeof(szBuff), &dwByteWrited,NULL);
if (!bRet) {
CloseHandle(hFile);
std::cout << "写入文件失败" << std::endl;
return 0;

}
CloseHandle(hFile);
std::cout << "Hello World!\n";
}

2.文件映射用于读写文件数据

文件映射可以用于将磁盘上的文件映射到内存中。这样,文件的内容可以直接从内存中读取或写入,而不必通过磁盘I/O进行数据交换

步骤:

1.打开文件 createfile

2.创建文件映射对象 createfilemapping

img

参数6:用于进程间共享 若无则填NULL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <iostream>
#include<Windows.h>
#include<memory>
using namespace std;
int main()
{
/*bool retFlag;
int retVal = TestFileApi(retFlag);
if (retFlag) return retVal;*/
shared_ptr<HANDLE> pFileHandle =make_shared<HANDLE>(
CreateFile(
"C:\\vs\\Dll共享段\\x64\\Debug\\Use.exe",
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ,//共享读
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL),
[](HANDLE hFile) {CloseHandle(hFile); });
if (pFileHandle.get() == INVALID_HANDLE_VALUE) {
std::cout << "打开文件失败" << std::endl;
return 0;
}
//创建文件映射对象
shared_ptr<HANDLE> pFileMap = make_shared<HANDLE>(
CreateFileMapping(pFileHandle.get(),
NULL,
PAGE_READWRITE,
0, 0,//操作整个文件
NULL),
[](HANDLE hFileMap) {CloseHandle(hFileMap); }
);
if (pFileMap.get() == NULL) {
std::cout << "创建文件映射对象失败" << std::endl;
return 0;
}
}

3.将文件映射到内存 mapviewoffile

img

参数3、4:从文件的哪个位置开始映射

参数5:映射文件多大到内存

4.使用

5.将文件从内存撤销映射

img

FileTest:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
int main()
{
/*bool retFlag;
int retVal = TestFileApi(retFlag);
if (retFlag) return retVal;*/
HANDLE hFile = CreateFile(
"C:\\vs\\Dll共享段\\x64\\Debug\\Use.exe",
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ,//共享读
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
shared_ptr<HANDLE> pFileHandle (& hFile,[](HANDLE hFile) {CloseHandle(hFile); });
if (pFileHandle.get() == INVALID_HANDLE_VALUE) {
std::cout << "打开文件失败" << std::endl;
return 0;
}
//创建文件映射对象
HANDLE hFileMap = CreateFileMapping(
hFile,NULL,
PAGE_READWRITE,
0,0,//整个文件
NULL
);
shared_ptr<HANDLE> pFileMap(&hFileMap, [](HANDLE hFileMap) {CloseHandle(hFileMap); });
if (pFileMap.get() == NULL) {
std::cout << "创建文件映射对象失败" << std::endl;
return 0;
}
LPVOID pBuff = MapViewOfFile(hFileMap,
FILE_MAP_ALL_ACCESS,//可读可写
0,0,//从文件头开始
0x1000//映射0x1000到内存
);
if (pBuff == 0) {
cout << "撤销文件映射对象失败" << endl;
return 0;
}
//使用
//撤销映射
UnmapViewOfFile(pBuff);
return 0;
}

之后即可直接修改内存中的数据

img

6.关闭文件文件映射对象 closehandle

7.关闭文件 closehandle

3.文件映射用于进程间通信(带文件)

使用步骤:
1.打开文件映射对象 createfilemapping/open file mapping

img

2.将文件映射到内存 mapviewoffile

3.使用

4.将文件从内存撤销映射

5.关闭文件文件映射对象 closehandle

6.关闭文件 closehandle

在sharefilemap进程中修改数据 filetest中的数据也会修改 二者共享

img

img

sharefilemap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//创建文件映射对象
HANDLE hFileMap = OpenFileMapping(
FILE_MAP_ALL_ACCESS,FALSE,"CR40SharedMappingFile"
);
shared_ptr<HANDLE> pFileMap(&hFileMap, [](HANDLE hFileMap) {CloseHandle(hFileMap); });
if (pFileMap.get() == NULL) {
std::cout << "创建文件映射对象失败" << std::endl;
return 0;
}
LPVOID pBuff = MapViewOfFile(hFileMap,
FILE_MAP_ALL_ACCESS,//可读可写
0, 0,//从文件头开始
0x1000//映射0x1000到内存
);
if (pBuff == 0) {
cout << "撤销文件映射对象失败" << endl;
return 0;
}

//使用
//撤销映射
UnmapViewOfFile(pBuff);

return 0;
std::cout << "Hello World!\n";

4.文件映射用于进程间通信(无文件)

创建步骤:

1.创建文件映射对象 createfilemapping

2.将文件映射到内存 mapviewoffile

3.使用

4.将文件从内存撤销映射

5.关闭文件文件映射对象 closehandle

6.关闭文件 closehandle

使用步骤:

1.打开文件映射对象 createfilemapping/open file mapping

2.将文件映射到内存 mapviewoffile

3.使用

4.将文件从内存撤销映射

5.关闭文件文件映射对象 closehandle

6.关闭文件 closehandle

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
HANDLE hFileMap = CreateFileMapping(
INVALID_HANDLE_VALUE, NULL,
PAGE_READWRITE,
0, 0x1000,
"CR40SharedMappingFile"
);
if (hFileMap == 0) {
std::cout << "创建文件映射对象失败" << std::endl;
return 0;
}
LPVOID pBuff = MapViewOfFile(hFileMap,
FILE_MAP_ALL_ACCESS,//可读可写
0, 0,//从文件头开始
0x1000//映射0x1000到内存
);
if (pBuff == 0) {
cout << "撤销文件映射对象失败" << endl;
return 0;
}

//使用
//撤销映射
UnmapViewOfFile(pBuff);