在DLL程序和调用程序中都定义了如下的记录类型数据.
  TRecordInfo = packed record
    BH : string;       //表号
    UserID : string;   // 用户号
    BCCM : string;     //本次抄码
    BYSL : string;     //本月用水量
    Alarm : String;    //报警信息
    BLX : string;      //表类型
    PointPos : string; //小数点位置
  end;
  TDynamicArray = array of TRecordInfo;且在DLL和调用程序中都引用了ShareMem;程序调用如下:procedure TfTest.Button6Click(Sender: TObject);
var
  GroupNo:Integer;
  Temp : string;
  i,RecordNum:integer;
  RecordInfo : TDynamicArray;
begin
  GroupNo:=StrToInt(edtGroupNo.Text);
  RecordNum := GetGroupRecordInfo(GroupNo,RecordInfo);
  case RecordNum of
    -1: application.MessageBox('串口未打开','信息提示',MB_OK);
    -2: application.MessageBox('连接超时','信息提示',MB_OK);
    -3: application.MessageBox('返回数据错误','信息提示',MB_OK);
    else
      begin
          Temp :='';
          try
             Temp:='BH          UserID           BCCM          BYSL          BLX            PointPos     ' + chr(13);
             for i:=0 to RecordNum-1 do
               begin
                 Temp := Temp + RecordInfo[i].BH + '     '  + RecordInfo[i].UserID + '     ' + RecordInfo[i].BCCM + '        '  +  RecordInfo[i].BYSL + '        ' + RecordInfo[i].BLX + '   ' + RecordInfo[i].PointPos + chr(13);
               end;  
          except
            on e:Exception do
              ShowMessage(e.Message);
          end;
          ShowMessage(temp);
      end;
  end;
end;
上面这段程序调用DLL中的函数可以取出数据也没有什么问题,可当我退出调用DLL的程序时会出现如下的错误:
---------------------------
Debugger Exception Notification
---------------------------
Project TestDLL.exe raised exception class EInvalidPointer with message 'Invalid pointer operation'. Process stopped. Use Step or Run to continue.
---------------------------还有各位赐教.这是什么问题引起的.

解决方案 »

  1.   

    退出时调用这个DLL函数.
    CloseComPort()这个DLL中的函数如下: function CloseComPort():Integer ;stdcall;
      begin
        Result := frmZHCJQ.CloseComPort;//关闭前面打的的串口
        ReleaseDLLRes;//释放DLL中创建窗体的资源
      end;
    其它的就没有访问了.
      

  2.   

    我试着将程序关闭时的代码注释掉之后出错如下的错误与上面有点不一要.---------------------------
    Debugger Exception Notification
    ---------------------------
    Project TestDLL.exe raised exception class EAccessViolation with message 'Access violation at address 21C14667. Read of address 21C14667'. Process stopped. Use Step or Run to continue.
    ---------------------------
      

  3.   

    调用DLL过程中是否调用了数据库连接,以及数据集,如果存在,那么请在DLL用完成后在dll中释放该资源
      

  4.   

    如果是DLL是DELPHI的,調用也是DELPHI, 一定要用STRING類型的話,要帶上ShareMem,並且放在所有的單元文件的最前面
      

  5.   

    还是地址飞掉了,跟踪一下看看,关闭过程中,每一个对象是否可用,特别是对TDynamicArray的操作
      

  6.   

    To:starluck 我就是按照你所说的添加ShareMem-------------------------------------
    To:fuzhong 在DLL中没有涉及到数据库的连接.-------------------------------------To:bdmh这个关闭过程中如何设置断点进行跟踪.
      

  7.   

    ShareMem引用的位置在哪里?另外代码不太全!现在看到DLL中引用结构体,结构体内有一些指针类型的东西就害怕~
      

  8.   


     貼下工程的開始代碼。和DLL中的函數出來。
      

  9.   

    从现有代码中没看出在DLL内分配内存的策略!
    一般是这样:
    从主调中生成结构体,并定义结构体指针,再定义数组指针,把地址传入dll,dll接收指针,按类型提取数据!
    另外如果稍微想公共一些的话,最好在结构体内用基础类型,如Char(10)等,也避免了在释放结构体时不小心造成的结构体内部对象的内存泄露!
      

  10.   

    DLL工程文件如下:
    library ZHCJQ;{ ...}uses
      ShareMem,
      SysUtils,
      Classes,
      Activex,
      UnitZHCJQ in 'UnitZHCJQ.pas' {frmZHCJQ};{$R *.res}
    //type
      //TDynamicArray = array of TRecordInfo;
      procedure InitDLL;
      begin
        CoInitialize(nil);
        if not Assigned(frmZHCJQ) then
          frmZHCJQ := TfrmZHCJQ.Create(nil);
        CoUnInitialize();
      end;
      procedure ReleaseDLLRes;stdcall;
      begin
        FreeAndNil(frmZHCJQ);
      end;
      {============================================================================
      函数说明:function OpenComPort(Port : Integer): Integer;stdcall;
      函数功能: 打开指定串口号
      函数参数:Port : 打开的串口号
      函数返回: 0:  成功打开串口
                -1: 串口已经打开
                -2: 打开串口出现错误
      创建日期:  2008-01-30
      修改日期: <序号>      <作者>      <时间>      <版本>        <描述>
      =============================================================================}
      function OpenComPort(Port : Integer): Integer;stdcall;
      begin
        InitDLL;
        Result := frmZHCJQ.OpenComPort(Port);
        //ReleaseDLLRes;
      end;
      {============================================================================
      函数说明:Function CloseComPort(): Integer;stdcall;
      函数功能: 关闭串口
      函数参数:无
      函数返回: 0:  成功成功串口
                -1:  串口已经关闭
                -2: 关闭串口出现错误
      创建日期:  2008-01-30
      修改日期: <序号>      <作者>      <时间>      <版本>        <描述>
      =============================================================================}
      function CloseComPort():Integer ;stdcall;
      begin
        if not Assigned(frmZHCJQ) then
          Exit;
        Result := frmZHCJQ.CloseComPort;
        ReleaseDLLRes;
        //ReleaseDLLRes;
      end;
      //......多个函数省略
      {============================================================================
      函数说明:Function GetGroupRecordInfo(GroupNo:Integer;var RecordInfo : TDynamicArray):Integer;stdcall;
      函数功能: 获取手抄机中GroupNo组中的水表记录信息(包括水表号、用户号、报警信息、本次抄码)。
      函数参数:GroupNo:显示器组数
      函数返回:RecordInfo: 返回一个记录型数组,每组的数据保存在这里面
                >0: GroupNo组中的水表数量
                -1: 串口未打开
                -2: 连接超时(即读取时间过了还没有读到相关的数据)
                -3: 返回数据有误
      创建日期:  2008-01-30
      修改日期: <序号>      <作者>      <时间>      <版本>        <描述>
                  1     ZhengJianxian  2009-03-27
      作    者:ZhengJianxian
      =============================================================================}
      function GetGroupRecordInfo(GroupNo:Integer;var RecordInfo : TDynamicArray):Integer ;stdcall
      begin
        //InitDLL;
        Result:=frmZHCJQ.GetGroupRecordInfo(GroupNo,RecordInfo);
      end;exports
      OpenComPort,
      CloseComPort,
      GetSCJType,
      GetGroupNum,
      GetRecordNum,
      GetGroupRecordNum,
      GetGroupRecordInfo,
      ClearRecordData,
      ReleaseDLLRes;
    GetGroupRecordInfo函数如下:
    {============================================================================
      函数说明:Function GetGroupRecordInfo(GroupNo:Integer;var RecordInfo : TDynamicArray):Integer;
      函数功能: 获取手抄机中GroupNo组中的水表记录信息(包括水表号、用户号、报警信息、本次抄码)。
      函数参数:GroupNo:显示器组数
      函数返回:>0: GroupNo组中的水表数量
                -1: 串口未打开
                -2: 连接超时(即读取时间过了还没有读到相关的数据)
                -3: 返回数据有误
      创建日期:  2008-01-30
      修改日期: <序号>      <作者>      <时间>      <版本>        <描述>
      =============================================================================}
      function TfrmZHCJQ.GetGroupRecordInfo(GroupNo:Integer;var RecordInfo : TDynamicArray): Integer ;
      var
        SendCmd : array of byte;    //发送命令数组
        ReceiveData : array of byte;    //接收采集器数据数组
        TBackRecord : array of byte;
        TimeOut : integer;  //超时标志
        Jyh ,i: integer;  //校验采集器返回数据校验码
      begin
        try
          SetLength(SendCmd,5); //设备命令数组的长数
          //将命令存入数组中
          SendCmd[0]:=$1E;
          SendCmd[1]:=$02;
          //查询第n组的数据---这里CommandData[2]与 CommandData[3]两个字节来表示一个四位的十六进制数
          SendCmd[2]:=StrToInt('$'+LeftStr(IntToHex(GroupNo,4),2));
          SendCmd[3]:=StrToInt('$'+RightStr(IntToHex(GroupNo,4),2));
          //SendCmd[2]:=$00;
          //SendCmd[3]:=$01;
          //对前面的命令进行校验,即为校验码
          SendCmd[4]:=(SendCmd[0]+SendCmd[1]+SendCmd[2]+SendCmd[3]) and $FF;
          if mscm1.PortOpen=True then
            begin
              mscm1.Output:=SendCmd; // 将命令发送给采集器
              Sleep(300);
              mscm1.InputLen:=5; //表示取缓冲区中前五个字节数据
              TimeOut:=1;
              while mscm1.PortOpen=True do
                begin
                  if mscm1.InBufferCount>=5 then
                    Begin
                      ReceiveData:=mscm1.Input;   //将数据保存在ReceiveData中
                      break;
                    end
                  else
                    begin
                      if TimeOut>8000 then
                        begin
                          //showmessage('连接设备超时,请检查设备的数据线并查看设备是否开启!');
                          Result:=-2;
                          exit;
                        end
                      else
                        TimeOut:=Timeout+1;
                    end;
                end;  // end while
              mscm1.InputLen:=0;  //表示取缓冲区中的所有数据
              repeat
                //循环等待所有的数据都保存在缓冲区时,退出循环
              until mscm1.InBufferCount=ReceiveData[4];
              TBackRecord:=mscm1.Input;
              //校验返回数据
              Jyh:=(frmZHCJQ.JiaoYianHe(TBackRecord) + frmZHCJQ.JiaoYianHe(ReceiveData) + ReceiveData[4]) mod 256;
              //application.MessageBox(pchar(Temp),'提示信息',MB_OK);
              //校验数据正确则进行采集数据
              if Jyh=TBackRecord[ReceiveData[4]-1] then
                begin
                  //读取AAT采集器数据
                  if (ReceiveData[0]=$1A) and (ReceiveData[1]=$02) then
                    begin
                        SetLength(RecordInfo,TBackRecord[5] div 12);
                        for i:=1 to TBackRecord[5] div 12 do
                          begin
                            //New(RecordInfo[i]);
                            try
                              RecordInfo[i-1].BH :=IntToHex(TbackRecord[0],2)+IntToHex(TBackRecord[1],2)+IntToHex(TBackRecord[2],2)+IntToHex(TBackRecord[3],2);
                              RecordInfo[i-1].UserID:=IntToHex(TBackRecord[(i-1)*12+8],2)+IntToHex(TBackRecord[(i-1)*12+9],2);  //获取表用户号
                              RecordInfo[i-1].BLX:=Copy(IntToHex(TBackRecord[(i-1)*12+7],2),1,1);  //获取表类型
                              RecordInfo[i-1].PointPos:=Copy(IntToHex(TBackRecord[(i-1)*12+7],2),0,1);  //获取小数点的位数
                              //获取表水量
                              RecordInfo[i-1].BCCM:=IntToHex(TBackRecord[(i-1)*12+10],2)+IntToHex(TBackRecord[(i-1)*12+11],2)+IntToHex(TBackRecord[(i-1)*12+12],2)+IntToHex(TBackRecord[(i-1)*12+13],2);
                              //将小数位数插入到表水量中,使之得到实际的水量值
                              //RecordInfo[i-1].BCCM:=GetLeft(pchar(BSL),StrLen(pchar(BSL))-StrToInt(BSLPoint))+'.'+GetRight(BSL,StrToInt(BSLPoint));
                            except
                              on e:Exception do
                                ShowMessage(e.Message);
                            end;
                          end; //end for
                        //RecordInfo:=nil;
                        Result := TBackRecord[5] div 12;
                    end
              else
                begin
                  Result:=-3;  //CRC校验不通过,即返回数据有错
                  exit;
                end;
          end
         else
          begin
            result:=-1;   //串口未打开
            exit;
          end;
        except
          on e:Exception do
            ShowMessage(e.Message);
        end;
      end;
      

  11.   

    调用程序的工程文件程序如下:
    program TestDLL;uses
      ShareMem,
      Forms,
      Test in 'Test.pas' {fTest};{$R *.res}begin
      Application.Initialize;
      Application.CreateForm(TfTest, fTest);
      Application.Run;
    end.
      

  12.   

    真是郁闷,程序没有改什么.只是刚才查资料说结构体中的成员使用PChar类型,然后不引用ShareMem,当我测试的时候发现读回的数据有部分为乱码.然后再改回来使用STRING类型后这回又可以了.
    汗~~~不知道什么原因引起的.代码中所调用的函数已经贴出来的.
      

  13.   

      ClearRecordData,
      ReleaseDLLRes;
    问题最大,看不到代码
      

  14.   

    procedure TfTest.Button6Click(Sender: TObject);
    var
      GroupNo:Integer;
      Temp : string;
      i,RecordNum:integer;
      RecordInfo : TDynamicArray;
    begin
      GroupNo:=StrToInt(edtGroupNo.Text);
      setLength(RecordInfo,10);  //   先分配內存試下。
      RecordNum := GetGroupRecordInfo(GroupNo,RecordInfo);
      case RecordNum of
        -1: application.MessageBox('串口未打开','信息提示',MB_OK);
        -2: application.MessageBox('连接超时','信息提示',MB_OK);
        -3: application.MessageBox('返回数据错误','信息提示',MB_OK);
        else
          begin
              Temp :='';
              try
                 Temp:='BH          UserID           BCCM          BYSL          BLX            PointPos     ' + chr(13);
                 for i:=0 to RecordNum-1 do
                   begin
                     Temp := Temp + RecordInfo[i].BH + '     '  + RecordInfo[i].UserID + '     ' + RecordInfo[i].BCCM + '        '  +  RecordInfo[i].BYSL + '        ' + RecordInfo[i].BLX + '   ' + RecordInfo[i].PointPos + chr(13);
                   end;  
              except
                on e:Exception do
                  ShowMessage(e.Message);
              end;
              ShowMessage(temp);
          end;
      end;
    end;
      

  15.   

    ReleaseDLLRes这个上面不是有吗?至于ClearRecordData这个在调用时根本没有调用到这个函数.所以与之没有关系.
      

  16.   

    To:starluck 请教这里分配内在和不分配有什么区别,我在DLL中已经对每个返回的数据进行分配内存,是根据读回的数据长度来进行分配的.
    当然我在程序里添加这个发现也没有什么错误.程序运行也是可以的.
      

  17.   

    frmZHCJQ上的操作检查过吗,比如frmZHCJQ.CloseComPort;
    你调试一下,看看dll中哪里报错