进程通讯-内存映像
其实这是一篇读书笔记,也是我学习Windows编程之路的开始
在win32中,通过使用映像文件在进程间实现共享文件或共享内存数据块,如果利用相同的映像名字或文件句柄,则不同的进程可以通过一个指针来读写一个文件或同一个内存数据块,并把它当做该进程内地址空间的一部分
在Windows9x/NT/200 向内存中装载文件时,使用了特殊的全局内存区。在该区域内,应用程序的虚拟内存地址和文件中的响应位置对应,由于所有进程恭喜了一个用于存储映像文件的全局内存区域,因而当两个进程装载相同模块(应用程序exe或dll文件)时,他们实际上是在内存中共享其执行代码
内存映像文件可以映射一个文件、一个文件中的指定区域或者指定的内存块,其中的数据就可以用内存读写指令直接访问,而不比频繁的调用ReadFile或WriteFIle这样的I/O系统函数,从而提高了文件存取速度和效率
# 示例代码
主要的目的是实现两个exe之间共享数据
发送数据端
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
const
WM_DATA = WM_USER + 1024;
type
PShareMem = ^TPShareMem;
TPShareMem = record
//共享数据
Data: array[0..255] of Char;
end;
TForm1 = class(TForm)
btn1: TButton;
procedure btn1Click(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
PShare: PShareMem;
implementation
{$R *.dfm}
var
HMapping: THandle;
HMapMutex: THandle;
const
MAP_FILE_SIZE = 1000;
REQUEST_TIME_OUT = 1000;
{打开建立共享内存}
procedure OpenMap;
begin
{创建一个映像文件}
HMapping := CreateFileMapping($FFFFFFFF, nil, PAGE_READWRITE, 0, SizeOf(TPShareMem), PChar('MapName'));
if HMapping = 0 then
begin
ShowMessage('不能创建内存映像文件');
Exit
end;
{将映像文件映射到进程的地址空间}
PShare := PShareMem(MapViewOfFile(HMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0));
if PShare = nil then
begin
CloseHandle(HMapping);
ShowMessage('映像文件在内存中不存在');
Application.Terminate;
Exit
end;
end;
{关闭共享内存}
procedure CloseMap;
begin
if PShare <> nil then
begin
{从进程的地址空间中撤销映像文件}
UnmapViewOfFile(PShare);
end;
if HMapping <> 0 then
begin
{关闭映像文件}
CloseHandle(HMapping);
end;
end;
{建立互斥对象}
function LockMap: Boolean;
begin
Result := True;
HMapMutex := CreateMutex(nil, False, PChar('My MUTEX NAME COES HERE'));
if HMapMutex = 0 then
begin
ShowMessage('不能创建互斥对象');
Result := False;
end
else
begin
if WaitForSingleObject(HMapMutex, REQUEST_TIME_OUT) = WAIT_FAILED then
begin
ShowMessage('不能对互斥对象枷锁');
Result := False;
end;
end;
end;
procedure UnlockMAP();
begin
{释放、关闭互斥对象}
ReleaseMutex(HMapMutex);
CloseHandle(HMapMutex);
end;
procedure TForm1.btn1Click(Sender: TObject);
var
str: PChar;
begin
str := PChar('简单的共享内存示例');
//把数据拷贝到共享内存
CopyMemory(@(pshare^.Data), str, Length(str)*SizeOf(Char));
//发送消息表明有数据
PostMessage(FindWindowW(nil, 'Form2'), WM_DATA, 1, 2);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
OpenMap();
LockMap();
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
UnlockMAP();
CloseMap();
end;
end.
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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
- 【Delphi下深入Windows编程】一书中CopyMemory使用的长度是Length(str),但是在Delphi增加了Unicode字符支持以后这样会导致长度计算不够,只发送半截字符
- 参考万一的博客中用ByteLength函数解决,但是也有人说,ByteLength函数只能对Unicode字符串求字节长度,如果要对Ansi字符串进行计算,那么结果会是正确值的两倍
- 最终解决方案:
Length(str)*SizeOf(Char)
接收数据端
unit Unit2;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
const
//自定义消息
WM_DATA = WM_USER + 1024;
type
PShareMem = ^TPShareMem;
TPShareMem = record
//共享数据 注意要与发送数据段的定义相同
Data: array[0..255] of Char;
end;
type
TForm2 = class(TForm)
mmo1: TMemo;
btn1: TButton;
procedure FormCreate(Sender: TObject);
procedure btn1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
procedure getShareInfo(var Msg: TMessage); message WM_DATA;
end;
var
Form2: TForm2;
PShare: PShareMem;
MapHandle: THandle;
implementation
{$R *.dfm}
{ TForm2 }
{处理wm_data 自定义消息}
procedure TForm2.btn1Click(Sender: TObject);
begin
CloseHandle(MapHandle);
end;
procedure TForm2.FormCreate(Sender: TObject);
begin
MapHandle := OpenFileMapping(FILE_MAP_WRITE, False, PChar('MapName'));
if MapHandle = 0 then
begin
ShowMessage('不能定位内存映像文件块');
end;
{将映像文件映射到进程的地址空间}
PShare := PShareMem(MapViewOfFile(MapHandle, FILE_MAP_ALL_ACCESS, 0, 0, 0));
if PShare = nil then
begin
CloseHandle(MapHandle);
ShowMessage('不能显示映像文件');
Application.Terminate;
Exit;
end;
FillChar(PShare^, SizeOf(TPShareMem), 0);
end;
procedure TForm2.getShareInfo(var Msg: TMessage);
begin
{如果是发送数据端发过来的参数是1}
if Msg.LParam = 2 then
begin
//显示共享内存中的数据
mmo1.Text := PShare^.Data;
end;
PShare := PShareMem(MapViewOfFile(MapHandle, FILE_MAP_ALL_ACCESS, 0, 0, 0));
if PShare = nil then
begin
CloseHandle(MapHandle);
ShowMessage('不能显示映像文件');
Application.Terminate;
Exit;
end;
FillChar(PShare^, SizeOf(TPShareMem), 0);
end;
end.
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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93