如题:我用delphi10.2.3编写了一个dll,这个dll里面,带了一个窗体,且导出了两个涵数供外部调用。
其中一个是初始化的。
一个是调用功能的。
现在发现,在别的语言开发的程序中,如果是简单的demo,比如:程序就只有一个简单的窗口,在窗体上调用这两个涵数时,90%是没问题的,但调用次数多了,会偶尔出现程序崩溃的情况。
但如果是在一个功能完善的应用程序中调用这个dll时,则100%,第二次调用时,dll就会造成主程序崩溃。
可能我表达的有点乱,下面我直接贴上代码,恳请高手指点下:
我dll中,用到了窗体,也用到了多线程,因为dll上面需要有界面显示效果等。dll代码如下:library leshua;uses
System.SysUtils,
System.Classes,
Unit1 in 'Unit1.pas',
Unit2 in 'Unit2.pas' {Form2};{$R *.res}
exports
initLeshua,
LeshuaPay;begin
end.
单元1代码如下:unit Unit1;interfaceuses
Winapi.Windows, System.SysUtils, System.Classes, System.Hash, Vcl.Controls,
Vcl.Forms, Vcl.ExtCtrls, IdHTTP, IdSSLOpenSSL, IdGlobal, XMLDoc, XMLIntf,
StrUtils, messages, unit2, IniFiles;type
str_thread_date = record
ls_djno: string;
ls_amount: string;
ls_paycode: string;
end;type
TMyThread = class(TThread)
private
{ Private declarations }
protected
procedure Execute; override; {执行}
procedure Run; {声明多一个过程,把功能代码写在这里再给Execute调用}
end;function initLeshua(as_shh: PAnsiChar; as_t0: PAnsiChar; handle: THandle): Integer; stdcall;function LeshuaPay(as_djno: PAnsiChar; as_amount: PAnsiChar; as_paycode: PAnsiChar; as_error: PAnsiChar): Integer; stdcall;function GetDllPath: string;procedure writeLog(logstr: string);var
Form2: TForm2;
gs_error: string; //错误内容
gi_return: Integer; //返回结果 < 0为失败, >= 0为成功
lstr_thread_data: str_thread_date;
appHandle: THandle;
oldHandle: THandle;
gs_url: string; //请求的地址
gs_key: string; //KEY
gs_shh: string; //商户号implementationvar
MyThread: TMyThread; {声明一个线程类对象}procedure TMyThread.Execute;
begin
{ Place thread code here }
writeLog('线程开始执行');
FreeOnTerminate := True; {加上这句线程用完了会自动注释}
writeLog('准备运行线程涵数run');
Run;
end;procedure TMyThread.Run;
var
i: integer;
ls_showtext: string;
ls_shh: string;
myinifile: TIniFile; //配置文件
begin
writeLog('线程涵数run开始'); myinifile := Tinifile.Create(ExtractFilePath(GetDllPath()) + 'leshuapay.ini');
ls_shh := myinifile.Readstring('system', 'business_no', '');
myinifile.Destroy;
writeLog('线程涵数run获取shh=' + ls_shh); for i := 1 to 10 do
begin
writeLog('线程涵数run:第' + IntToStr(i) + '次循环');
Form2.Canvas.Lock;
Form2.Panel1.Caption := '第' + IntToStr(i) + '次循环';
Form2.Canvas.Unlock;
writeLog('线程涵数run:进行延时1秒');
Sleep(1000);
end;
writeLog('线程涵数run:准备关闭Form2');
Form2.Close;
writeLog('线程涵数run:结束');
end;function initLeshua(as_shh: PAnsiChar; as_t0: PAnsiChar; handle: THandle): Integer; stdcall;
var
ls_shh: PAnsiChar;
myinifile: TIniFile; //配置文件
begin
if (appHandle = 0) then
appHandle := handle; myinifile := Tinifile.Create(ExtractFilePath(GetDllPath()) + 'leshuapay.ini');
myinifile.writestring('system', 'business_no', as_shh);
myinifile.Destroy; gs_key := 'ADB8145F4D8A820D30B20B1930';
gs_url := 'https://paygate.shuazf.com/cgi-bin/pay_gateway.cgi';
writeLog('初始化:商户号=[' + as_shh + '],请求地址=[' + gs_url + ']');
Result := 1;
end;function LeshuaPay(as_djno: PAnsiChar; as_amount: PAnsiChar; as_paycode: PAnsiChar; as_error: PAnsiChar): Integer; stdcall;
var
ls_error: string;
begin
writeLog('开始支付涵数');
MyThread := TMyThread.Create(False); {实例化线程类,为False时立即运行,为True时可加MyThread.Resume用来启动}
writeLog('打开线程完毕');
oldHandle := Application.Handle;
Application.Handle := appHandle;
writeLog('实例华窗体');
Application.CreateForm(TForm2, Form2);
try
writeLog('显示窗体');
Form2.ShowModal;
finally
writeLog('准备释放资源');
Form2.Free;
writeLog('窗体释放完毕');
Application.Handle := oldHandle;
writeLog('句柄还原');
end; ls_error := 'WXZF|0000290005619263';
writeLog('复制要返回的信息');
StrPCopy(as_error, AnsiString(ls_error));
writeLog('支付涵数返回');
Result := 1;
end;function GetDllPath: string;
var
ModuleName: string;
begin
SetLength(ModuleName, 255);
//取得Dll自身路径
GetModuleFileName(HInstance, PChar(ModuleName), Length(ModuleName));
Result := PChar(ModuleName);
end;procedure writeLog(logstr: string);
var
filev: TextFile;
path, name, logfile: string;
begin
//写日志
path := ExtractFilePath(GetDllPath()) + 'log\';
if not FileExists((path)) then
ForceDirectories(path); name := 'LESHUA_' + FormatDateTime('yyyymmdd', now); //取得日期
logfile := path + name + '.txt'; //日志存取路径
name := logfile; if FileExists(logfile) then
begin
AssignFile(filev, logfile);
append(filev);
writeln(filev, FormatDateTime('d', now) + '/' + FormatDateTime('hh:nn:ss.zzz', now) + ' ' + logstr);
end
else
begin
AssignFile(filev, logfile);
ReWrite(filev);
writeln(filev, FormatDateTime('d', now) + '/' + FormatDateTime('hh:nn:ss.zzz', now) + ' ' + logstr);
end;
CloseFile(filev);
end;end.单元二代码如下:单元二其实就只有一个空白窗体。
unit Unit2;interfaceuses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls;type
TForm2 = class(TForm)
Panel1: TPanel;
private
{ Private declarations }
public
{ Public declarations }
end;var
Form2: TForm2;implementation{$R *.dfm}end.我调用dll的主程序是PowerBuilder9.0的:
PB9声明
FUNCTION long initLeshua(string appid,string payments,uLong handle) library "leshua.dll" Alias for "initLeshua"
FUNCTION long LeshuaPay(string paydjno,string payamount,string paycode,ref string error) library "leshua.dll" Alias for "LeshuaPay"涵数调用如下:
初始化:
initLeshua("5208056","0",HANDle(this))功能调用:
string ls_error
string ls_paycode
string ls_djno
string ls_amount
integer li_return
char ls_err[]ls_error = space(255)
ls_djno = "LSZFCY20173" + string(today(),"YYMMDD") + string(now(),"HHMMSSFFF")
ls_paycode = trim(sle_1.text)
ls_amount = "1"li_return = LeshuaPay(ls_djno,ls_amount,ls_paycode,ls_error)
IF li_return < 0 THEN
messagebox("错误","调用失败:" + ls_error)
ELSE
messagebox("恭喜","调用成功:" + ls_error)
END IFPS:PowerBuilder部份的,我贴的是简单的demo程序的代码,功能完善的程序的里面调用跟这里也是一样的。现在我通过写的txt日志,显示在完善的程序中调用dll涵数时,第一次都会成功,但第二次会失败,日志记录最后一条,
是for循环里面写完第一条日志后,主程序就崩溃了,即我标的那一条后面,日志就没有了。
日志如下:求大师能指点下我这dll中是哪里有问题?或是什么东西使用方法不对?
其中一个是初始化的。
一个是调用功能的。
现在发现,在别的语言开发的程序中,如果是简单的demo,比如:程序就只有一个简单的窗口,在窗体上调用这两个涵数时,90%是没问题的,但调用次数多了,会偶尔出现程序崩溃的情况。
但如果是在一个功能完善的应用程序中调用这个dll时,则100%,第二次调用时,dll就会造成主程序崩溃。
可能我表达的有点乱,下面我直接贴上代码,恳请高手指点下:
我dll中,用到了窗体,也用到了多线程,因为dll上面需要有界面显示效果等。dll代码如下:library leshua;uses
System.SysUtils,
System.Classes,
Unit1 in 'Unit1.pas',
Unit2 in 'Unit2.pas' {Form2};{$R *.res}
exports
initLeshua,
LeshuaPay;begin
end.
单元1代码如下:unit Unit1;interfaceuses
Winapi.Windows, System.SysUtils, System.Classes, System.Hash, Vcl.Controls,
Vcl.Forms, Vcl.ExtCtrls, IdHTTP, IdSSLOpenSSL, IdGlobal, XMLDoc, XMLIntf,
StrUtils, messages, unit2, IniFiles;type
str_thread_date = record
ls_djno: string;
ls_amount: string;
ls_paycode: string;
end;type
TMyThread = class(TThread)
private
{ Private declarations }
protected
procedure Execute; override; {执行}
procedure Run; {声明多一个过程,把功能代码写在这里再给Execute调用}
end;function initLeshua(as_shh: PAnsiChar; as_t0: PAnsiChar; handle: THandle): Integer; stdcall;function LeshuaPay(as_djno: PAnsiChar; as_amount: PAnsiChar; as_paycode: PAnsiChar; as_error: PAnsiChar): Integer; stdcall;function GetDllPath: string;procedure writeLog(logstr: string);var
Form2: TForm2;
gs_error: string; //错误内容
gi_return: Integer; //返回结果 < 0为失败, >= 0为成功
lstr_thread_data: str_thread_date;
appHandle: THandle;
oldHandle: THandle;
gs_url: string; //请求的地址
gs_key: string; //KEY
gs_shh: string; //商户号implementationvar
MyThread: TMyThread; {声明一个线程类对象}procedure TMyThread.Execute;
begin
{ Place thread code here }
writeLog('线程开始执行');
FreeOnTerminate := True; {加上这句线程用完了会自动注释}
writeLog('准备运行线程涵数run');
Run;
end;procedure TMyThread.Run;
var
i: integer;
ls_showtext: string;
ls_shh: string;
myinifile: TIniFile; //配置文件
begin
writeLog('线程涵数run开始'); myinifile := Tinifile.Create(ExtractFilePath(GetDllPath()) + 'leshuapay.ini');
ls_shh := myinifile.Readstring('system', 'business_no', '');
myinifile.Destroy;
writeLog('线程涵数run获取shh=' + ls_shh); for i := 1 to 10 do
begin
writeLog('线程涵数run:第' + IntToStr(i) + '次循环');
Form2.Canvas.Lock;
Form2.Panel1.Caption := '第' + IntToStr(i) + '次循环';
Form2.Canvas.Unlock;
writeLog('线程涵数run:进行延时1秒');
Sleep(1000);
end;
writeLog('线程涵数run:准备关闭Form2');
Form2.Close;
writeLog('线程涵数run:结束');
end;function initLeshua(as_shh: PAnsiChar; as_t0: PAnsiChar; handle: THandle): Integer; stdcall;
var
ls_shh: PAnsiChar;
myinifile: TIniFile; //配置文件
begin
if (appHandle = 0) then
appHandle := handle; myinifile := Tinifile.Create(ExtractFilePath(GetDllPath()) + 'leshuapay.ini');
myinifile.writestring('system', 'business_no', as_shh);
myinifile.Destroy; gs_key := 'ADB8145F4D8A820D30B20B1930';
gs_url := 'https://paygate.shuazf.com/cgi-bin/pay_gateway.cgi';
writeLog('初始化:商户号=[' + as_shh + '],请求地址=[' + gs_url + ']');
Result := 1;
end;function LeshuaPay(as_djno: PAnsiChar; as_amount: PAnsiChar; as_paycode: PAnsiChar; as_error: PAnsiChar): Integer; stdcall;
var
ls_error: string;
begin
writeLog('开始支付涵数');
MyThread := TMyThread.Create(False); {实例化线程类,为False时立即运行,为True时可加MyThread.Resume用来启动}
writeLog('打开线程完毕');
oldHandle := Application.Handle;
Application.Handle := appHandle;
writeLog('实例华窗体');
Application.CreateForm(TForm2, Form2);
try
writeLog('显示窗体');
Form2.ShowModal;
finally
writeLog('准备释放资源');
Form2.Free;
writeLog('窗体释放完毕');
Application.Handle := oldHandle;
writeLog('句柄还原');
end; ls_error := 'WXZF|0000290005619263';
writeLog('复制要返回的信息');
StrPCopy(as_error, AnsiString(ls_error));
writeLog('支付涵数返回');
Result := 1;
end;function GetDllPath: string;
var
ModuleName: string;
begin
SetLength(ModuleName, 255);
//取得Dll自身路径
GetModuleFileName(HInstance, PChar(ModuleName), Length(ModuleName));
Result := PChar(ModuleName);
end;procedure writeLog(logstr: string);
var
filev: TextFile;
path, name, logfile: string;
begin
//写日志
path := ExtractFilePath(GetDllPath()) + 'log\';
if not FileExists((path)) then
ForceDirectories(path); name := 'LESHUA_' + FormatDateTime('yyyymmdd', now); //取得日期
logfile := path + name + '.txt'; //日志存取路径
name := logfile; if FileExists(logfile) then
begin
AssignFile(filev, logfile);
append(filev);
writeln(filev, FormatDateTime('d', now) + '/' + FormatDateTime('hh:nn:ss.zzz', now) + ' ' + logstr);
end
else
begin
AssignFile(filev, logfile);
ReWrite(filev);
writeln(filev, FormatDateTime('d', now) + '/' + FormatDateTime('hh:nn:ss.zzz', now) + ' ' + logstr);
end;
CloseFile(filev);
end;end.单元二代码如下:单元二其实就只有一个空白窗体。
unit Unit2;interfaceuses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls;type
TForm2 = class(TForm)
Panel1: TPanel;
private
{ Private declarations }
public
{ Public declarations }
end;var
Form2: TForm2;implementation{$R *.dfm}end.我调用dll的主程序是PowerBuilder9.0的:
PB9声明
FUNCTION long initLeshua(string appid,string payments,uLong handle) library "leshua.dll" Alias for "initLeshua"
FUNCTION long LeshuaPay(string paydjno,string payamount,string paycode,ref string error) library "leshua.dll" Alias for "LeshuaPay"涵数调用如下:
初始化:
initLeshua("5208056","0",HANDle(this))功能调用:
string ls_error
string ls_paycode
string ls_djno
string ls_amount
integer li_return
char ls_err[]ls_error = space(255)
ls_djno = "LSZFCY20173" + string(today(),"YYMMDD") + string(now(),"HHMMSSFFF")
ls_paycode = trim(sle_1.text)
ls_amount = "1"li_return = LeshuaPay(ls_djno,ls_amount,ls_paycode,ls_error)
IF li_return < 0 THEN
messagebox("错误","调用失败:" + ls_error)
ELSE
messagebox("恭喜","调用成功:" + ls_error)
END IFPS:PowerBuilder部份的,我贴的是简单的demo程序的代码,功能完善的程序的里面调用跟这里也是一样的。现在我通过写的txt日志,显示在完善的程序中调用dll涵数时,第一次都会成功,但第二次会失败,日志记录最后一条,
是for循环里面写完第一条日志后,主程序就崩溃了,即我标的那一条后面,日志就没有了。
日志如下:求大师能指点下我这dll中是哪里有问题?或是什么东西使用方法不对?
解决方案 »
- delphi里wwDBComboBox的使用
- 如何查找固定的数并提取出来
- 急!有关chart的问题,弄了一天了!
- Delphi中怎么样定义静态变量?
- 解决加到100分:按F1如何实现帮助功能,delphi 是怎么实现的?
- THotKey的赋值问题
- combobox的使用问题:怎样让combobox具有WIN2000的运行框的自动下拉和模糊查询功能?
- 十万火急,高分相送,delphi封装asp问题,请各位高手相助!
- 请问扩展名VSD的文件用何软件打开?急
- 我在delphi中写的汉字注释拷到写字本上或Word上就是乱码 ,各位请帮忙!
- DELPHI 操作word 与
- delphi xe1.2.3下json解析
writeLog('打开线程完毕');
oldHandle := Application.Handle;
Application.Handle := appHandle;
writeLog('实例华窗体');
Application.CreateForm(TForm2, Form2);
try
writeLog('显示窗体');
Form2.ShowModal;
finally
writeLog('准备释放资源');
Form2.Free;
//...
你建立线程之后窗体还没有建立,线程就可能引用Form2了,另外,Form2释放之后,线程可能还没有结束,此时再引用Form2也会异常
我刚刚弄成在打开线程前,去实创建窗体了。
但我测试后,发现,还是一样的有问题,日志如上图。看日志,我有两点没搞明白的:
1、我明明是线程里面执行的循环,为何循环没执行完,也没有去close窗体时,确会退出了线程,而执行后后续的代码?
2、第二次执行时,崩溃,基本都是在create窗体时,出错了,但我上次调用dll涵数时,在线程里面有去close窗体,也有free窗体,照理来说,再次create不会异常啊。综上,
1、我要怎么控制必须是线程跑完,才允许跑后面的代码?
2、我如何确保我本次调用涵数创建和打开的窗体完全关闭并释放,而不影响下次涵数再创建窗体?
var
ls_error: string;
begin
writeLog('开始支付涵数');
writeLog('处理窗口句柄Application.Handle=' + IntToStr(Application.Handle) + ' appHandle=' + IntToStr(appHandle));
oldHandle := Application.Handle;
Application.Handle := appHandle;
writeLog('开始实例化窗体createform');
Application.CreateForm(TForm2, Form2);
writeLog('准备打开线程');
MyThread := TMyThread.Create(False); {实例化线程类,为False时立即运行,为True时可加MyThread.Resume用来启动}
writeLog('打开线程完毕'); try
writeLog('显示窗体');
Form2.ShowModal;
finally
writeLog('准备释放资源');
Form2.Free;
writeLog('窗体释放完毕');
Application.Handle := oldHandle;
writeLog('句柄还原');
end; ls_error := 'WXZF|0000290005619263';
writeLog('复制要返回的信息');
StrPCopy(as_error, AnsiString(ls_error));
writeLog('支付涵数返回====================================================');
Result := 1;
end;
附上我改后的部份的代码。
难道delphi版高手都流失了?
冒似连查看人数,都没多少。
我现在是在线程里面create窗体的呢。
你是说在初始化的那个initLeshua涵数里面创建线程?还是窗体?
但这样不是一样的么?因为我程序中一样会多次调用这个initLeshua初始化涵数的。并不是只调用一次。
我现在是在线程里面create窗体的呢。不对啊,你是在:
function LeshuaPay(as_djno: PAnsiChar; as_amount: PAnsiChar; as_paycode: PAnsiChar; as_error: PAnsiChar): Integer; stdcall;
var
ls_error: string;
begin
writeLog('开始支付涵数');
MyThread := TMyThread.Create(False); {实例化线程类,为False时立即运行,为True时可加MyThread.Resume用来启动}
writeLog('打开线程完毕');
oldHandle := Application.Handle;
Application.Handle := appHandle;
writeLog('实例华窗体');
Application.CreateForm(TForm2, Form2);
里创建的啊
应该再Run()里创建啊
我现在是在线程里面create窗体的呢。不对啊,你是在:
function LeshuaPay(as_djno: PAnsiChar; as_amount: PAnsiChar; as_paycode: PAnsiChar; as_error: PAnsiChar): Integer; stdcall;
var
ls_error: string;
begin
writeLog('开始支付涵数');
MyThread := TMyThread.Create(False); {实例化线程类,为False时立即运行,为True时可加MyThread.Resume用来启动}
writeLog('打开线程完毕');
oldHandle := Application.Handle;
Application.Handle := appHandle;
writeLog('实例华窗体');
Application.CreateForm(TForm2, Form2);
里创建的啊
应该再Run()里创建啊哦,你是说在run里面的开始位置去create窗体,在run的结束位置去close并free窗体?我再试试,但我记得我好像试过这样的,也会崩溃。
DLL_PROCESS_DETACH中释放窗口
我百度了下,没搞明白这个在dll初始化和释放时去弄这个窗体,能请问一下,有demo或详细的说时的链接么?求个。
uses WinApi.Windows;// ...procedure DllMain(reason: integer);
begin
case reason of
DLL_PROCESS_ATTACH:
begin
//
end;
DLL_PROCESS_DETACH:
begin
//
end;
end;
end;begin
DllProc := DllMain;
DLLMain(DLL_PROCESS_ATTACH);
end.