症状:在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;
}
正确数据: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;
}
reBack.sBack := reBack.sBack + sBack[i];
//以前也遇到过这样的问题,后面解决方法是这样的:
dec(sBack,iBack);//将指针前移到起始位置,再释放.....-_-!!,居然正确了......
//FreeMem(sBack); //sBack是在DLL是分配的内存,我在此释放报错!
另外我要说明,直接运行Exe文件,一般是第一次操作成功,取得的数据正确,之后再次操作就不对了,得到错误数据。
调了半天的程序 后来才想到是Dll封状的问题
我也是第一次调用的时候没事
第二次调用的时候就有问题了
后来我在Dll库的前面加了一个WINAPI关键字后就解决了
建议楼主看一下 是否是DLL的问题
你说的是调用约定的问题吧,这个可能性应该小一些,delphi和Vc中一致,都是采用的cdecl方式。
iBack := theSetParam(sAddr,iParamNo, sData, length(sData) div 2, @sBack); //动态库函数
看看我的结贴率,你就会知道什么是真正的无语了……
另外PChar是一个指针,你需要自己分配和释放内存……
但是……在Delphi内的PChar是不用释放内存的…………
如果在VC的DLL中申请内存,不做释放,然后作为返回参数,传给Delphi的调用程序,那么之前申请的内存谁管?怎么处理?
theSetParam(sAddr,iParamNo, sData, length(sData) div 2, @sBack)
你声明中用的是pchar,调用为什么还要加@
VC的原型是这样的,不这么写那应该怎样写?
int CMy645_1997App::SetParam(char *sAddr,int iParamNo,char *sData,int iDataLen,char *&sBack)
theSetParam(sAddr,iParamNo, sData, length(sData) div 2, @sBack)
try
程序体
finally
free;
end;
theSetParam(sAddr,iParamNo, sData, length(sData) div 2, @sBack)
你声明中用的是pchar,调用为什么还要加@
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(...);
sBack = new char[iLen]; 这个分配,如果是用VC写的程序调用,估计就是:
SetParam(..., &sBack);
...
delete[] sBack;
就Free但在别在开发工具就傻眼了,Delphi的Free,跟VC不同级别,干瞪眼。只有用VC开发来释放那点内存,不然就一直申请,如果该函数调用频繁,估计一段时间后就没内存可用。解决方法就是自己用D写过那函数,不然用VC开发。
VirtualFree(nil, sBack)来释放那内存,不知行不行。试下。数据不正确,看不出来。
对于内存我想应该是用调用者来负责分配和释放的。否则感觉容易出现地址错或内存泄漏.
//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);
hex_2bcd
GetCheck
代码发出来看看
还有SetParam代入的参数值写一下
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);
}
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一下。
sFun[0] = sData[iDataLen*2-i*2-2];
sFun[1] = sData[iDataLen*2-i*2-1];
没有安装2005的机器上所得数据正常。
之前症状:本人机器装有VS2008,调用的DLL在装有VS2005的A君编译,调用所得数据也对,拿到本人机器,第一次正确,以后再调用所得数据都不正确。
问题解决,至于内在原因没搞清楚,明早结贴,留点时间有兴趣的朋友可以继续讨论讨论。