在Delphi中的多个应用程序之间共享对象

在Delphi中的多个应用程序之间共享对象,delphi,ipc,shared-memory,Delphi,Ipc,Shared Memory,我正在尝试找出如何在Delphi中的多个应用程序之间共享一个对象。我知道这样做的方法是通过IPC/windows共享内存调用(如CreateFileMapping等),但是在所有示例代码中,我发现它们使用一个简单的类型,如字符串,而我需要共享一个对象 我想知道这是否有可能,因为我一直在使用一个只共享指向对象的指针引用而不共享对象内存本身的应用程序。当我尝试从我的另一个应用程序检索并访问对象时,我会遇到访问冲突。我认为这是因为指针指向另一个应用程序的受保护内存 以下是我迄今为止尝试过的代码(如您所

我正在尝试找出如何在Delphi中的多个应用程序之间共享一个对象。我知道这样做的方法是通过IPC/windows共享内存调用(如CreateFileMapping等),但是在所有示例代码中,我发现它们使用一个简单的类型,如字符串,而我需要共享一个对象

我想知道这是否有可能,因为我一直在使用一个只共享指向对象的指针引用而不共享对象内存本身的应用程序。当我尝试从我的另一个应用程序检索并访问对象时,我会遇到访问冲突。我认为这是因为指针指向另一个应用程序的受保护内存

以下是我迄今为止尝试过的代码(如您所见,我正在尝试在多个应用程序之间共享一个TADOConnection对象,以便在应用程序之间仅使用/共享一个数据库连接)。如果有更好/更简单的方法(共享ADO连接),我很想知道怎么做

  TSharedData = record
    Connection: TAdoConnection;
  end;

  PSharedData = ^TSharedData;

var
  SharedData: PSharedData;
  hFileMapping: THandle;  
  Form1: TForm1;

implementation

{$R *.dfm}

function CreateNamedFileMapping(const Name: String): THandle;
begin
  Result := CreateFileMapping(INVALID_HANDLE_VALUE, nil, PAGE_READWRITE, 0,
    SizeOf(TSharedData)*8, PChar(Name));

  if Result > 0 then
    SharedData := MapViewOfFile(Result, FILE_MAP_ALL_ACCESS, 0, 0, 0);
end;

function GetSharedData: PSharedData;
begin
  result := nil;
  hFileMapping := OpenFileMapping(FILE_MAP_ALL_ACCESS, False, 'MySharedMemory');
  if (hFileMapping > 0) then
    Result := MapViewOfFile(hFileMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);
end;

procedure TForm1.createClick(Sender: TObject);
begin
   hFileMapping := CreateNamedFileMapping('MySharedMemory');
   if (hFileMapping > 0) and Assigned(SharedData) then
   begin
     SharedData^.Connection := TAdoConnection.Create(nil);
     // can't use Assign as it is not supported by _Connection
     SharedData^.Connection.ConnectionObject := AdoConnection1.ConnectionObject;
   end;
end;

procedure TForm1.retrieveClick(Sender: TObject);
begin
   SharedData := GetSharedData;
  if assigned(SharedData) then
    // should be set to true if everything was ok
    ShowMessage(BoolToStr( SharedData.Connection.Connected, true));
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
   AdoConnection1.Connected := False;

  if assigned(SharedData) then
    UnmapViewOfFile(SharedData);
  if hFileMapping > 0 then
    CloseHandle(hFileMapping);
end;


procedure TForm1.FormCreate(Sender: TObject);
begin
  AdoConnection1.Connected := true;
end;

end.
ADOConnection1是我表单上的一个对象。我知道我需要通过使用类似“Assign”的东西来复制整个对象内存,但是ADO ConnectionObject上不存在这种情况。为了确保问题不仅仅在于连接对象,我还尝试传递了一个简单的对象,比如TStringlist,然后使用assign复制内存,但它仍然在应用程序2中获得AV


如果我在同一个应用程序中运行create和retrieve,它可以正常工作。当我复制此应用程序并在应用程序#1中运行“创建”功能和在应用程序#2中运行“检索”功能时,我会遇到访问冲突

在评论中确定,不可能在不同进程之间共享对象。我简化了您的测试用例,以便能够弄清楚为什么会这样

在这个版本中,无法共享的对象比ADOConnection简单得多,ADOConnection只有一个整数字段。应用程序的第一个实例创建一个映射并写入一个包含整数、对象和字符串字段的记录。应用程序的任何后续实例都会尝试检索和显示该信息

unit Unit1;

interface

uses
  Winapi.Windows, System.SysUtils, System.Classes,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

type
  TSimpleObject = class(TObject)
  private
    FInt: Integer;
  end;

  TSharedData = record
    Int: Integer;
    SimpleObject: TSimpleObject;
    Str: string;
  end;
  PSharedData = ^TSharedData;

var
  SharedData: PSharedData;
  hFileMapping: THandle;

const
  MapName = 'MySharedMemory';

procedure TForm1.FormCreate(Sender: TObject);
var
  MapExists: Boolean;
begin
  hFileMapping := CreateFileMapping(INVALID_HANDLE_VALUE, nil, PAGE_READWRITE,
      0, SizeOf(TSharedData), MapName);
  Win32Check(hFileMapping <> 0);
  MapExists := GetLastError = ERROR_ALREADY_EXISTS;

  SharedData := MapViewOfFile(hFileMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);
  Win32Check(SharedData <> nil);

  if MapExists then begin
    ShowMessage(IntToStr(SharedData.Int));
    ShowMessage(IntToStr(SharedData.SimpleObject.FInt));
    ShowMessage(SharedData.Str);
  end else begin
    SharedData.Int := 555;
    SharedData.SimpleObject := TSimpleObject.Create;
    SharedData.SimpleObject.FInt := 666;
    SharedData.Str := 'test string';
  end;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  Win32Check(CloseHandle(hFileMapping));
  Win32Check(UnmapViewOfFile(SharedData));
end;
单元1;
接口
使用
Winapi.Windows、System.SysUtils、System.Class、,
Vcl.控件、Vcl.窗体、Vcl.对话框、Vcl.stdctrl;
类型
TForm1=类(TForm)
过程表单创建(发送方:ToObject);
销毁程序表(发送方:TObject);
结束;
变量
表1:TForm1;
实施
{$R*.dfm}
类型
TSimpleObject=类(ToObject)
私有的
FInt:整数;
结束;
TSharedData=记录
Int:整数;
SimpleObject:TSimpleObject;
Str:字符串;
结束;
PSharedData=^TSharedData;
变量
共享数据:PSharedData;
hFileMapping:THandle;
常数
MapName='MySharedMemory';
过程TForm1.FormCreate(发送方:TObject);
变量
映射存在:布尔;
开始
hFileMapping:=CreateFileMapping(无效的句柄值,nil,页读写,
0,SizeOf(TSharedData),MapName);
Win32Check(hFileMapping 0);
MapExists:=GetLastError=ERROR\u已经存在;
SharedData:=MapViewOfFile(hFileMapping,文件映射,所有访问,0,0,0);
Win32Check(SharedData nil);
如果存在映射,则开始
ShowMessage(IntToStr(SharedData.Int));
ShowMessage(IntToStr(SharedData.SimpleObject.FInt));
ShowMessage(SharedData.Str);
结束,否则开始
SharedData.Int:=555;
SharedData.SimpleObject:=TSimpleObject.Create;
SharedData.SimpleObject.FInt:=666;
Str:=“测试字符串”;
结束;
结束;
程序TForm1.FormDestroy(发送方:ToObject);
开始
Win32Check(CloseHandle(hFileMapping));
Win32Check(unmpviewoffile(SharedData));
结束;
应用程序的第二个实例将能够正确显示第一个值,即记录的整数字段。但是,它将使用对象的整数字段或记录的字符串字段失败。它将引发访问冲突或显示不正确的值


我在表单上放了一个备忘录,并转储了一些内存地址,以便能够看到发生了什么

procedure TForm1.FormCreate(Sender: TObject);
var
  MapExists: Boolean;
begin
  Memo1.Clear;
  hFileMapping := CreateFileMapping(INVALID_HANDLE_VALUE, nil, PAGE_READWRITE,
      0, SizeOf(TSharedData), MapName);
  Win32Check(hFileMapping <> 0);
  MapExists := GetLastError = ERROR_ALREADY_EXISTS;

  SharedData := MapViewOfFile(hFileMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);
  Win32Check(SharedData <> nil);

  if MapExists then begin
    Memo1.Lines.Add(Format('Found at %p', [SharedData]));
//    ShowMessage(IntToStr(SharedData.Int));
//    ShowMessage(IntToStr(SharedData.SimpleObject.FInt));
//    ShowMessage(SharedData.Str);
  end else begin
    Memo1.Lines.Add(Format('Created at %p', [SharedData]));
    SharedData.Int := 555;
    SharedData.SimpleObject := TSimpleObject.Create;
    SharedData.SimpleObject.FInt := 666;
    SharedData.Str := 'test string';
  end;
  Memo1.Lines.Add('');
  Memo1.Lines.Add(Format('@SharedData.Int: %p', [@SharedData.Int]));
  Memo1.Lines.Add(Format('PInteger(@SharedData.Int)^: %d', [PInteger(@SharedData.Int)^]));
  Memo1.Lines.Add('');
  Memo1.Lines.Add(Format('@SharedData.SimpleObject: %p', [@SharedData.SimpleObject]));
  Memo1.Lines.Add(Format('@SharedData.SimpleObject.FInt: %p', [@SharedData.SimpleObject.FInt]));
  Memo1.Lines.Add(Format('PInteger(@SharedData.SimpleObject.FInt)^: %d', [PInteger(@SharedData.SimpleObject.FInt)^]));
  Memo1.Lines.Add('');
  Memo1.Lines.Add(Format('Pointer(@SharedData.Str)^: %p', [Pointer(Pointer(@SharedData.Str)^)]));
  Memo1.Lines.Add('character payload');
  Memo1.Lines.Add('PChar(Pointer(SharedData.Str)): ' + PChar(Pointer(SharedData.Str)));
end;
过程TForm1.FormCreate(发送方:TObject);
变量
映射存在:布尔;
开始
备忘录1.清晰;
hFileMapping:=CreateFileMapping(无效的句柄值,nil,页读写,
0,SizeOf(TSharedData),MapName);
Win32Check(hFileMapping 0);
MapExists:=GetLastError=ERROR\u已经存在;
SharedData:=MapViewOfFile(hFileMapping,文件映射,所有访问,0,0,0);
Win32Check(SharedData nil);
如果存在映射,则开始
Memo1.Lines.Add(格式('Found at%p',[SharedData]);
//ShowMessage(IntToStr(SharedData.Int));
//ShowMessage(IntToStr(SharedData.SimpleObject.FInt));
//ShowMessage(SharedData.Str);
结束,否则开始
Memo1.Lines.Add(格式('createdthe%p',[SharedData]);
SharedData.Int:=555;
SharedData.SimpleObject:=TSimpleObject.Create;
SharedData.SimpleObject.FInt:=666;
Str:=“测试字符串”;
结束;
备忘录1.行。添加(“”);
Memo1.Lines.Add(格式('@SharedData.Int:%p',[@SharedData.Int]);
Memo1.Lines.Add(格式('PInteger(@SharedData.Int)^:%d',[PInteger(@SharedData.Int)^]);
备忘录1.行。添加(“”);
Memo1.Lines.Add(格式('@SharedData.SimpleObject:%p',[@SharedData.SimpleObject]);
Memo1.Lines.Add(格式('@SharedData.SimpleObject.FInt:%p',[@SharedData.SimpleObject.FInt]);
Memo1.Lines.Add(格式('PInteger(@SharedData.SimpleObject.FInt)^:%d',[PInteger(@SharedData.SimpleObject.FInt)^]);
备忘录1.行。添加(“”);
Memo1.Lines.Add(格式('Pointer(@SharedData.Str)^:%p',[Pointer(Pointer(@SharedData.Str)^)]);
备注1.行。添加('字符负载');
Memo1.Lines.Add('PChar(Pointer(SharedData.Str)):'+PChar(Pointer(SharedData.Str)));
结束;
下面,左边是应用程序的第一个实例,右边是第二个实例。请注意,第二个实例可能需要几次尝试才能显示,而不会出现访问冲突

第一行是地图的起始地址。请注意,这两种情况下,它们并不相同