好久没用Delphi了,不过还是觉得很有感情。呵
大家节日快乐!不知道这个时候发帖子会不会都出去旅游了,希望还有像我一样在前线的兄弟。回到问题:
我现在想在DLL中通过内存映射的方式共享ADO连接(会有多个,所以我用数组存储他们),当作一个连接池(自己随便编的),引用的应用程序首先查看内存是否有已经建立的连接,有这返回,没有则建立一个新的。我当前是分两步测试,创建连接,和取连接。
第一种情况:
创建和使用都在同一个应用程序中没有问题。第二种情况:
调用DLL的主程序打开两个,一个创建链接,另外一个引用这个连接,发现问题来了,记录中的其他两个简单类型的变量FDBNameSet,FConnectionCount没有问题,能正常读出,说明内存映射基本正常,但是对象FDBConnSet是TADOConnection类型的就有问题,他也有值,不为空,但是就是读出来就是有问题,见下面红色字体部分。
写得有点多,郁闷了两天,网上搜了好久没解决,所以还请您多担待一下,把它读完。
unit ConnectionPoolUnit;interfaceuses Windows,ADODB,Dialogs,SysUtils;Const
  REQUEST_TIMEOUT = 1000;
  //according to sb's comment, system will NOT allocate memory for the one defined in type section
  COUNT_MAX_CONNECTION = 50;
  NAME_MEM_MAP = 'Inter Application Memory Map File';
  NAME_MUTEX = 'Inter Application Mutex';type
  TConnectionPool=record
    FDBNameSet:array[0..COUNT_MAX_CONNECTION] of PChar;
    FDBConnSet:array[0..COUNT_MAX_CONNECTION] of TADOConnection;
    FConnectionCount:Integer;
  end;
  PConnectionPool=^TConnectionPool;var
  pConnPool:PConnectionPool;
  hMapping: THandle;
  hMapMutex: THandle;implementation{-Biz logic===================================================================-}procedure CreateAConnection(aDBName:PChar);stdcall;
var
  adoConn:TADOConnection;
  i_ConnCount:Integer;
begin
  adoConn:=TADOConnection.Create(nil);
  adoConn.LoginPrompt:=false;
  adoConn.ConnectionString:='FILE NAME=X.udl';  i_ConnCount:=pConnPool^.FConnectionCount;
  if i_ConnCount>10000 then i_ConnCount:=0;
  if i_ConnCount>=COUNT_MAX_CONNECTION then
  begin
    ShowMessage('The connection count exceeds max setting value.');
    exit;
  end;  pConnPool^.FDBNameSet[i_ConnCount]:=aDBName;
  pConnPool^.FDBConnSet[i_ConnCount]:=adoConn;
  Inc(i_ConnCount);
  pConnPool^.FConnectionCount:=i_ConnCount;  //adoConn.Free;
end;function RequestConnectionByRPC(aDBName:PChar):TADOConnection;stdcall;
begin
  result:=nil;
end;function GetConnectionFromPool(aDBName:PChar):TADOConnection;stdcall;
var
  i:Integer;
begin
  result:=nil;
  if pConnPool=nil then
  begin
    ShowMessage('No connection pool created.');
    exit;
  end;  for i:=0 to pConnPool^.FConnectionCount-1 do
  begin
    if String(aDBName) = String(pConnPool^.FDBNameSet[i]) then
    begin
      result:=pConnPool^.FDBConnSet[i];  //一个进程的时候没有问题,当另外一个进程掉的时候这句有问题,但是pConnPool^记录里其他两字段的值正常。
    end;
  end;
end;function AllocateConnection(aDBName:PChar):TADOConnection;stdcall;
begin
  result:=GetConnectionFromPool(aDBName);  if result=nil then
  begin
    result:=RequestConnectionByRPC(aDBName);
  end;  if result=nil then ShowMessage('No database connection be created.');
end;{-Biz logic===================================================================-}
{-Mem Map=====================================================================-}
procedure CloseMemMap;
begin
  if pConnPool <> nil then
    UnMapViewOfFile(pConnPool);
  if hMapping <> 0 then
    CloseHandle(hMapping);
end;function LockMemMap: Boolean;
begin
  Result := true;
  hMapMutex := CreateMutex(nil, false, NAME_MUTEX);
  if hMapMutex = 0 then
  begin
    ShowMessage('Failed to create a Map Mutex.');
    Result := false;
  end else
  begin
    if WaitForSingleObject(hMapMutex, REQUEST_TIMEOUT) = WAIT_FAILED then
    begin
      ShowMessage('The Map Mutex can NOT be locked.');
      Result := false;
    end;
  end;
end;procedure UnlockMemMap;
begin
  ReleaseMutex(hMapMutex);
  CloseHandle(hMapMutex);
end;function OpenMemMap:boolean;
begin
  result:=false;
  hMapping:=OpenFileMapping(FILE_MAP_WRITE,false,NAME_MEM_MAP);
  if hMapping=0 then
  begin
    hMapping := CreateFileMapping($FFFFFFFF, nil, PAGE_READWRITE, 0,
      SizeOf(TConnectionPool), NAME_MEM_MAP);
    if (hMapping = 0) then
    begin
      ShowMessage('Failed to create map file.');
      exit;
    end;
  end;  pConnPool := PConnectionPool(MapViewOfFile(hMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0));
  if pConnPool = nil then
  begin
    CloseHandle(hMapping);
    ShowMessage('The Memory Map can NOT be viewed.');
    exit;
  end;  result:=true;
end;
{-Mem Map=====================================================================-}
exports
  CreateAConnection, AllocateConnection;initialization
  OpenMemMap;
  LockMemMap;finalization
  CloseMemMap;
  UnlockMemMap;end.

解决方案 »

  1.   

    光映射指针有啥用啊
    另外你说“其他两字段的值正常”,我咋就不信 FDBNameSet 这种指针能正常呢
      

  2.   

    pConnPool 这个不用分配内存么?
      

  3.   

    谢谢两位回复。
    To Seamour:
    我说的其他两个字段是指下面这个记录类型的FDBNameSet和FConnectionCount的值是能成功的共享的,可能是因为系统基本类型,复制的时候不是只复制指针,貌似对象的话只复制指针引用,我非常同意你的说法,但是我不知道应该如何改。还请赐教。大学时看到指针就头痛。(:-TConnectionPool=record 
        FDBNameSet:array[0..COUNT_MAX_CONNECTION] of PChar; 
        FDBConnSet:array[0..COUNT_MAX_CONNECTION] of TADOConnection; 
        FConnectionCount:Integer; 
    end; To mdejtod:
    pConnPool应该是在创建映射的时候系统自动分配的.
      

  4.   

    导致 FDBNameSet 恰好“成功的共享”了的原因很多,比如你的两个进程在给 FDBNameSet 赋值的时候都使用了常量字符串,所以指向了pe文件映射后的常量区。而在不同进程空间中,同一个pe文件映射映射到相同的基址上的概率很大,所以在不同的进程空间中访问的时候,恰好都指向了内容相同的合法的地址上。
    但对象指针就没这么幸运了,所以表面上看,你的 FDBNameSet 指针“成功的共享”了,而 FDBConnSet 却没有,这两个的道理是一样的。你要是不信的话,等以后“恰好”没“成功的共享”的时候就慢慢调去吧。另,在不同的模块或进程间试图“共享” delphi 对象是个很糟糕的想法,劝你还是放弃吧
      

  5.   

    ADO连接,属于COM对象,不可以通过简单地内存复制跨进程访问.如果想要共享,建议建立DCOM服务程序,并且配置该DCOM服务程序为单实例(比如配置为以"交互式用户"启用).然后通过接口进行访问.