http://blog.csdn.net/iiprogram/article/details/309020 

 

用Delphi实现程序间的数据传递

用Delphi实现程序间的数据传递 
在实际应用中,我们经常需要多个程序相互配合来完成某些特定功能。例如两个应用程序间的同步、互斥;应用程序在起第二份实例时的参数自动传递…。要实现这些功能,就必须能实现程序间的数据传递。 

有些特殊的高级技术可在不同的程序间传递数据,如剪贴板、动态数据交换以及OLE自动化,但有条件限制并且相对较复杂。这里,我介绍三种有效的底层技术,希望对编程爱好者有所帮助。 
利用WM_COPYDATA消息 
使用该消息涉及一个TcopyDataStruct结构类型的指针。该结构中有三个成员: 
dwData是一个32位的附加参数 
cbData表示要传递的数据区的大小 
lpData表示要传递的数据区的指针 
下面举个例子。该例子由两个程序构成,分别为SendData和GetData。 
SendData程序向GetData程序发送消息,并传递edit1中的字符串;GetData在收到消息后,把SendData发送的字符串接受下来,并显示在相应的edit1中。 
SendData程序: 
var 
Form1:TForm1; 
implementation 
{$R*.DFM} 
procedureTForm1.Button1Click(Sender:Tobject); 
var 
ds:TCopyDataStruct; 
hd:Thandle; 
begin 
ds.cbData:=Length(Edit1.Text)+1; 
GetMem(ds.lpData,ds.cbData);//为传递的数据区分配内存 
StrCopy(ds.lpData,Pchar(Edit1.Text)); 
Hd:=FindWindow(nil,'Form2');//获得接受窗口的句柄 
ifHd<>0then SendMessage(Hd,WM_COPYDATA,Handle, 
Cardinal(@ds))//发送WM_COPYDATA消息 
else 
ShowMessage('目标窗口没找到!'); 
FreeMem(ds.lpData);//释放资源 
end; 

GetData程序: 
TForm2=class(Tform) 
Edit1:Tedit; 
private 
{Privatedeclarations} 
public 
procedureMymessage(vart:TWmCopyData);messageWM_COPYDATA; 
{Publicdeclarations} 
end; 
var 
Form2:TForm2; 
implementation 

procedureTForm2.Mymessage(vart:TWmCopyData); 
begin 
Edit1.text:=StrPas(t.CopyDataStruct^.lpData);//接受数据并显示。 
end; 

使用这种方法是WIN32应用程序进行交互的最简单的方法。 
使用全局原子 
Win32系统中,为了实现信息共享,系统维护了一张全局原子表。每个原子中存放了一些共享数据。关于对原子的操作,有一组专门的API函数: 
GlobalAddAtom在表中增加全局原子 
GlobalDeleteAtom在表中删除全局原子 
GlobalFindAtom在表中搜索全局原子 
GlobalGetAtomName从表中获取全局原子 
笔者用这种方法实现了避免程序二次启动,但把第二次启动所带的参数传到第一个实例中以进行相应的处理的程序。基本处理如下: 
在工程文件中: 
programPvdde; 
uses 
Forms,shellapi,Windows,dialogs,ddein'dde.pas'{Form1}; 

{$R*.RES} 

begin 
ifGlobalFindAtom(Pchar('PDDE_IS_RUNNING'))=0then 
//避免二次启动 
begin 

K:=GlobalAddAtom(Pchar('PDDE_IS_RUNNING')); 
Application.Initialize; 
Application.CreateForm(TForm1,Form1); 
Application.Run; 
end 
else 
begin 
//传递二次启动时的参数到第一个实例 
H:=FindWindow(Pchar('TForm1'),Pchar('资料保密严禁外传')); 
ifParamCount>0then 
begin 
L:=GlobalAddAtom(Pchar(ParamStr(1))); 
ifH<>0then 
SendMessage(H,WM_MYMESSAGE,0,L); 
{传递原子句柄} 

GlobalDeleteAtom(L);{使用后释放} 
end; 
Application.Terminate; 
end; 
end. 

在相应的窗口单元dde.pas增加对自定义消息WM_MYMESSAGE的处理: 

procedureTForm1.MyMessage(varT:Tmessage); 
{对WM_MYMESSAGE消息进行处理} 

var 
P:Array[0..255]ofchar; 
begin 
GlobalGetAtomName(T.Lparam,P,255);{接受数据到p数组中} 
end; 

使用存储映象文件 
这种方法相对较复杂一些。 

当Win95与Winows 
Nt向内存中装载文件时,使用了特殊的全局内存区。在该区域内,应用程序的虚拟内存地址和文件中的相应位置一一对应。由于所有进程共享了一个用于存储映象文件的全局内存区域,因而当两个进程装载相同模块(应用程序或DLL文件)时,它们实际可以在内存中共享其执行代码。 

笔者通过调用一个带有特殊参数的CreateFileMapping函数,来间接达到程序间共享内存的目的。下面简要解释一下该函数。 

HANDLECreateFileMapping(HANDLEhFile,//文件句柄 
LPSECURITY_ATTRIBUTESlpFileMappingAttributes,//可选安全属性 
DWORDflProtect,//映象文件保护方式 
DWORDdwMaximumSizeHigh,//映象文件区域的底值 
DWORDdwMaximumSizeLow,//映象文件区域的顶值 
LPCTSTRlpName//映象文件的名字); 

如果hFile是0xFFFFFFFF,在调用程序中必须指定dwMaximumSizeHigh 
和dwMaximumSizeLow参数的值以确定映象文件的大小。通过这样的参数指定,该函数就创建了一个由操作系统页文件支持的特殊逻辑映象文件,而不是由实际操作系统的文件支持的逻辑映象文件。这个逻辑映象文件可以通过复制、继承或者按名字来达到共享。至于其它参数的详细说明,请参看在线帮助。 
在建立了映象文件之后,我们可以通过调用另外一个API函数MapViewOfFile来访问它的内存,该函数会返回一个指向共享内存块的特定指针。 
LPVOIDMapViewOfFile( 
HANDLEhFileMappingObject,//映象文件句柄 
DWORDdwDesiredAccess,//访问方式 
DWORDdwFileOffsetHigh,//映象文件区域的底值 
DWORDdwFileOffsetLow,//映象文件区域的顶值 
DWORDdwNumberOfBytesToMap//映射字节数 
); 
如果dwNumberOfBytesToMap是0,映射整个文件。 

以下举例说明: 

private 
hMapFile:Thandle; 
MapFilePointer:Pointer; 
public 

{Publicdeclarations} 
end; 

var 
Form1:TForm1; 
implementation 

{$R*.DFM} 

procedureTForm1.FormCreate(Sender:Tobject); 
begin 
hMapFile:=CreateFileMapping( 
$FFFFFFFF,//特殊内存映射句柄 
nil,page_ReadWrite,0,10000,'DdhDemoMappedFile');//文件名 
ifhMapFile<>0then 
MapFilePointer:=MapViewOfFile(hMapFile,//上面映象文件的句柄 
File_Map_All_Access,0,0,0)//访问整个映象文件 
else 
ShowMessage('hMapFile=0'); 
ifMapFilePointer=nilthen 
ShowMessage('MapFilePointer=nil'); 
end; 

procedureTForm1.BtnWriteClick(Sender:Tobject); 
begin 
StrCopy(Pchar(MapFilePointer), 
Pchar(EditWrite.Text));//把内容写入共享内存 
end; 

procedureTForm1.BtnReadClick(Sender:Tobject); 
var 
S:string; 
begin 
S:=Pchar(MapFilePointer);//从共享内存读出内容 
EditRead.Text:=S; 
end; 

用这种方法,不但可以在不同的程序之间共享数据,还可以在同一程序的不同实例间共享数据。为了及时通知其它进程共享数据的变化,可以自定义一条用户消息,通过发消息来实现,这里不再赘述。