怎样让EXE和DLL之间的数据安全的实现互访? ( 积分: 300 )

  • 主题发起人 主题发起人 chzi826
  • 开始时间 开始时间
C

chzi826

Unregistered / Unconfirmed
GUEST, unregistred user!
主程序EXE里有一记录类型,需要赋值的数据很多,如:
Type Taa=Record
Id:Word;
s1:Array[1..500]Of String;
s2:Array[1..500]Of Integer;
........
End;

主程序EXE里会有很多个线程,每个线程都定义有该记录类型,每个线程都会调用DLL对该记录进行不同数据的装填,装填完毕各线程会各自进行分析处理。

我的程序在线程里是直接用实参方式把该记录传递给Dll的,DLL就直接对记录进行赋值:
procedure (var aa:Taa);stdcall;
begin
aa.id:=1;
aa.s1[1]:='..';
.....
end;

出现问题
我现在使用的是Delphi2007,编译后的程序开启6,7个线程同时执行以上操作,在我的机子上跑比较稳定,偶尔会出现异常错,但在朋友的机子上(试了2台)几乎时一创建4,5个线程就绝对会出异常错,在做程序初期就知道原本EXE和Dll之间的数据用实参传递是不可行的,但听说Delphi2007已经能很好的解决这个问题了,所以才使用了该方法,但就现在看来当数据量大到一定程度还是不安全。
所以希望各位友人帮帮忙,有什么简单的方法可以让EXE和DLL之间的大量数据能安全的互访。
 
用指针
Type
PTaa = ^Taa;
Taa=Record
Id:Word;
s1:Array[1..500]Of String;
s2:Array[1..500]Of Integer;
........
End;

procedure (var aa:PTaa);stdcall;
begin
aa^.id:=1;
aa^.s1[1]:='..';
.....
end;
 
Dll中也要定义有该记录类型
 
还是用指针吧
再加上线程同步
应该可以的
 
是的,线程间的同步我已经用上了临界区。
实参和指针在本质上不是没区别的吗?有没有其它的方法?
 
谁说传递var参数不行.
 
没有说不行,只是在数据量大时感觉才不行,经常会报“堆栈溢出”的错,我的例子只是数据的很少一部分,如果用Sizeof来测出我定义的类型的大小,起码有700K以上,这只是其中一个线程所用到的,把类型的大小缩小的话堆栈溢出现的情况就会变少。
 
没人有好的方法吗?
 
用内存映射文件试试,可能可以满足你的需求.
 
to sky1001
内存映射文件有没有例子?
 
内存映射文件

公共部分:
type
TShareMem = packed record
//你的数据
end;
PShareMem = ^TShareMem;


EXE部分:
hMapObj := OpenFileMapping(FILE_MAP_WRITE,{获取完全访问映射文件}
False,{不可继承的}
LPCTSTR(MappingFileName));{映射文件名字}
if hMapObj = 0 then
begin
ShowMessage('不能定位内存映射文件块!');
Halt;
end;

pShMem := MapViewOfFile(hMapObj,FILE_MAP_WRITE,0,0,0);
if pShMem = nil then
begin
ShowMessage('映射文件错误'+ IntToStr(GetLastError));
CloseHandle(hMapObj);
Halt;
end;


DLL部分:

initialization
hMappingFile := OpenFileMapping(FILE_MAP_WRITE,False,MappingFileName);
if hMappingFile=0 then
hMappingFile := CreateFileMapping($FFFFFFFF,nil,PAGE_READWRITE,0,SizeOf(TShareMem),MappingFileName)
else
if hMappingFile=0 then Exception.Create('不能建立共享内存!');

pShMem := MapViewOfFile(hMappingFile,FILE_MAP_WRITE or FILE_MAP_READ,0,0,0);
if pShMem = nil then
begin
CloseHandle(hMappingFile);
Exception.Create('不能映射共享内存!');
end;

finalization
UnMapViewOfFile(pShMem); {取消映射视图}
CloseHandle(hMappingFile); {关闭映射文件句柄}
 
楼上正解,内存映射、线程同步。
 
我先试试,这个改动比较大,可能要花一段时间弄,如果行的话马上送分
 
有没有考虑把你的DLL改为BPL,我一般用BPL没有出现过问题,用的D7
 
那,,假如有如下这样的数据类型需要在EXE和DLL间传递,按照上面的样子,不就连代码都没地方写了?

type
TStrRec = record
UnitStr:array[0..8191]of WideChar;
end;

PBigRec = ^TBigRec;
TBigRec = record
Id:Word;
s1:array[0..8191]of TStrRec;
s2:array[0..8191]of TStrRec;
Int1:array[0..8191]of Integer;
Int2:array[0..8191]of Integer;
end;
 
其实根本就没有必要死缠着参数传递写代码。

像这样的例子,只要传个索引值把数据取回来就可以了:

const
MaxStrLength = 8192;

var
BigRec:TBigRec;

procedure FillStrToWideChar(const s:string;WP:PWideChar);
var
WS:WideString;
begin
WS := Copy(S,1,Length(S));
if Length(WS) >= MaxStrLength then
begin
SetLength(WS,8192);
WS[8192]:=WideChar(#0);
end;

Move(WS[1],WP^,Length(WS)*sizeof(WideChar));
end;

procedure FillBigRecIndex0;
begin
BigRec.Id := 0;
FillStrToWideChar('you Index 0 data for fill',@(BigRec.s1[0].UnitStr[0]));
end;

procedure FillBigRecIndex1;
begin
BigRec.Id := 0;
FillStrToWideChar('you Index 1 data for fill',@(BigRec.s1[0].UnitStr[0]));
end;

procedure FillBigRecIndex2;
begin
BigRec.Id := 0;
FillStrToWideChar('you Index 2 data for fill',@(BigRec.s1[0].UnitStr[0]));
end;

procedure SetBigRec(Idx:integer);
begin
FillChar(BigRec,sizeof(TBigRec),0);
case Idx of
0: FillBigRecIndex0;
1: FillBigRecIndex1;
2: FillBigRecIndex2;
end;
end;

function GetDataByIndex(idx:integer):PBigRec;stdcall;
begin
SetBigRec(idx);
Result:= @BigRec;
end;

exports
GetDataByIndex;

通过对数据的同步保护,完全可以做到简单省事。
 
调用即快又明确:
function GetDataByIndex(idx:integer):PBigRec;stdcall;external 'BigData.dll';
var
MyBigRec0,MyBigRec1,MyBigRec2:TBigRec;
procedure TForm1.Button1Click(Sender: TObject);
var
PRec:PBigRec;
begin
PRec := GetDataByIndex(0);
Move(PRec^,MyBigRec0,Sizeof(TBigRec));

PRec := GetDataByIndex(1);
Move(PRec^,MyBigRec1,Sizeof(TBigRec));

PRec := GetDataByIndex(2);
Move(PRec^,MyBigRec2,Sizeof(TBigRec));
end;

即使放到线程里,只要简单地做一些工作,也可以保证数据绝对正确(请注意扩大线程缺省堆栈的尺寸)。

不过,这个例子不要拷贝了去试,数据太大了,除非有 2G 内存。
 
后退
顶部