Delphi DLL中如何设置Inno安装程序的Unicode版本和字符串参数,有更长的解决方案吗?

2026-04-10 16:342阅读0评论SEO教程
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计943个文字,预计阅读时间需要4分钟。

Delphi DLL中如何设置Inno安装程序的Unicode版本和字符串参数,有更长的解决方案吗?

在Delphi 10.1中,我编写了一个DLL,使用Devart IBDac组件,允许在安装中备份和恢复Firebird DB(将应用程序从Firebird 1.5升级到3)。通过Ansi Inno Setup 5.5.9进行安装,一切正常。由于我想避免使用AnsiString和PAnsiChar。

我在Delphi 10.1中编写了一个DLL,它使用Devart IBDac组件来允许在安装中备份和恢复Firebird DB(将应用程序从Firebird 1.5升级到3).使用Ansi Inno Setup 5.5.9一切正常.因为我想避免AnsiString和PAnsiChar作为参数(为了更方便地使用Delphi)我安装了Inno Setup Unicode版本5.5.9(u)并将参数从AnsiString / PAnsiChar更改为DLL中的String以及Inno Setup Script .我也在Inno Setup脚本中尝试过WideString,但这根本不起作用. Inno Setup不承认PChar.

奇怪的是脚本部分工作,参数被正确传输到DLL但我在与参数无关的情况下遇到GPF:

>在DLL中的字符串上使用trim(无参数!)会创建GPF.
>创建DBConnection-Object也会创建GPF.

我认为参数类型是这种邪恶的根源.这是我在DLL和脚本中唯一改变的东西. AnsiString的版本仍然运行良好.

感谢您分享任何想法,我在这里做错了什么.

更新:请求的示例代码:

Delphi DLL中如何设置Inno安装程序的Unicode版本和字符串参数,有更长的解决方案吗?

从资源运行脚本的Delphi过程在第一个语句中失败.在第一行的MessageBox中,我验证了所有参数都正确到达.

procedure runScript(aServer, aDatabase, aUser, aPW, aPort, DLL, script: String); stdcall; var ResStream: TResourceStream; DB: TIBCConnection; SC: TIBCScript; { Muss Ansistring sein wegen Resourcestream } s: ansistring; begin try DB := TIBCConnection.create(nil); SC := TIBCScript.create(nil); SC.Connection := DB; try DB.clientlibrary := DLL; DB.Database := aDatabase; DB.Server := aServer; DB.Username := aUser; DB.Password := aPW; DB.Port := aPort; DB.connected := true; ResStream := TResourceStream.create(hInstance, script, RT_RCDATA); setlength(s, ResStream.size); ResStream.ReadBuffer(s[1], ResStream.size); SC.SQL.Text := String(s); SC.Execute; DB.close; finally SC.free; DB.free; end; except on e: Exception do begin MessageBox(0, PChar(e.message), PChar('Setup - Fehler in "' + script + '"'), mb_ok); raise; end; end; end;

Inno Setup(Unicode)中过程的定义.其他必需的DLL在第一个函数定义中声明,因此它们都存在.它适用于AnsiString / PAnsiChar!

procedure runScript(aServer, aDatabase, aUser, aPW, aPort, DLL, script: String); external 'runScript@files:itcsetupfb.dll stdcall setuponly';

此恢复功能有效但仅当我在函数中不使用TRIM()时:

procedure restoreDB(destServer, destDatabase, destSysdbaPW, aBackupfile, aPort, DLL: String; PBRange: integer; PbHandle, LblHandle, WizHandle: THandle); stdcall; var IBCRestoreService1: TIBCRestoreService; cnt: integer; s: String; Msg: TMsg; begin IBCRestoreService1 := TIBCRestoreService.create(nil); SendMessage(PbHandle, PBM_SETRANGE, 0, PBRange shl 16); cnt := 0; try try with IBCRestoreService1 do begin clientlibrary := DLL; Database.Text := destDatabase; Server := destServer; Username := 'sysdba'; Password := destSysdbaPW; Port := aPort; Options := [roFixFssMetadata, roNoValidityCheck, roFixFssData, roOneRelationAtATime, roDeactivateIndexes]; FixFssCharset := 'ISO8859_1'; BackupFile.Text := aBackupfile; PageSize := 16384; verbose := true; Attach; try ServiceStart; while IsServiceRunning do begin inc(cnt); if cnt > PBRange then cnt := 0; s := GetNextLine + ' '; { this was formerly s:=trim(strinGreplace(s,'gbak:','', } { [rfReplaceall,rfIgnoreCase])). I Identified the trim to be the } { cause of the GPF. Very strange. } delete(s, 1, 5); while (length(s) > 0) and (s[1] = #32) do delete(s, 1, 1); { if we have no message file (local fb15) } if pos('format message', s) > 0 then s := 'Arbeite ...'; SendMessage(LblHandle, $0C, 0, longint(s)); SendMessage(PbHandle, PBM_SETPOS, cnt, 0); while PeekMessage(Msg, WizHandle, 0, 0, PM_REMOVE) do begin TranslateMessage(Msg); DispatchMessage(Msg); end; UpdateWindow(PbHandle); end; finally detach; end; end; finally IBCRestoreService1.free; end; except on e: Exception do begin MessageBox(0, PChar('Die Wiederherstellung der EMILpro Datenbank ist leider fehlgeschlagen. Folgender Fehler trat auf: ' + #13#10#10 + e.message), 'Setup - Fehler bei Wiederherstellung', mb_ok); raise; end; end; end;

这个函数在Inno Setup中声明如下:

procedure restoreDB(destServer, destDatabase, destSysdbaPW, aBackupfile, aPort, DLL: String; PBRange: integer; PbHandle,LblHandle,WizHandle: THandle); external 'restoreDB@files:itcsetupfb.dll,fbclient.dll,gds32.dll,icudt52.dll,icudt52l.dat,icuin52.dll,icuuc52.dll,fbintl.dll,firebird.conf,firebird.msg,ib_util.dll,engine12.dll,msvcp100.dll,msvcr100.dll,fbintl.conf stdcall setuponly'; 您不能在Inno Setup的DLL API中使用字符串类型,原因至少有两个:

>当您将函数参数声明为字符串时,Inno Setup将始终假定实际参数类型为PChar.
>字符串是“智能”类型,用于执行内部分配和引用计数.这样的类不能传递DLL边界,因为DLL不能释放由应用程序分配的内存,反之亦然,因为每个都有自己的内存管理器.更多内存布局和类的内部逻辑可能因Delphi版本(用于构建Inno Setup和DLL的版本)而异.

在Delphi中将参数实现为PWideChar(= PChar),并在Unicode Inno Setup中将参数声明为(宽)字符串.我认为在Delphi中使用PWideChar进行“in”参数没有任何不便.您可以在任何可以使用字符串的地方使用它.

有关类似的讨论,请参阅
Exchanging strings (PChar) between a Freepascal compiled DLL and a Delphi compiled EXE

本文共计943个文字,预计阅读时间需要4分钟。

Delphi DLL中如何设置Inno安装程序的Unicode版本和字符串参数,有更长的解决方案吗?

在Delphi 10.1中,我编写了一个DLL,使用Devart IBDac组件,允许在安装中备份和恢复Firebird DB(将应用程序从Firebird 1.5升级到3)。通过Ansi Inno Setup 5.5.9进行安装,一切正常。由于我想避免使用AnsiString和PAnsiChar。

我在Delphi 10.1中编写了一个DLL,它使用Devart IBDac组件来允许在安装中备份和恢复Firebird DB(将应用程序从Firebird 1.5升级到3).使用Ansi Inno Setup 5.5.9一切正常.因为我想避免AnsiString和PAnsiChar作为参数(为了更方便地使用Delphi)我安装了Inno Setup Unicode版本5.5.9(u)并将参数从AnsiString / PAnsiChar更改为DLL中的String以及Inno Setup Script .我也在Inno Setup脚本中尝试过WideString,但这根本不起作用. Inno Setup不承认PChar.

奇怪的是脚本部分工作,参数被正确传输到DLL但我在与参数无关的情况下遇到GPF:

>在DLL中的字符串上使用trim(无参数!)会创建GPF.
>创建DBConnection-Object也会创建GPF.

我认为参数类型是这种邪恶的根源.这是我在DLL和脚本中唯一改变的东西. AnsiString的版本仍然运行良好.

感谢您分享任何想法,我在这里做错了什么.

更新:请求的示例代码:

Delphi DLL中如何设置Inno安装程序的Unicode版本和字符串参数,有更长的解决方案吗?

从资源运行脚本的Delphi过程在第一个语句中失败.在第一行的MessageBox中,我验证了所有参数都正确到达.

procedure runScript(aServer, aDatabase, aUser, aPW, aPort, DLL, script: String); stdcall; var ResStream: TResourceStream; DB: TIBCConnection; SC: TIBCScript; { Muss Ansistring sein wegen Resourcestream } s: ansistring; begin try DB := TIBCConnection.create(nil); SC := TIBCScript.create(nil); SC.Connection := DB; try DB.clientlibrary := DLL; DB.Database := aDatabase; DB.Server := aServer; DB.Username := aUser; DB.Password := aPW; DB.Port := aPort; DB.connected := true; ResStream := TResourceStream.create(hInstance, script, RT_RCDATA); setlength(s, ResStream.size); ResStream.ReadBuffer(s[1], ResStream.size); SC.SQL.Text := String(s); SC.Execute; DB.close; finally SC.free; DB.free; end; except on e: Exception do begin MessageBox(0, PChar(e.message), PChar('Setup - Fehler in "' + script + '"'), mb_ok); raise; end; end; end;

Inno Setup(Unicode)中过程的定义.其他必需的DLL在第一个函数定义中声明,因此它们都存在.它适用于AnsiString / PAnsiChar!

procedure runScript(aServer, aDatabase, aUser, aPW, aPort, DLL, script: String); external 'runScript@files:itcsetupfb.dll stdcall setuponly';

此恢复功能有效但仅当我在函数中不使用TRIM()时:

procedure restoreDB(destServer, destDatabase, destSysdbaPW, aBackupfile, aPort, DLL: String; PBRange: integer; PbHandle, LblHandle, WizHandle: THandle); stdcall; var IBCRestoreService1: TIBCRestoreService; cnt: integer; s: String; Msg: TMsg; begin IBCRestoreService1 := TIBCRestoreService.create(nil); SendMessage(PbHandle, PBM_SETRANGE, 0, PBRange shl 16); cnt := 0; try try with IBCRestoreService1 do begin clientlibrary := DLL; Database.Text := destDatabase; Server := destServer; Username := 'sysdba'; Password := destSysdbaPW; Port := aPort; Options := [roFixFssMetadata, roNoValidityCheck, roFixFssData, roOneRelationAtATime, roDeactivateIndexes]; FixFssCharset := 'ISO8859_1'; BackupFile.Text := aBackupfile; PageSize := 16384; verbose := true; Attach; try ServiceStart; while IsServiceRunning do begin inc(cnt); if cnt > PBRange then cnt := 0; s := GetNextLine + ' '; { this was formerly s:=trim(strinGreplace(s,'gbak:','', } { [rfReplaceall,rfIgnoreCase])). I Identified the trim to be the } { cause of the GPF. Very strange. } delete(s, 1, 5); while (length(s) > 0) and (s[1] = #32) do delete(s, 1, 1); { if we have no message file (local fb15) } if pos('format message', s) > 0 then s := 'Arbeite ...'; SendMessage(LblHandle, $0C, 0, longint(s)); SendMessage(PbHandle, PBM_SETPOS, cnt, 0); while PeekMessage(Msg, WizHandle, 0, 0, PM_REMOVE) do begin TranslateMessage(Msg); DispatchMessage(Msg); end; UpdateWindow(PbHandle); end; finally detach; end; end; finally IBCRestoreService1.free; end; except on e: Exception do begin MessageBox(0, PChar('Die Wiederherstellung der EMILpro Datenbank ist leider fehlgeschlagen. Folgender Fehler trat auf: ' + #13#10#10 + e.message), 'Setup - Fehler bei Wiederherstellung', mb_ok); raise; end; end; end;

这个函数在Inno Setup中声明如下:

procedure restoreDB(destServer, destDatabase, destSysdbaPW, aBackupfile, aPort, DLL: String; PBRange: integer; PbHandle,LblHandle,WizHandle: THandle); external 'restoreDB@files:itcsetupfb.dll,fbclient.dll,gds32.dll,icudt52.dll,icudt52l.dat,icuin52.dll,icuuc52.dll,fbintl.dll,firebird.conf,firebird.msg,ib_util.dll,engine12.dll,msvcp100.dll,msvcr100.dll,fbintl.conf stdcall setuponly'; 您不能在Inno Setup的DLL API中使用字符串类型,原因至少有两个:

>当您将函数参数声明为字符串时,Inno Setup将始终假定实际参数类型为PChar.
>字符串是“智能”类型,用于执行内部分配和引用计数.这样的类不能传递DLL边界,因为DLL不能释放由应用程序分配的内存,反之亦然,因为每个都有自己的内存管理器.更多内存布局和类的内部逻辑可能因Delphi版本(用于构建Inno Setup和DLL的版本)而异.

在Delphi中将参数实现为PWideChar(= PChar),并在Unicode Inno Setup中将参数声明为(宽)字符串.我认为在Delphi中使用PWideChar进行“in”参数没有任何不便.您可以在任何可以使用字符串的地方使用它.

有关类似的讨论,请参阅
Exchanging strings (PChar) between a Freepascal compiled DLL and a Delphi compiled EXE