作者: Jim Wang 公众号: 巴博萨船长

任务难点

准备一个安装程序,以验证系统的体系结构并安装.NET。

1. 介绍

此安装脚本示例的目的是启动一个安装程序,该安装程序检查系统上当前安装的.NET版本,并仅在需要时安装一个。安装程序将在软件包中包括Microsoft提供的Web安装程序。 安装程序还将根据运行所在的体系结构安装一些库。

2. 背景

出于我们的目的,我们需要可从Microsoft下载站点下载的InnoSetup编译器和.NET Framework Web安装程序文件。Microsoft维护一组注册表项,这些注册表项指示已安装的.NET Framework版本和Service Pack。C#MVP Scott Dorman在此Stack Overflow线程中发布了一个列表,其中包含版本1.0到4.0(在撰写本文时)。除1.0以外,大多数.NET Framework版本所需的注册表项都非常相似。无论如何,我们将忽略该版本已被1.1淘汰的版本。

4.5版和更高版本有些棘手,因为它们可以作为4.0版的就地更新安装并重用完全相同的注册表项。MSDN官方页面“ 如何:确定安装的.NET Framework版本”建议检查DWORDvalue 的存在Release,所以我在下面做这件事。替代方法是检查.NET 4.0 的REG_SZVersion4.0.30319,.NET 4.5的4.5.50709和.NET 4.6的4.6.00079。(可悲的是,在安装它们时,我不认为要记录4.5.1和4.5.2的版本字符串。)

4.5.1版引入了另一个复杂性:根据Windows版本,可能有两个Release值!该脚本通过将所有4.5版或更高版本的查询视为_最低_要求来处理此问题,即,如果该Release值等于或大于任何系统上指定版本的最小值,则这些查询将成功。.NET 4.5或更高版本也可以满足.NET 4.0的查询,因为后者是作为就地更新安装的。对较早版本的查询需要完全匹配,即,即使应用程序兼容,.NET 4.0或更高版本也_无法_满足它们。

3. 代码部分

在[Code]部分,我们定义如下函数来检测以安装的.NET版本。该部分代码取自Jordan Russell的网站。 在下面的Inno Setup脚本代码块中,函数IsDotNetDetected检查是否安装了指定的.NET Framework版本和至少指定的Service Pack级别。所有列出的版本字符串均适用于最终发行版本;测试版和发行候选版通常具有不同的版本号。函数InitializeSetup演示了如何使用IsDotNetDetected不带Service Pack的.NET Framework 4.6进行检查。 原始代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
[Code]
function IsDotNetDetected(version: string; service: cardinal): boolean;
// Indicates whether the specified version and service pack of the .NET Framework is installed.
//
// version -- Specify one of these strings for the required .NET Framework version:
// 'v1.1' .NET Framework 1.1
// 'v2.0' .NET Framework 2.0
// 'v3.0' .NET Framework 3.0
// 'v3.5' .NET Framework 3.5
// 'v4\Client' .NET Framework 4.0 Client Profile
// 'v4\Full' .NET Framework 4.0 Full Installation
// 'v4.5' .NET Framework 4.5
// 'v4.5.1' .NET Framework 4.5.1
// 'v4.5.2' .NET Framework 4.5.2
// 'v4.6' .NET Framework 4.6
// 'v4.6.1' .NET Framework 4.6.1
// 'v4.6.2' .NET Framework 4.6.2
// 'v4.7' .NET Framework 4.7
// 'v4.7.1' .NET Framework 4.7.1
// 'v4.7.2' .NET Framework 4.7.2
// 'v4.8' .NET Framework 4.8
//
// service -- Specify any non-negative integer for the required service pack level:
// 0 No service packs required
// 1, 2, etc. Service pack 1, 2, etc. required
var
key, versionKey: string;
install, release, serviceCount, versionRelease: cardinal;
success: boolean;
begin
versionKey := version;
versionRelease := 0;

// .NET 1.1 and 2.0 embed release number in version key
if version = 'v1.1' then begin
versionKey := 'v1.1.4322';
end else if version = 'v2.0' then begin
versionKey := 'v2.0.50727';
end

// .NET 4.5 and newer install as update to .NET 4.0 Full
else if Pos('v4.', version) = 1 then begin
versionKey := 'v4\Full';
case version of
'v4.5': versionRelease := 378389;
'v4.5.1': versionRelease := 378675; // 378758 on Windows 8 and older
'v4.5.2': versionRelease := 379893;
'v4.6': versionRelease := 393295; // 393297 on Windows 8.1 and older
'v4.6.1': versionRelease := 394254; // 394271 before Win10 November Update
'v4.6.2': versionRelease := 394802; // 394806 before Win10 Anniversary Update
'v4.7': versionRelease := 460798; // 460805 before Win10 Creators Update
'v4.7.1': versionRelease := 461308; // 461310 before Win10 Fall Creators Update
'v4.7.2': versionRelease := 461808; // 461814 before Win10 April 2018 Update
'v4.8': versionRelease := 528040; // 528049 before Win10 May 2019 Update
end;
end;

// installation key group for all .NET versions
key := 'SOFTWARE\Microsoft\NET Framework Setup\NDP\' + versionKey;

// .NET 3.0 uses value InstallSuccess in subkey Setup
if Pos('v3.0', version) = 1 then begin
success := RegQueryDWordValue(HKLM, key + '\Setup', 'InstallSuccess', install);
end else begin
success := RegQueryDWordValue(HKLM, key, 'Install', install);
end;

// .NET 4.0 and newer use value Servicing instead of SP
if Pos('v4', version) = 1 then begin
success := success and RegQueryDWordValue(HKLM, key, 'Servicing', serviceCount);
end else begin
success := success and RegQueryDWordValue(HKLM, key, 'SP', serviceCount);
end;

// .NET 4.5 and newer use additional value Release
if versionRelease > 0 then begin
success := success and RegQueryDWordValue(HKLM, key, 'Release', release);
success := success and (release >= versionRelease);
end;

result := success and (install = 1) and (serviceCount >= service);
end;


function InitializeSetup(): Boolean;
begin
if not IsDotNetDetected('v4.6', 0) then begin
MsgBox('MyApp requires Microsoft .NET Framework 4.6.'#13#13
'Please use Windows Update to install this version,'#13
'and then re-run the MyApp setup program.', mbInformation, MB_OK);
result := false;
end else
result := true;
end;

针对此代码,我们修改为如下形式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
[Code]
function IsDotNetDetected(version: string; service: cardinal): boolean;
// Indicates whether the specified version and service pack of the .NET Framework is installed.
// see the link: https://docs.microsoft.com/en-us/dotnet/framework/migration-guide/how-to-determine-which-versions-are-installed?redirectedfrom=MSDN#net_b
//
// version -- Specify one of these strings for the required .NET Framework version:
// 'v1.1.4322' .NET Framework 1.1
// 'v2.0.50727' .NET Framework 2.0
// 'v3.0' .NET Framework 3.0
// 'v3.5' .NET Framework 3.5
// 'v4\Client' .NET Framework 4.0 Client Profile
// 'v4\Full' .NET Framework 4.0 Full Installation
// 'v4.5' .NET Framework 4.5 378389
// 'v4.6' .NET Framework 4.6 393295
// 'v4.6.1' .NET Framework 4.6.1 394254
// 'v4.6.2' .NET Framework 4.6.2 394802
// 'v4.7' .NET Framework 4.7 393295
//
// service -- Specify any non-negative integer for the required service pack level:
// 0 No service packs required
// 1, 2, etc. Service pack 1, 2, etc. required
var
key: string;
install, release, serviceCount: cardinal;
check45, success: boolean;
var reqNetVer : string;
begin
// .NET 4.5 installs as update to .NET 4.0 Full
if version = 'v4.5' then begin
version := 'v4\Full';
check45 := true;
end else
check45 := false;

// installation key group for all .NET versions
key := 'SOFTWARE\Microsoft\NET Framework Setup\NDP\' + version;

// .NET 3.0 uses value InstallSuccess in subkey Setup
if Pos('v3.0', version) = 1 then begin
success := RegQueryDWordValue(HKLM, key + '\Setup', 'InstallSuccess', install);
end else begin
success := RegQueryDWordValue(HKLM, key, 'Install', install);
end;

// .NET 4.0/4.5 uses value Servicing instead of SP
if Pos('v4', version) = 1 then begin
success := success and RegQueryDWordValue(HKLM, key, 'Servicing', serviceCount);
end else begin
success := success and RegQueryDWordValue(HKLM, key, 'SP', serviceCount);
end;

// .NET 4.5 uses additional value Release
if check45 then begin
success := success and RegQueryDWordValue(HKLM, key, 'Release', release);
success := success and (release >= 378389);
end;

result := success and (install = 1) and (serviceCount >= service);
end;

这是一个全局函数,用于验证是否正确安装了作为参数传递的.NET Framework版本。 现在,我们应该编写一个由安装程序在运行时调用的简单函数,以验证是否已安装所需的.NET版本。我们将以4.0为例。

1
2
3
4
function IsRequiredDotNetDetected(): Boolean;  
begin
result := IsDotNetDetected('v4\Full', 0);
end;

如果需要,我们还可以在设置过程中发布一条消息:

1
2
3
4
5
6
7
8
9
function InitializeSetup(): Boolean;
begin
if not IsDotNetDetected('v4\Full', 0) then begin
MsgBox('{#MyAppName} requires Microsoft .NET Framework 4.0 Client Profile.'#13#13
'The installer will attempt to install it', mbInformation, MB_OK);
end

result := true;
end;

代码部分已经完成,现在我们可以关注**[Files]**部分。

1
2
[Files]
Source: "{#MyDistFolder}\dotNetFx40_Full_setup.exe"; DestDir: {tmp}; Flags: deleteafterinstall; Check: not IsRequiredDotNetDetected

安装程序将复制.NET安装程序文件(这大约是850 KB的智能文件),只有在没有已经存在于系统中,到系统临时目录。安装完成后,该文件将被删除。现在已经复制了网络安装程序,如果需要,我们需要运行它。 在**[run]**部分中,输入以下内容:

1
2
3
[Run]
Filename: {tmp}\dotNetFx40_Full_setup.exe; Parameters: "/q:a /c:""install /l
/q"""; Check: not IsRequiredDotNetDetected; StatusMsg: Microsoft Framework 4.0 is beïng installed. Please wait...

4. x86 / x64选项

此示例显示安装程序如何根据系统体系结构运行。它将安装系统所需的本机库,以进行更智能的安装。在 **[setuo]**部分中,定义以下指令:

1
ArchitecturesInstallIn64BitMode=x64

这两行将根据系统安装一个lib文件夹:

1
2
Source: "{#MyDistFolder}\lib\x64\*"; DestDir: "{app}\lib\x64"; Check: Is64BitInstallMode; Flags: ignoreversion recursesubdirs createallsubdirs
Source: "{#MyDistFolder}\lib\x86\*"; DestDir: "{app}\lib\x86"; Check: not Is64BitInstallMode; Flags: ignoreversion recursesubdirs createallsubdirs

5. 有关安装.NET过程代码的其它解决方案

如果不行实现InnoSetup中的AfterInstall的标识,希望.NET的安装在文件**[Files]**执行的某一步骤时被执行,就需要主意.NET的执行顺序。请参看如下两部分代码。

使用安装的返回值

1
2
3
4
5
6
7
8
9
10
11
12
[Code]
procedure InstallFramework;
var
ResultCode: Integer;
begin
if not Exec(ExpandConstant('{tmp}\dotNetFx40_Full_x86_x64.exe'), '/q /norestart', '', SW_SHOW, ewWaitUntilTerminated, ResultCode) then
begin
{ you can interact with the user that the installation failed }
MsgBox('.NET installation failed with code: ' + IntToStr(ResultCode) + '.',
mbError, MB_OK);
end;
end;

使用进度条显示安装进度,但是由于要获得.NET安装程序的安装进度并不容易,因此我将在安装执行过程中向用户简单显示无休止的选取框进度栏。 即,该进度条仅在此具用于显示,不能准确表示安装的进度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
procedure InstallFramework;
var
StatusText: string;
ResultCode: Integer;
begin
StatusText := WizardForm.StatusLabel.Caption;
WizardForm.StatusLabel.Caption := 'Installing .NET framework...';
WizardForm.ProgressGauge.Style := npbstMarquee;
try
if not Exec(ExpandConstant('{tmp}\dotNetFx45_Full_asetup.exe'), '/q /norestart', '', SW_SHOW, ewWaitUntilTerminated, ResultCode) then
begin
// you can interact with the user that the installation failed
MsgBox('.NET installation failed with code: ' + IntToStr(ResultCode) + '.',
mbError, MB_OK);
CancelWithoutPrompt := true;
WizardForm.Close;
end;
finally
WizardForm.StatusLabel.Caption := StatusText;
WizardForm.ProgressGauge.Style := npbstNormal;
end;
end;

6. 提示

Inno 是具有一个内置变量{dotnet40}的,ExpandConstant(‘{dotnet40}’)。其它有关的变量为:

{dotnet11}

32-bit .NET Framework version 1.1 root directory.

An exception will be raised if an attempt is made to expand this constant on a system with no .NET Framework version 1.1 present.

32位.NET Framework 1.1版根目录。

会将系统扩展到当前的.NET Framework版本1.1会发生例外。

{dotnet20}

.NET Framework version 2.0-3.5 root directory. {dotnet20} is equivalent to {dotnet2032} unless the install is running in [64-bit install mode], in which case it is equivalent to {dotnet2064}.

An exception will be raised if an attempt is made to expand this constant on a system with no .NET Framework version 2.0-3.5 present.

.NET Framework 2.0-3.5版根目录。除非安装以64位安装模式运行,否则{dotnet20}等效于{dotnet2032},在这种情况下,等效于{dotnet2064}。

会将系统扩展到当前的.NET Framework版本2.0-3.5将会发生例外。

{dotnet2032}

32-bit .NET Framework version 2.0-3.5 root directory.

An exception will be raised if an attempt is made to expand this constant on a system with no .NET Framework version 2.0-3.5 present.

32位.NET Framework 2.0-3.5版根目录。

会将系统扩展到当前的.NET Framework版本2.0-3.5将会发生例外。

{dotnet2064}

64-bit Windows only: 64-bit .NET Framework version 2.0-3.5 root directory.

An exception will be raised if an attempt is made to expand this constant on a system with no .NET Framework version 2.0-3.5 present.

仅适用于64位Windows:64位.NET Framework 2.0-3.5版根目录。

会将系统扩展到当前的.NET Framework版本2.0-3.5将会发生例外。

{dotnet40}

.NET Framework version 4.0 and later root directory. {dotnet40} is equivalent to {dotnet4032} unless the install is running in [64-bit install mode], in which case it is equivalent to {dotnet4064}.

An exception will be raised if an attempt is made to expand this constant on a system with no .NET Framework version 4.0 or later present.

.NET Framework 4.0版和更高版本的根目录。除非安装以64位安装模式运行,否则{dotnet40}等效于{dotnet4032},在这种情况下,等效于{dotnet4064}。

将系统扩展到.NET Framework 4.0或更高版本时,将发生例外。

{dotnet4032}

32-bit .NET Framework version 4.0 and later root directory.

An exception will be raised if an attempt is made to expand this constant on a system with no .NET Framework version 4.0 or later present.

32位.NET Framework 4.0版和更高版本的根目录。

将系统扩展到.NET Framework 4.0或更高版本时,将发生例外。

{dotnet4064}

64-bit Windows only: 64-bit .NET Framework version 4.0 and later root directory.

An exception will be raised if an attempt is made to expand this constant on a system with no .NET Framework version 4.0 or later present.

仅适用于64位Windows:64位.NET Framework 4.0版和更高版本的根目录。

将系统扩展到.NET Framework 4.0或更高版本时,将发生例外。

7. 使用RegAsm.exe 注册 .NET DLL 文件

其中**[run]**的代码如下, 主意其WorkingDir参数,其表示运行注册程序的时候,运行的目录:

1
Filename: "{dotnet20}\RegAsm.exe"; Parameters: /codebase YourDLL.dll; WorkingDir: {app}; StatusMsg: "Registering Controls..."; Flags: runminimized

8. 其它完整解决方案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
// Other parts of installer file go here
[CustomMessages]
IDP_DownloadFailed=Download of .NET Framework 4.7.2 failed. .NET Framework 4.7 is required to run VidCoder.
IDP_RetryCancel=Click 'Retry' to try downloading the files again, or click 'Cancel' to terminate setup.
InstallingDotNetFramework=Installing .NET Framework 4.7.2. This might take a few minutes...
DotNetFrameworkFailedToLaunch=Failed to launch .NET Framework Installer with error "%1". Please fix the error then run this installer again.
DotNetFrameworkFailed1602=.NET Framework installation was cancelled. This installation can continue, but be aware that this application may not run unless the .NET Framework installation is completed successfully.
DotNetFrameworkFailed1603=A fatal error occurred while installing the .NET Framework. Please fix the error, then run the installer again.
DotNetFrameworkFailed5100=Your computer does not meet the requirements of the .NET Framework. Please consult the documentation.
DotNetFrameworkFailedOther=The .NET Framework installer exited with an unexpected status code "%1". Please review any other messages shown by the installer to determine whether the installation completed successfully, and abort this installation and fix the problem if it did not.

[Code]

var
requiresRestart: boolean;

function NetFrameworkIsMissing(): Boolean;
var
bSuccess: Boolean;
regVersion: Cardinal;
begin
Result := True;

bSuccess := RegQueryDWordValue(HKLM, 'Software\Microsoft\NET Framework Setup\NDP\v4\Full', 'Release', regVersion);
if (True = bSuccess) and (regVersion >= 461308) then begin
Result := False;
end;
end;

procedure InitializeWizard;
begin
if NetFrameworkIsMissing() then
begin
idpAddFile('http://go.microsoft.com/fwlink/?LinkId=863262', ExpandConstant('{tmp}\NetFrameworkInstaller.exe'));
idpDownloadAfter(wpReady);
end;
end;

function InstallFramework(): String;
var
StatusText: string;
ResultCode: Integer;
begin
StatusText := WizardForm.StatusLabel.Caption;
WizardForm.StatusLabel.Caption := CustomMessage('InstallingDotNetFramework');
WizardForm.ProgressGauge.Style := npbstMarquee;
try
if not Exec(ExpandConstant('{tmp}\NetFrameworkInstaller.exe'), '/passive /norestart /showrmui /showfinalerror', '', SW_SHOW, ewWaitUntilTerminated, ResultCode) then
begin
Result := FmtMessage(CustomMessage('DotNetFrameworkFailedToLaunch'), [SysErrorMessage(resultCode)]);
end
else
begin
// See https://msdn.microsoft.com/en-us/library/ee942965(v=vs.110).aspx#return_codes
case resultCode of
0: begin
// Successful
end;
1602 : begin
MsgBox(CustomMessage('DotNetFrameworkFailed1602'), mbInformation, MB_OK);
end;
1603: begin
Result := CustomMessage('DotNetFrameworkFailed1603');
end;
1641: begin
requiresRestart := True;
end;
3010: begin
requiresRestart := True;
end;
5100: begin
Result := CustomMessage('DotNetFrameworkFailed5100');
end;
else begin
MsgBox(FmtMessage(CustomMessage('DotNetFrameworkFailedOther'), [IntToStr(resultCode)]), mbError, MB_OK);
end;
end;
end;
finally
WizardForm.StatusLabel.Caption := StatusText;
WizardForm.ProgressGauge.Style := npbstNormal;

DeleteFile(ExpandConstant('{tmp}\NetFrameworkInstaller.exe'));
end;
end;

function PrepareToInstall(var NeedsRestart: Boolean): String;
begin
// 'NeedsRestart' only has an effect if we return a non-empty string, thus aborting the installation.
// If the installers indicate that they want a restart, this should be done at the end of installation.
// Therefore we set the global 'restartRequired' if a restart is needed, and return this from NeedRestart()

if NetFrameworkIsMissing() then
begin
Result := InstallFramework();
end;
end;

function NeedRestart(): Boolean;
begin
Result := requiresRestart;
end;

根据此版本修改后适用于自己项目的.NET框架安装代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
function InstallFrameworkWebRuntime(): String;
var
WasVisible: Boolean;
ResultCode: Integer;
begin
ExtractTemporaryFile('{#DotNetFrameWorkWebInstaller}');
WasVisible := WizardForm.PreparingLabel.Visible;
try
WizardForm.PreparingLabel.Visible := True;
WizardForm.PreparingLabel.Caption := FmtMessage(CustomMessage('DotNetFrameworkWebInstallerCaption'), [TO_COMPARE_DOT_NET]);
ExtractTemporaryFile('{#DotNetFrameWorkWebInstaller}');
//if not Exec(ExpandConstant('{tmp}\{#DotNetFrameWorkWebInstaller}'), '/q /norestart', '', SW_SHOW, ewWaitUntilTerminated, ResultCode) then
if not Exec(ExpandConstant('{tmp}\{#DotNetFrameWorkWebInstaller}'), '/passive /norestart /showrmui /showfinalerror', '', SW_SHOW, ewWaitUntilTerminated, ResultCode) then
begin
// https://docs.microsoft.com/de-de/windows/win32/debug/system-error-codes--0-499-
// you can interact with the user that the installation failed, for example: error code 2: the system cannot find the file specified.
Result := FmtMessage(CustomMessage('DotNetFrameworkFailedToLaunch'), [SysErrorMessage(ResultCode)])
end
else
begin
case ResultCode of
0: begin
// Successful
end;
1602 : begin
MsgBox(CustomMessage('DotNetFrameworkFailed1602'), mbInformation, MB_OK);
end;
1603: begin
Result := CustomMessage('DotNetFrameworkFailed1603');
end;
1641: begin
bRequiresRestart := True;
end;
3010: begin
bRequiresRestart := True;
end;
5100: begin
Result := CustomMessage('DotNetFrameworkFailed5100');
end;
else begin
//MsgBox(FmtMessage(CustomMessage('DotNetFrameworkFailedOther'), [IntToStr(resultCode)]), mbError, MB_OK);
Result := FmtMessage(CustomMessage('DotNetFrameworkFailedOther'), IntToStr(ResultCode));
end;
end;
end;
finally
WizardForm.PreparingLabel.Visible := WasVisible;
end;
DeleteFile(ExpandConstant('{tmp}{#DotNetFrameWorkWebInstaller}'));
end;

上述代码中

1
ExtractTemporaryFile('{#DotNetFrameWorkWebInstaller}'); 

强制解压安装文件。该动作早于[Files]的运行。在安装结束之后删除该文件,使用代码如:

1
DeleteFile(ExpandConstant('{tmp}{#DotNetFrameWorkWebInstaller}'));

有关.NET安装包运行时的错误代码可以参考连接:

// See https://msdn.microsoft.com/en-us/library/ee942965(v=vs.110).aspx#return_codes

有关Exec函数,其错误代码为Windows系统错误代码,运行时的错误文本,可以参考连接:

// See [https://docs.microsoft.com/de-de/windows/win32/debug/system-error-codes--0-499-](

9. Inno Setup 对注册表的操作

由于安装.NET的框架的目的就是要使用一下依赖.NET框架的动态库文件。而这部分DLL文件能够顺利使用就需要对该DLL文件进行注册。

9.1 Regasm 程序集注册工具

Regasm 简介 :

EXE files such as RegAsm.exe are categorized as Executable Application (Windows Executable) files. As a Windows Executable file, it was created for use in Windows 10 by [Microsoft].

像RegAsm.exe这样的EXE文件都被分类为可执行应用程序(Windows Executable)文件。 作为Windows可执行文件,它由Microsoft创建供Windows 10使用。

The first version of RegAsm.exe for Windows Vista was introduced on 11/08/2006 in Windows Vista. The most recent version [file version 10] was introduced on 07/29/2015 for Windows 10. RegAsm.exe is bundled with the software package in Windows 10, Windows 8.1, and Windows 8.

Windows Vista的RegAsm.exe的第一版于2006年11月8日在Windows Vista中引入。 Windows 10的最新版本(文件版本10)已于07/29/2015引入。RegAsm.exe与Windows 10,Windows 8.1和Windows 8中的软件包捆绑在一起。

Please see below for more detailed information, EXE file troubleshooting instructions, and free downloads of different versions of RegAsm.exe.

请参阅下面的详细信息,EXE文件故障排除说明以及RegAsm.exe不同版本的免费下载。

作用: 读取程序集中的元数据,并将所需的项添加到注册表中。注册表允许 COM 客户程序以透明方式创建 .NET Framework 类。类一经注册,任何 COM 客户程序都可以使用它,就好像该类是一个 COM 类。类仅在安装程序集时注册一次。程序集中的类实例直到被实际注册时,才能从 COM 中创建。

使用方法:

regasm assemblyFile [options]

重要参数:

/codebase 在注册表中创建一个 Codebase 项。Codebase 项指定未安装到全局程序集缓存中的程序集的文件路径。如果随后要安装正在注册到全局程序集缓存中的程序集,则不应指定此选项。用 /codebase 选项指定的 assemblyFile 参数必须是具有强名称的程序集。

9.2 Regsvr32 程序集注册工具

作用: Regsvr32命令用于注册COM组件,是 Windows 系统提供的用来向系统注册控件或者卸载控件的命令,以命令行方式运行。WinXP及以上系统的regsvr32.exe在windows\system32文件夹下;2000系统的regsvr32.exe在winnt\system32文件夹下。

9.3 两个DLL注册工具的差异

使用regasm.exe来注册.NET 组件,regsvr32并不适用于.NET组件。 (或者您也可以将regasm.exe理解为.NET的regsvr32)。regsvr32不适用于.NET的组件的注册,感觉意思是对于非托管的代码使用regsvr32来注册,对于托管的代码使用regasm来注册。

regsvr32 will load the library and try to call the DllRegisterServer() from that library. It doesn’t care what DllRegisterServer() actually does - it just calls that function and checks the returned value. You use it to register COM servers in unmanaged DLLs. It can’t generate a .tlb file.

regsvr32将加载该库并尝试从该库中调用DllRegisterServer()。 不管DllRegisterServer()实际做什么,它只是调用该函数并检查返回的值。 您可以使用它在非托管DLL中注册COM服务器。 无法生成.tlb文件。

regasm will register a COM-exposed .NET assembly as a COM server. You use it for .NET assemblies. It can generate a .tlb file given the assembly only - it inspects the type infromation stored in the assembly and includes the COM-exposed entities into the type library.

regasm将把暴露于COM的.NET程序集注册为COM服务器。 您将其用于.NET程序集。 仅在给定程序集的情况下,它可以生成.tlb文件-它检查存储在程序集中的类型信息,并将COM公开的实体包括到类型库中。

9.4 .NET中“托管代码”和“非托管代码”的区别

托管代码 (managed code)

托管代码是Visual Basic .NET和C#编译器创建的代码。它运行在CLR(公共语言运行时),除其他外,它提供像垃圾收集,运行时类型检查和引用检查的服务。所以,认为它是,“我的代码是由CLR管理。

Visual Basic和C#只能生成托管代码,所以,如果你正在用这些语言编写一个应用程序,你正在写一个由CLR管理的应用程序。如果您在Visual C .NET中编写应用程序,您可以根据需要生成托管代码,但它是可选的。

非托管代码 (unmanaged code)

非托管代码直接编译为机器代码。因此,通过该定义,所有由传统C/C++编译器编译的代码是“非托管代码”。此外,由于它编译为机器代码而不是中间语言,所以它是不可移植的。

没有可用的内存管理或任何其他CLR提供。

由于您不能使用Visual Basic或C#创建非托管代码,在Visual Studio中,所有非托管代码都用C/C++编写。

混合两者

由于Visual C可以编译为托管代码或非托管代码,因此可以在同一个应用程序中混合这两个代码。这模糊了两者之间的界限,并使定义复杂化,但值得一提的是,你知道,如果你使用的第三方库有一些严重的非托管代码,你仍然可能有内存泄漏。

区别:

1、托管代码是一种中间语言,运行在CLR上;

非托管代码被编译为机器码,运行在机器上。

2、托管代码独立于平台和语言,能更好的实现不同语言平台之间的兼容;

非托管代码依赖于平台和语言。

3、托管代码可享受CLR提供的服务(如安全检测、垃圾回收等),不需要自己完成这些操作;

非托管代码需要自己提供安全检测、垃圾回收等操作。

托管代码就意味着托管数据?答案是否定的。

对于Visual Basic和C#来说,生活是简单的,因为你没有其它选择。当你在那些语言里面声明一个类,那么这个类的实例会在托管堆中被创建,垃圾收集器(GC)会帮我们管理这些对象的回收。但是在Visual C++中,你有另一个选择。即使你正创建一个托管程序,你可以决定哪些类是托管类型,哪些类是非托管类型的。

这就是非托管类型:

class Foo { private: int x; public: Foo(): x(0){} Foo(int xx): x(xx) {} };

这就是托管类型

__gc class Bar { private: int x; public: Bar(): x(0){} Bar(int xx): x(xx) {} };

他们唯一的区别就是类Bar的定义中有__gc关键字。这个关键字会给代码带来巨大的区别。

9.5 使用Inno Setup的 [Registry] 段注册.NET相关DLL文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
[Registry]
; ProgID part, TechTemp
Root: HKCR; Subkey: "XXXXXXXXXXXX.MMMMMMMMMMMMMMM"; Flags: uninsdeletekeyifempty
Root: HKCR; Subkey: "XXXXXXXXXXXX.MMMMMMMMMMMMMMM\CLSID"; Flags: uninsdeletekey
Root: HKCR; Subkey: "XXXXXXXXXXXX.MMMMMMMMMMMMMMM\CLSID"; ValueType: string; ValueName: ""; ValueData: "{{{#DotNetDllCLSID}}"; Flags: uninsdeletevalue
; CLSID Part, TechTemp
Root: HKCR; Subkey: "CLSID\{{{#DotNetDllCLSID}}"; Flags: uninsdeletekeyifempty
Root: HKCR; Subkey: "CLSID\{{{#DotNetDllCLSID}}"; ValueType: string; ValueName: ""; ValueData: "XXXXXXXXXXXX.MMMMMMMMMMMMMMM"; Flags: uninsdeletevalue
; CLSID Part\InprocServer32, TechTemp
Root: HKCR; Subkey: "CLSID\{{{#DotNetDllCLSID}}\InprocServer32"; Flags: uninsdeletekey
Root: HKCR; Subkey: "CLSID\{{{#DotNetDllCLSID}}\InprocServer32"; ValueType: string; ValueName: ""; ValueData: "mscoree.dll"; Flags: uninsdeletevalue
Root: HKCR; Subkey: "CLSID\{{{#DotNetDllCLSID}}\InprocServer32"; ValueType: string; ValueName: "ThreadingModel"; ValueData: "Both"; Flags: uninsdeletevalue
Root: HKCR; Subkey: "CLSID\{{{#DotNetDllCLSID}}\InprocServer32"; ValueType: string; ValueName: "Class"; ValueData: "XXXXXXXXXXXX.MMMMMMMMMMMMMMM"; Flags: uninsdeletevalue
Root: HKCR; Subkey: "CLSID\{{{#DotNetDllCLSID}}\InprocServer32"; ValueType: string; ValueName: "Assembly"; ValueData: "xxxxxxxxxxxxxxxxxxx, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"; Flags: uninsdeletevalue
Root: HKCR; Subkey: "CLSID\{{{#DotNetDllCLSID}}\InprocServer32"; ValueType: string; ValueName: "RuntimeVersion"; ValueData: "v4.0.30319"; Flags: uninsdeletevalue
Root: HKCR; Subkey: "CLSID\{{{#DotNetDllCLSID}}\InprocServer32"; ValueType: string; ValueName: "CodeBase"; ValueData: "file:///{#CommonappdataPathBackSlash}/{#AppPathBackSlash}/bin/NET/xxxxxxxxxxxxxxxxxxx.dll"; Flags: uninsdeletevalue
; CLSID Part\InprocServer32\1.0.0.0, TechTemp
Root: HKCR; Subkey: "CLSID\{{{#DotNetDllCLSID}}\InprocServer32\1.0.0.0"; Flags: uninsdeletekey
Root: HKCR; Subkey: "CLSID\{{{#DotNetDllCLSID}}\InprocServer32\1.0.0.0"; ValueType: string; ValueName: "Class"; ValueData: "XXXXXXXXXXXX.MMMMMMMMMMMMMMM"; Flags: uninsdeletevalue
Root: HKCR; Subkey: "CLSID\{{{#DotNetDllCLSID}}\InprocServer32\1.0.0.0"; ValueType: string; ValueName: "Assembly"; ValueData: "xxxxxxxxxxxxxxxxxxx, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"; Flags: uninsdeletevalue
Root: HKCR; Subkey: "CLSID\{{{#DotNetDllCLSID}}\InprocServer32\1.0.0.0"; ValueType: string; ValueName: "RuntimeVersion"; ValueData: "v4.0.30319"; Flags: uninsdeletevalue
Root: HKCR; Subkey: "CLSID\{{{#DotNetDllCLSID}}\InprocServer32\1.0.0.0"; ValueType: string; ValueName: "CodeBase"; ValueData: "file:///{#CommonappdataPathBackSlash}/{#AppPathBackSlash}/bin/NET/xxxxxxxxxxxxxxxxxxx.dll"; Flags: uninsdeletevalue
; CLSID Part\Implemented Categories, TechTemp
Root: HKCR; Subkey: "CLSID\{{{#DotNetDllCLSID}}\Implemented Categories"; Flags: uninsdeletekey
Root: HKCR; Subkey: "CLSID\{{{#DotNetDllCLSID}}\Implemented Categories\{{{#DotNetDllCLSIDImplementedCategories}}"; Flags: uninsdeletekey
; CLSID Part\ProgId, TechTemp
Root: HKCR; Subkey: "CLSID\{{{#DotNetDllCLSID}}\ProgId"; Flags: uninsdeletekey
Root: HKCR; Subkey: "CLSID\{{{#DotNetDllCLSID}}\ProgId"; ValueType: string; ValueName: ""; ValueData: "XXXXXXXXXXXX.MMMMMMMMMMMMMMM"; Flags: uninsdeletevalue

10. 项目完整代码,最小测试版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
; -- Example1.iss --
; Demonstrates copying 3 files and creating an icon.

; SEE THE DOCUMENTATION FOR DETAILS ON CREATING .ISS SCRIPT FILES!

#define ********AppPath
#expr ********AppPath = "\********\trunk\hcnt\wire\"

#define DotNetRegistToolPath
#expr DotNetRegistToolPath = ""

#define DotNetFrameWorkInstallerPath
#expr DotNetFrameWorkInstallerPath = "..\NET\NET_Framework_Runtime\462\"

#define DotNet4OCTechTemplDllPath
#expr DotNet4OCTechTemplDllPath = "..\helper\xxxxxxxxxxxxxxxxxxx"

#define DotNetFrameWorkOfflineInstaller
#expr DotNetFrameWorkOfflineInstaller = "ndp48-x86-x64-allos-enu.exe"

#define DotNetFrameWorkWebInstaller
#expr DotNetFrameWorkWebInstaller = "ndp48-web.exe"

;#define DotNetDllCLSID
;#expr DotNetDllCLSID = "62519924-26A8-3770-9F82-0A0BC26EE7D4"

;#define DotNetDllCLSIDImplementedCategories
;#expr DotNetDllCLSIDImplementedCategories = "62C8FE65-4EBB-45E7-B440-6E39B2CDBF29"

;#define AppPathBackSlash
;#expr AppPathBackSlash = "********/trunk/hcnt/wire"

;#define CommonappdataPathBackSlash
;#expr CommonappdataPathBackSlash = "C:/ProgramData"

[Setup]
;SignTool=MyCustom sign /a /tr http://sha256timestamp.ws.symantec.com/sha256/timestamp /d $q******** for SolidWorks 2020 Beta3$q $f
PrivilegesRequired=admin
AppName=My Program
AppVersion=1.5
WizardStyle=modern
DefaultDirName={autopf}\My Program
DefaultGroupName=My Program
UninstallDisplayIcon={app}\MyProg.exe
Compression=lzma2
SolidCompression=yes
OutputDir=userdocs:Inno Setup Examples Output
ArchitecturesInstallIn64BitMode = x64
ArchitecturesAllowed = x64
SetupLogging=yes


[Languages]
Name: ENGLISH; MessagesFile: compiler:Default.isl
Name: DEUTSCH; MessagesFile: compiler:Languages\German.isl;

[Files]
; .NET Part
; .NET Framework Runtime, offline installer english all os runtime
Source: "{#DotNetFrameWorkInstallerPath}{#DotNetFrameWorkOfflineInstaller}"; DestDir: "{tmp}"; Flags: dontcopy noencryption;
; .NET Framework Runtime, web installer for other languages
Source: "{#DotNetFrameWorkInstallerPath}{#DotNetFrameWorkWebInstaller}"; DestDir: "{tmp}"; Flags: dontcopy noencryption;
; xxxxxxxxxxxxxxxxxxx.dll + Hilfsdlls
Source: "{#DotNet4OCTechTemplDllPath}\*.*"; DestDir: "{commonappdata}\{#********AppPath}\bin\NET\"; Flags: ignoreversion recursesubdirs; Permissions: everyone-full;

Source: "MyProg.exe"; DestDir: "{app}"
Source: "MyProg.chm"; DestDir: "{app}"
Source: "Readme.txt"; DestDir: "{app}"; Flags: isreadme

[Run]
; Regist the .NET DLL file by using the tool regasm.exe, return 0: ERROR_SUCCESS; 1:ERROR_INVALID_FUNCTION; 2:ERROR_FILE_NOT_FOUND; 3: ERROR_PATH_NOT_FOUND
Filename: {code:GetCurrentRegAsmPath|{#DotNetRegistToolPath}}\regasm.exe; Parameters: " ""{commonappdata}\{#********AppPath}\bin\NET\xxxxxxxxxxxxxxxxxxx.dll"" /codebase /verbose"; WorkingDir: "{commonappdata}\{#********AppPath}\bin\NET\"; StatusMsg: Registering DLLs; Flags: 64bit runhidden waituntilterminated;

[UninstallRun]
; Regist the .NET DLL file by using the tool regasm.exe, return 0: ERROR_SUCCESS; 1:ERROR_INVALID_FUNCTION; 2:ERROR_FILE_NOT_FOUND; 3: ERROR_PATH_NOT_FOUND
Filename: {code:GetCurrentRegAsmPath|{#DotNetRegistToolPath}}\regasm.exe; Parameters: "/unregister ""{commonappdata}\{#********AppPath}\bin\NET\xxxxxxxxxxxxxxxxxxx.dll"" /codebase /verbose"; WorkingDir: "{commonappdata}\{#********AppPath}\bin\NET\"; StatusMsg: Unregistering DLLs; Flags: 64bit runhidden waituntilterminated;



[CustomMessages]
; .NET Framwork installation processing
ENGLISH.DotNetFrameworkOfflineInstallerCaption = Installing Microsoft .NET framework %1 offline installer for Windows...
ENGLISH.DotNetFrameworkWebInstallerCaption = Installing Microsoft .NET framework %1 Web installer for Windows...
ENGLISH.DotNetFrameworkFailedToLaunch = .NET installation failed with code: %1. Please fix the error then run this installer again.
ENGLISH.DotNetFrameworkFailed1602 = .NET Framework installation was cancelled. This installation can continue, but be aware that this application may not run unless the .NET Framework installation is completed successfully.
ENGLISH.DotNetFrameworkFailed1603 = A fatal error occurred while installing the .NET Framework. Please fix the error, then run the installer again.
ENGLISH.DotNetFrameworkFailed5100 = Your computer does not meet the requirements of the .NET Framework. Please consult the documentation.
ENGLISH.DotNetFrameworkFailedOther = The .NET Framework installer exited with an unexpected status code "%1". Please review any other messages shown by the installer to determine whether the installation completed successfully, and abort this installation and fix the problem if it did not.

; .NET Framwork installation processing
DEUTSCH.DotNetFrameworkOfflineInstallerCaption = Installation von Microsoft .NET Framework %1 offline Installationsprogramm für Windows ...
DEUTSCH.DotNetFrameworkWebInstallerCaption = Installation von Microsoft .NET Framework %1 Web Installationsprogramm für Windows ...
DEUTSCH.DotNetFrameworkFailedToLaunch = .NET Installation fehlgeschlagen mit Code: %1. Bitte beheben Sie den Fehler und führen Sie das Installationsprogramm erneut aus.
DEUTSCH.DotNetFrameworkFailed1602 = .NET Framework-Installation wurde abgebrochen. Diese Installation kann fortgesetzt werden. Beachten Sie jedoch, dass diese Anwendung möglicherweise erst ausgeführt wird, wenn die .NET Framework-Installation erfolgreich abgeschlossen wurde.
DEUTSCH.DotNetFrameworkFailed1603 = Bei der Installation von .NET Framework ist ein schwerwiegender Fehler aufgetreten. Bitte beheben Sie den Fehler und führen Sie das Installationsprogramm erneut aus.
DEUTSCH.DotNetFrameworkFailed5100 = Ihr Computer entspricht nicht den Anforderungen von .NET Framework. Bitte konsultieren Sie die Dokumentation.
DEUTSCH.DotNetFrameworkFailedOther = Das .NET Framework-Installationsprogramm wurde mit dem unerwarteten Statuscode "%1" beendet. Überprüfen Sie alle anderen vom Installationsprogramm angezeigten Meldungen, um festzustellen, ob die Installation erfolgreich abgeschlossen wurde, und brechen Sie diese Installation ab, und beheben Sie das Problem, wenn dies nicht der Fall ist.


;; Please do not delete the following, you can use it to check the resgition of the .NET DLL xxxxxxxxxxxxxxxxxxx.dll
;; ProgID part, TechTemp
;Root: HKCR; Subkey: "XXXXXXXXXXXX.MMMMMMMMMMMMMMM"; Flags: uninsdeletekeyifempty
;Root: HKCR; Subkey: "XXXXXXXXXXXX.MMMMMMMMMMMMMMM\CLSID"; Flags: uninsdeletekey
;Root: HKCR; Subkey: "XXXXXXXXXXXX.MMMMMMMMMMMMMMM\CLSID"; ValueType: string; ValueName: ""; ValueData: "{{{#DotNetDllCLSID}}"; Flags: uninsdeletevalue
;; CLSID Part, TechTemp
;Root: HKCR; Subkey: "CLSID\{{{#DotNetDllCLSID}}"; Flags: uninsdeletekeyifempty
;Root: HKCR; Subkey: "CLSID\{{{#DotNetDllCLSID}}"; ValueType: string; ValueName: ""; ValueData: "XXXXXXXXXXXX.MMMMMMMMMMMMMMM"; Flags: uninsdeletevalue
;; CLSID Part\InprocServer32, TechTemp
;Root: HKCR; Subkey: "CLSID\{{{#DotNetDllCLSID}}\InprocServer32"; Flags: uninsdeletekey
;Root: HKCR; Subkey: "CLSID\{{{#DotNetDllCLSID}}\InprocServer32"; ValueType: string; ValueName: ""; ValueData: "mscoree.dll"; Flags: uninsdeletevalue
;Root: HKCR; Subkey: "CLSID\{{{#DotNetDllCLSID}}\InprocServer32"; ValueType: string; ValueName: "ThreadingModel"; ValueData: "Both"; Flags: uninsdeletevalue
;Root: HKCR; Subkey: "CLSID\{{{#DotNetDllCLSID}}\InprocServer32"; ValueType: string; ValueName: "Class"; ValueData: "XXXXXXXXXXXX.MMMMMMMMMMMMMMM"; Flags: uninsdeletevalue
;Root: HKCR; Subkey: "CLSID\{{{#DotNetDllCLSID}}\InprocServer32"; ValueType: string; ValueName: "Assembly"; ValueData: "xxxxxxxxxxxxxxxxxxx, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"; Flags: uninsdeletevalue
;Root: HKCR; Subkey: "CLSID\{{{#DotNetDllCLSID}}\InprocServer32"; ValueType: string; ValueName: "RuntimeVersion"; ValueData: "v4.0.30319"; Flags: uninsdeletevalue
;Root: HKCR; Subkey: "CLSID\{{{#DotNetDllCLSID}}\InprocServer32"; ValueType: string; ValueName: "CodeBase"; ValueData: "file:///{#CommonappdataPathBackSlash}/{#AppPathBackSlash}/bin/NET/xxxxxxxxxxxxxxxxxxx.dll"; Flags: uninsdeletevalue
;; CLSID Part\InprocServer32\1.0.0.0, TechTemp
;Root: HKCR; Subkey: "CLSID\{{{#DotNetDllCLSID}}\InprocServer32\1.0.0.0"; Flags: uninsdeletekey
;Root: HKCR; Subkey: "CLSID\{{{#DotNetDllCLSID}}\InprocServer32\1.0.0.0"; ValueType: string; ValueName: "Class"; ValueData: "XXXXXXXXXXXX.MMMMMMMMMMMMMMM"; Flags: uninsdeletevalue
;Root: HKCR; Subkey: "CLSID\{{{#DotNetDllCLSID}}\InprocServer32\1.0.0.0"; ValueType: string; ValueName: "Assembly"; ValueData: "xxxxxxxxxxxxxxxxxxx, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"; Flags: uninsdeletevalue
;Root: HKCR; Subkey: "CLSID\{{{#DotNetDllCLSID}}\InprocServer32\1.0.0.0"; ValueType: string; ValueName: "RuntimeVersion"; ValueData: "v4.0.30319"; Flags: uninsdeletevalue
;Root: HKCR; Subkey: "CLSID\{{{#DotNetDllCLSID}}\InprocServer32\1.0.0.0"; ValueType: string; ValueName: "CodeBase"; ValueData: "file:///{#CommonappdataPathBackSlash}/{#AppPathBackSlash}/bin/NET/xxxxxxxxxxxxxxxxxxx.dll"; Flags: uninsdeletevalue
;; CLSID Part\Implemented Categories, TechTemp
;Root: HKCR; Subkey: "CLSID\{{{#DotNetDllCLSID}}\Implemented Categories"; Flags: uninsdeletekey
;Root: HKCR; Subkey: "CLSID\{{{#DotNetDllCLSID}}\Implemented Categories\{{{#DotNetDllCLSIDImplementedCategories}}"; Flags: uninsdeletekey
;; CLSID Part\ProgId, TechTemp
;Root: HKCR; Subkey: "CLSID\{{{#DotNetDllCLSID}}\ProgId"; Flags: uninsdeletekey
;Root: HKCR; Subkey: "CLSID\{{{#DotNetDllCLSID}}\ProgId"; ValueType: string; ValueName: ""; ValueData: "XXXXXXXXXXXX.MMMMMMMMMMMMMMM"; Flags: uninsdeletevalue

[Icons]
Name: "{group}\My Program"; Filename: "{app}\MyProg.exe"


[Code]
const TO_COMPARE_DOT_NET = 'v4.6.2';

var bCancelWithoutPrompt: boolean;
bConnectWithNetwork: boolean;
bRequiresRestart: boolean;


function GetCurrentRegAsmPath(S: String): String;
var version: String;
key: String;
installPath: String;
success: boolean;
begin
Result := S;
// .NET 4.6.2 installs also as update to .NET 4.0 Full, you can also be on the safe side, check the windows 10 anniversary update
// if the build was newer than (build 10.0.14393) with the code GetWindowsVersion >= $A003839
if TO_COMPARE_DOT_NET = 'v4.6.2' then begin
version := 'v4\Full';
end;
// .NET 4.5 installs as update to .NET 4.0 Full
if TO_COMPARE_DOT_NET = 'v4.5' then begin
version := 'v4\Full';
end;
// installation key group for all .NET versions
key := 'SOFTWARE\Microsoft\NET Framework Setup\NDP\' + version;
success := RegQueryStringValue(HKLM, key, 'InstallPath', installPath);
if (success = True) and (S = '') then
begin
Result := installPath;
end
end;

function InstallFrameworkWebRuntime(): String;
var
WasVisible: Boolean;
ResultCode: Integer;
begin
ExtractTemporaryFile('{#DotNetFrameWorkWebInstaller}');
WasVisible := WizardForm.PreparingLabel.Visible;
try
WizardForm.PreparingLabel.Visible := True;
WizardForm.PreparingLabel.Caption := FmtMessage(CustomMessage('DotNetFrameworkWebInstallerCaption'), [TO_COMPARE_DOT_NET]);
ExtractTemporaryFile('{#DotNetFrameWorkWebInstaller}');
//if not Exec(ExpandConstant('{tmp}\{#DotNetFrameWorkWebInstaller}'), '/q /norestart', '', SW_SHOW, ewWaitUntilTerminated, ResultCode) then
if not Exec(ExpandConstant('{tmp}\{#DotNetFrameWorkWebInstaller}'), '/passive /norestart /showrmui /showfinalerror', '', SW_SHOW, ewWaitUntilTerminated, ResultCode) then
begin
// https://docs.microsoft.com/de-de/windows/win32/debug/system-error-codes--0-499-
// you can interact with the user that the installation failed, for example: error code 2: the system cannot find the file specified.
Result := FmtMessage(CustomMessage('DotNetFrameworkFailedToLaunch'), [SysErrorMessage(ResultCode)])
end
else
begin
case ResultCode of
0: begin
// Successful
end;
1602 : begin
MsgBox(CustomMessage('DotNetFrameworkFailed1602'), mbInformation, MB_OK);
end;
1603: begin
Result := CustomMessage('DotNetFrameworkFailed1603');
end;
1641: begin
bRequiresRestart := True;
end;
3010: begin
bRequiresRestart := True;
end;
5100: begin
Result := CustomMessage('DotNetFrameworkFailed5100');
end;
else begin
//MsgBox(FmtMessage(CustomMessage('DotNetFrameworkFailedOther'), [IntToStr(resultCode)]), mbError, MB_OK);
Result := FmtMessage(CustomMessage('DotNetFrameworkFailedOther'), IntToStr(ResultCode));
end;
end;
end;
finally
WizardForm.PreparingLabel.Visible := WasVisible;
end;
DeleteFile(ExpandConstant('{tmp}\{#DotNetFrameWorkWebInstaller}'));
end;

function InstallFrameworkOfflineRuntime(): String;
var
WasVisible: Boolean;
ResultCode: Integer;
begin
WasVisible := WizardForm.PreparingLabel.Visible;
try
WizardForm.PreparingLabel.Visible := True;
WizardForm.PreparingLabel.Caption := FmtMessage(CustomMessage('DotNetFrameworkOfflineInstallerCaption'), [TO_COMPARE_DOT_NET]);
ExtractTemporaryFile('{#DotNetFrameWorkOfflineInstaller}');
//if not Exec(ExpandConstant('{tmp}\{#DotNetFrameWorkOfflineInstaller}'), '/q /norestart', '', SW_SHOW, ewWaitUntilTerminated, ResultCode) then
if not Exec(ExpandConstant('{tmp}\{#DotNetFrameWorkOfflineInstaller}'), '/passive /norestart /showrmui /showfinalerror', '', SW_SHOW, ewWaitUntilTerminated, ResultCode) then
begin
// https://docs.microsoft.com/de-de/windows/win32/debug/system-error-codes--0-499-
// you can interact with the user that the installation failed, for example: error code 2: the system cannot find the file specified.
Result := FmtMessage(CustomMessage('DotNetFrameworkFailedToLaunch'), [SysErrorMessage(ResultCode)])
end
else
begin
case ResultCode of
0: begin
// Successful
end;
1602 : begin
MsgBox(CustomMessage('DotNetFrameworkFailed1602'), mbInformation, MB_OK);
end;
1603: begin
Result := CustomMessage('DotNetFrameworkFailed1603');
end;
1641: begin
bRequiresRestart := True;
end;
3010: begin
bRequiresRestart := True;
end;
5100: begin
Result := CustomMessage('DotNetFrameworkFailed5100');
end;
else begin
//MsgBox(FmtMessage(CustomMessage('DotNetFrameworkFailedOther'), [IntToStr(resultCode)]), mbError, MB_OK);
Result := FmtMessage(CustomMessage('DotNetFrameworkFailedOther'), IntToStr(ResultCode));
end;
end;
end;
finally
WizardForm.PreparingLabel.Visible := WasVisible;
end;
DeleteFile(ExpandConstant('{tmp}\{#DotNetFrameWorkOfflineInstaller}'));
end;

function IsNetWorkActivatedTried: boolean;
var
WinHttpReq: Variant;
Connected: Boolean;
iTriedTime: Integer;
begin
Connected := False;
iTriedTime := 0;
repeat
iTriedTime := iTriedTime + 1;
Log('Checking connection to the server, try ' + + IntToStr(iTriedTime) + ' time.');
try
WinHttpReq := CreateOleObject('WinHttp.WinHttpRequest.5.1');
WinHttpReq.SetTimeouts('3000', '3000', '3000', '3000');
// Use your real server host name
WinHttpReq.Open('GET', 'https://www.camtek.de/', False);
WinHttpReq.Send('');
Log('Connected to the server; status: ' + IntToStr(WinHttpReq.Status) + ' ' +
WinHttpReq.StatusText);
Connected := True;
except
Log('Error connecting to the server, msg: ' + GetExceptionMessage + 'try again! ');
end;
until (iTriedTime = 3) or (Connected = True) ;
Result := Connected;
end;

function IsDotNetDetected(version: string; service: cardinal): boolean;
// Indicates whether the specified version and service pack of the .NET Framework is installed.
// see the link: https://docs.microsoft.com/en-us/dotnet/framework/migration-guide/how-to-determine-which-versions-are-installed?redirectedfrom=MSDN#net_b
//
// version -- Specify one of these strings for the required .NET Framework version:
// 'v1.1.4322' .NET Framework 1.1
// 'v2.0.50727' .NET Framework 2.0
// 'v3.0' .NET Framework 3.0
// 'v3.5' .NET Framework 3.5
// 'v4\Client' .NET Framework 4.0 Client Profile
// 'v4\Full' .NET Framework 4.0 Full Installation
// 'v4.5' .NET Framework 4.5 378389
// 'v4.6' .NET Framework 4.6 393295
// 'v4.6.1' .NET Framework 4.6.1 394254
// 'v4.6.2' .NET Framework 4.6.2 394802
// 'v4.7' .NET Framework 4.7 393295
//
// service -- Specify any non-negative integer for the required service pack level:
// 0 No service packs required
// 1, 2, etc. Service pack 1, 2, etc. required
var
key: string;
install, release, serviceCount: cardinal;
check45, check462, success: boolean;
begin

// .NET 4.6.2 installs also as update to .NET 4.0 Full, you can also be on the safe side, check the windows 10 anniversary update
// if the build was newer than (build 10.0.14393) with the code GetWindowsVersion >= $A003839
if version = 'v4.6.2' then begin
version := 'v4\Full';
check462 := true;
end else
check462 := false;

// .NET 4.5 installs as update to .NET 4.0 Full
if version = 'v4.5' then begin
version := 'v4\Full';
check45 := true;
end else
check45 := false;

// installation key group for all .NET versions
key := 'SOFTWARE\Microsoft\NET Framework Setup\NDP\' + version;

// .NET 3.0 uses value InstallSuccess in subkey Setup
if Pos('v3.0', version) = 1 then begin
success := RegQueryDWordValue(HKLM, key + '\Setup', 'InstallSuccess', install);
end else begin
success := RegQueryDWordValue(HKLM, key, 'Install', install);
end;

// .NET 4.0/4.5 uses value Servicing instead of SP
if Pos('v4', version) = 1 then begin
success := success and RegQueryDWordValue(HKLM, key, 'Servicing', serviceCount);
end else begin
success := success and RegQueryDWordValue(HKLM, key, 'SP', serviceCount);
end;

// .NET 4.5 uses additional value Release
if check45 then begin
success := success and RegQueryDWordValue(HKLM, key, 'Release', release);
success := success and (release >= 378389);
end;

// .NET 4.6.2 uses additional value Release
if check462 then begin
success := success and RegQueryDWordValue(HKLM, key, 'Release', release);
success := success and (release >= 394802);
end;

Result := success and (install = 1) and (serviceCount >= service);
end;

function IsRequiredDotNetDetected: Boolean;
begin
Result := IsDotNetDetected(TO_COMPARE_DOT_NET, 0);
end;

function InstallDotNetOfflineRuntime: Boolean;
begin
if not bConnectWithNetwork then
begin
Result := True;
end
else
begin
if ActiveLanguage = 'ENGLISH' then
begin
Result := True;
end
else
begin
Result := False;
end
end
end;

function InstallDotNetWebRuntime: Boolean;
begin
if (not bConnectWithNetwork) and ( not ActiveLanguage = 'ENGLISH') then
begin
Result := True;
end
else
begin
Result := False;
end
end;

function InitializeSetup(): Boolean;
begin
bConnectWithNetwork := IsNetWorkActivatedTried;
Result := True;
end;

function PrepareToInstall(var NeedsRestart: Boolean): String;
var bInstallWeb: boolean;
bInstallStd: boolean;
begin
// 'NeedsRestart' only has an effect if we return a non-empty string, thus aborting the installation.
// If the installers indicate that they want a restart, this should be done at the end of installation.
// Therefore we set the global 'restartRequired' if a restart is needed, and return this from NeedRestart()
bCancelWithoutPrompt := false;
if not IsRequiredDotNetDetected then
begin
bInstallWeb := InstallDotNetWebRuntime;
bInstallStd := InstallDotNetOfflineRuntime;
if ( bInstallWeb = True) then
begin
Result := InstallFrameworkWebRuntime
end
else if ( bInstallStd = True) then
begin
Result := InstallFrameworkOfflineRuntime
end
else
begin
Result := InstallFrameworkWebRuntime
end;
end;
end;

function NeedRestart(): Boolean;
begin
Result := bRequiresRestart;
end;

欢迎订阅我的个人微信公众号,一起讨论学习。


版权声明:
文章首发于 Jim Wang's blog , 转载文章请务必以超链接形式标明文章出处,作者信息及本版权声明。