Skip to content

Commit 3211834

Browse files
seto77claude
andcommitted
Fix resource leaks, save/copy path bugs, and tooltip/i18n gaps across simulators
Code fixes (with Crystallography/Crystallography.Controls submodule updates): - Diffraction/Image simulators: consolidate the hand-rolled HDC->Metafile save/copy scaffolding into ClipboardMetafileHelper.SaveOrCopyDrawingAsEnhMetafile, dispose bitmaps/dialogs/streams, use persistent clipboard copies, case-insensitive extension handling, and hoist loop-invariant path computations in individual-save loops - FormDiffractionSimulatorGeometry: centralize PseudoBitmap replacement (dispose + OverlappedImage invalidation) in SetPseudoBitmap; read IPA metadata via a single ImageIO.ReadImage pass; fail instead of reusing stale images on unreadable/real-space files; use pixSizeY in the detector pixel-size average; null-safe disposal in Designer Dispose - FormDiffractionSimulatorCBED: remove a double Math.Acos on a value that is already an angle (AngleResolution) - Version/ProgramUpdates: culture-invariant version parsing - release.yml: refuse to publish when an orphan tag points at a different commit than the build UI/i18n: - Show dropdown menu-item tooltips (ShowItemToolTips) in FormMain, FormDiffractionSimulator, FormImageSimulator, FormStructureViewer - FormTrajectory: add poleFigureControl tooltip in all 11 languages - FormStructureViewer: move the save-image ToolTipText to the actual submenu item (saveImageToolStripMenuItem1) in all languages - FormSpotIDV2: drop "(experimental)" from Global fit / Donut button labels (kept in tooltips); keep action buttons on one row - FormDiffractionSimulatorCBED: expand the CBED abbreviation in the Simulate button tooltip (EN/JA) - FormEBSD: English label for the PCHIP developer button - FormPolycrystallineDiffractionSimulator: widen the Appearance group for long localized labels Docs: - CODE_SIGNING.md / THIRD-PARTY-NOTICES.md: describe the active signing scope (x64 MSI + x64 portable exe; Arm64 unsigned) as implemented Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent b378bf3 commit 3211834

55 files changed

Lines changed: 274 additions & 174 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/release.yml

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# 260405Cl Version.cs の History 先頭行が変わったら自動でインストーラをビルド・リリースする
1+
# 260405Cl Version.cs の History 先頭行が変わったら自動でインストーラをビルド・リリースする
22
# 260602Cl portable ZIP も同じリリースに添付。MSI を主配布のまま (管理 PC 向けの代替として self-contained ZIP を追加)
33
# 260623Cl Phase 3 (ARM64化方針 §6 = 完全 inline 統合): 単一 job を 5 job (prepare/build-x64/build-arm64/smoke-arm64/release)
44
# に再編し、arm64 (ZIP+MSI) を同一 run でビルド・実機 smoke して x64 と一緒に 1 回の gh release create で原子的に公開する。
@@ -93,6 +93,25 @@ jobs:
9393
"needed=false" >> $env:GITHUB_OUTPUT
9494
return
9595
}
96+
97+
# 260715Ch: release が無いのに同名 tag だけ残っている場合、gh release create の --target は
98+
# 既存 tag には効かない。別 commit の孤立 tag を黙って再利用すると、公開バイナリと source tag が
99+
# 不一致になるため、先に tag の commit を検査する。同じ GITHUB_SHA なら安全な復旧として許可する。
100+
if (-not $releaseExists) {
101+
$checkRef = "refs/release-check/${tag}"
102+
git fetch origin "refs/tags/${tag}:${checkRef}" --depth=1 2>$null
103+
$tagExists = ($LASTEXITCODE -eq 0)
104+
$global:LASTEXITCODE = 0
105+
if ($tagExists) {
106+
$tagSha = (git rev-parse "${checkRef}^{commit}").Trim()
107+
if ($tagSha -ne $env:GITHUB_SHA) {
108+
$message = "Tag $tag already exists at $tagSha, but this build is $($env:GITHUB_SHA). Refusing to publish mismatched artifacts."
109+
if ($dryRun) { Write-Warning $message } else { throw $message }
110+
} else {
111+
Write-Host "Tag $tag already exists at this build commit; it is safe to create the missing release."
112+
}
113+
}
114+
}
96115
# 旧 force 処理 (260530Cl 廃止):
97116
# if ($releaseExists) {
98117
# if ("(inputs.force)" -eq "true") { # 260530Cl 旧コードでは inputs.force を式参照していた

CODE_SIGNING.md

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
# Code signing policy
1+
# Code signing policy
22

33
This document describes the code-signing policy for ReciPro release artifacts.
44

55
## Current status
66

7-
ReciPro has been accepted into the **SignPath Foundation** free code-signing program for open-source projects, and a production signing certificate was issued to the ReciPro SignPath organization on 2026-06-29. Since **v.4.942** (released 2026-07-01), release artifacts are signed as part of the automated release pipeline.
7+
ReciPro has been accepted into the **SignPath Foundation** free code-signing program for open-source projects, and a production signing certificate was issued to the ReciPro SignPath organization on 2026-06-29. Since **v.4.942** (released 2026-07-01), the x64 installer and the x64 portable executable are signed as part of the automated release pipeline. The experimental Arm64 assets remain unsigned. <!-- 260715Ch: match the active release.yml signing scope -->
88

99
Releases up to v.4.941 predate code signing: unless a GitHub Release explicitly states that `ReciPro-setup.msi` (named `ReciProSetup.msi` up to v.4.939) is digitally signed, users should not assume that the installer is signed.
1010

@@ -16,19 +16,20 @@ Official release artifacts are published only from the ReciPro GitHub Releases p
1616

1717
Users should avoid downloading ReciPro installers from unofficial mirrors or third-party redistribution sites.
1818

19-
## Intended signing model
19+
## Active signing model
2020

2121
Free code signing on Windows provided by [SignPath.io](https://about.signpath.io), certificate by [SignPath Foundation](https://signpath.org).
2222

23-
Once enabled for a given release, release artifacts are signed using Windows Authenticode signing before being published to GitHub Releases. For SignPath Foundation signing, the signer shown by Windows may be `SignPath Foundation` rather than the personal name of the ReciPro maintainer. Because SignPath Foundation signing requires a maintainer to manually approve each signing request, there may be a delay between a new version being pushed and the corresponding GitHub Release appearing, while the signing request awaits approval.
23+
The x64 installer package and the x64 portable `ReciPro.exe` are signed using Windows Authenticode before they are published to GitHub Releases. For SignPath Foundation signing, the signer shown by Windows may be `SignPath Foundation` rather than the personal name of the ReciPro maintainer. Because SignPath Foundation signing requires a maintainer to manually approve each signing request, there may be a delay between a new version being pushed and the corresponding GitHub Release appearing, while the signing request awaits approval. <!-- 260715Ch -->
2424

2525
## Scope of signing
2626

27-
The intended signing scope is:
27+
The current signing scope is: <!-- 260715Ch: verified against release.yml and the v.4.943 artifacts -->
2828

29-
- `ReciPro-setup.msi` and `ReciPro-setup_arm64.msi` (including the legacy-named copy `ReciProSetup.msi`, which is the identical x64 installer kept for auto-update compatibility with older versions)
30-
- ReciPro executable files built from this repository, including the single-file `ReciPro.exe` inside the portable ZIP packages (win-x64; the experimental win-arm64 package may remain unsigned until it graduates from experimental status)
31-
- ReciPro libraries built from this repository
29+
- `ReciPro-setup.msi` and its identical legacy-named copy `ReciProSetup.msi` (x64)
30+
- the single-file `ReciPro.exe` inside the x64 portable ZIP
31+
32+
The framework-dependent `ReciPro.exe` and DLLs stored inside the signed MSI are not individually Authenticode-signed; their integrity is covered by the signed MSI container. The loose `Crystallography.Native*.dll` files in the x64 portable ZIP are also not individually signed. The experimental Arm64 MSI, portable executable, and native DLLs remain unsigned until Arm64 graduates into the production signing scope.
3233

3334
Third-party binaries should not be re-signed as if they were maintained by ReciPro unless their provenance and redistribution terms explicitly permit that use. See `THIRD-PARTY-NOTICES.md` for bundled or referenced third-party components and data.
3435

Crystallography.Controls

ReciPro/DiffractionSimulator/FormDiffractionSimulator.Designer.cs

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ReciPro/DiffractionSimulator/FormDiffractionSimulator.cs

Lines changed: 33 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -2859,65 +2859,57 @@ public void SaveOrCopy(bool save, bool isImage, bool drawOverlappedImage, string
28592859
{
28602860
if (isImage)
28612861
{
2862-
var bmp = new Bitmap(graphicsBox.ClientSize.Width, graphicsBox.ClientSize.Height);
2862+
//var bmp = new Bitmap(graphicsBox.ClientSize.Width, graphicsBox.ClientSize.Height); // 旧: 保存・永続コピー後も Bitmap が未解放
2863+
using var bmp = new Bitmap(graphicsBox.ClientSize.Width, graphicsBox.ClientSize.Height); // (260715Ch)
28632864
//var g = Graphics.FromImage(bmp); // (260611Ch) 旧: Graphics が未解放
28642865
using (var g = Graphics.FromImage(bmp)) // (260611Ch) Bitmap は Clipboard に渡す場合があるため Graphics だけ先に解放
28652866
Draw(g, true, drawOverlappedImage);
2866-
if (bmp != null)
2867+
if (save)
28672868
{
2868-
if (save)
2869+
if (filename == "")
28692870
{
2870-
if (filename == "")
2871-
{
2872-
SaveFileDialog dlg = new() { Filter = "*.png|*.png" };
2873-
if (dlg.ShowDialog() == DialogResult.OK)
2874-
filename = dlg.FileName;
2875-
}
2871+
//SaveFileDialog dlg = new() { Filter = "*.png|*.png" }; // 旧: ダイアログが未解放
2872+
using SaveFileDialog dlg = new() { Filter = "*.png|*.png" }; // (260715Ch)
2873+
if (dlg.ShowDialog() != DialogResult.OK)
2874+
return; // (260715Ch) キャンセル後に空ファイル名を処理しない
2875+
filename = dlg.FileName;
2876+
}
28762877

2877-
if (!filename.EndsWith(".png"))
2878-
filename += ".png";
2878+
//if (!filename.EndsWith(".png")) // 旧: 大文字拡張子を重複付加
2879+
if (!filename.EndsWith(".png", StringComparison.OrdinalIgnoreCase)) // (260715Ch)
2880+
filename += ".png";
28792881

2880-
if (Path.Exists(Path.GetDirectoryName(filename)))
2881-
bmp.Save(filename, ImageFormat.Png);
2882-
}
2883-
else
2884-
{
2885-
Clipboard.SetDataObject(bmp);
2886-
}
2882+
//if (Path.Exists(Path.GetDirectoryName(filename))) // 旧: 相対ファイル名は DirectoryName が空になり保存されない
2883+
if (Directory.Exists(Path.GetDirectoryName(Path.GetFullPath(filename)))) // (260715Ch)
2884+
bmp.Save(filename, ImageFormat.Png);
2885+
}
2886+
else
2887+
{
2888+
//Clipboard.SetDataObject(bmp); // 旧: Bitmap の寿命を Clipboard 任せにして未解放
2889+
Clipboard.SetDataObject(bmp, true); // (260715Ch) 永続コピー完了後に using で安全に解放
28872890
}
28882891
}
28892892
else
28902893
{
2891-
using Graphics grfx = CreateGraphics();
2892-
IntPtr ipHdc = grfx.GetHdc();
2893-
MemoryStream ms = new();
2894-
Metafile mf = new(ms, ipHdc, EmfType.EmfPlusDual);
2895-
grfx.ReleaseHdc(ipHdc);
2896-
grfx.Dispose();
2897-
//var g = Graphics.FromImage(mf); Draw(g, true, drawOverlappedImage); g.Dispose(); // (260611Ch) 旧: 手動 Dispose
2898-
using (var g = Graphics.FromImage(mf)) // (260611Ch)
2899-
{
2900-
Draw(g, true, drawOverlappedImage);
2901-
}
2902-
2894+
// 260716Cl 旧: using Graphics grfx = CreateGraphics(); using MemoryStream ms = new(); ipHdc = grfx.GetHdc();
2895+
// try { mf = new Metafile(ms, ipHdc, EmfType.EmfPlusDual); } finally { grfx.ReleaseHdc(ipHdc); } using (mf) { Draw → 保存 or PutEnhMetafileOnClipboard }
2896+
// と HDC→Metafile 定型を自前実装していた (260715Ch)。同型が 3 箇所に複製されていたため
2897+
// ClipboardMetafileHelper.SaveOrCopyDrawingAsEnhMetafile へ集約し、ファイル名確定 (ダイアログ) を録画より先に行う (キャンセル時に無駄な録画をしない)。
29032898
if (save)
29042899
{
29052900
if (filename == "")
29062901
{
2907-
SaveFileDialog dlg = new() { Filter = "*.emf|*.emf" };
2908-
if (dlg.ShowDialog() == DialogResult.OK)
2909-
filename = dlg.FileName;
2902+
//SaveFileDialog dlg = new() { Filter = "*.emf|*.emf" }; // 旧: ダイアログが未解放、キャンセル後も空名で続行
2903+
using SaveFileDialog dlg = new() { Filter = "*.emf|*.emf" }; // (260715Ch)
2904+
if (dlg.ShowDialog() != DialogResult.OK)
2905+
return; // (260715Ch)
2906+
filename = dlg.FileName;
29102907
}
2911-
if (filename.EndsWith(".emf"))
2908+
//if (filename.EndsWith(".emf")) filename += ".emf"; // 旧: 正しい拡張子に .emf を重複付加
2909+
if (!filename.EndsWith(".emf", StringComparison.OrdinalIgnoreCase)) // (260715Ch)
29122910
filename += ".emf";
2913-
2914-
FileStream fsm = new(filename, FileMode.Create, FileAccess.Write);
2915-
fsm.Write(ms.GetBuffer(), 0, (int)ms.Length);
2916-
fsm.Close();
2917-
29182911
}
2919-
else
2920-
ClipboardMetafileHelper.PutEnhMetafileOnClipboard(this.Handle, mf);
2912+
ClipboardMetafileHelper.SaveOrCopyDrawingAsEnhMetafile(this.Handle, g => Draw(g, true, drawOverlappedImage), save ? filename : ""); // 260716Cl
29212913
}
29222914
}
29232915

ReciPro/DiffractionSimulator/FormDiffractionSimulator.resx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2870,8 +2870,9 @@ farther from the Ewald sphere more transparent.</value>
28702870
<value>33</value>
28712871
</data>
28722872
<data name="numericBoxReciprocalThreshold.ToolTip" xml:space="preserve">
2873-
<value>逆格子点を表示する相対強度のしきい値(%)を設定し
2874-
ます。これより弱いスポットは非表示になります。</value>
2873+
<value>Set the relative-intensity threshold (%) for displaying
2874+
reciprocal-lattice points. Spots below this threshold
2875+
are hidden.</value>
28752876
</data>
28762877
<data name="&gt;&gt;tableLayoutPanel1.Name" xml:space="preserve">
28772878
<value>tableLayoutPanel1</value>
@@ -8602,4 +8603,4 @@ structure factor) used to draw Kikuchi lines.</value>
86028603
<data name="sizeControl1.ToolTip" xml:space="preserve">
86038604
<value>Set pixel sizes of the simulated pattern</value>
86048605
</data>
8605-
</root>
8606+
</root>

ReciPro/DiffractionSimulator/FormDiffractionSimulatorCBED.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,8 @@ private void Bethe_CbedCompleted(object sender, RunWorkerCompletedEventArgs e)
295295
private void setImagePixelSize()
296296
{
297297
//以下の二つのパラメータはγ値の調整のところで使う。(あまり意味のないパラメータなので、廃止したい)
298-
AngleResolution = Math.Acos(Vector3DBase.AngleBetVectors(Directions[Directions.Length / 2], Directions[Directions.Length / 2 + 1]) / 2);
298+
//AngleResolution = Math.Acos(Vector3DBase.AngleBetVectors(Directions[Directions.Length / 2], Directions[Directions.Length / 2 + 1]) / 2);// (260715Ch) 旧: 既に radian の角度へ回転行列 trace 用 acos を二重適用していた
299+
AngleResolution = Vector3DBase.AngleBetVectors(Directions[Directions.Length / 2], Directions[Directions.Length / 2 + 1]);// (260715Ch)
299300
ImagePixelSize = FormDiffractionSimulator.CameraLength2 * Math.Tan(AngleResolution);
300301
}
301302

ReciPro/DiffractionSimulator/FormDiffractionSimulatorCBED.ja.resx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,8 @@
112112
<value>開始</value>
113113
</data>
114114
<data name="buttonSimulate.ToolTip" xml:space="preserve">
115-
<value>CBEDシミュレーションを実行</value>
115+
<value>現在の設定で収束電子回折 (CBED) パターンを
116+
シミュレートします。</value>
116117
</data>
117118
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
118119
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />

ReciPro/DiffractionSimulator/FormDiffractionSimulatorCBED.resx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,8 @@
149149
<value>272, 17</value>
150150
</metadata>
151151
<data name="buttonSimulate.ToolTip" xml:space="preserve">
152-
<value>Execute a CBED simulation</value>
152+
<value>Simulate a convergent-beam electron diffraction
153+
(CBED) pattern for the current settings.</value>
153154
</data>
154155
<data name="&gt;&gt;buttonSimulate.Name" xml:space="preserve">
155156
<value>buttonSimulate</value>
@@ -2062,4 +2063,4 @@ disk pattern currently displayed.</value>
20622063
<data name="&gt;&gt;$this.Type" xml:space="preserve">
20632064
<value>Crystallography.Controls.FormBase, Crystallography.Controls, Culture=neutral, PublicKeyToken=null</value>
20642065
</data>
2065-
</root>
2066+
</root>

0 commit comments

Comments
 (0)