症状:在IDE环境下直接运行或单步执行数据正确,直接运行可执行文件数据不正确
正确数据:68 03 00 00 00 00 00 68 04 09 44 F3 33 33 33 33 55 55 55 E2 16 
错误数据:68 03 00 00 00 00 00 68 04 09 44 F3 33 33 33 33 93 93 93 9C 16 
初步诊断内存分配、释放问题,在集成环境下有内存保护机制所以没有报错,但不能确认到底问题出在哪处!!
另外,一直玩不转PChar这个类型,如果哪位对此理解深刻望能给指点一二,尤其在作为参数在DLL与调用程序之间传递返回数据的使用,包括,在哪分配,在哪释放。请高人指点。
目前的权限只能给200分,以后再补吧。程序代码如下(虽多莫怕,按照我写的次序很快就明白了):
用到的一个记录类型
type
  ReturnChar = record
    iLen : integer;
    sBack: string;
end;
---------------------- 
发送数据
procedure TfrmBaseSet.SetData(node: TdxTreeListNode);
var
  i: integer;
  sSetData: string;
  reBack: ReturnChar;
  sDisp: string;
begin
  if node <> nil then
  begin
    i := strToInt('$'+node.Values[4]);
    sSetData := node.Values[2] + '00000000';    reback := SetParam(pchar(sAddr), i, PChar(sSetData)); //调用函数往下看
    for i := 1 to length(reback.sBack) do
      sDisp := sDisp  + format('%.2x',[ord(reback.sBack[i])]) + ' '; 截获的显示数据
      ShowMessage(sDisp);
  end;
--------------------------------
function SetParam(sAddr:pchar; iParamNo:integer; sData:pchar) : ReturnChar ;
var
  iBack:integer;
  th:Thandle;
  theSetParam:TSetParam; 声明原型在下边
  tp:TFarProc;
  reback:ReturnChar;
  i : integer;
  sBack : pchar;
begin
    iBack := 0;
    th := LoadLibrary('.\645_1997.dll');
    if (th > 0) then
    begin
       try
         tp := GetProcAddress(th,'SetParam');
         if (tp <> nil) then
         begin
           theSetParam := TSetParam(tp);
           //getMem(sBack,sizeof(pchar));
           iBack := theSetParam(sAddr,iParamNo, sData, length(sData) div 2, @sBack); //动态库函数
           reback.iLen := iBack;
           for i := 0 to iBack -1 do
            reBack.sBack := reBack.sBack + sBack[i];
           //FreeMem(sBack); //sBack是在DLL是分配的内存,我在此释放报错!
         end //if (tp <> nil) then
         else
         begin
           iback := 0;
           reBack.iLen := iBack;
           reBack.sBack := '';
         end;
       finally
         FreeLibrary(th);
       end;//try
    end;//if (th > 0) then
  result :=  reBack;
end;
----------------------------------------
  TSetParam               = function(sAddr:pchar;iParamNo:integer;sData:pchar;iDataLen:integer;sBack:pchar) : integer;cdecl;//-----------------------------VC写的动态库函数--------------------------------------------
//参数:sAddr:表地址;iParamNo:功能码;sData:设定值;iDataLen:数据长度;sBack:返回报文
//
int CMy645_1997App::SetParam(char *sAddr,int iParamNo,char *sData,int iDataLen,char *&sBack)
{
int iLen;//报文长度
// BYTE check;//校验码
char* sFun;
iLen = 15 + iDataLen;
sBack = new char[iLen];
sBack[0] = 0x68;
//写入地址码
char *s = GetFormatAddr(sAddr);
for (int i = 0;i<6;i++)
{
sBack[i+1] = s[i];
}
sBack[7] = 0x68;
sBack[8] = 0x04;//04写数据
sBack[9] = 0x02+iDataLen;
sBack[10] = (iParamNo & 0xFF) + 0x33;
sBack[11] = ((iParamNo >> 8) & 0xFF) + 0x33;
sFun = new char[3];
for (int i = 0;i<iDataLen;i++)
{
sFun[0] = sData[iDataLen*2-i*2-2];
sFun[1] = sData[iDataLen*2-i*2-1]; sBack[12 + i] = hex_2bcd(atoi(sFun)) + 0x33;
}
sBack[12 + iDataLen] = GetCheck(sBack,12+iDataLen);
sBack[13 + iDataLen] = 0x16; delete [] s;
delete [] sFun; return iLen-1;
}

解决方案 »

  1.   

    for i := 0 to iBack -1 do 
                reBack.sBack := reBack.sBack + sBack[i]; 
    //以前也遇到过这样的问题,后面解决方法是这样的:
    dec(sBack,iBack);//将指针前移到起始位置,再释放.....-_-!!,居然正确了......
              //FreeMem(sBack); //sBack是在DLL是分配的内存,我在此释放报错! 
      

  2.   

    1楼朋友说的方法,我试过了,还是不行。
    另外我要说明,直接运行Exe文件,一般是第一次操作成功,取得的数据正确,之后再次操作就不对了,得到错误数据
      

  3.   

    你这种情况我以前也遇到过的
    调了半天的程序 后来才想到是Dll封状的问题 
    我也是第一次调用的时候没事
    第二次调用的时候就有问题了 
    后来我在Dll库的前面加了一个WINAPI关键字后就解决了
    建议楼主看一下 是否是DLL的问题 
      

  4.   


    你说的是调用约定的问题吧,这个可能性应该小一些,delphi和Vc中一致,都是采用的cdecl方式。
      

  5.   

    我怀疑就是这句中的两个参数问题
    iBack := theSetParam(sAddr,iParamNo, sData, length(sData) div 2, @sBack); //动态库函数 
      

  6.   

    ……
    看看我的结贴率,你就会知道什么是真正的无语了……
    另外PChar是一个指针,你需要自己分配和释放内存……
    但是……在Delphi内的PChar是不用释放内存的…………
      

  7.   


    如果在VC的DLL中申请内存,不做释放,然后作为返回参数,传给Delphi的调用程序,那么之前申请的内存谁管?怎么处理?
      

  8.   

    function(sAddr:pchar;iParamNo:integer;sData:pchar;iDataLen:integer;sBack:pchar) : integer;
    theSetParam(sAddr,iParamNo, sData, length(sData) div 2, @sBack)
    你声明中用的是pchar,调用为什么还要加@
      

  9.   


    VC的原型是这样的,不这么写那应该怎样写?
    int CMy645_1997App::SetParam(char *sAddr,int iParamNo,char *sData,int iDataLen,char *&sBack) 
      

  10.   

    我说的是你调用时,把@去掉
    theSetParam(sAddr,iParamNo, sData, length(sData) div 2, @sBack) 
      

  11.   

    加个
    try
    程序体
    finally
    free;
    end;
      

  12.   

    function(sAddr:pchar;iParamNo:integer;sData:pchar;iDataLen:integer;sBack:pchar) : integer; 
    theSetParam(sAddr,iParamNo, sData, length(sData) div 2, @sBack) 
    你声明中用的是pchar,调用为什么还要加@ 
      

  13.   

    动态库里的内存分配,你最好是在动态库内释放.如果想做回调参数用的话,不如直接传一个缓冲区指针来得爽快了.比如:
     Type  CallBackBuff=array [0..1024] of char;//最后一位为#0;function SetParam(sAddr:pchar;iParamNo:integer;sData:pchar;iDataLen:integer;sBack:pchar) : integer; 
    stdcall;external 'XX.dll';用的时候 
    var 
      refBuff:Pchar;
      buff:CallBackBuff;  FillChar(buff[0],1025,#0);
      refBuff:=@buff[0];
      SetParam(...);
      
      

  14.   

    DLL的内存要DLL自己去处理了,Delphi不去插手,这个DLL是VC写的,就要VC来释放……
      

  15.   

    那VC函数,就根本没考虑到给不同程序调用的问题,直接就写上:
    sBack = new char[iLen]; 这个分配,如果是用VC写的程序调用,估计就是:
    SetParam(..., &sBack);
    ...
    delete[] sBack;
    就Free但在别在开发工具就傻眼了,Delphi的Free,跟VC不同级别,干瞪眼。只有用VC开发来释放那点内存,不然就一直申请,如果该函数调用频繁,估计一段时间后就没内存可用。解决方法就是自己用D写过那函数,不然用VC开发。
      

  16.   

    嗯,可以试试:
    VirtualFree(nil, sBack)来释放那内存,不知行不行。试下。数据不正确,看不出来。
      

  17.   

    我觉得dll参数涉及指针(包括PChar)的,对于dll内不做修改的一般用const修饰,这样可以比较直观的区分。
    对于内存我想应该是用调用者来负责分配和释放的。否则感觉容易出现地址错或内存泄漏.
      

  18.   

    怎么还不成啊???我去掉了VC动态库的动态分配的代码,然后在Delphi里调用时分配和释放。大伙帮看看还哪里不对。VC库去掉下面这句: 
    //sBack = new char[iLen]; 去掉此句
     
    Delphi调用:
                GetMem(sBack,((length(sData) div 2) + 15));
               iBack := theSetParam(sAddr,iParamNo, sData, length(sData) div 2, @sBack);
               reback.iLen := iBack;
               for i := 0 to iBack -1 do
                reBack.sBack := reBack.sBack + sBack[i];
               FreeMem(sBack);
      

  19.   

    将VC函数
    hex_2bcd
    GetCheck
    代码发出来看看
    还有SetParam代入的参数值写一下
      

  20.   


     BYTE hex_2bcd(BYTE s)
    {
    BYTE temp;
    BYTE t;
    temp = s / 10;
    if(!temp)
    t = s ;
    else
    t = temp * 6 + s;
    return t;
    }BYTE GetCheck(char *sFrame,int iLen)
    {
    BYTE iBack; iBack = 0;
    for (int i=0;i<iLen;i++)
    {
    iBack += sFrame[i];
    }
    return (iBack & 0xFF);
    }
      

  21.   

            sFun = new char[3]; 
    for (int i = 0;i <iDataLen;i++) 

    min: 0,  1
    max:12, 13
    sFun[0] = sData[iDataLen*2-i*2-2]; 
    sFun[1] = sData[iDataLen*2-i*2-1]; 

    sBack[12 + i] = hex_2bcd(atoi(sFun)) + 0x33; 
    } 问题可能是上面那几行代码。
    >>sFun = new char[3];
    这行代码中,new出来的数据一般初始为0,但又有可能不为0,
    这样在:
    >>sBack[12 + i] = hex_2bcd(atoi(sFun)) + 0x33; 
    atoi中,可能在hex_2bcd传值中,N次中可能有几率不相同。改一下那代码再试。
    char sFun[3] = {0};
    或new完后,memset一下。
      

  22.   

    不过在sBack[12 + i] = hex_2bcd(atoi(sFun)) + 0x33; 之前不已经赋值了吗?
    sFun[0] = sData[iDataLen*2-i*2-2];
    sFun[1] = sData[iDataLen*2-i*2-1];
      

  23.   

    你new了3个char,只赋值了两个char,第三个是变数。
      

  24.   

    最终确定主要原因 Vs2005编译的DLL选择的是(在静态库中使用 MFC),改为(在共享 DLL 中使用 MFC)编译出的文件在
    没有安装2005的机器上所得数据正常。
    之前症状:本人机器装有VS2008,调用的DLL在装有VS2005的A君编译,调用所得数据也对,拿到本人机器,第一次正确,以后再调用所得数据都不正确。
    问题解决,至于内在原因没搞清楚,明早结贴,留点时间有兴趣的朋友可以继续讨论讨论。
      

  25.   

    dec(sBack,iBack);//将指针前移到起始位置,再释放.