工作环境:
Windows XP + Delphi 7
代码如下:
.....(省略)
function MyPost(Url:String;Params: TStrings): String;
begin
Result := 'HelloWorld';
end;function ProcessDataFile(Url,FileName: Pchar): Pchar;stdcall;
var
Report : TStrings;
begin
Report := TStringList.Create;
try
Report.LoadFromFile(Strpas(filename));
Result := Pchar(MyPost(Strpas(Url),Report));
finally
Report.Free;
end;end;exports
ProcessDataFile;beginend.问题是:
每次调用这个DLL,当文件大于3K的时候都会出现地址访问出错。Access Violation at address **** in module "MyDll.dll". Read address ****.但是相同的函数如果在主程序的空间中就不会出错(把相同的函数放在主程序中),请教各位大虾,这个是什么原因?
Windows XP + Delphi 7
代码如下:
.....(省略)
function MyPost(Url:String;Params: TStrings): String;
begin
Result := 'HelloWorld';
end;function ProcessDataFile(Url,FileName: Pchar): Pchar;stdcall;
var
Report : TStrings;
begin
Report := TStringList.Create;
try
Report.LoadFromFile(Strpas(filename));
Result := Pchar(MyPost(Strpas(Url),Report));
finally
Report.Free;
end;end;exports
ProcessDataFile;beginend.问题是:
每次调用这个DLL,当文件大于3K的时候都会出现地址访问出错。Access Violation at address **** in module "MyDll.dll". Read address ****.但是相同的函数如果在主程序的空间中就不会出错(把相同的函数放在主程序中),请教各位大虾,这个是什么原因?
2、 使用pchar 代替 String ;
3、如果参数有多个,可以尝试使用TStringList 玩玩;
String只是用在DLL内部的过程和函数,不是接口函数。
接口函数用的就是Pchar,而现在的问题就是内部的过程函数出了问题。
MyPost返回一个字符串,而你又把它转成了pchar这样造成了字符串的生存周期和指针的生存周期不同步,这个习惯很不好,因为字符串变量的生存周期是程序自动控制的,而pchar指针是手动控制的,容易造成访问违规
2 尽量避免在dll做内存申请和释放,可以的话,直接把pchar在exe中申请,然后传入dll
3 MyPost这个函数对传入的内容完全没使用,LZ故意把代码和谐了吧
上面的代码可以运行,但是一样会报错,楼上的朋友可以尝试一下。
如果文件大于某个size的时候,系统就会报access violation.
var
x: string;
begin
...
Result := PChar(x);
end.由于 x 是个局部变量,所以当函数结束后,x 的生存期也就结束了,rtl 会自动清理 x。因此这个函数的返回值实际上是一个野指针。但很不幸的是,当 x 是常量时,函数结束后不会销毁;当 x 不够长时(数量级不大于1M),内存管理器一般不会把它所在内存页交还给操作系统,又不会进行清0的操作,所以对它的访问一般不会直接表现出错。
由于菜鸟实在占了绝大多数的比例,有了解到这个问题的原理的水平的人实在太少,有这个水平的人一般也不会出现这么低级的问题,所以国内论坛直接写上面代码的人比比皆是,而且这些人也没有能力写出正确的测试程序。下面是一段测试程序,能够说明为什么 Result := PChar(LocalStrVar) 为什么是非法的:program Project1;{$APPTYPE CONSOLE}uses
FastMM4,
SysUtils;function foo: PChar;
begin
Result := PChar(StringOfChar('*', 8*1024*1024));
end;begin
try
Writeln(foo[1]);
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
Readln;
end.把“返回值”的长度改短,会发现“没有出错”。不是长度有限制,而是错了却碰巧没显现出来,当到一定长度之后才正确的出现了出错的结果。
function _MyTestFunc(const AInput: PAnsiChar; AOutput: PAnsiChar): Integer;
var
S: String;
begin
S := StrPas(AInput);
//…… 任意操作 ……
if AOutput = nil then
Result := Length(S)
else
StrPCopy(AOutput, S);
end;
EXE中
function MyTestFunc(const AInput: String): String;
var
L: Integer;
P: PAnsiChar;
begin
L := _MyTestFunc(PAnsiChar(AInput), nil);
GetMem(P, L + 1);
try
FillChar(P^, L + 1, #0);
_MyTestFunc(PAnsiChar(AInput), P);
Result := StrPas(P);
finally
FreeMem(P, L + 1);
end;
end;
function _MyTestFunc(const AInput: PAnsiChar; AOutput: PAnsiChar): Integer;
AOutput的内存交给调用者去管理不要下面这样写,内存管理混乱
function _MyTestFunc(const AInput: PAnsiChar): PAnsiChar;