Skip to content

Commit 8f0e44b

Browse files
committed
Refactor and enhance script export functionality
Refactored the `ExportCommandScript` logic into a dedicated method to improve modularity. Enhanced PowerShell script generation with pre-flight checks, batch mode support, and robust error handling. Improved null handling for user inputs and reformatted `MessageBox.Show` calls for better readability. Updated Trusted Signing logic to handle missing `CorrelationIdData` and added safeguards for timestamp provider retrieval. Performed general code cleanup to improve maintainability.
1 parent 9b3acf8 commit 8f0e44b

1 file changed

Lines changed: 84 additions & 26 deletions

File tree

src/SignToolGUI/Forms/MainForm.cs

Lines changed: 84 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2997,6 +2997,11 @@ private bool FindSignToolExe()
29972997
#endregion
29982998

29992999
private void buttonExportAsScript_Click(object sender, EventArgs e)
3000+
{
3001+
ExportCommandScript();
3002+
}
3003+
3004+
private void ExportCommandScript()
30003005
{
30013006
try
30023007
{
@@ -3007,7 +3012,8 @@ private void buttonExportAsScript_Click(object sender, EventArgs e)
30073012
foreach (var item in checkedListBoxFiles.CheckedItems)
30083013
{
30093014
var s = item?.ToString() ?? string.Empty;
3010-
var cleaned = System.Text.RegularExpressions.Regex.Replace(s, @"\s*\[(Valid|Invalid)\]", "", System.Text.RegularExpressions.RegexOptions.IgnoreCase);
3015+
var cleaned = System.Text.RegularExpressions.Regex.Replace(
3016+
s, @"\s*\[(Valid|Invalid)\]", "", System.Text.RegularExpressions.RegexOptions.IgnoreCase);
30113017
files.Add(cleaned);
30123018
}
30133019
}
@@ -3016,14 +3022,16 @@ private void buttonExportAsScript_Click(object sender, EventArgs e)
30163022
foreach (var item in checkedListBoxFiles.Items)
30173023
{
30183024
var s = item?.ToString() ?? string.Empty;
3019-
var cleaned = System.Text.RegularExpressions.Regex.Replace(s, @"\s*\[(Valid|Invalid)\]", "", System.Text.RegularExpressions.RegexOptions.IgnoreCase);
3025+
var cleaned = System.Text.RegularExpressions.Regex.Replace(
3026+
s, @"\s*\[(Valid|Invalid)\]", "", System.Text.RegularExpressions.RegexOptions.IgnoreCase);
30203027
files.Add(cleaned);
30213028
}
30223029
}
30233030

30243031
if (files.Count == 0)
30253032
{
3026-
MessageBox.Show("No files available to export.", "Export Command Script", MessageBoxButtons.OK, MessageBoxIcon.Information);
3033+
MessageBox.Show("No files available to export.", "Export Command Script",
3034+
MessageBoxButtons.OK, MessageBoxIcon.Information);
30273035
return;
30283036
}
30293037

@@ -3044,10 +3052,12 @@ private void buttonExportAsScript_Click(object sender, EventArgs e)
30443052
sb.AppendLine();
30453053

30463054
// Base vars
3047-
var signToolFullPath = Path.GetFullPath(textBoxSignToolPath.Text);
3055+
var signToolFullPath = Path.GetFullPath(textBoxSignToolPath.Text ?? string.Empty);
30483056
sb.AppendLine("$SignTool = '" + EscapePS(signToolFullPath) + "'");
30493057
sb.AppendLine("$VerboseSwitch = " + (menuItemSignVerbose.Checked ? "$true" : "$false"));
30503058
sb.AppendLine("$DebugSwitch = " + (menuItemSignDebug.Checked ? "$true" : "$false"));
3059+
// Optional: toggle batch mode from a checkbox if you add one (e.g., checkBoxBatchMode.Checked). Default false.
3060+
sb.AppendLine("$BatchMode = $false");
30513061
if (!radioButtonTrustedSigning.Checked)
30523062
{
30533063
// Only used by Windows Store and PFX modes
@@ -3084,13 +3094,25 @@ private void buttonExportAsScript_Click(object sender, EventArgs e)
30843094
sb.AppendLine("# Windows Certificate Store signing");
30853095
sb.AppendLine("$Thumbprint = '" + EscapePS(thumbprint ?? string.Empty) + "'");
30863096
sb.AppendLine();
3087-
sb.AppendLine("foreach ($f in $files) {");
3097+
3098+
// Pre-flight + file normalization
3099+
sb.AppendLine("if (!(Test-Path -LiteralPath $SignTool)) { throw \"SignTool not found: $SignTool\" }");
3100+
sb.AppendLine("$targetFiles = @()");
3101+
sb.AppendLine("foreach ($f in $files) { if (Test-Path -LiteralPath $f) { $targetFiles += (Resolve-Path -LiteralPath $f).Path } else { Write-Warning \"File not found, skipping: $f\" } }");
3102+
sb.AppendLine("if ($targetFiles.Count -eq 0) { throw 'No input files to sign.' }");
3103+
sb.AppendLine("$failures = @()");
3104+
sb.AppendLine();
3105+
3106+
// Per-file signing with exit code checks
3107+
sb.AppendLine("foreach ($f in $targetFiles) {");
30883108
sb.AppendLine(" $sigArgs = @('sign')");
30893109
sb.AppendLine(" Add-GlobalSwitches ([ref]$sigArgs)");
30903110
sb.AppendLine(" if ($UseTimestamp -and $TimestampUrl) { $sigArgs += '/fd'; $sigArgs += 'sha256'; $sigArgs += '/tr'; $sigArgs += $TimestampUrl; $sigArgs += '/td'; $sigArgs += 'sha256' } else { $sigArgs += '/fd'; $sigArgs += 'sha256' }");
30913111
sb.AppendLine(" $sigArgs += '/sha1'; $sigArgs += $Thumbprint; $sigArgs += $f");
30923112
sb.AppendLine(" & $SignTool @sigArgs");
3113+
sb.AppendLine(" if ($LASTEXITCODE -ne 0) { Write-Warning \"Signing failed (exit $LASTEXITCODE): $f\"; $failures += $f } else { Write-Host \"Signed: $f\" }");
30933114
sb.AppendLine("}");
3115+
sb.AppendLine("if ($failures.Count -gt 0) { throw (\"One or more files failed to sign:`n - \" + ($failures -join \"`n - \")) }");
30943116
}
30953117
else if (radioButtonPFXCertificate.Checked)
30963118
{
@@ -3103,13 +3125,28 @@ private void buttonExportAsScript_Click(object sender, EventArgs e)
31033125
sb.AppendLine("$bstr = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($sec)");
31043126
sb.AppendLine("try { $PfxPassword = [Runtime.InteropServices.Marshal]::PtrToStringBSTR($bstr) } finally { [Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr) }");
31053127
sb.AppendLine();
3106-
sb.AppendLine("foreach ($f in $files) {");
3107-
sb.AppendLine(" $sigArgs = @('sign')");
3108-
sb.AppendLine(" Add-GlobalSwitches ([ref]$sigArgs)");
3109-
sb.AppendLine(" if ($UseTimestamp -and $TimestampUrl) { $sigArgs += '/fd'; $sigArgs += 'sha256'; $sigArgs += '/tr'; $sigArgs += $TimestampUrl; $sigArgs += '/td'; $sigArgs += 'sha256' } else { $sigArgs += '/fd'; $sigArgs += 'sha256' }");
3110-
sb.AppendLine(" $sigArgs += '/f'; $sigArgs += $PfxPath; $sigArgs += '/p'; $sigArgs += $PfxPassword; $sigArgs += '/a'; $sigArgs += $f");
3111-
sb.AppendLine(" & $SignTool @sigArgs");
3112-
sb.AppendLine("}");
3128+
3129+
// Pre-flight + file normalization
3130+
sb.AppendLine("if (!(Test-Path -LiteralPath $SignTool)) { throw \"SignTool not found: $SignTool\" }");
3131+
sb.AppendLine("if (!(Test-Path -LiteralPath $PfxPath)) { throw \"PFX file not found: $PfxPath\" }");
3132+
sb.AppendLine("$targetFiles = @()");
3133+
sb.AppendLine("foreach ($f in $files) { if (Test-Path -LiteralPath $f) { $targetFiles += (Resolve-Path -LiteralPath $f).Path } else { Write-Warning \"File not found, skipping: $f\" } }");
3134+
sb.AppendLine("if ($targetFiles.Count -eq 0) { throw 'No input files to sign.' }");
3135+
sb.AppendLine("$failures = @()");
3136+
sb.AppendLine();
3137+
3138+
// Per-file signing with exit code checks
3139+
sb.AppendLine("try {");
3140+
sb.AppendLine(" foreach ($f in $targetFiles) {");
3141+
sb.AppendLine(" $sigArgs = @('sign')");
3142+
sb.AppendLine(" Add-GlobalSwitches ([ref]$sigArgs)");
3143+
sb.AppendLine(" if ($UseTimestamp -and $TimestampUrl) { $sigArgs += '/fd'; $sigArgs += 'sha256'; $sigArgs += '/tr'; $sigArgs += $TimestampUrl; $sigArgs += '/td'; $sigArgs += 'sha256' } else { $sigArgs += '/fd'; $sigArgs += 'sha256' }");
3144+
sb.AppendLine(" $sigArgs += '/f'; $sigArgs += $PfxPath; $sigArgs += '/p'; $sigArgs += $PfxPassword; $sigArgs += '/a'; $sigArgs += $f");
3145+
sb.AppendLine(" & $SignTool @sigArgs");
3146+
sb.AppendLine(" if ($LASTEXITCODE -ne 0) { Write-Warning \"Signing failed (exit $LASTEXITCODE): $f\"; $failures += $f } else { Write-Host \"Signed: $f\" }");
3147+
sb.AppendLine(" }");
3148+
sb.AppendLine(" if ($failures.Count -gt 0) { throw (\"One or more files failed to sign:`n - \" + ($failures -join \"`n - \")) }");
3149+
sb.AppendLine("} finally { $PfxPassword = $null }");
31133150
}
31143151
else if (radioButtonTrustedSigning.Checked)
31153152
{
@@ -3123,9 +3160,13 @@ private void buttonExportAsScript_Click(object sender, EventArgs e)
31233160
endpointUrl = tp.Url;
31243161
else
31253162
{
3126-
var enabled = _timestampManager.GetEnabledServers();
3127-
if (enabled.Count > 0)
3128-
endpointUrl = enabled.First().Url;
3163+
try
3164+
{
3165+
var enabled = _timestampManager.GetEnabledServers();
3166+
if (enabled.Count > 0)
3167+
endpointUrl = enabled.First().Url;
3168+
}
3169+
catch { /* ignore */ }
31293170
}
31303171

31313172
sb.AppendLine("# Trusted Signing");
@@ -3136,17 +3177,32 @@ private void buttonExportAsScript_Click(object sender, EventArgs e)
31363177
sb.AppendLine("$CorrelationIdData = '" + EscapePS(textBoxCorrelationId.Text) + "'");
31373178
sb.AppendLine("$TimestampUrl = '" + EscapePS(tsTimestampUrl) + "'");
31383179
sb.AppendLine();
3139-
sb.AppendLine("# Create temporary DMDF JSON file");
3140-
sb.AppendLine("$dmdfPath = Join-Path $env:TEMP (('signtoolgui_' + [guid]::NewGuid().ToString()) + '.json')");
3141-
sb.AppendLine("$dmdfContent = @{ Endpoint = $Endpoint; CodeSigningAccountName = $AccountName; CertificateProfileName = $CertificateProfileName; CorrelationIdData = $CorrelationIdData } | ConvertTo-Json -Depth 3");
3142-
sb.AppendLine("$dmdfContent | Out-File -FilePath $dmdfPath -Encoding utf8");
3180+
3181+
// Pre-flight + file normalization
3182+
sb.AppendLine("if (!(Test-Path -LiteralPath $SignTool)) { throw \"SignTool not found: $SignTool\" }");
3183+
sb.AppendLine("if (!(Test-Path -LiteralPath $DlibPath)) { throw \"Dlib not found: $DlibPath\" }");
3184+
sb.AppendLine("$targetFiles = @()");
3185+
sb.AppendLine("foreach ($f in $files) { if (Test-Path -LiteralPath $f) { $targetFiles += (Resolve-Path -LiteralPath $f).Path } else { Write-Warning \"File not found, skipping: $f\" } }");
3186+
sb.AppendLine("if ($targetFiles.Count -eq 0) { throw 'No input files to sign.' }");
3187+
sb.AppendLine();
3188+
3189+
// DMDF payload
3190+
sb.AppendLine("if ([string]::IsNullOrWhiteSpace($CorrelationIdData)) { $CorrelationIdData = [guid]::NewGuid().ToString() }");
3191+
sb.AppendLine("$dmdf = @{ Endpoint = $Endpoint; CodeSigningAccountName = $AccountName; CertificateProfileName = $CertificateProfileName; CorrelationIdData = $CorrelationIdData }");
3192+
sb.AppendLine("$dmdfPath = Join-Path $env:TEMP (\"signtoolgui_{0}.json\" -f ([guid]::NewGuid()))");
3193+
sb.AppendLine("$dmdf | ConvertTo-Json -Depth 3 | Out-File -FilePath $dmdfPath -Encoding utf8");
31433194
sb.AppendLine();
3195+
3196+
// Common args and signing
3197+
sb.AppendLine("$commonArgs = @('/fd','sha256','/tr',$TimestampUrl,'/td','sha256','/dlib',$DlibPath,'/dmdf',$dmdfPath)");
3198+
sb.AppendLine("$failures = @()");
31443199
sb.AppendLine("try {");
3145-
sb.AppendLine(" foreach ($f in $files) {");
3146-
sb.AppendLine(" $sigArgs = @('sign')");
3147-
sb.AppendLine(" Add-GlobalSwitches ([ref]$sigArgs)");
3148-
sb.AppendLine(" $sigArgs += '/fd'; $sigArgs += 'sha256'; $sigArgs += '/tr'; $sigArgs += $TimestampUrl; $sigArgs += '/td'; $sigArgs += 'sha256'; $sigArgs += '/dlib'; $sigArgs += $DlibPath; $sigArgs += '/dmdf'; $sigArgs += $dmdfPath; $sigArgs += $f");
3149-
sb.AppendLine(" & $SignTool @sigArgs");
3200+
sb.AppendLine(" if ($BatchMode) {");
3201+
sb.AppendLine(" $sigArgs = @('sign'); Add-GlobalSwitches ([ref]$sigArgs); $sigArgs += $commonArgs; $sigArgs += $targetFiles; & $SignTool @sigArgs");
3202+
sb.AppendLine(" if ($LASTEXITCODE -ne 0) { $failures = $targetFiles; throw \"signtool exited with code $LASTEXITCODE in batch mode.\" } else { Write-Host (\"Signed {0} file(s) in batch.\" -f $targetFiles.Count) }");
3203+
sb.AppendLine(" } else {");
3204+
sb.AppendLine(" foreach ($f in $targetFiles) { $sigArgs = @('sign'); Add-GlobalSwitches ([ref]$sigArgs); $sigArgs += $commonArgs; $sigArgs += $f; & $SignTool @sigArgs; if ($LASTEXITCODE -ne 0) { Write-Warning \"Signing failed (exit $LASTEXITCODE): $f\"; $failures += $f } else { Write-Host \"Signed: $f\" } }");
3205+
sb.AppendLine(" if ($failures.Count -gt 0) { throw (\"One or more files failed to sign:`n - \" + ($failures -join \"`n - \")) }");
31503206
sb.AppendLine(" }");
31513207
sb.AppendLine("}");
31523208
sb.AppendLine("finally { Remove-Item -Path $dmdfPath -Force -ErrorAction Ignore }");
@@ -3160,12 +3216,14 @@ private void buttonExportAsScript_Click(object sender, EventArgs e)
31603216
File.WriteAllText(sfd.FileName, sb.ToString(), Encoding.UTF8);
31613217

31623218
Message("Command script exported: '" + sfd.FileName + "'", EventType.Information, 20100);
3163-
MessageBox.Show("Command script exported:\n" + sfd.FileName, "Export Command Script", MessageBoxButtons.OK, MessageBoxIcon.Information);
3219+
MessageBox.Show("Command script exported:\n" + sfd.FileName, "Export Command Script",
3220+
MessageBoxButtons.OK, MessageBoxIcon.Information);
31643221
}
31653222
}
31663223
catch (Exception ex)
31673224
{
3168-
MessageBox.Show("Failed to export command script: " + ex.Message, "Export Command Script", MessageBoxButtons.OK, MessageBoxIcon.Error);
3225+
MessageBox.Show("Failed to export command script: " + ex.Message, "Export Command Script",
3226+
MessageBoxButtons.OK, MessageBoxIcon.Error);
31693227
Message("Failed to export command script: " + ex.Message, EventType.Error, 20101);
31703228
}
31713229
}

0 commit comments

Comments
 (0)