Inno设置:添加GUI以连接到SQL [英] Inno Setup: add GUI to connect to SQL
问题描述
在这里完成Inno Setup和Pascal newbie。我想在 setup.exe
中打包一个 .sql
文件。
安装程序将可供下载,因此我无法在安装项目中嵌入任何连接字符串。安装程序基本上通过运行一个简单的 .sql
脚本
$来将.NET CLR程序集(dll作为十六进制流)安装到提供的SQL服务器数据库中。 b $ b
我已经看到这里关于连接到SQL服务器的其他帖子,但没有解决我的问题的硬编码连接字符串或复制/实现通用数据连接对话框,弹出在所有MS应用程序, DB连接
理想情况下,创建连接字符串应通过MS已发布 http://archive.msdn.microsoft.com/Connection ,但不知道如何链接到弹出安装过程中。
如果不可能没有(太多)的努力,另一个选项将是一个自定义削减版本的对话框屏幕在Inno设置,只是服务器名称\path文本框。
一个复选框,用于指定是使用Windows还是SQL服务器身份验证(选择SQL身份验证时启用用户名/密码文本框)
此时尝试连接,并显示可用数据库的下拉列表。
我可以得到server\instance文本框工作,但是不知道如何实现Windows身份验证\SQL身份验证组合框和后续操作
提示?
编辑:感谢TLama,MS提供的连接对话框UI看起来像没有去。我已经使用Inno安装表单向导获得了外观方面,但一些功能仍然阻碍我:
我不知道如何
-
启用/禁用
lblUser
,lblPassword
,
txtUsername
,txtPassword
当chkSQLAuth.selected
is true / false。 -
启用
lstDatabase
lstDatabase
>使用指定的凭证(连接到服务器并执行
SELECT name FROM master.dbo.sysdatabases WHERE HAS_DBACCESS(name)= 1 ORDER BY name
/ li> 我认为一旦这样做,我应该能够找出如何针对选定的数据库执行我的SQL脚本!
[安装]
AppName = test
AppVersion = 1.0
LicenseFile = C :\Program Files(x86)\Inno Script Studio\License.rtf
CreateAppDir = False
UsePreviousGroup = False
DisableProgramGroupPage = yes
Uninstallable = no
[Files]
源:C:\ Install Install Assembly.sql; DestDir:{tmp};标志:dontcopy
[CustomMessages]
CustomForm_Caption =连接到数据库服务器
CustomForm_Description =输入连接到数据库服务器所需的信息
CustomForm_lblServer_Caption0 =服务器名称:
CustomForm_lblAuthType_Caption0 =登录凭据
CustomForm_lblUser_Caption0 =用户名:
CustomForm_lblPassword_Caption0 =密码:
CustomForm_lblDatabase_Caption0 =数据库:
CustomForm_chkSQLAuth_Caption0 =使用SQL Server身份验证
CustomForm_chkWindowsAuth_Caption0 =使用Windows身份验证
[代码]
var
lblServer:TLabel;
lblAuthType:TLabel;
lblUser:TLabel;
lblPassword:TLabel;
lblDatabase:TLabel;
chkSQLAuth:TRadioButton;
txtServer:TEdit;
chkWindowsAuth:TRadioButton;
txtUsername:TEdit;
txtPassword:TPasswordEdit;
lstDatabase:TComboBox;
var
页面:TWizardPage;
{CustomForm_Activate}
过程CustomForm_Activate(页面:TWizardPage);
begin
//在这里输入代码...
end;
{CustomForm_ShouldSkipPage}
函数CustomForm_ShouldSkipPage(页面:TWizardPage):Boolean;
begin
结果:= False;
end;
{CustomForm_BackButtonClick}
函数CustomForm_BackButtonClick(页面:TWizardPage):Boolean;
begin
结果:= True;
end;
{CustomForm_NextkButtonClick}
函数CustomForm_NextButtonClick(页:TWizardPage):Boolean;
begin
结果:= True;
end;
{CustomForm_CancelButtonClick}
过程CustomForm_CancelButtonClick(页面:TWizardPage; var Cancel,Confirm:Boolean);
begin
//在这里输入代码...
end;
{CustomForm_CreatePage}
函数CustomForm_CreatePage(PreviousPageId:Integer):Integer;
begin
Page:= CreateCustomPage(
PreviousPageId,
ExpandConstant('{cm:CustomForm_Caption}'),
ExpandConstant('{cm:CustomForm_Description}') b $ b);
{lblServer}
lblServer:= TLabel.Create(Page);
with lblServer do
begin
父级:= Page.Surface;
Caption:= ExpandConstant('{cm:CustomForm_lblServer_Caption0}');
Left:= ScaleX(24);
Top:= ScaleY(8);
Width:= ScaleX(68);
高度:= ScaleY(13);
end;
{txtServer}
txtServer:= TEdit.Create(Page);
with txtServer do
begin
父级:= Page.Surface;
Left:= ScaleX(112);
Top:= ScaleY(8);
Width:= ScaleX(273);
高度:= ScaleY(21);
TabOrder:= 0;
end;
{lblAuthType}
lblAuthType:= TLabel.Create(Page);
with lblAuthType do
begin
父级:= Page.Surface;
Caption:= ExpandConstant('{cm:CustomForm_lblAuthType_Caption0}');
Left:= ScaleX(24);
Top:= ScaleY(48);
Width:= ScaleX(87);
高度:= ScaleY(13);
end;
{chkWindowsAuth}
chkWindowsAuth:= TRadioButton.Create(Page);
with chkWindowsAuth do
begin
父级:= Page.Surface;
Caption:= ExpandConstant('{cm:CustomForm_chkWindowsAuth_Caption0}');
Left:= ScaleX(32);
Top:= ScaleY(64);
Width:= ScaleX(177);
高度:= ScaleY(17);
Checked:= True;
TabOrder:= 1;
TabStop:= True;
end;
{chkSQLAuth}
chkSQLAuth:= TRadioButton.Create(Page);
with chkSQLAuth do
begin
父级:= Page.Surface;
Caption:= ExpandConstant('{cm:CustomForm_chkSQLAuth_Caption0}');
Left:= ScaleX(32);
Top:= ScaleY(84);
Width:= ScaleX(185);
高度:= ScaleY(17);
TabOrder:= 2;
end;
{lblUser}
lblUser:= TLabel.Create(Page);
with lblUser do
begin
父级:= Page.Surface;
Caption:= ExpandConstant('{cm:CustomForm_lblUser_Caption0}');
Left:= ScaleX(56);
Top:= ScaleY(104);
Width:= ScaleX(58);
高度:= ScaleY(13);
Enabled:= False;
end;
{lblPassword}
lblPassword:= TLabel.Create(Page);
with lblPassword do
begin
父级:= Page.Surface;
Caption:= ExpandConstant('{cm:CustomForm_lblPassword_Caption0}');
Left:= ScaleX(56);
Top:= ScaleY(128);
Width:= ScaleX(53);
高度:= ScaleY(13);
Enabled:= False;
end;
{txtUsername}
txtUsername:= TEdit.Create(Page);
with txtUsername do
begin
父级:= Page.Surface;
Left:= ScaleX(120);
Top:= ScaleY(104);
Width:= ScaleX(241);
高度:= ScaleY(21);
Enabled:= False;
TabOrder:= 3;
end;
{txtPassword}
txtPassword:= TPasswordEdit.Create(Page);
with txtPassword do
begin
父级:= Page.Surface;
Left:= ScaleX(120);
Top:= ScaleY(128);
Width:= ScaleX(241);
高度:= ScaleY(21);
Enabled:= False;
TabOrder:= 4;
end;
{lblDatabase}
lblDatabase:= TLabel.Create(Page);
with lblDatabase do
begin
父级:= Page.Surface;
Caption:= ExpandConstant('{cm:CustomForm_lblDatabase_Caption0}');
Left:= ScaleX(56);
Top:= ScaleY(168);
Width:= ScaleX(53);
高度:= ScaleY(13);
end;
{lstDatabase}
lstDatabase:= TComboBox.Create(Page);
with lstDatabase do
begin
父级:= Page.Surface;
Left:= ScaleX(120);
Top:= ScaleY(168);
Width:= ScaleX(145);
高度:= ScaleY(21);
TabOrder:= 5;
end;
with Page do
begin
OnActivate:= @CustomForm_Activate;
OnShouldSkipPage:= @CustomForm_ShouldSkipPage;
OnBackButtonClick:= @CustomForm_BackButtonClick;
OnNextButtonClick:= @CustomForm_NextButtonClick;
OnCancelButtonClick:= @CustomForm_CancelButtonClick;
end;
结果:= Page.ID;
end;
procedure CurPageChanged(CurPageID:Integer);
begin
如果CurPageID = Page.ID然后
WizardForm.NextButton.Enabled:= False;
end;
{CustomForm_InitializeWizard}
procedure InitializeWizard();
begin
CustomForm_CreatePage(wpWelcome);
end;
这里是为了防止别人想要类似的功能。只需更改许可证的链接和 .sql
文件引用
[Setup]
AppName = test
AppVersion = 1.0
LicenseFile = C:\setup demo\License.rtf
CreateAppDir = False
UsePreviousGroup = False
DisableProgramGroupPage = yes
Uninstallable = no
[Files]
源:C:\setup demo\script 2008R2.sql ;标志:dontcopy
源:C:\setup demo\script 2012.sql;标志:dontcopy
[CustomMessages]
CustomForm_Caption =连接到数据库服务器
CustomForm_Description =输入连接到数据库服务器所需的信息
CustomForm_lblServer_Caption0 =服务器名称:
CustomForm_lblAuthType_Caption0 =登录凭证
CustomForm_lblUser_Caption0 =用户名:
CustomForm_lblPassword_Caption0 =密码:
CustomForm_lblDatabase_Caption0 =数据库:
CustomForm_lblVersion_Caption0 = SQL版本:
CustomForm_chkSQLAuth_Caption0 =使用SQL服务器身份验证
CustomForm_chkWindowsAuth_Caption0 =使用Windows身份验证
CustomForm_lstVersion_Line0 = 2008 R2
CustomForm_lstVersion_Line1 = 2012
[代码]
const
adCmdUnspecified = $ FFFFFFFF ;
adCmdUnknown = $ 00000008;
adCmdText = $ 00000001;
adCmdTable = $ 00000002;
adCmdStoredProc = $ 00000004;
adCmdFile = $ 00000100;
adCmdTableDirect = $ 00000200;
adOptionUnspecified = $ FFFFFFFF;
adAsyncExecute = $ 00000010;
adAsyncFetch = $ 00000020;
adAsyncFetchNonBlocking = $ 00000040;
adExecuteNoRecords = $ 00000080;
adExecuteStream = $ 00000400;
adExecuteRecord = $ 00000800;
var
lblVersion:TLabel;
lstVersion:TComboBox;
lblServer:TLabel;
lblAuthType:TLabel;
lblUser:TLabel;
lblPassword:TLabel;
lblDatabase:TLabel;
chkSQLAuth:TRadioButton;
txtServer:TEdit;
chkWindowsAuth:TRadioButton;
txtUsername:TEdit;
txtPassword:TPasswordEdit;
lstDatabase:TComboBox;
bIsNextEnabled:Boolean;
var
页面:TWizardPage;
//用于通过sql脚本错误生成错误代码
procedure ExitProcess(exitCode:integer);
external'ExitProcess@kernel32.dll stdcall';
//版本下拉默认为空白。选择版本后启用服务器文本框。这将强制用户首先选择版本。
过程VersionOnChange(Sender:TObject);
begin
lblServer.Enabled:= True;
txtServer.Enabled:= True;
end;
//启用/禁用子文本框&文本已输入到服务器文本框中时的功能。没有意义填充子项目,除非服务器存在值。
过程ServerOnChange(Sender:TObject);
begin
lstDatabase.Items.Clear;
lstDatabase.Text:='';
bIsNextEnabled:= False;
WizardForm.NextButton.Enabled:= bIsNextEnabled;
if Length(txtServer.Text)> 0 then
begin
lblAuthType.Enabled:= True;
lblDatabase.Enabled:= True;
lstDatabase.Enabled:= True;
chkWindowsAuth.Enabled:= True;
chkSQLAuth.Enabled:= True;
end
else
begin
lblAuthType.Enabled:= False;
lblDatabase.Enabled:= False;
lstDatabase.Enabled:= False;
chkWindowsAuth.Enabled:= False;
chkSQLAuth.Enabled:= False;
end
end;
//根据所选的身份验证类型启用/禁用用户/传递文本框。只有SQL Auth
过程需要用户/传递AuthOnChange(Sender:TObject);
begin
如果chkSQLAuth.Checked然后
begin
lblUser.Enabled:= true;
lblPassword.Enabled:= true;
txtUsername.Enabled:= true;
txtPassword.Enabled:= true;
end
Else
begin
lblUser.Enabled:= false;
lblPassword.Enabled:= false;
txtUsername.Enabled:= false;
txtPassword.Enabled:= false;
end
end;
//输入数据库名称后启用下一步按钮。
过程DatabaseOnChange(Sender:TObject);
begin
if(Length(lstDatabase.Text)> 0)和(lstDatabase.Enabled)then
begin
bIsNextEnabled:= True;
WizardForm.NextButton.Enabled:= bIsNextEnabled;
end
else
begin
bIsNextEnabled:= False;
WizardForm.NextButton.Enabled:= bIsNextEnabled;
end
end;
//使用指定的凭据检索服务器上可访问的数据库列表。
//这个列表显示在数据库下拉列表
procedure RetrieveDatabaseList(Sender:TObject);
var
ADOCommand:Variant;
ADORecordset:Variant;
ADOConnection:Variant;
begin
lstDatabase.Items.Clear;
try
//创建ADO连接对象
ADOConnection:= CreateOleObject('ADODB.Connection');
//构建连接字符串;有关详细信息,请在Internet上搜索ADO
//连接字符串
ADOConnection.ConnectionString:=
'Provider = SQLOLEDB;'+ //提供者
'数据源= + txtServer.Text +';'+ //服务器名称
'应用程序名称='+'{#SetupSetting(AppName)}'+'DB列表;'
如果chkWindowsAuth.Checked然后
ADOConnection.ConnectionString:= ADOConnection.ConnectionString +
'Integrated Security = SSPI;'// Windows Auth
else
ADOConnection.ConnectionString:= ADOConnection.ConnectionString +
' User Id ='+ txtUsername.Text +';'+ //用户名
'Password ='+ txtPassword.Text +';'; // password
//通过指定的ConnectionString打开连接
ADOConnection.Open;
try
//创建ADO命令对象
ADOCommand:= CreateOleObject('ADODB.Command');
//将当前打开的连接分配给ADO命令对象
ADOCommand.ActiveConnection:= ADOConnection;
//为提供者分配要发出的命令的文本
ADOCommand.CommandText:='SELECT name FROM master.dbo.sysdatabases WHERE HAS_DBACCESS(name)= 1 ORDER BY name';
//此属性设置意味着您要执行
// CommandText text命令;它做同样,喜欢如果你
//只在执行语句中使用adCmdText标志
ADOCommand.CommandType:= adCmdText;
//这将执行命令并返回数据集
ADORecordset:= ADOCommand.Execute;
//从使用基于0的索引字段访问的数据集中获取值;
//注意,你不能直接连接常量字符串
//与Variant数据值
,而不是ADORecordset.eof do
begin
lstDatabase.Items。 Add(ADORecordset.Fields(0));
ADORecordset.MoveNext;
end
finally
ADOConnection.Close;
end;
except
MsgBox(GetExceptionMessage,mbError,MB_OK);
end;
end;
//根据用户定义的server.database执行[files]节中指定的文件(硬编码)
procedure DeploySQL();
var
Script2008R2:AnsiString;
Script2012:AnsiString;
ADOCommand:Variant;
ADOConnection:Variant;
begin
//提取所需版本的脚本
如果lstVersion.Text ='2008 R2'然后
ExtractTemporaryFile('Script 2008R2.sql')
如果lstVersion。 Text ='2012'then
ExtractTemporaryFile('Script 2012.sql');
try
//创建ADO连接对象
ADOConnection:= CreateOleObject('ADODB.Connection');
//构建连接字符串;有关详细信息,请在Internet上搜索ADO
//连接字符串
ADOConnection.ConnectionString:=
'Provider = SQLOLEDB;'+ //提供者
'数据源= + txtServer.Text +';'+ //服务器名称
'初始目录='+ lstDatabase.Text +';'+ //服务器名称
'应用程序名称='+'{#SetupSetting AppName)}'+'Execute SQL;';
if chkWindowsAuth.Checked then
ADOConnection.ConnectionString:= ADOConnection.ConnectionString +
'Integrated Security = SSPI;'// Windows Auth
else
ADOConnection.ConnectionString: = ADOConnection.ConnectionString +
'User Id ='+ txtUsername.Text +';'+ //用户名
'Password ='+ txtPassword.Text +';'; // password
//通过指定的ConnectionString打开连接
ADOConnection.Open;
try
//创建ADO命令对象
ADOCommand:= CreateOleObject('ADODB.Command');
//将当前打开的连接分配给ADO命令对象
ADOCommand.ActiveConnection:= ADOConnection;
//将文件中的脚本加载到变量中。异或,因为两个版本不应该同时存在。
if(LoadStringFromFile(ExpandConstant('{tmp} \Script 2012.sql'),Script2012))xor(LoadStringFromFile(ExpandConstant('{tmp} \Script 2008R2.sql'),Script2008R2))then
begin
//分配要针对提供程序发出的命令的文本。附加全部3,因为其中一个安装程序集字符串将始终为空。
ADOCommand.CommandText:= Script2008R2 + Script2012;
//这将执行脚本;这里的adCmdText标志意味着
//你将执行CommandText文本命令,而
// adExecuteNoRecords标志确保没有数据行将从提供程序获得
//,应提高性能
ADOCommand.Execute(NULL,NULL,adCmdText或adExecuteNoRecords);
end
else
begin
MsgBox('安装文件丢失。',mbError,MB_OK);
ExitProcess(7);
end
finally
ADOConnection.Close;
end;
except
MsgBox(GetExceptionMessage,mbError,MB_OK);
ExitProcess(5);
end;
end;
{CustomForm_NextkButtonClick}
//尝试连接到提供的db。不需要捕获错误/关闭连接错误,因为失败的连接从未打开。
function CustomForm_NextButtonClick(页:TWizardPage):Boolean;
var
ADOConnection:Variant;
begin
// try
//创建ADO连接对象
ADOConnection:= CreateOleObject('ADODB.Connection');
//构建连接字符串;有关详细信息,请在Internet上搜索ADO
//连接字符串
ADOConnection.ConnectionString:=
'Provider = SQLOLEDB;'+ //提供者
'数据源= + txtServer.Text +';'+ //服务器名称
'初始目录='+ lstDatabase.Text +';'+ //服务器名称
'应用程序名称='+'{#SetupSetting AppName)}'+'Execute SQL;';
if chkWindowsAuth.Checked then
ADOConnection.ConnectionString:= ADOConnection.ConnectionString +
'Integrated Security = SSPI;'// Windows Auth
else
ADOConnection.ConnectionString: = ADOConnection.ConnectionString +
'User Id ='+ txtUsername.Text +';'+ //用户名
'Password ='+ txtPassword.Text +';'; // password
//通过指定的ConnectionString打开连接
ADOConnection.Open;
结果:= True;
end;
{CustomForm_CreatePage}
函数CustomForm_CreatePage(PreviousPageId:Integer):Integer;
begin
Page:= CreateCustomPage(
PreviousPageId,
ExpandConstant('{cm:CustomForm_Caption}'),
ExpandConstant('{cm:CustomForm_Description }')
);
{lblVersion}
lblVersion:= TLabel.Create(Page);
with lblVersion do
begin
父级:= Page.Surface;
Caption:= ExpandConstant('{cm:CustomForm_lblVersion_Caption0}');
Left:= ScaleX(24);
Top:= ScaleY(8);
Width:= ScaleX(61);
高度:= ScaleY(13);
end;
{lstVersion}
lstVersion:= TComboBox.Create(Page);
with lstVersion do
begin
父级:= Page.Surface;
Left:= ScaleX(112);
Top:= ScaleY(8);
Width:= ScaleX(145);
高度:= ScaleY(21);
样式:= csDropDownList;
DropDownCount:= 2;
TabOrder:= 0;
Items.Add(ExpandConstant('{cm:CustomForm_lstVersion_Line0}'));
Items.Add(ExpandConstant('{cm:CustomForm_lstVersion_Line1}'));
OnChange:= @VersionOnChange;
end;
{lblServer}
lblServer:= TLabel.Create(Page);
with lblServer do
begin
父级:= Page.Surface;
Caption:= ExpandConstant('{cm:CustomForm_lblServer_Caption0}');
Left:= ScaleX(24);
Top:= ScaleY(32);
Width:= ScaleX(68);
高度:= ScaleY(13);
Enabled:= False;
end;
{txt Server}
txtServer:= TEdit.Create(Page);
with txtServer do
begin
父级:= Page.Surface;
Left:= ScaleX(112);
Top:= ScaleY(32);
Width:= ScaleX(273);
高度:= ScaleY(21);
TabOrder:= 1;
Enabled:= False;
OnChange:= @ServerOnChange;
end;
{lblAuthType}
lblAuthType:= TLabel.Create(Page);
with lblAuthType do
begin
父级:= Page.Surface;
Caption:= ExpandConstant('{cm:CustomForm_lblAuthType_Caption0}');
Left:= ScaleX(24);
Top:= ScaleY(72);
Width:= ScaleX(87);
高度:= ScaleY(13);
Enabled = = False;
end;
{chkWindowsAuth}
chkWindowsAuth:= TRadioButton.Create(Page);
with chkWindowsAuth do
begin
父级:= Page.Surface;
Caption:= ExpandConstant('{cm:CustomForm_chkWindowsAuth_Caption0}');
Left:= ScaleX(32);
Top:= ScaleY(88);
Width:= ScaleX(177);
高度:= ScaleY(17);
Checked:= True;
TabOrder:= 2;
TabStop:= True;
OnClick:= @AuthOnChange;
Enabled:= False;
end;
{chkSQLAuth}
chkSQLAuth:= TRadioButton.Create(Page);
with chkSQLAuth do
begin
父级:= Page.Surface;
Caption:= ExpandConstant('{cm:CustomForm_chkSQLAuth_Caption0}');
Left:= ScaleX(32);
Top:= ScaleY(108);
Width:= ScaleX(185);
高度:= ScaleY(17);
TabOrder:= 3;
OnClick:= @AuthOnChange;
Enabled:= False;
end;
{lblUser}
lblUser:= TLabel.Create(Page);
with lblUser do
begin
父级:= Page.Surface;
Caption:= ExpandConstant('{cm:CustomForm_lblUser_Caption0}');
Left:= ScaleX(56);
Top:= ScaleY(128);
Width:= ScaleX(58);
高度:= ScaleY(13);
Enabled:= False;
end;
{lblPassword}
lblPassword:= TLabel.Create(Page);
with lblPassword do
begin
父级:= Page.Surface;
Caption:= ExpandConstant('{cm:CustomForm_lblPassword_Caption0}');
Left:= ScaleX(56);
Top:= ScaleY(152);
Width:= ScaleX(53);
高度:= ScaleY(13);
Enabled:= False;
end;
{txtUsername}
txtUsername:= TEdit.Create(Page);
with txtUsername do
begin
父级:= Page.Surface;
Left:= ScaleX(120);
Top:= ScaleY(128);
Width:= ScaleX(241);
高度:= ScaleY(21);
Enabled:= False;
TabOrder:= 4;
end;
{txtPassword}
txtPassword:= TPasswordEdit.Create(Page);
with txtPassword do
begin
父级:= Page.Surface;
Left:= ScaleX(120);
Top:= ScaleY(152);
Width:= ScaleX(241);
高度:= ScaleY(21);
Enabled:= False;
TabOrder:= 5;
end;
{lblDatabase}
lblDatabase:= TLabel.Create(Page);
with lblDatabase do
begin
父级:= Page.Surface;
Caption:= ExpandConstant('{cm:CustomForm_lblDatabase_Caption0}');
Left:= ScaleX(56);
Top:= ScaleY(192);
Width:= ScaleX(53);
高度:= ScaleY(13);
Enabled:= False;
end;
{lstDatabase}
lstDatabase:= TComboBox.Create(Page);
with lstDatabase do
begin
父级:= Page.Surface;
Left:= ScaleX(120);
Top:= ScaleY(192);
Width:= ScaleX(145);
高度:= ScaleY(21);
Enabled:= False;
TabOrder:= 6;
OnDropDown:= @RetrieveDatabaseList;
OnChange:= @DatabaseOnChange;
end;
with Page do
begin
OnNextButtonClick:= @CustomForm_NextButtonClick;
end;
结果:= Page.ID;
end;
procedure CurPageChanged(CurPageID:Integer);
begin
//设置下一个按钮的初始状态。应在页面首次加载时禁用,但应在用户单击时启用。
if CurPageID = Page.ID then
WizardForm.NextButton.Enabled:= bIsNextEnabled;
end;
过程CurStepChanged(CurStep:TSetupStep);
begin
//预安装步骤似乎是进行实际安装的最佳时间。问题是这不是传统的安装。没有什么被复制到用户的pc
如果CurStep = ssInstall然后
DeploySQL;
end;
procedure InitializeWizard();
begin
bIsNextEnabled:= False;
CustomForm_CreatePage(wpLicense);
end;
Complete Inno Setup and Pascal newbie here. I want to package a single .sql
file inside a setup.exe
.
The setup will be available for download so I can't embed any connection strings within the setup project. The setup basically installs an .NET CLR assembly (dll as hex stream) into the supplied SQL server database by running a simple .sql
script
I've seen the other posts on here about connecting to an SQL server but none address my issue of a hardcoded connection string or replicating/implementing the generic Data Connection Dialog that pops up in all MS apps for making a DB connection
Ideally the creation of the connection string should be handled via the Data Connection Dialog that MS have released the code for http://archive.msdn.microsoft.com/Connection but have no idea how to link this to pop up during the install.
If not possible without (too much) effort then another option would be to have a custom cut down version of the dialog screen within Inno Setup with just the server name\path text box.
A checkbox to specify whether to use Windows or SQL server authentication (username/pass text boxes gets enabled when SQL authentication is selected)
At which point the connection is attempted and a drop down with the available databases appears.
I can get the server\instance text box working but have no idea how to implement the Windows authentication\SQL authentication combo box and subsequent actions
Tips?
edit: Thanks TLama, the MS supplied connection dialog UI seems like a no go then. I've gotten the 'appearance' aspect right using the Inno Setup form wizard, but some of the functionality still baffles me:
I have no idea how to
enable/disable the
lblUser
,lblPassword
,txtUsername
,txtPassword
whenchkSQLAuth.selected
is true/false.enable the
lstDatabase
combo box and label once there is content in thetxtServer
textbox.populate the
lstDatabase
combo box using the credentials specified (connect to server and execute"SELECT name FROM master.dbo.sysdatabases WHERE HAS_DBACCESS(name) = 1 ORDER BY name"
) on click of thelstDatabase
.Then enable the Next button when a database is selected.
I think once that's done I should be able to figure out how to execute my SQL script against the selected database!
[Setup]
AppName=test
AppVersion=1.0
LicenseFile=C:\Program Files (x86)\Inno Script Studio\License.rtf
CreateAppDir=False
UsePreviousGroup=False
DisableProgramGroupPage=yes
Uninstallable=no
[Files]
Source: "C:\Install Assembly.sql"; DestDir: "{tmp}"; Flags: dontcopy
[CustomMessages]
CustomForm_Caption=Connect to Database Server
CustomForm_Description=Enter the information required to connect to the database server
CustomForm_lblServer_Caption0=Server name:
CustomForm_lblAuthType_Caption0=Log on credentials
CustomForm_lblUser_Caption0=User name:
CustomForm_lblPassword_Caption0=Password:
CustomForm_lblDatabase_Caption0=Database:
CustomForm_chkSQLAuth_Caption0=Use SQL Server Authentication
CustomForm_chkWindowsAuth_Caption0=Use Windows Authentication
[Code]
var
lblServer: TLabel;
lblAuthType: TLabel;
lblUser: TLabel;
lblPassword: TLabel;
lblDatabase: TLabel;
chkSQLAuth: TRadioButton;
txtServer: TEdit;
chkWindowsAuth: TRadioButton;
txtUsername: TEdit;
txtPassword: TPasswordEdit;
lstDatabase: TComboBox;
var
Page: TWizardPage;
{ CustomForm_Activate }
procedure CustomForm_Activate(Page: TWizardPage);
begin
// enter code here...
end;
{ CustomForm_ShouldSkipPage }
function CustomForm_ShouldSkipPage(Page: TWizardPage): Boolean;
begin
Result := False;
end;
{ CustomForm_BackButtonClick }
function CustomForm_BackButtonClick(Page: TWizardPage): Boolean;
begin
Result := True;
end;
{ CustomForm_NextkButtonClick }
function CustomForm_NextButtonClick(Page: TWizardPage): Boolean;
begin
Result := True;
end;
{ CustomForm_CancelButtonClick }
procedure CustomForm_CancelButtonClick(Page: TWizardPage; var Cancel, Confirm: Boolean);
begin
// enter code here...
end;
{ CustomForm_CreatePage }
function CustomForm_CreatePage(PreviousPageId: Integer): Integer;
begin
Page := CreateCustomPage(
PreviousPageId,
ExpandConstant('{cm:CustomForm_Caption}'),
ExpandConstant('{cm:CustomForm_Description}')
);
{ lblServer }
lblServer := TLabel.Create(Page);
with lblServer do
begin
Parent := Page.Surface;
Caption := ExpandConstant('{cm:CustomForm_lblServer_Caption0}');
Left := ScaleX(24);
Top := ScaleY(8);
Width := ScaleX(68);
Height := ScaleY(13);
end;
{ txtServer }
txtServer := TEdit.Create(Page);
with txtServer do
begin
Parent := Page.Surface;
Left := ScaleX(112);
Top := ScaleY(8);
Width := ScaleX(273);
Height := ScaleY(21);
TabOrder := 0;
end;
{ lblAuthType }
lblAuthType := TLabel.Create(Page);
with lblAuthType do
begin
Parent := Page.Surface;
Caption := ExpandConstant('{cm:CustomForm_lblAuthType_Caption0}');
Left := ScaleX(24);
Top := ScaleY(48);
Width := ScaleX(87);
Height := ScaleY(13);
end;
{ chkWindowsAuth }
chkWindowsAuth := TRadioButton.Create(Page);
with chkWindowsAuth do
begin
Parent := Page.Surface;
Caption := ExpandConstant('{cm:CustomForm_chkWindowsAuth_Caption0}');
Left := ScaleX(32);
Top := ScaleY(64);
Width := ScaleX(177);
Height := ScaleY(17);
Checked := True;
TabOrder := 1;
TabStop := True;
end;
{ chkSQLAuth }
chkSQLAuth := TRadioButton.Create(Page);
with chkSQLAuth do
begin
Parent := Page.Surface;
Caption := ExpandConstant('{cm:CustomForm_chkSQLAuth_Caption0}');
Left := ScaleX(32);
Top := ScaleY(84);
Width := ScaleX(185);
Height := ScaleY(17);
TabOrder := 2;
end;
{ lblUser }
lblUser := TLabel.Create(Page);
with lblUser do
begin
Parent := Page.Surface;
Caption := ExpandConstant('{cm:CustomForm_lblUser_Caption0}');
Left := ScaleX(56);
Top := ScaleY(104);
Width := ScaleX(58);
Height := ScaleY(13);
Enabled := False;
end;
{ lblPassword }
lblPassword := TLabel.Create(Page);
with lblPassword do
begin
Parent := Page.Surface;
Caption := ExpandConstant('{cm:CustomForm_lblPassword_Caption0}');
Left := ScaleX(56);
Top := ScaleY(128);
Width := ScaleX(53);
Height := ScaleY(13);
Enabled := False;
end;
{ txtUsername }
txtUsername := TEdit.Create(Page);
with txtUsername do
begin
Parent := Page.Surface;
Left := ScaleX(120);
Top := ScaleY(104);
Width := ScaleX(241);
Height := ScaleY(21);
Enabled := False;
TabOrder := 3;
end;
{ txtPassword }
txtPassword := TPasswordEdit.Create(Page);
with txtPassword do
begin
Parent := Page.Surface;
Left := ScaleX(120);
Top := ScaleY(128);
Width := ScaleX(241);
Height := ScaleY(21);
Enabled := False;
TabOrder := 4;
end;
{ lblDatabase }
lblDatabase := TLabel.Create(Page);
with lblDatabase do
begin
Parent := Page.Surface;
Caption := ExpandConstant('{cm:CustomForm_lblDatabase_Caption0}');
Left := ScaleX(56);
Top := ScaleY(168);
Width := ScaleX(53);
Height := ScaleY(13);
end;
{ lstDatabase }
lstDatabase := TComboBox.Create(Page);
with lstDatabase do
begin
Parent := Page.Surface;
Left := ScaleX(120);
Top := ScaleY(168);
Width := ScaleX(145);
Height := ScaleY(21);
TabOrder := 5;
end;
with Page do
begin
OnActivate := @CustomForm_Activate;
OnShouldSkipPage := @CustomForm_ShouldSkipPage;
OnBackButtonClick := @CustomForm_BackButtonClick;
OnNextButtonClick := @CustomForm_NextButtonClick;
OnCancelButtonClick := @CustomForm_CancelButtonClick;
end;
Result := Page.ID;
end;
procedure CurPageChanged(CurPageID: Integer);
begin
if CurPageID = Page.ID then
WizardForm.NextButton.Enabled := False;
end;
{ CustomForm_InitializeWizard }
procedure InitializeWizard();
begin
CustomForm_CreatePage(wpWelcome);
end;
Got it working. Here it is in case someone else wants similar functionality. Just change the link to the license and the .sql
file references
[Setup]
AppName=test
AppVersion=1.0
LicenseFile=C:\setup demo\License.rtf
CreateAppDir=False
UsePreviousGroup=False
DisableProgramGroupPage=yes
Uninstallable=no
[Files]
Source: "C:\setup demo\script 2008R2.sql"; Flags: dontcopy
Source: "C:\setup demo\script 2012.sql"; Flags: dontcopy
[CustomMessages]
CustomForm_Caption=Connect to Database Server
CustomForm_Description=Enter the information required to connect to the database server
CustomForm_lblServer_Caption0=Server name:
CustomForm_lblAuthType_Caption0=Log on credentials
CustomForm_lblUser_Caption0=User name:
CustomForm_lblPassword_Caption0=Password:
CustomForm_lblDatabase_Caption0=Database:
CustomForm_lblVersion_Caption0=SQL Version:
CustomForm_chkSQLAuth_Caption0=Use SQL Server Authentication
CustomForm_chkWindowsAuth_Caption0=Use Windows Authentication
CustomForm_lstVersion_Line0=2008 R2
CustomForm_lstVersion_Line1=2012
[Code]
const
adCmdUnspecified = $FFFFFFFF;
adCmdUnknown = $00000008;
adCmdText = $00000001;
adCmdTable = $00000002;
adCmdStoredProc = $00000004;
adCmdFile = $00000100;
adCmdTableDirect = $00000200;
adOptionUnspecified = $FFFFFFFF;
adAsyncExecute = $00000010;
adAsyncFetch = $00000020;
adAsyncFetchNonBlocking = $00000040;
adExecuteNoRecords = $00000080;
adExecuteStream = $00000400;
adExecuteRecord = $00000800;
var
lblVersion: TLabel;
lstVersion: TComboBox;
lblServer: TLabel;
lblAuthType: TLabel;
lblUser: TLabel;
lblPassword: TLabel;
lblDatabase: TLabel;
chkSQLAuth: TRadioButton;
txtServer: TEdit;
chkWindowsAuth: TRadioButton;
txtUsername: TEdit;
txtPassword: TPasswordEdit;
lstDatabase: TComboBox;
bIsNextEnabled: Boolean;
var
Page: TWizardPage;
// Used to generate error code by sql script errors
procedure ExitProcess(exitCode:integer);
external 'ExitProcess@kernel32.dll stdcall';
// Version drop down defaults to blank. Enable server textbox once a version is selected. This forces user to select the version first.
Procedure VersionOnChange (Sender: TObject);
begin
lblServer.Enabled := True;
txtServer.Enabled := True;
end;
// enable/disable child text boxes & functions when text has been entered into Server textbox. Makes no sense to populate child items unless a value exists for server.
Procedure ServerOnChange (Sender: TObject);
begin
lstDatabase.Items.Clear;
lstDatabase.Text := '';
bIsNextEnabled := False;
WizardForm.NextButton.Enabled := bIsNextEnabled;
if Length(txtServer.Text) > 0 then
begin
lblAuthType.Enabled := True;
lblDatabase.Enabled := True;
lstDatabase.Enabled := True;
chkWindowsAuth.Enabled := True;
chkSQLAuth.Enabled := True;
end
else
begin
lblAuthType.Enabled := False;
lblDatabase.Enabled := False;
lstDatabase.Enabled := False;
chkWindowsAuth.Enabled := False;
chkSQLAuth.Enabled := False;
end
end;
// enable/disable user/pass text boxes depending on selected auth type. A user/pass is only required for SQL Auth
procedure AuthOnChange (Sender: TObject);
begin
if chkSQLAuth.Checked then
begin
lblUser.Enabled := true;
lblPassword.Enabled := true;
txtUsername.Enabled := true;
txtPassword.Enabled := true;
end
Else
begin
lblUser.Enabled := false;
lblPassword.Enabled := false;
txtUsername.Enabled := false;
txtPassword.Enabled := false;
end
end;
// Enable next button once a database name has been entered.
Procedure DatabaseOnChange (Sender: TObject);
begin
if (Length(lstDatabase.Text) > 0) and (lstDatabase.Enabled) then
begin
bIsNextEnabled := True;
WizardForm.NextButton.Enabled := bIsNextEnabled;
end
else
begin
bIsNextEnabled := False;
WizardForm.NextButton.Enabled := bIsNextEnabled;
end
end;
// Retrieve a list of databases accessible on the server with the credentials specified.
// This list is shown in the database dropdown list
procedure RetrieveDatabaseList(Sender: TObject);
var
ADOCommand: Variant;
ADORecordset: Variant;
ADOConnection: Variant;
begin
lstDatabase.Items.Clear;
try
// create the ADO connection object
ADOConnection := CreateOleObject('ADODB.Connection');
// build a connection string; for more information, search for ADO
// connection string on the Internet
ADOConnection.ConnectionString :=
'Provider=SQLOLEDB;' + // provider
'Data Source=' + txtServer.Text + ';' + // server name
'Application Name=' + '{#SetupSetting("AppName")}' + ' DB List;'
if chkWindowsAuth.Checked then
ADOConnection.ConnectionString := ADOConnection.ConnectionString +
'Integrated Security=SSPI;' // Windows Auth
else
ADOConnection.ConnectionString := ADOConnection.ConnectionString +
'User Id=' + txtUsername.Text + ';' + // user name
'Password=' + txtPassword.Text + ';'; // password
// open the connection by the assigned ConnectionString
ADOConnection.Open;
try
// create the ADO command object
ADOCommand := CreateOleObject('ADODB.Command');
// assign the currently opened connection to ADO command object
ADOCommand.ActiveConnection := ADOConnection;
// assign text of a command to be issued against a provider
ADOCommand.CommandText := 'SELECT name FROM master.dbo.sysdatabases WHERE HAS_DBACCESS(name) = 1 ORDER BY name';
// this property setting means, that you're going to execute the
// CommandText text command; it does the same, like if you would
// use only adCmdText flag in the Execute statement
ADOCommand.CommandType := adCmdText;
// this will execute the command and return dataset
ADORecordset := ADOCommand.Execute;
// get values from a dataset using 0 based indexed field access;
// notice, that you can't directly concatenate constant strings
// with Variant data values
while not ADORecordset.eof do
begin
lstDatabase.Items.Add(ADORecordset.Fields(0));
ADORecordset.MoveNext;
end
finally
ADOConnection.Close;
end;
except
MsgBox(GetExceptionMessage, mbError, MB_OK);
end;
end;
// Execute files specified in [files] section (hardcoded) against the user defined server.database
procedure DeploySQL();
var
Script2008R2: AnsiString;
Script2012: AnsiString;
ADOCommand: Variant;
ADOConnection: Variant;
begin
// extract required version of script
if lstVersion.Text='2008 R2' then
ExtractTemporaryFile('Script 2008R2.sql')
if lstVersion.Text='2012' then
ExtractTemporaryFile('Script 2012.sql');
try
// create the ADO connection object
ADOConnection := CreateOleObject('ADODB.Connection');
// build a connection string; for more information, search for ADO
// connection string on the Internet
ADOConnection.ConnectionString :=
'Provider=SQLOLEDB;' + // provider
'Data Source=' + txtServer.Text + ';' + // server name
'Initial Catalog=' + lstDatabase.Text + ';' + // server name
'Application Name=' + '{#SetupSetting("AppName")}' + ' Execute SQL;' ;
if chkWindowsAuth.Checked then
ADOConnection.ConnectionString := ADOConnection.ConnectionString +
'Integrated Security=SSPI;' // Windows Auth
else
ADOConnection.ConnectionString := ADOConnection.ConnectionString +
'User Id=' + txtUsername.Text + ';' + // user name
'Password=' + txtPassword.Text + ';'; // password
// open the connection by the assigned ConnectionString
ADOConnection.Open;
try
// create the ADO command object
ADOCommand := CreateOleObject('ADODB.Command');
// assign the currently opened connection to ADO command object
ADOCommand.ActiveConnection := ADOConnection;
// load a script from file into variable. Exclusive OR because both versions should never exist at the same time.
if (LoadStringFromFile(ExpandConstant('{tmp}\Script 2012.sql'), Script2012)) xor (LoadStringFromFile(ExpandConstant('{tmp}\Script 2008R2.sql'), Script2008R2)) then
begin
// assign text of a command to be issued against a provider. Append all 3 because one of the install assembly strings will always be empty.
ADOCommand.CommandText := Script2008R2 + Script2012;
// this will execute the script; the adCmdText flag here means
// you're going to execute the CommandText text command, while
// the adExecuteNoRecords flag ensures no data row will be get
// from a provider, what should improve performance
ADOCommand.Execute(NULL, NULL, adCmdText or adExecuteNoRecords);
end
else
begin
MsgBox('Installation files missing.', mbError, MB_OK);
ExitProcess(7);
end
finally
ADOConnection.Close;
end;
except
MsgBox(GetExceptionMessage, mbError, MB_OK);
ExitProcess(5);
end;
end;
{ CustomForm_NextkButtonClick }
// try to connect to supplied db. Dont need to catch errors/close conn on error because a failed connection is never opened.
function CustomForm_NextButtonClick(Page: TWizardPage): Boolean;
var
ADOConnection: Variant;
begin
//try
// create the ADO connection object
ADOConnection := CreateOleObject('ADODB.Connection');
// build a connection string; for more information, search for ADO
// connection string on the Internet
ADOConnection.ConnectionString :=
'Provider=SQLOLEDB;' + // provider
'Data Source=' + txtServer.Text + ';' + // server name
'Initial Catalog=' + lstDatabase.Text + ';' + // server name
'Application Name=' + '{#SetupSetting("AppName")}' + ' Execute SQL;' ;
if chkWindowsAuth.Checked then
ADOConnection.ConnectionString := ADOConnection.ConnectionString +
'Integrated Security=SSPI;' // Windows Auth
else
ADOConnection.ConnectionString := ADOConnection.ConnectionString +
'User Id=' + txtUsername.Text + ';' + // user name
'Password=' + txtPassword.Text + ';'; // password
// open the connection by the assigned ConnectionString
ADOConnection.Open;
Result := True;
end;
{ CustomForm_CreatePage }
function CustomForm_CreatePage(PreviousPageId: Integer): Integer;
begin
Page := CreateCustomPage(
PreviousPageId,
ExpandConstant('{cm:CustomForm_Caption}'),
ExpandConstant('{cm:CustomForm_Description}')
);
{ lblVersion }
lblVersion := TLabel.Create(Page);
with lblVersion do
begin
Parent := Page.Surface;
Caption := ExpandConstant('{cm:CustomForm_lblVersion_Caption0}');
Left := ScaleX(24);
Top := ScaleY(8);
Width := ScaleX(61);
Height := ScaleY(13);
end;
{ lstVersion }
lstVersion := TComboBox.Create(Page);
with lstVersion do
begin
Parent := Page.Surface;
Left := ScaleX(112);
Top := ScaleY(8);
Width := ScaleX(145);
Height := ScaleY(21);
Style := csDropDownList;
DropDownCount := 2;
TabOrder := 0;
Items.Add(ExpandConstant('{cm:CustomForm_lstVersion_Line0}'));
Items.Add(ExpandConstant('{cm:CustomForm_lstVersion_Line1}'));
OnChange:= @VersionOnChange;
end;
{ lblServer }
lblServer := TLabel.Create(Page);
with lblServer do
begin
Parent := Page.Surface;
Caption := ExpandConstant('{cm:CustomForm_lblServer_Caption0}');
Left := ScaleX(24);
Top := ScaleY(32);
Width := ScaleX(68);
Height := ScaleY(13);
Enabled := False;
end;
{ txtServer }
txtServer := TEdit.Create(Page);
with txtServer do
begin
Parent := Page.Surface;
Left := ScaleX(112);
Top := ScaleY(32);
Width := ScaleX(273);
Height := ScaleY(21);
TabOrder := 1;
Enabled := False;
OnChange := @ServerOnChange;
end;
{ lblAuthType }
lblAuthType := TLabel.Create(Page);
with lblAuthType do
begin
Parent := Page.Surface;
Caption := ExpandConstant('{cm:CustomForm_lblAuthType_Caption0}');
Left := ScaleX(24);
Top := ScaleY(72);
Width := ScaleX(87);
Height := ScaleY(13);
Enabled := False;
end;
{ chkWindowsAuth }
chkWindowsAuth := TRadioButton.Create(Page);
with chkWindowsAuth do
begin
Parent := Page.Surface;
Caption := ExpandConstant('{cm:CustomForm_chkWindowsAuth_Caption0}');
Left := ScaleX(32);
Top := ScaleY(88);
Width := ScaleX(177);
Height := ScaleY(17);
Checked := True;
TabOrder := 2;
TabStop := True;
OnClick := @AuthOnChange;
Enabled := False;
end;
{ chkSQLAuth }
chkSQLAuth := TRadioButton.Create(Page);
with chkSQLAuth do
begin
Parent := Page.Surface;
Caption := ExpandConstant('{cm:CustomForm_chkSQLAuth_Caption0}');
Left := ScaleX(32);
Top := ScaleY(108);
Width := ScaleX(185);
Height := ScaleY(17);
TabOrder := 3;
OnClick := @AuthOnChange;
Enabled := False;
end;
{ lblUser }
lblUser := TLabel.Create(Page);
with lblUser do
begin
Parent := Page.Surface;
Caption := ExpandConstant('{cm:CustomForm_lblUser_Caption0}');
Left := ScaleX(56);
Top := ScaleY(128);
Width := ScaleX(58);
Height := ScaleY(13);
Enabled := False;
end;
{ lblPassword }
lblPassword := TLabel.Create(Page);
with lblPassword do
begin
Parent := Page.Surface;
Caption := ExpandConstant('{cm:CustomForm_lblPassword_Caption0}');
Left := ScaleX(56);
Top := ScaleY(152);
Width := ScaleX(53);
Height := ScaleY(13);
Enabled := False;
end;
{ txtUsername }
txtUsername := TEdit.Create(Page);
with txtUsername do
begin
Parent := Page.Surface;
Left := ScaleX(120);
Top := ScaleY(128);
Width := ScaleX(241);
Height := ScaleY(21);
Enabled := False;
TabOrder := 4;
end;
{ txtPassword }
txtPassword := TPasswordEdit.Create(Page);
with txtPassword do
begin
Parent := Page.Surface;
Left := ScaleX(120);
Top := ScaleY(152);
Width := ScaleX(241);
Height := ScaleY(21);
Enabled := False;
TabOrder := 5;
end;
{ lblDatabase }
lblDatabase := TLabel.Create(Page);
with lblDatabase do
begin
Parent := Page.Surface;
Caption := ExpandConstant('{cm:CustomForm_lblDatabase_Caption0}');
Left := ScaleX(56);
Top := ScaleY(192);
Width := ScaleX(53);
Height := ScaleY(13);
Enabled := False;
end;
{ lstDatabase }
lstDatabase := TComboBox.Create(Page);
with lstDatabase do
begin
Parent := Page.Surface;
Left := ScaleX(120);
Top := ScaleY(192);
Width := ScaleX(145);
Height := ScaleY(21);
Enabled := False;
TabOrder := 6;
OnDropDown:= @RetrieveDatabaseList;
OnChange:= @DatabaseOnChange;
end;
with Page do
begin
OnNextButtonClick := @CustomForm_NextButtonClick;
end;
Result := Page.ID;
end;
procedure CurPageChanged(CurPageID: Integer);
begin
// set initial status of next button. Should be disabled when page is first loaded, but should be enabled if user clicked back.
if CurPageID = Page.ID then
WizardForm.NextButton.Enabled := bIsNextEnabled;
end;
procedure CurStepChanged(CurStep: TSetupStep);
begin
// The preinstall step seems like the best time to do the actual install. The problem is that this is not a traditional install. Nothing is copied to the users' pc
if CurStep = ssInstall then
DeploySQL;
end;
procedure InitializeWizard();
begin
bIsNextEnabled := False;
CustomForm_CreatePage(wpLicense);
end;
这篇关于Inno设置:添加GUI以连接到SQL的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!