最近需要编写一个DLL文件,供其他系统调用,目的是返回大量数据记录,记录结构包括:卡号、时间、机号。
DLL程序如下:
  uses
  ShareMem, SysUtils...; // 使用到ShareMem
  type
  //定义刷卡记录结构
  PEvent = ^TEvent;
  TEvent = Packed record
  ChipNo : Pchar; //芯片号码
  DotID : Byte; //分控机号ID
  Time : Pchar; //刷卡时间
  CardType : Byte; //卡片类型
  pNext : PEvent; //指针指向下一组卡
  end;
    
  //NetIp--设备IP EventCount--返回的总记录数 lpEvent--记录指针
  Function GetEvent(NetIp:Pchar;Var EventCount:Word;var lpEvent: PEvent):LongInt; stdcall;
  var   
  EventCount : Word;
  pNext : PEvent;
  m ,DevID : Integer;
  ChipNo ,dTime :String;  
  begin
  EventCount := 2 ;
  GetMem( lpEvent, sizeof( TEvent ) * EventCount ); //lpEvent作为变量,在此分配内存 ,不知道对不?
  pNext := lpEvent;
  For m :=1 to EventCount do
  begin
  ChipNo :='4A5B3E12'; //芯片号码
  dTime :='20111213234512';//刷卡时间
  DevID := 1; //刷卡设备  Buffer := AllocMem(Length(ChipNo)+1);
  StrpCopy(Buffer,Pchar(ChipNo));
  pNext^.ChipNo :=Buffer ;
  pNext^.DotID :=DevID;  Buffer := AllocMem(Length(dTime)+1);
  StrpCopy(Buffer,Pchar(dTime));
  pNext^.Time :=Buffer;  if M<>EventCount-1 then
  begin
  pNext^.pNext := PEvent( DWord( pNext ) + sizeof( TEvent ) );
  pNext := pNext^.pNext;
  end
  else
  begin
  pNext^.pNext := nil;
  end;
  end;
  Result :=0;
  end;
 主程序如下:
  TGetEvent =Function(NetIp:Pchar;Var EventCount:Word;var lpEvent: PEvent):LongInt; stdcall;   
    
  procedure TTestMain_Frm.Button15Click(Sender: TObject);
  var
  Rest : Longint;
  NetIp : Pchar;
  pCard : PEvent;
  begin
  NetIp := Pchar('192.168.1.1');
  //GetMem( pCard, sizeof( TEvent ) * 2); // 定义内存空间也报错!
  Rest := GetEvent (NetIp,EventCount,pCard);
  if Rest=0 then  
  begin
  for i:=0 to 1 do //这里只能取到到最后一条记录,循环2次就报错!
  begin
  if ppNext <> nil then
  begin
  Str := IntToStr(i+1)+' 卡芯片号码:'+Pchar(ppNext^.ChipNo)+ ' 卡类型:'+IntToStr(ppNext^.CardType);
  memo1.Lines.Add(str);
  ppNext := ppNext^.pNext;
  end
  else break;
  end ;
  dispose(pCard); //释放变量指针内存。
  end;
  end;问题:
  DLL代码如果不封装,直接放到Delphi中,以上代码没有问题,能取到2条记录;但是封装DLL后,调用,就报错,这个DLL还用提供其他语言使用,搞了2天了,不知道啥原因??? 

解决方案 »

  1.   

    最好把你的pchar类型定义成定长的char数组,String类型也很不好
      

  2.   

    现在可以确定,应该不是Pchar类型问题。
      

  3.   

    那你就调试一下dll吧,或者记日志记录一下,特别是string类型,别的系统容易出问题
      

  4.   

    代码太长没仔细看,给两个建议,
    1 建议内存在外部申请,外部释放
    2 GetMem( lpEvent, sizeof( TEvent ) * EventCount );
    这里你申请空间时pchar只是个指针,getmem不会知道给pchar赋多大的地址空间,要么你申请足够大的空间去碰运气,要么把pchar改为char数组
    而你给ChipNo赋字符串时,又把字符串的生命周期扯进来了,造成代码引用逻辑混乱
      

  5.   

    进DLL跟踪调试吧,好像问题代码的位置都还没找到。
    ChipNo赋字符串的位置在DLL内部,问题不大,传入传出的时候注意用Pchar就可以了。
      

  6.   

    刚才看了各位建议,将Pchar字符屏蔽,改下字段结构,全部为Byte类型
    //定义刷卡记录结构
      PEvent = ^TEvent;
      TEvent = Packed record
         DotID : Byte; //分控机号ID
         CardType : Byte; //卡片类型
         pNext : PEvent; //指针指向下一组卡
      end;
    调用dll后,同样只能取到指针第一个内容。
      

  7.   

    谢谢各位指点,重新修改了代码:
    将Pchar字符屏蔽,改下字段结构,全部为Byte类型,去掉所有PChar变量、参数,就返回Byte数据
    //定义刷卡记录结构
      PEvent = ^TEvent;
      TEvent = Packed record
      DotID : Byte; //分控机号ID
      CardType : Byte; //卡片类型
      pNext : PEvent; //指针指向下一组卡
      end; //EventCount--返回的总记录数 lpEvent--记录指针
      Function GetEvent(Var EventCount:Word;var lpEvent: PEvent):LongInt; stdcall;
      var   
      EventCount : Word;
      pNext : PEvent;
      m ,DevID : Integer;
      ChipNo ,dTime :String;   
      begin
      EventCount := 2 ;
      GetMem( lpEvent, sizeof( TEvent ) * EventCount ); pNext := lpEvent;
      For m :=1 to EventCount do
      begin
      pNext^.DotID :=1;
      pNext^.CardType:=M;
      if M<>EventCount then //以前贴上来临时写错
      begin
      pNext^.pNext := PEvent( DWord( pNext ) + sizeof( TEvent ) );
      pNext := pNext^.pNext;
      end
      else
      begin
      pNext^.pNext := nil;
      end;
      end;
      Result :=0;
      end;
     主程序如下:
      TGetEvent =Function(Var EventCount:Word;var lpEvent: PEvent):LongInt; stdcall;   
        
      procedure TTestMain_Frm.Button15Click(Sender: TObject);
      var
      Rest : Longint;
      pCard : PEvent;
      begin
         //GetMem( pCard, sizeof( TEvent ) * 2); // 定义内存空间也报错!
      Rest := GetEvent (EventCount,pCard);
      if Rest=0 then   
      begin
      for i:=0 to 1 do //这里只能取到到最后一条记录,循环2次就报错!
      begin
      if pCard<> nil then
      begin
      Str := IntToStr(i+1)+' 卡芯片号码:'+Pchar(pCard^.ChipNo)+ ' 卡类型:'+IntToStr(pCard^.CardType);
      memo1.Lines.Add(str);
      ppNext := pCard^.pNext;
      end
      else break;
      end ;
      dispose(pCard); //释放变量指针内存。
      end;
      end;
     
    以上修改:全掉所有PChar类型,同样调用报错。
      

  8.   

    DLL里申请的内存要在DLL内释放,不然会出现内存泄露(像GetMemory出错啊,或是申请到了未空闲的内存)
    你可以从DLL中导出一个释放内存的函数,然后用它来释放指针