📄 delphi_shm.txt
字号:
小程序,程序1创建共享内存,程序2访问这个共享内存;
然后启动程序1创建共享内存;启动程序2访问共享内存,则程序2能够访问到共享内存;
然后关闭程序1,程序2再一次去访问共享内存;则程序2能够访问共享内存;
然后再关闭程序2,再启动程序2,则程序2无法访问共享内存,出现I/O error 6;
程序1创建贡献内存的代码是放在一个按钮的点击事件:
procedure TForm2.BitBtn1Click(Sender: TObject);
var
Myshm:Pshm;
Fhandle:string;
hFileMapping: THandle;
Fsize:integer;
lp:pshm;
begin
Myshm:=Pshm(GlobalAlloc(GPTR,sizeof(Tshm)));
//virtualalloc(myshm,sizeof(Tshm),FILE_MAP_ALL_ACCESS,Dword(1));
fillchar(Myshm^,sizeof(Tshm),#0);
//zeromemory(Myshm^,sizeof(Tshm));
//myshm:=nil;
try
myshm:=readcfg1(0) ;
except
on E: Exception do
begin
showmessage(e.Message ':::' Inttostr(getlasterror));
end;
end;
FHandle:='EEED';
// showmessage(Fhandle);
hFileMapping := OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, PChar(FHandle));
Fsize:= sizeof(Tshm);
if hFileMapping = 0 then
hFileMapping := CreateFileMapping($FFFFFFFF, nil, PAGE_READWRITE , 0, FSize, PChar(FHandle));
if hFileMapping = 0 then
raise Exception.Create('CreateFileMapping failed with error code ' IntToStr(GetLastError));
lp := Pshm(MapViewOfFile(hFileMapping, FILE_MAP_ALL_ACCESS, 0, 0, FSize));
if lp = nil then
raise Exception.Create('MapViewOfFile failed with error code ' IntToStr(GetLastError));
copymemory(lp,Myshm,Fsize); //给共享内存赋值
end;
其中Pshm是我的共享内存的一个数据结构,是个record,而代码myshm:=readcfg1(0) 就是读取一个二进制文件【这个二进制文件存放record类型的数据】;
程序2访问这段共享内存的代码也是在另外一个按钮上:
procedure TForm1.Button19Click(Sender: TObject);
var
hFileMapping: THandle;
Fhandle:string;
Fsize:Integer;
lpshm:pshm;
begin
FHandle:='EEED';
hFileMapping := OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, PChar(FHandle));
Fsize:= sizeof(Tshm);
lpshm := Pshm(MapViewOfFile(hFileMapping, FILE_MAP_ALL_ACCESS, 0, 0, FSize));
if lpshm = nil then
raise Exception.Create('MapViewOfFile failed with error code ' IntToStr(GetLastError));
end;
请问这么创建的共享内存为什么不能常驻系统里面【至少重启计算机之前也要常驻吧】,而我记得C语言创建共享内存的那些函数 shmget,shmat等创建的共享内存都可以常驻的啊。
请问在delphi里面怎么创建常驻的共享内存?至少是计算机启动之前常驻;或者再至少程序1关闭后,程序2关闭后再重新执行还能继续访问共享内存?当然最好的效果是常驻。
--------------------------------------------------------------------------------
有没有在程序2的关闭事件中释放了共享内存块呢?
--------------------------------------------------------------------------------
To pilicat(Delphi迷):
在程序2里面的任何地方都没有释放共享内存块。
难道需要释放掉吗??
现在在程序2的那个按钮的点击事件的最后加上
UnMapViewOffile(lpshm);
GlobalFree(Dword(lpshm));
后,问题还是一样的,还是程序1关闭后,程序2关闭后再重新执行,程序2还是无法访问共享内存段。
--------------------------------------------------------------------------------
感觉CreateFileMapping创建的共享内存在创建共享内存的程序退出后,就把FHandle对应的map给释放了。
--------------------------------------------------------------------------------
是的 既然你的程序1已经关闭了 它当然不应该保留任何内存和句柄.
内核对象在引用计数为0的情况下会自动释放。所以程序2中不能通过名字去找到这个映射对象。
你可以创建一个磁盘上物理文件,然后程序1和程序2都通过CreateFileMapping来创建/获取内存映射对象. 指定这个文件的句柄为第一个参数。
--------------------------------------------------------------------------------
To graycarl(gray) :
我现在改成了你说的办法,就是创建一个磁盘商的物理文件;
程序1通过CreateFileMapping创建后,程序2再执行CreateFileMapping会不会覆盖了程序1的CreateFileMapping生成的文件?
如果会覆盖了,那么程序1就没有意义了;
如果不会覆盖,请问程序2的CreateFileMapping应当怎么设置参数??
--------------------------------------------------------------------------------
To graycarl(gray) :
补充一下,程序1我通过filecreate函数生成文件;
程序2是否在CreateFileMapping之前先要通过fileopen得到文件的句柄??
--------------------------------------------------------------------------------
To graycarl(gray) :
程序1的创建贡献内存的按钮事件:
var
Myshm:Pshm;
Fhandle:string;
hFileMapping: THandle;
Fsize:integer;
lp:pshm;
MyHandle: Integer;
MyFilepath:string;
flag:integer;
begin
Myshm:=Pshm(GlobalAlloc(GPTR,sizeof(Tshm)));
lp:=Pshm(GlobalAlloc(GPTR,sizeof(Tshm)));
//virtualalloc(myshm,sizeof(Tshm),FILE_MAP_ALL_ACCESS,Dword(1));
fillchar(Myshm^,sizeof(Tshm),#0);
fillchar(lp^,sizeof(Tshm),#0);
//zeromemory(Myshm^,sizeof(Tshm));
//myshm:=nil;
flag:=self.RadioGroup1.ItemIndex;
try
myshm:=readcfg1(flag) ;
except
on E: Exception do
begin
showmessage(e.Message ':::' Inttostr(getlasterror));
end;
end;
FHandle:='EEED';
// showmessage(Fhandle);
hFileMapping := OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, PChar(FHandle));
Fsize:= sizeof(Tshm);
if hFileMapping = 0 then
begin
MyFilepath:=getEnvironmentVariable('ENTCFG') '\' 'SHMFFILE';
showmessage(Myfilepath);
MyHandle:= FileCreate(MyFilepath);
//hFileMapping := CreateFileMapping($FFFFFFFF, nil, PAGE_READWRITE , 0, FSize, PChar(FHandle));
hFileMapping := CreateFileMapping(MyHandle, nil, PAGE_READWRITE , 0, FSize, PChar(FHandle));
end;
if hFileMapping = 0 then
raise Exception.Create('CreateFileMapping failed with error code ' IntToStr(GetLastError));
lp := Pshm(MapViewOfFile(hFileMapping, FILE_MAP_ALL_ACCESS, 0, 0, FSize));
if lp = nil then
raise Exception.Create('MapViewOfFile failed with error code ' IntToStr(GetLastError));
copymemory(lp,Myshm,Fsize);
end;
程序2的访问共享内存的按钮事件:
procedure TForm1.Button19Click(Sender: TObject);
var
hFileMapping: THandle;
Fhandle:string;
Fsize:Integer;
lpshm:pshm;
MyfilePath:string;
MyHandle: Integer;
begin
FHandle:='EEED';
Fsize:= sizeof(Tshm);
//if hFileMapping = 0 then
//begin
MyFilepath:=getEnvironmentVariable('ENTCFG') '\' 'SHMFFILE';
Myhandle:= Fileopen(MyfilePath,fmOpenRead or fmShareCompat);
hFileMapping := CreateFileMapping(MyHandle, nil, PAGE_READWRITE , 0, FSize, PChar(FHandle));
//end;
if hFileMapping = 0 then
raise Exception.Create('CreateFileMapping failed with error code ' IntToStr(GetLastError));
lpshm := Pshm(MapViewOfFile(hFileMapping, FILE_MAP_ALL_ACCESS, 0, 0, FSize));
if lpshm = nil then
raise Exception.Create('MapViewOfFile failed with error code ' IntToStr(GetLastError));
showmessage('client Id:' inttostr(lpshm.clientnodeinfo[0].id));
showmessage('client addr:' lpshm.clientnodeinfo[0].addr);
showmessage('client name:' lpshm.clientnodeinfo[4].name);
showmessage('client username:' lpshm.clientnodeinfo[0].username);
showmessage('client agId:' inttostr(lpshm.clientnodeinfo[0].agid));
end;
问题还是一样的。
--------------------------------------------------------------------------------
程序2中CreateFileMapping时出的错误吗?
尝试把最后一个参数PChar(FHandle)改为nil
--------------------------------------------------------------------------------
另外 在上一行需要插入一句
if Myhandle= INVALID_HANDLE_VALUE then RaiseLastOSError;
--------------------------------------------------------------------------------
To graycarl(gray)
经过千万次测试,问题得到解决,就是象你说的用创建磁盘文件的方法;不够在程序1应当这样创建文件:
MyHandle:= Integer(CreateFile(PChar(MyFilepath), GENERIC_READ or GENERIC_WRITE, 0,nil,CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0));
在程序2应当这样创建文件:
MyHandle:= createfile(pansichar(MyFilepath),GENERIC_READ or GENERIC_WRITE ,FILE_SHARE_READ or FILE_SHARE_WRITE, nil,OPEN_EXISTING,FILE_ATTRIBUTE_ARCHIVE or FILE_ATTRIBUTE_HIDDEN or FILE_ATTRIBUTE_NORMAL or FILE_ATTRIBUTE_READONLY ,0);
这种方法在程序1退出后,程序2执行,或者执行后关闭再执行都可以访问;
不知道这种方法是不是可取的??
--------------------------------------------------------------------------------
To graycarl(gray):
我跟踪了一下程序,我上面说的方法在程序1退出后,程序2执行,其实程序2重新创建了,只不够是程序2没有重新生成一个文件【因为用OPEN_EXISTING】,而是直接使用程序1生成的文件;看来程序2还是重新Map了一下。
--------------------------------------------------------------------------------
你的情况并不是共享内存,而只是在进程A写了个文件 而B读取了这个文件数据 每个进程都重新创建了一个FileMapping对象来操作文件内容。
实际上用FileMapping来操作这种小文件不见得比File I/O效率更好。建议你完全可以把它改成简单的写文件/读文件方式。
利用FileMapping共享内存,是为了能在两个同时执行的进程之间交换数据,FileMapping对象跟物理内存甚至进程的虚拟内存在概念上都是不同的东西,不同进程将FileMapping对象映射到自己的内存地址空间上进行操作,操作系统能保证任何进程对这段内存空间进行的更改会在其他进程里同一FileMapping对象所对应的那段地址空间里体现,这样进程间就可以高效的交换数据了。
另外 打开现有文件时CreateFile参数里指定的FILE_ATTRIBUTE都是被忽略的。
--------------------------------------------------------------------------------
感觉用FileMaping只有在创建共享内存的程序处于运行状态,其他程序才能够直接访问共享内存。
--------------------------------------------------------------------------------
还是C语言好,可以用ftok得到一个文件的key,然后用shmget申请共享内存块,然后用shmat映射到本程序里面,再设置其共享内存的内容;
其他程序即使在创建共享内存的程序退出后【好像机器重新启动也没有问题】,只要通过ftok得到同一个文件的key,然后shmget,shmat就可以得到这块共享内存的内容。
不知道delphi有没有这种功能啊??感觉用FileMaping只有在创建共享内存的程序处于运行状态,其他程序才能够直接访问共享内存。
上一篇:TSoapConnection控件取得DatasetProvider 下一篇:sql
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -