From d9f9856c647f2ece54fc2261fcfa086637c1f9e2 Mon Sep 17 00:00:00 2001 From: berryzplus Date: Fri, 17 Jul 2026 23:09:52 +0900 Subject: [PATCH 01/19] =?UTF-8?q?=E3=83=95=E3=82=A1=E3=82=A4=E3=83=AB?= =?UTF-8?q?=E3=83=80=E3=82=A4=E3=82=A2=E3=83=AD=E3=82=B0=E3=81=B8=E3=81=AE?= =?UTF-8?q?=E5=85=A5=E5=8A=9B=E3=82=92=E3=83=95=E3=82=A1=E3=82=A4=E3=83=AB?= =?UTF-8?q?=E5=90=8D=E3=81=AE=E3=81=BF=E3=81=AB=E3=81=99=E3=82=8B=E3=81=AE?= =?UTF-8?q?=E3=82=92=E3=82=84=E3=82=81=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/cpp/tests1/test-cdlgopenfile.cpp | 10 ++-------- src/test/cpp/tests1/window/UiaTestSuite.hpp | 8 ++++---- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/test/cpp/tests1/test-cdlgopenfile.cpp b/src/test/cpp/tests1/test-cdlgopenfile.cpp index 58d6275e00..5b3d9c7c11 100644 --- a/src/test/cpp/tests1/test-cdlgopenfile.cpp +++ b/src/test/cpp/tests1/test-cdlgopenfile.cpp @@ -252,10 +252,7 @@ TEST_F(SelectFileTest, SelectFile001) constexpr bool resolvePath = true; // パス解決する場合のテスト std::jthread j([&] { - if (const auto hWndDlgOpenFile = WaitForDialog(L"開く")) { - EmulateSetValue(GetFocusedElement(), path.c_str()); // 絶対パスを入れる - EmulateHitEnter(); - } + EmulateEnterOpenFileName(path); }); EXPECT_THAT(CDlgOpenFile::SelectFile(hWndDlg, hWndFolder, L"*.ini", resolvePath, EFITER_NONE), IsTrue()); @@ -273,10 +270,7 @@ TEST_F(SelectFileTest, SelectFile002) constexpr bool resolvePath = false; // パス解決しない場合のテスト std::jthread j([&] { - if (const auto hWndDlgOpenFile = WaitForDialog(L"開く")) { - EmulateSetValue(GetFocusedElement(), path.c_str()); // 絶対パスを入れる - EmulateHitEnter(); - } + EmulateEnterOpenFileName(path); }); EXPECT_THAT(CDlgOpenFile::SelectFile(hWndDlg, hWndFolder, L"*.ini", resolvePath, EFITER_NONE), IsTrue()); diff --git a/src/test/cpp/tests1/window/UiaTestSuite.hpp b/src/test/cpp/tests1/window/UiaTestSuite.hpp index 5ebca40c40..dd6a244cbc 100644 --- a/src/test/cpp/tests1/window/UiaTestSuite.hpp +++ b/src/test/cpp/tests1/window/UiaTestSuite.hpp @@ -91,18 +91,18 @@ struct UiaTestSuite EmulateInvoke(pItem); } - void EmulateEnterOpenFileName(const std::filesystem::path& exportPath) const + void EmulateEnterOpenFileName(const std::filesystem::path& path) const { if (const auto hWndDlgOpenFile = WaitForDialog(L"開く")) { - EmulateSetValue(GetFocusedElement(), exportPath.filename().c_str()); + EmulateSetValue(GetFocusedElement(), path.c_str()); EmulateHitEnter(); } } - void EmulateEnterSaveFileName(const std::filesystem::path& exportPath) const + void EmulateEnterSaveFileName(const std::filesystem::path& path) const { if (const auto hWndDlgSaveAs = WaitForDialog(L"名前を付けて保存")) { - EmulateSetValue(GetFocusedElement(), exportPath.filename().c_str()); + EmulateSetValue(GetFocusedElement(), path.c_str()); EmulateHitEnter(); } } From 5e57a59bc33d2c5b2245c429b78d51b30047128b Mon Sep 17 00:00:00 2001 From: berryzplus Date: Fri, 17 Jul 2026 23:10:32 +0900 Subject: [PATCH 02/19] =?UTF-8?q?WinMainTest=E3=81=AE=E3=82=A6=E3=82=A3?= =?UTF-8?q?=E3=83=B3=E3=83=89=E3=82=A6=E5=BE=85=E6=A9=9F=E3=82=92=E5=BC=B7?= =?UTF-8?q?=E5=8C=96=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/cpp/tests1/test-window.cpp | 4 ++-- src/test/cpp/tests1/test-winmain.cpp | 12 ++++++------ src/test/cpp/tests1/window/UiaTestSuite.hpp | 12 +++++++++--- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/test/cpp/tests1/test-window.cpp b/src/test/cpp/tests1/test-window.cpp index b98ca737e8..191f1b6025 100644 --- a/src/test/cpp/tests1/test-window.cpp +++ b/src/test/cpp/tests1/test-window.cpp @@ -1627,13 +1627,13 @@ TEST_F(EditWndTest, ShowPropType003) const auto hWndPage = GetActivePage(hWndDlg); EmulateInvokeButton(hWndPage, L"文字色統一(<)..."); - if (const auto hWndDlgSameColor = WaitForWindow(MAKEINTRESOURCEW(dialog::ModalDialogCloser::DIALOG_CLASS), L"文字色統一")) { + if (const auto hWndDlgSameColor = WaitForDialog(L"文字色統一")) { EmulateInvokeButton(hWndDlgSameColor, L"全チェック(A)"); EmulateInvokeButton(hWndDlgSameColor, L"全解除(N)"); EmulateInvokeButton(hWndDlgSameColor, L"OK"); } EmulateInvokeButton(hWndPage, L"背景色統一(>)..."); - if (const auto hWndDlgSameColor = WaitForWindow(MAKEINTRESOURCEW(dialog::ModalDialogCloser::DIALOG_CLASS), L"背景色統一")) { + if (const auto hWndDlgSameColor = WaitForDialog(L"背景色統一")) { EmulateInvokeButton(hWndDlgSameColor, L"全チェック(A)"); EmulateInvokeButton(hWndDlgSameColor, L"全解除(N)"); EmulateInvokeButton(hWndDlgSameColor, L"OK"); diff --git a/src/test/cpp/tests1/test-winmain.cpp b/src/test/cpp/tests1/test-winmain.cpp index 81abd5f203..7c34891532 100644 --- a/src/test/cpp/tests1/test-winmain.cpp +++ b/src/test/cpp/tests1/test-winmain.cpp @@ -845,9 +845,9 @@ TEST_F(WinMainFuncTest, DoGrep001) std::array args{ LR"(-GREPMODE)"s, - LR"(-GKEY="test")"s, - LR"(-GFILE="*.*;#.git;#.svn;#.vs;!*.msi;!*.exe;!*.obj;!*.pdb;!*.ilk;!*.res;!*.pch;!*.iobj;!*.ipdb")"s, - std::format(LR"(-GFOLDER="{}")", iniPath.parent_path().c_str()), + LR"(-GKEY="localhost")"s, + LR"(-GFILE="*.*;#en-US;#DriverData;#UMDF;#udc;#mde;#wd;!*.sys;!*.dll;!*.exe;!*.mui;!*.nls;!*.chm;!*.dat;!*.tmp;!*.wdf")"s, + LR"(-GFOLDER="C:\WINDOWS\System32\Drivers")"s, LR"(-GOPT=SP1)"s }; @@ -863,7 +863,7 @@ TEST_F(WinMainFuncTest, DoGrep001) } // 編集ウインドウを閉じる - const auto hWndFound = cxx::FindWindowW(GSTR_EDITWINDOWNAME); + const auto hWndFound = WaitForEditor(); testing::RequestForeignWindowClose(hWndFound); // 編集ウインドウが閉じられた後、プロセスが完全に終了するまで待つ @@ -891,7 +891,7 @@ TEST_F(WinMainFuncTest, OpenDebugWindow001) const auto ep = testing::CreateEditorProcess(std::array{ LR"(-DEBUGMODE)" }, profileName); // 編集ウインドウが有効になるのを待って閉じる - const auto hWndFound = WaitForWindow(GSTR_EDITWINDOWNAME); + const auto hWndFound = WaitForEditor(); testing::RequestForeignWindowClose(hWndFound); // 編集ウインドウが閉じられた後、プロセスが完全に終了するまで待つ @@ -935,7 +935,7 @@ TEST_F(WinMainFuncTest, ShowDlgGrep101) EXPECT_TRUE(dlgClosed) << "Grep dialog should be closed."; // 編集ウインドウを閉じる - const auto hWndFound = cxx::FindWindowW(GSTR_EDITWINDOWNAME); + const auto hWndFound = WaitForEditor(); testing::RequestForeignWindowClose(hWndFound); // 編集ウインドウが閉じられた後、プロセスが完全に終了するまで待つ diff --git a/src/test/cpp/tests1/window/UiaTestSuite.hpp b/src/test/cpp/tests1/window/UiaTestSuite.hpp index dd6a244cbc..3177a9471c 100644 --- a/src/test/cpp/tests1/window/UiaTestSuite.hpp +++ b/src/test/cpp/tests1/window/UiaTestSuite.hpp @@ -6,6 +6,7 @@ */ #pragma once +#include "config/system_constants.h" #include "cxx/com_pointer.hpp" #include "dlg/ModalDialogCloser.hpp" #include "util/tchar_convert.h" @@ -236,9 +237,9 @@ struct UiaTestSuite }); } - HWND WaitForDialog(const std::wstring& title) const + HWND WaitForDialog(const std::wstring& title, ULONGLONG timeoutMillis = 60000) const { - return WaitForWindow(MAKEINTRESOURCEW(dialog::ModalDialogCloser::DIALOG_CLASS), title); + return WaitForWindow(MAKEINTRESOURCEW(dialog::ModalDialogCloser::DIALOG_CLASS), title, timeoutMillis); } IUIAutomationElementPtr WaitForFocus(ULONGLONG startTick, ULONGLONG timeoutMillis) const @@ -259,7 +260,7 @@ struct UiaTestSuite return pFocusedElement; } - HWND WaitForWindow(LPCWSTR targetClass, const std::optional& title = std::nullopt, bool waitCaret = true, ULONGLONG timeoutMillis = defaultTimeoutMillis) const + HWND WaitForWindow(LPCWSTR targetClass, const std::optional& title = std::nullopt, ULONGLONG timeoutMillis = defaultTimeoutMillis, bool waitCaret = true) const { const auto startTick = ::GetTickCount64(); @@ -282,6 +283,11 @@ struct UiaTestSuite return nullptr; } + HWND WaitForEditor(const std::optional& title = std::nullopt, ULONGLONG timeoutMillis = 60000) const + { + return WaitForWindow(GSTR_EDITWINDOWNAME, title, timeoutMillis); + } + template requires (1 < sizeof...(Conditions)) && (std::convertible_to&& ...) IUIAutomationConditionPtr CreateAndCondition(Conditions... conditions) const From 67ffccbaadb3d49402bb7f5aa5c333543685a58a Mon Sep 17 00:00:00 2001 From: berryzplus Date: Fri, 17 Jul 2026 23:10:51 +0900 Subject: [PATCH 03/19] =?UTF-8?q?cxx::HandleHolder=E3=82=92=E5=86=8D?= =?UTF-8?q?=E5=AE=9A=E7=BE=A9=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CMutexの親クラスにできるよう、lockのconstを外す。 --- src/main/cpp/cxx/ResourceHolder.hpp | 12 +++++++- src/test/cpp/tests1/test-winmain.cpp | 43 +++++++++++++++++++++------- 2 files changed, 43 insertions(+), 12 deletions(-) diff --git a/src/main/cpp/cxx/ResourceHolder.hpp b/src/main/cpp/cxx/ResourceHolder.hpp index c66641bad9..a837c0e817 100644 --- a/src/main/cpp/cxx/ResourceHolder.hpp +++ b/src/main/cpp/cxx/ResourceHolder.hpp @@ -74,6 +74,12 @@ struct ResourceHolder { } + ResourceHolder(const Me&) = delete; + Me& operator=(const Me&) = delete; + + ResourceHolder(Me&& other) noexcept = default; + Me& operator=(Me&& rhs) noexcept = default; + // bound 更新(OwnedResourceHolder の updateContext 相当) template void updateBound(Bound&&... bound) noexcept @@ -84,15 +90,19 @@ struct ResourceHolder pointer get() const noexcept { return m_Holder.get(); } pointer release() noexcept { return m_Holder.release(); } + void reset(resource_type p) { m_Holder.reset(p); } Me& operator = (resource_type t) { - m_Holder.reset(t); + reset(t); return *this; } + explicit operator bool() const noexcept { return static_cast(m_Holder); } + /* implicit */ operator resource_type() const noexcept { return get(); } }; } // end of namespace cxx + #endif /* SAKURA_RESOURCEHOLDER_0BDC8DF8_F2AC_486F_8BF7_FD7E5D1B76DE_H_ */ diff --git a/src/test/cpp/tests1/test-winmain.cpp b/src/test/cpp/tests1/test-winmain.cpp index 7c34891532..7cba0c1ea7 100644 --- a/src/test/cpp/tests1/test-winmain.cpp +++ b/src/test/cpp/tests1/test-winmain.cpp @@ -108,7 +108,7 @@ void writeTextFile( } //! HANDLE型のスマートポインタ -class HandleHolder final : public cxx::ResourceHolder<&::CloseHandle> +class HandleHolder : public cxx::ResourceHolder<&::CloseHandle> { private: using Base = cxx::ResourceHolder<&::CloseHandle>; @@ -120,30 +120,47 @@ class HandleHolder final : public cxx::ResourceHolder<&::CloseHandle> */ using Base::ResourceHolder; - void lock() const noexcept + virtual ~HandleHolder() = default; + + void lock() { Lock(INFINITE); //無限に待つ } - bool try_lock() const noexcept + bool try_lock() { return Lock(0); //ロック取得を試行 } template - bool try_lock_for(const std::chrono::duration& rel_time) const noexcept + bool try_lock_for(const std::chrono::duration& rel_time) { const auto milliseconds = std::chrono::duration_cast(rel_time); return Lock(DWORD(milliseconds.count())); } - bool Lock(DWORD dwTimeout = INFINITE) const noexcept + bool unlock() + { + return Unlock(); + } + + virtual bool Lock(DWORD dwTimeout = INFINITE) { // ロック取得を試行 const auto dwRet = ::WaitForSingleObject(get(), dwTimeout); + if (WAIT_FAILED == dwRet) { + // エラー + return false; + } + return WAIT_OBJECT_0 == dwRet || WAIT_ABANDONED == dwRet; } + + virtual bool Unlock() + { + return true; + } }; } // namespace cxx @@ -302,7 +319,7 @@ void RequestForeignWindowClose(HWND hWnd) } //! 外部プロセスの終了を待つ -void WaitForForeignProcessExit(const cxx::HandleHolder& process) +void WaitForForeignProcessExit(cxx::HandleHolder& process) { // 編集ウインドウが閉じられた後、プロセスが完全に終了するまで待つ if (!process.try_lock_for(std::chrono::milliseconds(45000))) { @@ -346,7 +363,7 @@ void TerminateControlProcess( // プロセス情報の問い合せを行うためのハンドルを開く // タイムアウト時に強制終了へフォールバックできるよう、TERMINATE 権限も付与する - cxx::HandleHolder process = ::OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE | PROCESS_TERMINATE, FALSE, dwControlProcessId); + cxx::HandleHolder process{ ::OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE | PROCESS_TERMINATE, FALSE, dwControlProcessId) }; if (!process) { // プロセスIDが無効は「既に終了している」なので、除外する if (ERROR_INVALID_PARAMETER == ::GetLastError()) { @@ -852,7 +869,8 @@ TEST_F(WinMainFuncTest, DoGrep001) }; // エディタープロセスを起動する - const auto ep = testing::CreateEditorProcess(args, profileName); + auto ep = testing::CreateEditorProcess(args, profileName); + EXPECT_THAT(ep, NotNull()); // Grepダイアログが表示されるのを待って閉じる for (const auto startTick = ::GetTickCount64(); ::GetTickCount64() - startTick < 5000;) { @@ -888,7 +906,8 @@ TEST_F(WinMainFuncTest, OpenDebugWindow001) const auto dwControlProcessId = testing::CreateControlProcess(profileName); // エディタープロセスを起動する - const auto ep = testing::CreateEditorProcess(std::array{ LR"(-DEBUGMODE)" }, profileName); + auto ep = testing::CreateEditorProcess(std::array{ LR"(-DEBUGMODE)" }, profileName); + EXPECT_THAT(ep, NotNull()); // 編集ウインドウが有効になるのを待って閉じる const auto hWndFound = WaitForEditor(); @@ -917,7 +936,8 @@ TEST_F(WinMainFuncTest, ShowDlgGrep101) const auto dwControlProcessId = testing::CreateControlProcess(profileName); // エディタープロセスを起動する - const auto ep = testing::CreateEditorProcess(std::array{ LR"(-GREPDLG)", LR"(-GREPMODE)" }, profileName); + auto ep = testing::CreateEditorProcess(std::array{ LR"(-GREPDLG)", LR"(-GREPMODE)" }, profileName); + EXPECT_THAT(ep, NotNull()); // Grepダイアログが表示されるのを待って閉じる const auto hWndDlgGrep = WaitForDialog(L"Grep"); @@ -958,7 +978,8 @@ TEST_F(WinMainFuncTest, ShowDlgProfileMgr101) const auto profileName{ GetProfileName() }; // エディタープロセスを起動する - const auto ep = testing::CreateEditorProcess(std::array{ LR"(-PROFMGR)" }, profileName); + auto ep = testing::CreateEditorProcess(std::array{ LR"(-PROFMGR)" }, profileName); + EXPECT_THAT(ep, NotNull()); // プロファイルマネージャが表示されるのを待って閉じる const auto hWndDlgProfileMgr = WaitForDialog(L"プロファイルマネージャ"); From eb57fffd165b0952e4b3a7ff0bb4bbf9613c56ea Mon Sep 17 00:00:00 2001 From: berryzplus Date: Fri, 17 Jul 2026 23:11:24 +0900 Subject: [PATCH 04/19] =?UTF-8?q?WinMainFuncTest=E3=81=A7=E3=82=82StartWin?= =?UTF-8?q?dowCloser=E3=82=92=E4=BD=BF=E3=81=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 表示されたダイアログのクローズに実績のあるやり方を使う。 --- src/test/cpp/tests1/test-winmain.cpp | 29 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/src/test/cpp/tests1/test-winmain.cpp b/src/test/cpp/tests1/test-winmain.cpp index 7cba0c1ea7..3cfcbb7f57 100644 --- a/src/test/cpp/tests1/test-winmain.cpp +++ b/src/test/cpp/tests1/test-winmain.cpp @@ -935,24 +935,18 @@ TEST_F(WinMainFuncTest, ShowDlgGrep101) // コントロールプロセスを起動する const auto dwControlProcessId = testing::CreateControlProcess(profileName); + // 表示されたGrepダイアログを閉じるためのスレッドを起動する + std::jthread t = StartWindowCloser(L"Grep", [this] (HWND hWndDlg) { + EmulateInvokeButton(hWndDlg, L"キャンセル(X)"); + }); + // エディタープロセスを起動する auto ep = testing::CreateEditorProcess(std::array{ LR"(-GREPDLG)", LR"(-GREPMODE)" }, profileName); EXPECT_THAT(ep, NotNull()); - // Grepダイアログが表示されるのを待って閉じる - const auto hWndDlgGrep = WaitForDialog(L"Grep"); - EmulateInvokeButton(hWndDlgGrep, L"キャンセル(X)"); - - bool dlgClosed = false; - for (const auto startTick = ::GetTickCount64(); ::GetTickCount64() - startTick < 5000;) { - if (const auto hWndFound = ::FindWindowW(MAKEINTRESOURCEW(dialog::ModalDialogCloser::DIALOG_CLASS), L"Grep"); !hWndFound) { - dlgClosed = true; - break; - } - Sleep(10); // 10msスリープしてリトライ - } - EXPECT_TRUE(dlgClosed) << "Grep dialog should be closed."; + // Grepダイアログが表示されるのを待って閉じる + t.join(); // 編集ウインドウを閉じる const auto hWndFound = WaitForEditor(); @@ -977,13 +971,18 @@ TEST_F(WinMainFuncTest, ShowDlgProfileMgr101) // テスト用プロファイル名 const auto profileName{ GetProfileName() }; + // 表示されたプロファイルマネージャを閉じるためのスレッドを起動する + std::jthread t = StartWindowCloser(L"プロファイルマネージャ", [&] (HWND hWndDlg) { + // プロファイルマネージャを閉じる + EmulateInvokeButton(hWndDlg, L"閉じる(X)"); + }); + // エディタープロセスを起動する auto ep = testing::CreateEditorProcess(std::array{ LR"(-PROFMGR)" }, profileName); EXPECT_THAT(ep, NotNull()); // プロファイルマネージャが表示されるのを待って閉じる - const auto hWndDlgProfileMgr = WaitForDialog(L"プロファイルマネージャ"); - EmulateInvokeButton(hWndDlgProfileMgr, L"閉じる(X)"); + t.join(); // 編集ウインドウが閉じられた後、プロセスが完全に終了するまで待つ testing::WaitForForeignProcessExit(ep); From 6b734ebfe3998e1e1ea3325ac1a9bc1ea9a4eae2 Mon Sep 17 00:00:00 2001 From: berryzplus Date: Fri, 17 Jul 2026 23:11:42 +0900 Subject: [PATCH 05/19] =?UTF-8?q?=E3=83=86=E3=82=B9=E3=83=88=E3=81=ABcxx::?= =?UTF-8?q?ProcessHolder=E3=82=92=E5=B0=8E=E5=85=A5=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/cpp/tests1/test-winmain.cpp | 170 +++++++++++++++++---------- 1 file changed, 111 insertions(+), 59 deletions(-) diff --git a/src/test/cpp/tests1/test-winmain.cpp b/src/test/cpp/tests1/test-winmain.cpp index 3cfcbb7f57..9f7df8b82a 100644 --- a/src/test/cpp/tests1/test-winmain.cpp +++ b/src/test/cpp/tests1/test-winmain.cpp @@ -163,43 +163,107 @@ class HandleHolder : public cxx::ResourceHolder<&::CloseHandle> } }; +/*! + * @brief 起動したプロセスオブジェクト + * + * @note 使い物になるかどうか試作してみた + */ +class ProcessHolder : public cxx::HandleHolder +{ +private: + using Base = cxx::HandleHolder; + using Me = ProcessHolder; + +public: + explicit ProcessHolder( + HANDLE hProcess, + DWORD dwProcessId, + DWORD dwThreadId + ) + : Base(hProcess) + , dwProcessId(dwProcessId) + , dwThreadId(dwThreadId) + { + } + + ProcessHolder(const Me&) = delete; + Me& operator=(const Me&) = delete; + + ProcessHolder(Me&& other) noexcept = default; + Me& operator=(Me&& rhs) noexcept = default; + + DWORD dwProcessId; + DWORD dwThreadId; +}; + } // namespace cxx namespace testing { +/*! + * @brief コマンドライン引数を引用符で囲む + * + * @param arg [in] コマンドライン引数 + * @return 引用符で囲まれたコマンドライン引数 + */ +std::wstring QuoteArg(const std::wstring_view arg) +{ + const auto endsWithQuote = arg.ends_with(LR"(")"); + const auto containsQuotes = std::wstring_view::npos != arg.find_first_of(LR"(")"); + if (const auto needsEscape = std::wstring_view::npos != arg.find_first_of(L"\t "); + !endsWithQuote && containsQuotes) + { + return std::format(LR"("{:s}")", std::regex_replace(arg.data(), std::wregex(LR"(")"), LR"("")")); + } + else if (!endsWithQuote && needsEscape) + { + return std::format(LR"("{:s}")", arg); + } + else + { + return std::wstring(arg); + } +} + /*! * @brief サクラエディタのプロセスを起動する * - * @tparam T コマンドライン引数のコンテナ型 * @param si スタートアップ情報 * @param args コマンドライン引数 * @param optWorkingDir カレントディレクトリ(省略した場合は起動元と同じ) * @param optProfileName プロファイル名(省略した場合は指定なし) - * @return 起動したプロセスのハンドルオブジェクト + * @return 起動したプロセスオブジェクト * @note 使い物になるかどうか試作してみた */ -template - requires std::ranges::range && std::convertible_to, std::wstring_view> -cxx::HandleHolder _CreateSakuraProcess( +cxx::ProcessHolder CreateSakuraProcess( STARTUPINFO& si, - const T& args, + std::vector& args, const std::optional& optWorkingDir = std::nullopt, - const std::optional& optProfileName = std::nullopt + const std::optional& optProfileName = std::nullopt, + DWORD dwCreationFlag = CREATE_DEFAULT_ERROR_MODE ) { const auto exePath = GetExeFileName(); - auto strCommandLine = std::format(LR"("{}")", exePath.native()); - strCommandLine = std::accumulate(std::begin(args), std::end(args), strCommandLine, [](const std::wstring& a, std::wstring_view b) { return std::format(LR"({} {})", a, b); }); if (optProfileName.has_value()) { - strCommandLine += std::format(LR"( -PROF="{}")", *optProfileName); + args.emplace(args.begin(), std::format(LR"(-PROF="{}")", *optProfileName)); } - auto lpszCommandLine = std::data(strCommandLine); + args.emplace(args.begin(), std::format(LR"("{:s}")", exePath.native())); - DWORD dwCreationFlag = CREATE_DEFAULT_ERROR_MODE; + auto strCommandLine = std::accumulate(args.begin(), args.end(), std::wstring(), [](const std::wstring& a, std::wstring_view b) { return std::format(LR"({} {})", a, QuoteArg(b)); }); + strCommandLine.erase(strCommandLine.cbegin()); // 先頭のスペースを削除 - LPCWSTR lpszWorkingDir = optWorkingDir.has_value() ? optWorkingDir.value().c_str() : nullptr; + auto lpszCommandLine = std::data(strCommandLine); + + LPCWSTR lpszWorkingDir = nullptr; + if (optWorkingDir.has_value()) { + if (const auto attr = ::GetFileAttributesW(optWorkingDir->c_str()); + INVALID_FILE_ATTRIBUTES != attr && (attr & FILE_ATTRIBUTE_DIRECTORY)) + { + lpszWorkingDir = optWorkingDir->c_str(); + } + } // プロセス情報(出力用構造体なので値は入れない) PROCESS_INFORMATION pi{}; @@ -224,17 +288,17 @@ cxx::HandleHolder _CreateSakuraProcess( // 開いたハンドルは使わないので閉じておく ::CloseHandle(pi.hThread); - return cxx::HandleHolder(pi.hProcess); + return cxx::ProcessHolder{ pi.hProcess, pi.dwProcessId, pi.dwThreadId }; } /*! * @brief コントロールプロセスを起動する * * @param profileName プロファイル名 - * @return コントロールプロセスのプロセスID + * @return 起動したプロセスオブジェクト * @note 使い物になるかどうか試作してみた */ -DWORD CreateControlProcess(std::wstring_view profileName) +cxx::ProcessHolder CreateControlProcess(std::wstring_view profileName) { // 初期化完了イベントの名前を決める SFilePath initEventName{ GSTR_EVENT_SAKURA_CP_INITIALIZED }; @@ -242,7 +306,7 @@ DWORD CreateControlProcess(std::wstring_view profileName) // プロセス起動前に初期化完了イベントを作成する cxx::HandleHolder hEvent = ::CreateEventW(nullptr, TRUE, FALSE, initEventName); - if (!hEvent) { + if (!hEvent || ERROR_ALREADY_EXISTS == ::GetLastError()) { cxx::raise_system_error("create event failed."); } @@ -254,16 +318,17 @@ DWORD CreateControlProcess(std::wstring_view profileName) si.lpTitle = std::data(title); si.wShowWindow = SW_SHOWDEFAULT; + std::vector commandArgs{ LR"(-NOWIN)" }; + // コントロールプロセスを起動する - const auto cp = _CreateSakuraProcess(si, std::array{ LR"(-NOWIN)" }, cxx::GetSystemDirectoryW(), profileName); + auto cp = CreateSakuraProcess(si, commandArgs, cxx::GetSystemDirectoryW(), profileName); + EXPECT_THAT(cp, NotNull()); // 初期化完了を待つ - if (!hEvent.try_lock_for(std::chrono::milliseconds(60000))){ - cxx::raise_system_error("waitEvent is timeout."); - } + hEvent.lock(); - // プロセスIDを取得して返す - return ::GetProcessId(cp); + // プロセスオブジェクトを返す + return cxx::ProcessHolder{ cp.release(), cp.dwProcessId, cp.dwThreadId }; } /*! @@ -272,12 +337,12 @@ DWORD CreateControlProcess(std::wstring_view profileName) * @tparam T コマンドライン引数のコンテナ型 * @param args コマンドライン引数 * @param profileName プロファイル名 - * @return 起動したプロセスのハンドルオブジェクト + * @return 起動したプロセスオブジェクト * @note 使い物になるかどうか試作してみた */ template requires std::ranges::range && std::convertible_to, std::wstring_view> -cxx::HandleHolder CreateEditorProcess( +cxx::ProcessHolder CreateEditorProcess( const T& args, std::wstring_view profileName ) @@ -295,7 +360,7 @@ cxx::HandleHolder CreateEditorProcess( si.dwFlags = STARTF_USESHOWWINDOW; si.wShowWindow = SW_SHOWDEFAULT; - return _CreateSakuraProcess(si, commandArgs, std::nullopt, profileName); + return CreateSakuraProcess(si, commandArgs, std::nullopt, profileName); } //! 外部ウインドウにクローズを要求する @@ -318,23 +383,6 @@ void RequestForeignWindowClose(HWND hWnd) } } -//! 外部プロセスの終了を待つ -void WaitForForeignProcessExit(cxx::HandleHolder& process) -{ - // 編集ウインドウが閉じられた後、プロセスが完全に終了するまで待つ - if (!process.try_lock_for(std::chrono::milliseconds(45000))) { - // 終了できないなら強制終了させる - if (const auto exitCode = 1; !::TerminateProcess(process.get(), exitCode)) { - cxx::raise_system_error("waitProcess is timeout and terminate process failed."); - } - - // TerminateProcess は非同期なので操作完了を待つ - if (!process.try_lock_for(std::chrono::milliseconds(5000))) { - cxx::raise_system_error("waitProcess is timeout and force terminate is timeout."); - } - } -} - /*! * @brief コントロールプロセスに終了指示を出して終了を待つ */ @@ -373,7 +421,7 @@ void TerminateControlProcess( } // メインウインドウが閉じられた後、プロセスが完全に終了するまで待つ - WaitForForeignProcessExit(process); + process.lock(); } } // namespace testing @@ -514,10 +562,11 @@ struct TWinMainTest : public T, public window::UiaTestSuite { void CControlProcess_StartAndTerminate(std::wstring_view profileName) const { // コントロールプロセスを起動する - const auto dwControlProcessId = testing::CreateControlProcess(profileName); + auto cp = testing::CreateControlProcess(profileName); + EXPECT_THAT(cp, NotNull()); // コントロールプロセスに終了指示を出して終了を待つ - testing::TerminateControlProcess(profileName, dwControlProcessId); + testing::TerminateControlProcess(profileName, cp.dwProcessId); } }; @@ -673,7 +722,8 @@ TEST_P(WinMainTest, runEditorProcess) } // コントロールプロセスを起動する - const auto dwControlProcessId = testing::CreateControlProcess(profileName); + auto cp = testing::CreateControlProcess(profileName); + EXPECT_THAT(cp, NotNull()); // 起動時実行マクロの中身を作る constexpr std::array macroCommands = { @@ -803,7 +853,7 @@ TEST_P(WinMainTest, runEditorProcess) EXPECT_EXIT({ StartEditorProcess(command); }, ::testing::ExitedWithCode(0), ".*" ); // コントロールプロセスに終了指示を出して終了を待つ - testing::TerminateControlProcess(profileName, dwControlProcessId); + testing::TerminateControlProcess(profileName, cp.dwProcessId); // コントロールプロセスが終了すると、INIファイルが作成される EXPECT_THAT(fexist(iniPath), IsTrue()); @@ -858,7 +908,8 @@ TEST_F(WinMainFuncTest, DoGrep001) cxx::writeTextFile(iniPath, iniLines); // コントロールプロセスを起動する - const auto dwControlProcessId = testing::CreateControlProcess(profileName); + auto cp = testing::CreateControlProcess(profileName); + EXPECT_THAT(cp, NotNull()); std::array args{ LR"(-GREPMODE)"s, @@ -885,10 +936,10 @@ TEST_F(WinMainFuncTest, DoGrep001) testing::RequestForeignWindowClose(hWndFound); // 編集ウインドウが閉じられた後、プロセスが完全に終了するまで待つ - testing::WaitForForeignProcessExit(ep); + ep.lock(); // コントロールプロセスに終了指示を出して終了を待つ - testing::TerminateControlProcess(profileName, dwControlProcessId); + testing::TerminateControlProcess(profileName, cp.dwProcessId); } /*! @@ -903,7 +954,8 @@ TEST_F(WinMainFuncTest, OpenDebugWindow001) const auto profileName{ GetProfileName() }; // コントロールプロセスを起動する - const auto dwControlProcessId = testing::CreateControlProcess(profileName); + auto cp = testing::CreateControlProcess(profileName); + EXPECT_THAT(cp, NotNull()); // エディタープロセスを起動する auto ep = testing::CreateEditorProcess(std::array{ LR"(-DEBUGMODE)" }, profileName); @@ -914,10 +966,10 @@ TEST_F(WinMainFuncTest, OpenDebugWindow001) testing::RequestForeignWindowClose(hWndFound); // 編集ウインドウが閉じられた後、プロセスが完全に終了するまで待つ - testing::WaitForForeignProcessExit(ep); + ep.lock(); // コントロールプロセスに終了指示を出して終了を待つ - testing::TerminateControlProcess(profileName, dwControlProcessId); + testing::TerminateControlProcess(profileName, cp.dwProcessId); }); } @@ -933,7 +985,8 @@ TEST_F(WinMainFuncTest, ShowDlgGrep101) const auto profileName{ GetProfileName() }; // コントロールプロセスを起動する - const auto dwControlProcessId = testing::CreateControlProcess(profileName); + auto cp = testing::CreateControlProcess(profileName); + EXPECT_THAT(cp, NotNull()); // 表示されたGrepダイアログを閉じるためのスレッドを起動する std::jthread t = StartWindowCloser(L"Grep", [this] (HWND hWndDlg) { @@ -944,7 +997,6 @@ TEST_F(WinMainFuncTest, ShowDlgGrep101) auto ep = testing::CreateEditorProcess(std::array{ LR"(-GREPDLG)", LR"(-GREPMODE)" }, profileName); EXPECT_THAT(ep, NotNull()); - // Grepダイアログが表示されるのを待って閉じる t.join(); @@ -953,10 +1005,10 @@ TEST_F(WinMainFuncTest, ShowDlgGrep101) testing::RequestForeignWindowClose(hWndFound); // 編集ウインドウが閉じられた後、プロセスが完全に終了するまで待つ - testing::WaitForForeignProcessExit(ep); + ep.lock(); // コントロールプロセスに終了指示を出して終了を待つ - testing::TerminateControlProcess(profileName, dwControlProcessId); + testing::TerminateControlProcess(profileName, cp.dwProcessId); }); } @@ -985,7 +1037,7 @@ TEST_F(WinMainFuncTest, ShowDlgProfileMgr101) t.join(); // 編集ウインドウが閉じられた後、プロセスが完全に終了するまで待つ - testing::WaitForForeignProcessExit(ep); + ep.lock(); }); } From 8b7b482f096bc0102744d0bdd527fe4a1a96059f Mon Sep 17 00:00:00 2001 From: berryzplus Date: Fri, 17 Jul 2026 23:11:59 +0900 Subject: [PATCH 06/19] =?UTF-8?q?=E3=82=B3=E3=83=B3=E3=83=88=E3=83=AD?= =?UTF-8?q?=E3=83=BC=E3=83=AB=E3=83=97=E3=83=AD=E3=82=BB=E3=82=B9=E5=88=9D?= =?UTF-8?q?=E6=9C=9F=E5=8C=96=E3=81=AE=E5=AE=8C=E4=BA=86=E5=BE=85=E6=A9=9F?= =?UTF-8?q?=E3=82=92=E6=94=B9=E5=96=84=E3=81=99=E3=82=8B(1/2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sakura_core/_main/CControlProcess.cpp | 25 +++-------- sakura_core/_main/CControlProcess.h | 4 +- sakura_core/_main/CProcessFactory.cpp | 65 +++++++++++---------------- sakura_core/_main/CProcessFactory.h | 4 +- src/test/cpp/tests1/test-winmain.cpp | 3 +- 5 files changed, 40 insertions(+), 61 deletions(-) diff --git a/sakura_core/_main/CControlProcess.cpp b/sakura_core/_main/CControlProcess.cpp index e99eac807e..e0adc0a474 100644 --- a/sakura_core/_main/CControlProcess.cpp +++ b/sakura_core/_main/CControlProcess.cpp @@ -9,7 +9,7 @@ Copyright (C) 2002, aroka CProcessより分離, YAZAKI Copyright (C) 2006, ryoji Copyright (C) 2007, ryoji - Copyright (C) 2018-2022, Sakura Editor Organization + Copyright (C) 2018-2026, Sakura Editor Organization This source code is designed for sakura editor. Please contact the copyright holder to use this code for other purpose. @@ -130,16 +130,13 @@ bool CControlProcess::InitializeProcess() const auto pszProfileName = GetProfileName(); - // 初期化完了イベントを作成する + // 初期化完了イベントの名前を組み立てる std::wstring strInitEvent = GSTR_EVENT_SAKURA_CP_INITIALIZED; strInitEvent += pszProfileName; - m_hEventCPInitialized = ::CreateEvent( nullptr, TRUE, FALSE, strInitEvent.c_str() ); - if( nullptr == m_hEventCPInitialized ) - { - ErrorBeep(); - TopErrorMessage( nullptr, L"CreateEvent()失敗。\n終了します。" ); - return false; - } + + // 起動元が作成した初期化完了イベントを開く。 + using HandleHolder = cxx::ResourceHolder<&::CloseHandle>; + HandleHolder hEvent{ ::OpenEventW(EVENT_MODIFY_STATE, FALSE, std::data(strInitEvent)) }; /* コントロールプロセスの目印 */ std::wstring strCtrlProcEvent = GSTR_MUTEX_SAKURA_CP; @@ -194,11 +191,7 @@ bool CControlProcess::InitializeProcess() GetDllShareData().m_sHandles.m_hwndTray = hwnd; // 初期化完了イベントをシグナル状態にする - if( !::SetEvent( m_hEventCPInitialized ) ){ - ErrorBeep(); - TopErrorMessage( nullptr, LS(STR_ERR_CTRLMTX4) ); - return false; - } + if (hEvent) ::SetEvent(hEvent); return true; } @@ -234,10 +227,6 @@ CControlProcess::~CControlProcess() { delete m_pcTray; - if( m_hEventCPInitialized ){ - ::ResetEvent( m_hEventCPInitialized ); - } - ::CloseHandle( m_hEventCPInitialized ); if( m_hMutexCP ){ ::ReleaseMutex( m_hMutexCP ); } diff --git a/sakura_core/_main/CControlProcess.h b/sakura_core/_main/CControlProcess.h index 9579314ed0..65feb27172 100644 --- a/sakura_core/_main/CControlProcess.h +++ b/sakura_core/_main/CControlProcess.h @@ -7,7 +7,7 @@ /* Copyright (C) 2002, aroka 新規作成, YAZAKI Copyright (C) 2006, ryoji - Copyright (C) 2018-2022, Sakura Editor Organization + Copyright (C) 2018-2026, Sakura Editor Organization This source code is designed for sakura editor. Please contact the copyright holder to use this code for other purpose. @@ -55,7 +55,7 @@ class CControlProcess final : public CProcess { HANDLE m_hMutex = nullptr; //!< アプリケーション実行検出用ミューテックス HANDLE m_hMutexCP = nullptr; //!< コントロールプロセスミューテックス - HANDLE m_hEventCPInitialized = nullptr; //!< コントロールプロセス初期化完了イベント 2006.04.10 ryoji CControlTray* m_pcTray = nullptr; }; + #endif /* SAKURA_CCONTROLPROCESS_AFB90808_4287_4A11_B7FB_9CD21CF8BFD6_H_ */ diff --git a/sakura_core/_main/CProcessFactory.cpp b/sakura_core/_main/CProcessFactory.cpp index a469574a8f..e1f35e98a9 100644 --- a/sakura_core/_main/CProcessFactory.cpp +++ b/sakura_core/_main/CProcessFactory.cpp @@ -10,7 +10,7 @@ Copyright (C) 2001, masami shoji Copyright (C) 2002, aroka WinMainより分離 Copyright (C) 2006, ryoji - Copyright (C) 2018-2022, Sakura Editor Organization + Copyright (C) 2018-2026, Sakura Editor Organization This source code is designed for sakura editor. Please contact the copyright holder to use this code for other purpose. @@ -76,9 +76,9 @@ CProcess* CProcessFactory::Create( HINSTANCE hInstance, LPCWSTR lpCmdLine ) if( !IsExistControlProcess() ){ StartControlProcess(); } - if( WaitForInitializedControlProcess() ){ // 2006.04.10 ryoji コントロールプロセスの初期化完了待ち + process = new CNormalProcess( hInstance, lpCmdLine ); - } + } return process; } @@ -155,7 +155,6 @@ bool CProcessFactory::IsExistControlProcess() return false; // コントロールプロセスは存在していないか、まだ CreateMutex() してない } -// From Here Aug. 28, 2001 genta /*! @brief コントロールプロセスを起動する @@ -170,6 +169,22 @@ bool CProcessFactory::StartControlProcess() { MY_RUNNINGTIMER(cRunningTimer,L"StartControlProcess" ); + using HandleHolder = cxx::ResourceHolder<&::CloseHandle>; + + const auto pszProfileName = GetProfileName(); + + // 初期化完了イベントの名前を組み立てる + SFilePath initEventName{ GSTR_EVENT_SAKURA_CP_INITIALIZED }; + initEventName.append(pszProfileName); + + // 初期化完了イベントを作成する + HandleHolder hEvent{ ::CreateEventW(nullptr, TRUE, FALSE, initEventName) }; + if (!hEvent || ERROR_ALREADY_EXISTS == ::GetLastError()) { + // L"エディタまたはシステムがビジー状態です。\nしばらく待って開きなおしてください。 + TopErrorMessage(nullptr, LS(STR_ERR_DLGPROCFACT5)); + return false; + } + // プロセスの起動 PROCESS_INFORMATION p; STARTUPINFO s; @@ -229,42 +244,16 @@ bool CProcessFactory::StartControlProcess() return false; } - ::CloseHandle( p.hThread ); - ::CloseHandle( p.hProcess ); - - return true; -} -// To Here Aug. 28, 2001 genta - -/*! - @brief コントロールプロセスの初期化完了イベントを待つ。 + HandleHolder hProcess{ p.hProcess }; + HandleHolder hThread{ p.hThread }; - @author ryoji by assitance with karoto - @date 2006/04/10 -*/ -bool CProcessFactory::WaitForInitializedControlProcess() -{ - // 初期化完了イベントを待つ - // - // Note: コントロールプロセス側は多重起動防止用ミューテックスを ::CreateMutex() で - // 作成するよりも先に初期化完了イベントを ::CreateEvent() で作成する。 - // - const auto pszProfileName = GetProfileName(); - std::wstring strInitEvent = GSTR_EVENT_SAKURA_CP_INITIALIZED; - strInitEvent += pszProfileName; - HANDLE hEvent; - hEvent = ::CreateEventW( nullptr, TRUE, FALSE, strInitEvent.c_str() ); - if( nullptr == hEvent ){ - TopErrorMessage( nullptr, L"エディタまたはシステムがビジー状態です。\nしばらく待って開きなおしてください。" ); + // 初期化完了を待つ + std::array handles{ hEvent.get(), hProcess.get()}; + if (const auto dwRet = ::WaitForMultipleObjects(DWORD(std::size(handles)), std::data(handles), FALSE, 15000); WAIT_OBJECT_0 != dwRet) { + // L"エディタまたはシステムがビジー状態です。\nしばらく待って開きなおしてください。 + TopErrorMessage(nullptr, LS(STR_ERR_DLGPROCFACT5)); return false; } - DWORD dwRet; - dwRet = ::WaitForSingleObject( hEvent, 30000 ); - if( WAIT_TIMEOUT == dwRet ){ // コントロールプロセスの初期化が終了しない - ::CloseHandle( hEvent ); - TopErrorMessage( nullptr, L"エディタまたはシステムがビジー状態です。\nしばらく待って開きなおしてください。" ); - return false; - } - ::CloseHandle( hEvent ); + return true; } diff --git a/sakura_core/_main/CProcessFactory.h b/sakura_core/_main/CProcessFactory.h index aabb409955..0f7a544e93 100644 --- a/sakura_core/_main/CProcessFactory.h +++ b/sakura_core/_main/CProcessFactory.h @@ -7,7 +7,7 @@ /* Copyright (C) 2002, aroka 新規作成 Copyright (C) 2006, ryoji - Copyright (C) 2018-2022, Sakura Editor Organization + Copyright (C) 2018-2026, Sakura Editor Organization This source code is designed for sakura editor. Please contact the copyright holder to use this code for other purpose. @@ -43,6 +43,6 @@ class CProcessFactory { bool IsStartingControlProcess(); bool IsExistControlProcess(); bool StartControlProcess(); - bool WaitForInitializedControlProcess(); // 2006.04.10 ryoji コントロールプロセスの初期化完了イベントを待つ }; + #endif /* SAKURA_CPROCESSFACTORY_5006562F_7795_40FF_AA4C_FFB94842F7C5_H_ */ diff --git a/src/test/cpp/tests1/test-winmain.cpp b/src/test/cpp/tests1/test-winmain.cpp index 9f7df8b82a..576c2683d1 100644 --- a/src/test/cpp/tests1/test-winmain.cpp +++ b/src/test/cpp/tests1/test-winmain.cpp @@ -325,7 +325,8 @@ cxx::ProcessHolder CreateControlProcess(std::wstring_view profileName) EXPECT_THAT(cp, NotNull()); // 初期化完了を待つ - hEvent.lock(); + std::array handles{ hEvent.get(), cp.get() }; + ::WaitForMultipleObjects(DWORD(std::size(handles)), std::data(handles), FALSE, 15000); // プロセスオブジェクトを返す return cxx::ProcessHolder{ cp.release(), cp.dwProcessId, cp.dwThreadId }; From d0eb08cf8a222be5f9dd6679093d2357ae099c9f Mon Sep 17 00:00:00 2001 From: berryzplus Date: Fri, 17 Jul 2026 23:12:15 +0900 Subject: [PATCH 07/19] =?UTF-8?q?=E3=82=B3=E3=83=B3=E3=83=88=E3=83=AD?= =?UTF-8?q?=E3=83=BC=E3=83=AB=E3=83=97=E3=83=AD=E3=82=BB=E3=82=B9=E5=88=9D?= =?UTF-8?q?=E6=9C=9F=E5=8C=96=E3=81=AE=E5=AE=8C=E4=BA=86=E5=BE=85=E6=A9=9F?= =?UTF-8?q?=E3=82=92=E6=94=B9=E5=96=84=E3=81=99=E3=82=8B(2/2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CProcessFactoryの固有機能ではないため、いずれ他クラスに移動する。 実際の移動まで行うと差分が大きくなりすぎるので、一旦static化のみに留める。 --- sakura_core/_main/CNormalProcess.cpp | 5 +- sakura_core/_main/CProcessFactory.cpp | 11 ++-- sakura_core/_main/CProcessFactory.h | 5 +- src/test/cpp/tests1/test-winmain.cpp | 73 +++++++++++++++++++++++++++ 4 files changed, 84 insertions(+), 10 deletions(-) diff --git a/sakura_core/_main/CNormalProcess.cpp b/sakura_core/_main/CNormalProcess.cpp index a2287972cf..782499d5b9 100644 --- a/sakura_core/_main/CNormalProcess.cpp +++ b/sakura_core/_main/CNormalProcess.cpp @@ -22,6 +22,9 @@ #include "StdAfx.h" #include "CNormalProcess.h" + +#include "_main/CProcessFactory.h" + #include "CCommandLine.h" #include "CControlTray.h" #include "window/CEditWnd.h" // 2002/2/3 aroka @@ -82,7 +85,7 @@ bool CNormalProcess::InitializeProcess() } /* 共有メモリを初期化する */ - if ( !CProcess::InitializeProcess() ){ + if (!CProcessFactory::IsExistControlProcess() && !CProcessFactory::StartControlProcess() || !CProcess::InitializeProcess()) { return false; } diff --git a/sakura_core/_main/CProcessFactory.cpp b/sakura_core/_main/CProcessFactory.cpp index e1f35e98a9..0c600d9385 100644 --- a/sakura_core/_main/CProcessFactory.cpp +++ b/sakura_core/_main/CProcessFactory.cpp @@ -68,14 +68,11 @@ CProcess* CProcessFactory::Create( HINSTANCE hInstance, LPCWSTR lpCmdLine ) // しかし、そのような場合でもミューテックスを最初に確保したコントロールプロセスが唯一生き残る。 // if( IsStartingControlProcess() ){ - if( !IsExistControlProcess() ){ + process = new CControlProcess( hInstance, lpCmdLine ); - } + } else{ - if( !IsExistControlProcess() ){ - StartControlProcess(); - } process = new CNormalProcess( hInstance, lpCmdLine ); @@ -140,7 +137,7 @@ bool CProcessFactory::IsStartingControlProcess() @date 2002/01/03 @date 2006/04/10 ryoji */ -bool CProcessFactory::IsExistControlProcess() +/* static */ bool CProcessFactory::IsExistControlProcess() { const auto pszProfileName = GetProfileName(); std::wstring strMutexSakuraCp = GSTR_MUTEX_SAKURA_CP; @@ -165,7 +162,7 @@ bool CProcessFactory::IsExistControlProcess() @date Aug. 28, 2001 @date 2008.05.05 novice GetModuleHandle(NULL)→NULLに変更 */ -bool CProcessFactory::StartControlProcess() +/* static */ bool CProcessFactory::StartControlProcess() { MY_RUNNINGTIMER(cRunningTimer,L"StartControlProcess" ); diff --git a/sakura_core/_main/CProcessFactory.h b/sakura_core/_main/CProcessFactory.h index 0f7a544e93..68f96a5464 100644 --- a/sakura_core/_main/CProcessFactory.h +++ b/sakura_core/_main/CProcessFactory.h @@ -35,14 +35,15 @@ class CProcess; */ class CProcessFactory { public: + static bool IsExistControlProcess(); + static bool StartControlProcess(); + CProcess* Create( HINSTANCE hInstance, LPCWSTR lpCmdLine ); protected: private: bool IsValidVersion(); bool ProfileSelect(HINSTANCE hInstance, LPCWSTR lpCmdLine); bool IsStartingControlProcess(); - bool IsExistControlProcess(); - bool StartControlProcess(); }; #endif /* SAKURA_CPROCESSFACTORY_5006562F_7795_40FF_AA4C_FFB94842F7C5_H_ */ diff --git a/src/test/cpp/tests1/test-winmain.cpp b/src/test/cpp/tests1/test-winmain.cpp index 576c2683d1..98bca3915b 100644 --- a/src/test/cpp/tests1/test-winmain.cpp +++ b/src/test/cpp/tests1/test-winmain.cpp @@ -885,6 +885,79 @@ struct WinMainFuncTest : public TWinMainTest<::testing::Test> { } }; +/*! + * @brief WinMainを起動してみるテスト + * プログラムが起動する正常ルートに潜む障害を検出するためのもの。 + * コントロールプロセス起動の失敗をテストする。(ミューテックス競合) + */ +TEST_F(WinMainFuncTest, CreateControlProcess101) +{ + // テスト用プロファイル名 + const auto profileName{ GetProfileName() }; + + // ミューテックスの名前を組み立てる + SFilePath szMutexName{ GSTR_MUTEX_SAKURA_CP }; + szMutexName += profileName; + + // ミューテックスを作成してロックする + cxx::HandleHolder hMutex{ ::CreateMutexW(nullptr, TRUE, szMutexName) }; + EXPECT_THAT(hMutex, NotNull()); + + // コントロールプロセスを起動する + EXPECT_EXIT({ StartEditorProcess(std::format(LR"(-NOWIN -PROF="{:s}")", profileName)); }, ::testing::ExitedWithCode(0), ".*"); // たぶんバグです。エラー終了なのに0を返してる。 +} + +/*! + * @brief WinMainを起動してみるテスト + * プログラムが起動する正常ルートに潜む障害を検出するためのもの。 + * コントロールプロセス起動の失敗をテストする。(異なるバージョン) + */ +TEST_F(WinMainFuncTest, CreateControlProcess102) +{ + // テスト用プロファイル名 + const auto profileName{ GetProfileName() }; + + // 共有データの名前を組み立てる + SFilePath shareDataName{ GSTR_SHAREDATA }; + shareDataName.append(profileName); + + // ファイルマッピングオブジェクトを作る + const auto hFileMap = ::CreateFileMappingW( + INVALID_HANDLE_VALUE, + nullptr, + PAGE_READWRITE | SEC_COMMIT, + 0, + sizeof(DLLSHAREDATA), + shareDataName + ); + + EXPECT_THAT(hFileMap, NotNull()); + + // スマートポインターに入れる + cxx::HandleHolder fileMapHolder{ hFileMap }; + + // ファイルマッピングオブジェクトをマップする + using MappedDataHolder = cxx::ResourceHolder<&::UnmapViewOfFile, DLLSHAREDATA*>; + MappedDataHolder mappedData = (DLLSHAREDATA*)::MapViewOfFile( + hFileMap, + FILE_MAP_ALL_ACCESS, + 0, + 0, + 0 + ); + + EXPECT_THAT(mappedData, NotNull()); + + auto pShareData = static_cast(mappedData); + + EXPECT_THAT(pShareData, NotNull()); + + pShareData->m_nSize = sizeof(DLLSHAREDATA) + 1; + + // コントロールプロセスを起動する + EXPECT_EXIT({ StartEditorProcess(std::format(LR"(-NOWIN -PROF="{:s}")", profileName)); }, ::testing::ExitedWithCode(0), ".*"); // たぶんバグです。エラー終了なのに0を返してる。 +} + /*! * @brief WinMainを起動してみるテスト * プログラムが起動する正常ルートに潜む障害を検出するためのもの。 From ae2c15801b76fbcd5c6164435d3a70b408106e57 Mon Sep 17 00:00:00 2001 From: berryzplus Date: Fri, 17 Jul 2026 23:12:32 +0900 Subject: [PATCH 08/19] =?UTF-8?q?=E3=82=A8=E3=83=87=E3=82=A3=E3=82=BF?= =?UTF-8?q?=EF=BC=8D=E5=88=9D=E6=9C=9F=E5=8C=96=E5=AE=8C=E4=BA=86=E3=82=A4?= =?UTF-8?q?=E3=83=99=E3=83=B3=E3=83=88=E3=82=92=E5=B0=8E=E5=85=A5=E3=81=99?= =?UTF-8?q?=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sakura_core/_main/CNormalProcess.cpp | 11 ++++ sakura_core/config/system_constants.h | 5 +- src/test/cpp/tests1/test-winmain.cpp | 80 ++++++++++++++++++++++----- 3 files changed, 82 insertions(+), 14 deletions(-) diff --git a/sakura_core/_main/CNormalProcess.cpp b/sakura_core/_main/CNormalProcess.cpp index 782499d5b9..7145ad7de0 100644 --- a/sakura_core/_main/CNormalProcess.cpp +++ b/sakura_core/_main/CNormalProcess.cpp @@ -84,6 +84,15 @@ bool CNormalProcess::InitializeProcess() return false; } + // エディター初期化完了イベントを開く + SFilePath initEventName{ std::format(GSTR_EVENT_SAKURA_EP_INITIALIZED, ::GetCurrentThreadId()) }; + using HandleHolder = cxx::ResourceHolder<&::CloseHandle>; + HandleHolder hEvent{ ::OpenEventW(STANDARD_RIGHTS_REQUIRED | EVENT_MODIFY_STATE | SYNCHRONIZE, FALSE, initEventName) }; + + // スコープを抜けるときシグナル状態になるようにする + using InitEventHolder = cxx::ResourceHolder<&::SetEvent>; + InitEventHolder initEvent{ hEvent.get() }; + /* 共有メモリを初期化する */ if (!CProcessFactory::IsExistControlProcess() && !CProcessFactory::StartControlProcess() || !CProcess::InitializeProcess()) { return false; @@ -438,6 +447,8 @@ bool CNormalProcess::InitializeProcess() // 複数ファイル読み込み OpenFiles( pEditWnd->GetHwnd() ); + initEvent = nullptr; + return pEditWnd->GetHwnd() ? true : false; } diff --git a/sakura_core/config/system_constants.h b/sakura_core/config/system_constants.h index ec3292a897..c88a8b3647 100644 --- a/sakura_core/config/system_constants.h +++ b/sakura_core/config/system_constants.h @@ -11,7 +11,7 @@ */ /* Copyright (C) 2008, kobake - Copyright (C) 2018-2022, Sakura Editor Organization + Copyright (C) 2018-2026, Sakura Editor Organization SPDX-License-Identifier: Zlib */ @@ -585,6 +585,9 @@ //! 初期化完了イベント #define GSTR_EVENT_SAKURA_CP_INITIALIZED (L"EventSakuraEditorCPInitialized" _T(CON_SKR_MACHINE_SUFFIX_) _T(_CODE_SUFFIX_) _T(_DEBUG_SUFFIX_) _T(STR_SHAREDATA_VERSION)) +//! 初期化完了イベント +inline constexpr std::wstring_view GSTR_EVENT_SAKURA_EP_INITIALIZED = L"EventSakuraEditorEPInitialized_{:d}"; + // -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- // // ウィンドウクラス // // -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- // diff --git a/src/test/cpp/tests1/test-winmain.cpp b/src/test/cpp/tests1/test-winmain.cpp index 98bca3915b..03589aa7a3 100644 --- a/src/test/cpp/tests1/test-winmain.cpp +++ b/src/test/cpp/tests1/test-winmain.cpp @@ -332,6 +332,39 @@ cxx::ProcessHolder CreateControlProcess(std::wstring_view profileName) return cxx::ProcessHolder{ cp.release(), cp.dwProcessId, cp.dwThreadId }; } +/*! + * @brief プロセスを起動する + * + * @tparam T コマンドライン引数のコンテナ型 + * @param args コマンドライン引数 + * @param profileName プロファイル名 + * @return 起動したプロセスオブジェクト + * @note 使い物になるかどうか試作してみた + */ +template + requires std::ranges::range && std::convertible_to, std::wstring_view> +cxx::ProcessHolder CreateSakuraProcess( + const T& args, + std::wstring_view profileName, + DWORD dwCreationFlag = CREATE_DEFAULT_ERROR_MODE +) +{ + // コマンドライン引数の編集用vector + std::vector commandArgs{ std::begin(args), std::end(args) }; + + // スタートアップ情報(入力用構造体なので値を入れる) + STARTUPINFO si = { sizeof(STARTUPINFO) }; + si.dwFlags = STARTF_USESHOWWINDOW; + si.wShowWindow = SW_SHOWDEFAULT; + + // エディタープロセスを起動する + auto ep = CreateSakuraProcess(si, commandArgs, std::nullopt, profileName, dwCreationFlag); + EXPECT_THAT(ep, NotNull()); + + // プロセスオブジェクトを返す + return cxx::ProcessHolder{ ep.release(), ep.dwProcessId, ep.dwThreadId }; +} + /*! * @brief エディタープロセスを起動する * @@ -345,7 +378,8 @@ template requires std::ranges::range && std::convertible_to, std::wstring_view> cxx::ProcessHolder CreateEditorProcess( const T& args, - std::wstring_view profileName + std::wstring_view profileName, + bool sync = true ) { // コマンドライン引数の編集用vector @@ -361,7 +395,33 @@ cxx::ProcessHolder CreateEditorProcess( si.dwFlags = STARTF_USESHOWWINDOW; si.wShowWindow = SW_SHOWDEFAULT; - return CreateSakuraProcess(si, commandArgs, std::nullopt, profileName); + DWORD dwCreationFlag = CREATE_DEFAULT_ERROR_MODE; + if (sync) dwCreationFlag |= CREATE_SUSPENDED; + + // エディタープロセスを起動する + auto ep = CreateSakuraProcess(si, commandArgs, std::nullopt, profileName, dwCreationFlag); + EXPECT_THAT(ep, NotNull()); + + if (!sync) return cxx::ProcessHolder{ ep.release(), ep.dwProcessId, ep.dwThreadId }; + + // エディターのメインスレッドを開く + cxx::HandleHolder hThread{ ::OpenThread(THREAD_SUSPEND_RESUME, FALSE, ep.dwThreadId) }; + + // 初期化完了イベントを作成する + SFilePath initEventName{ std::format(GSTR_EVENT_SAKURA_EP_INITIALIZED, ep.dwThreadId) }; + cxx::HandleHolder hEvent{ ::CreateEventW(nullptr, TRUE, FALSE, initEventName) }; + + // エディターのメインスレッドを再開する + ::ResumeThread(hThread); + + // エディター初期化完了を待つ + std::array handles{ hEvent.get(), ep.get() }; + if (const auto dwRet = ::WaitForMultipleObjects(DWORD(std::size(handles)), std::data(handles), FALSE, 30000); WAIT_OBJECT_0 != dwRet) { + return cxx::ProcessHolder{ ep.release(), ep.dwProcessId, ep.dwThreadId }; + } + + // プロセスオブジェクトを返す + return cxx::ProcessHolder{ ep.release(), ep.dwProcessId, ep.dwThreadId }; } //! 外部ウインドウにクローズを要求する @@ -997,15 +1057,7 @@ TEST_F(WinMainFuncTest, DoGrep001) auto ep = testing::CreateEditorProcess(args, profileName); EXPECT_THAT(ep, NotNull()); - // Grepダイアログが表示されるのを待って閉じる - for (const auto startTick = ::GetTickCount64(); ::GetTickCount64() - startTick < 5000;) { - if (const auto hWndFound = ::FindWindowW(GSTR_EDITWINDOWNAME, nullptr); hWndFound) { - break; - } - Sleep(10); // 10msスリープしてリトライ - } - - // 編集ウインドウを閉じる + // 編集ウインドウにクローズを要求する const auto hWndFound = WaitForEditor(); testing::RequestForeignWindowClose(hWndFound); @@ -1035,7 +1087,7 @@ TEST_F(WinMainFuncTest, OpenDebugWindow001) auto ep = testing::CreateEditorProcess(std::array{ LR"(-DEBUGMODE)" }, profileName); EXPECT_THAT(ep, NotNull()); - // 編集ウインドウが有効になるのを待って閉じる + // 編集ウインドウにクローズを要求する const auto hWndFound = WaitForEditor(); testing::RequestForeignWindowClose(hWndFound); @@ -1074,8 +1126,10 @@ TEST_F(WinMainFuncTest, ShowDlgGrep101) // Grepダイアログが表示されるのを待って閉じる t.join(); - // 編集ウインドウを閉じる const auto hWndFound = WaitForEditor(); + EXPECT_THAT(hWndFound, NotNull()); + + // 編集ウインドウにクローズを要求する testing::RequestForeignWindowClose(hWndFound); // 編集ウインドウが閉じられた後、プロセスが完全に終了するまで待つ From 34f4e49009a5bccb4fd4b0a3d97cb4ce2199f566 Mon Sep 17 00:00:00 2001 From: berryzplus Date: Fri, 17 Jul 2026 23:12:49 +0900 Subject: [PATCH 09/19] =?UTF-8?q?=E3=83=86=E3=82=B9=E3=83=88=E3=81=ABcxx::?= =?UTF-8?q?EditorProcessHolder=E3=82=92=E5=B0=8E=E5=85=A5=E3=81=99?= =?UTF-8?q?=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/cpp/tests1/test-winmain.cpp | 104 ++++++++++++++++++++------- 1 file changed, 79 insertions(+), 25 deletions(-) diff --git a/src/test/cpp/tests1/test-winmain.cpp b/src/test/cpp/tests1/test-winmain.cpp index 03589aa7a3..3b0bc3a35a 100644 --- a/src/test/cpp/tests1/test-winmain.cpp +++ b/src/test/cpp/tests1/test-winmain.cpp @@ -23,6 +23,7 @@ #include "basis/CMyString.h" #include "mem/CNativeW.h" #include "env/DLLSHAREDATA.h" +#include "env/CSakuraEnvironment.h" #include "util/file.h" #include "config/system_constants.h" #include "_main/CCommandLine.h" @@ -196,6 +197,38 @@ class ProcessHolder : public cxx::HandleHolder DWORD dwThreadId; }; +/*! + * @brief 起動したエディタープロセスオブジェクト + * + * @note 使い物になるかどうか試作してみた + */ +class EditorProcessHolder : public cxx::ProcessHolder +{ +private: + using Base = cxx::ProcessHolder; + using Me = EditorProcessHolder; + +public: + explicit EditorProcessHolder( + HANDLE hProcess, + DWORD dwProcessId, + DWORD dwThreadId, + HWND hWnd + ) + : Base(hProcess, dwProcessId, dwThreadId) + , hWnd(hWnd) + { + } + + EditorProcessHolder(const Me&) = delete; + Me& operator=(const Me&) = delete; + + EditorProcessHolder(Me&& other) noexcept = default; + Me& operator=(Me&& rhs) noexcept = default; + + HWND hWnd; +}; + } // namespace cxx namespace testing { @@ -365,6 +398,28 @@ cxx::ProcessHolder CreateSakuraProcess( return cxx::ProcessHolder{ ep.release(), ep.dwProcessId, ep.dwThreadId }; } +/*! + * @brief 編集ウィンドウを列挙するコールバック関数 + * + * @param hWnd 列挙されたウィンドウのハンドル + * @param lParam 列挙の呼び出し元から渡されたパラメータ(HWND* を期待) + * @retval TRUE 列挙続行(編集ウィンドウではなかった。) + * @retval FALSE 列挙停止(編集ウィンドウが見付かった。) + */ +BOOL CALLBACK EnumEditorWindowProc( + _In_ HWND hWnd, + _In_ LPARAM lParam +) +{ + if (!hWnd || !lParam || !IsSakuraMainWindow(hWnd)) return TRUE; // 検索続行 + + auto phWndFound = std::bit_cast(lParam); + + *phWndFound = hWnd; + + return FALSE; +} + /*! * @brief エディタープロセスを起動する * @@ -376,7 +431,7 @@ cxx::ProcessHolder CreateSakuraProcess( */ template requires std::ranges::range && std::convertible_to, std::wstring_view> -cxx::ProcessHolder CreateEditorProcess( +cxx::EditorProcessHolder CreateEditorProcess( const T& args, std::wstring_view profileName, bool sync = true @@ -402,7 +457,7 @@ cxx::ProcessHolder CreateEditorProcess( auto ep = CreateSakuraProcess(si, commandArgs, std::nullopt, profileName, dwCreationFlag); EXPECT_THAT(ep, NotNull()); - if (!sync) return cxx::ProcessHolder{ ep.release(), ep.dwProcessId, ep.dwThreadId }; + if (!sync) return cxx::EditorProcessHolder{ ep.release(), ep.dwProcessId, ep.dwThreadId, HWND(nullptr) }; // エディターのメインスレッドを開く cxx::HandleHolder hThread{ ::OpenThread(THREAD_SUSPEND_RESUME, FALSE, ep.dwThreadId) }; @@ -417,11 +472,19 @@ cxx::ProcessHolder CreateEditorProcess( // エディター初期化完了を待つ std::array handles{ hEvent.get(), ep.get() }; if (const auto dwRet = ::WaitForMultipleObjects(DWORD(std::size(handles)), std::data(handles), FALSE, 30000); WAIT_OBJECT_0 != dwRet) { - return cxx::ProcessHolder{ ep.release(), ep.dwProcessId, ep.dwThreadId }; + return cxx::EditorProcessHolder{ ep.release(), ep.dwProcessId, ep.dwThreadId, nullptr }; } + // メインウインドウを取得する + HWND hWndFound = nullptr; + + // スレッドに含まれるウインドウを列挙する + ::EnumThreadWindows(ep.dwThreadId, EnumEditorWindowProc, LPARAM(&hWndFound)); + + EXPECT_THAT(hWndFound, NotNull()); + // プロセスオブジェクトを返す - return cxx::ProcessHolder{ ep.release(), ep.dwProcessId, ep.dwThreadId }; + return cxx::EditorProcessHolder{ ep.release(), ep.dwProcessId, ep.dwThreadId, hWndFound }; } //! 外部ウインドウにクローズを要求する @@ -429,18 +492,11 @@ void RequestForeignWindowClose(HWND hWnd) { // ウインドウが閉じられるまで繰り返す while (::IsWindow(hWnd)) { - // ウインドウにクローズを要求する - if (!::SendMessageTimeoutW(hWnd, WM_CLOSE, 0, 0, - SMTO_NOTIMEOUTIFNOTHUNG | SMTO_ERRORONEXIT, - 5000, - nullptr - )) { - // Sendが失敗したらPostしておく - ::PostMessageW(hWnd, WM_CLOSE, 0, 0); - - // 少し待つ - ::Sleep(100); - } + // プロセス間通信なのでポストする + ::PostMessageW(hWnd, WM_CLOSE, 0, 0); + + // 少し待つ + ::Sleep(100); } } @@ -1056,10 +1112,10 @@ TEST_F(WinMainFuncTest, DoGrep001) // エディタープロセスを起動する auto ep = testing::CreateEditorProcess(args, profileName); EXPECT_THAT(ep, NotNull()); + EXPECT_THAT(ep.hWnd, NotNull()); // 編集ウインドウにクローズを要求する - const auto hWndFound = WaitForEditor(); - testing::RequestForeignWindowClose(hWndFound); + testing::RequestForeignWindowClose(ep.hWnd); // 編集ウインドウが閉じられた後、プロセスが完全に終了するまで待つ ep.lock(); @@ -1086,10 +1142,10 @@ TEST_F(WinMainFuncTest, OpenDebugWindow001) // エディタープロセスを起動する auto ep = testing::CreateEditorProcess(std::array{ LR"(-DEBUGMODE)" }, profileName); EXPECT_THAT(ep, NotNull()); + EXPECT_THAT(ep.hWnd, NotNull()); // 編集ウインドウにクローズを要求する - const auto hWndFound = WaitForEditor(); - testing::RequestForeignWindowClose(hWndFound); + testing::RequestForeignWindowClose(ep.hWnd); // 編集ウインドウが閉じられた後、プロセスが完全に終了するまで待つ ep.lock(); @@ -1122,15 +1178,13 @@ TEST_F(WinMainFuncTest, ShowDlgGrep101) // エディタープロセスを起動する auto ep = testing::CreateEditorProcess(std::array{ LR"(-GREPDLG)", LR"(-GREPMODE)" }, profileName); EXPECT_THAT(ep, NotNull()); + EXPECT_THAT(ep.hWnd, NotNull()); // Grepダイアログが表示されるのを待って閉じる t.join(); - const auto hWndFound = WaitForEditor(); - EXPECT_THAT(hWndFound, NotNull()); - // 編集ウインドウにクローズを要求する - testing::RequestForeignWindowClose(hWndFound); + testing::RequestForeignWindowClose(ep.hWnd); // 編集ウインドウが閉じられた後、プロセスが完全に終了するまで待つ ep.lock(); @@ -1158,7 +1212,7 @@ TEST_F(WinMainFuncTest, ShowDlgProfileMgr101) }); // エディタープロセスを起動する - auto ep = testing::CreateEditorProcess(std::array{ LR"(-PROFMGR)" }, profileName); + auto ep = testing::CreateEditorProcess(std::array{ LR"(-PROFMGR)" }, profileName, false); EXPECT_THAT(ep, NotNull()); // プロファイルマネージャが表示されるのを待って閉じる From 06b1af9deed256adc3f1ce1125c7ab5fbb4d96fc Mon Sep 17 00:00:00 2001 From: berryzplus Date: Fri, 17 Jul 2026 23:13:06 +0900 Subject: [PATCH 10/19] =?UTF-8?q?SonarQube=E6=8C=87=E6=91=98=E5=AF=BE?= =?UTF-8?q?=E5=BF=9C=20Use=20the=20init-statement?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sakura_core/_main/CNormalProcess.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/sakura_core/_main/CNormalProcess.cpp b/sakura_core/_main/CNormalProcess.cpp index 7145ad7de0..4673c96f4e 100644 --- a/sakura_core/_main/CNormalProcess.cpp +++ b/sakura_core/_main/CNormalProcess.cpp @@ -171,7 +171,9 @@ bool CNormalProcess::InitializeProcess() m_pcEditApp = CEditApp::getInstance(); m_pcEditApp->Create(GetProcessInstance(), nGroupId); CEditWnd* pEditWnd = m_pcEditApp->GetEditWindow(); - if( nullptr == pEditWnd->GetHwnd() ){ + + const auto hEditWnd = pEditWnd->GetHwnd(); + if (!hEditWnd) { ::ReleaseMutex( hMutex ); ::CloseHandle( hMutex ); return false; // 2009.06.23 ryoji CEditWnd::Create()失敗のため終了 @@ -207,7 +209,6 @@ bool CNormalProcess::InitializeProcess() // 2010.06.16 Moca Grepでもオプション指定を適用 pEditWnd->SetDocumentTypeWhenCreate( fi.m_nCharCode, false, nType ); pEditWnd->m_cDlgFuncList.Refresh(); // アウトラインを予め表示しておく - HWND hEditWnd = pEditWnd->GetHwnd(); if( !::IsIconic( hEditWnd ) && pEditWnd->m_cDlgFuncList.GetHwnd() ){ RECT rc; ::GetClientRect( hEditWnd, &rc ); @@ -291,8 +292,7 @@ bool CNormalProcess::InitializeProcess() pEditWnd->m_cDlgGrep.m_szFolder[nSize-1] = L'\0'; // Feb. 23, 2003 Moca Owner windowが正しく指定されていなかった - int nRet = pEditWnd->m_cDlgGrep.DoModal( GetProcessInstance(), pEditWnd->GetHwnd(), nullptr); - if( FALSE != nRet ){ + if (pEditWnd->m_cDlgGrep.DoModal(GetProcessInstance(), hEditWnd, LPCWSTR(nullptr))) { pEditWnd->GetActiveView().GetCommander().HandleCommand(F_GREP, true, 0, 0, 0, 0); }else{ // 自分はGrepでない @@ -407,7 +407,6 @@ bool CNormalProcess::InitializeProcess() //WM_SIZEをポスト { // ファイル読み込みしなかった場合にはこの WM_SIZE がアウトライン画面を配置する - HWND hEditWnd = pEditWnd->GetHwnd(); if( !::IsIconic( hEditWnd ) ){ RECT rc; ::GetClientRect( hEditWnd, &rc ); @@ -431,8 +430,9 @@ bool CNormalProcess::InitializeProcess() pEditWnd->GetDocument()->RunAutoMacro( GetDllShareData().m_Common.m_sMacro.m_nMacroOnOpened ); // 起動時マクロオプション - LPCWSTR pszMacro = CCommandLine::getInstance()->GetMacro(); - if( pEditWnd->GetHwnd() && pszMacro && pszMacro[0] != L'\0' ){ + if (const auto pszMacro = CCommandLine::getInstance()->GetMacro(); + pszMacro && pszMacro[0] != L'\0') + { LPCWSTR pszMacroType = CCommandLine::getInstance()->GetMacroType(); if( pszMacroType == nullptr || pszMacroType[0] == L'\0' || _wcsicmp(pszMacroType, L"file") == 0 ){ pszMacroType = nullptr; From b350b82565489889b614cc496098194c61a7910a Mon Sep 17 00:00:00 2001 From: berryzplus Date: Fri, 17 Jul 2026 23:13:19 +0900 Subject: [PATCH 11/19] =?UTF-8?q?SonarQube=E6=8C=87=E6=91=98=E5=AF=BE?= =?UTF-8?q?=E5=BF=9C=20This=20function=20should=20be=20declared=20"const".?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sakura_core/_main/CNormalProcess.cpp | 6 +++--- sakura_core/_main/CNormalProcess.h | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/sakura_core/_main/CNormalProcess.cpp b/sakura_core/_main/CNormalProcess.cpp index 4673c96f4e..e18707a124 100644 --- a/sakura_core/_main/CNormalProcess.cpp +++ b/sakura_core/_main/CNormalProcess.cpp @@ -522,14 +522,14 @@ HANDLE CNormalProcess::_GetInitializeMutex() const @date 2015.03.14 novice 新規作成 */ -void CNormalProcess::OpenFiles( HWND hwnd ) +void CNormalProcess::OpenFiles(HWND hwnd) const { EditInfo fi; CCommandLine::getInstance()->GetEditInfo( &fi ); bool bViewMode = CCommandLine::getInstance()->IsViewMode(); - int fileNum = CCommandLine::getInstance()->GetFileNum(); - if( fileNum > 0 ){ + if (auto fileNum = CCommandLine::getInstance()->GetFileNum(); + 0 < fileNum) { int nDropFileNumMax = GetDllShareData().m_Common.m_sFile.m_nDropFileNumMax - 1; // ファイルドロップ数の上限に合わせる if( fileNum > nDropFileNumMax ){ diff --git a/sakura_core/_main/CNormalProcess.h b/sakura_core/_main/CNormalProcess.h index 1ec3d66cda..6ad9c1fdd5 100644 --- a/sakura_core/_main/CNormalProcess.h +++ b/sakura_core/_main/CNormalProcess.h @@ -45,10 +45,11 @@ class CNormalProcess final : public CProcess { protected: //実装補助 HANDLE _GetInitializeMutex() const; // 2002/2/8 aroka - void OpenFiles(HWND hwnd); + void OpenFiles(HWND hEditWnd) const; private: CEditApp* m_pcEditApp = nullptr; //2007.10.23 kobake CMigemo m_cMigemo; }; + #endif /* SAKURA_CNORMALPROCESS_F2808B31_61DC_4BE0_8661_9626478AC7F9_H_ */ From 7f7a8aa72c9329ae8e271272808ec979a6a45b06 Mon Sep 17 00:00:00 2001 From: berryzplus Date: Fri, 17 Jul 2026 23:13:34 +0900 Subject: [PATCH 12/19] =?UTF-8?q?SonarQube=E6=8C=87=E6=91=98=E5=AF=BE?= =?UTF-8?q?=E5=BF=9C=20Remove=20this=20redundant=20access=20specifier?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sakura_core/_main/CNormalProcess.h | 1 - 1 file changed, 1 deletion(-) diff --git a/sakura_core/_main/CNormalProcess.h b/sakura_core/_main/CNormalProcess.h index 6ad9c1fdd5..45c8a3d8a7 100644 --- a/sakura_core/_main/CNormalProcess.h +++ b/sakura_core/_main/CNormalProcess.h @@ -42,7 +42,6 @@ class CNormalProcess final : public CProcess { bool MainLoop() override; void OnExitProcess() override; -protected: //実装補助 HANDLE _GetInitializeMutex() const; // 2002/2/8 aroka void OpenFiles(HWND hEditWnd) const; From 0668d9bd8e8e14e0ab506b5bc5f85164ab820b01 Mon Sep 17 00:00:00 2001 From: berryzplus Date: Fri, 17 Jul 2026 23:13:51 +0900 Subject: [PATCH 13/19] =?UTF-8?q?CNormalProcess::OpenFiles=E3=82=92?= =?UTF-8?q?=E5=90=8C=E6=9C=9F=E3=81=AB=E5=A4=89=E3=81=88=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sakura_core/_main/CNormalProcess.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sakura_core/_main/CNormalProcess.cpp b/sakura_core/_main/CNormalProcess.cpp index e18707a124..2a124ebacc 100644 --- a/sakura_core/_main/CNormalProcess.cpp +++ b/sakura_core/_main/CNormalProcess.cpp @@ -540,7 +540,7 @@ void CNormalProcess::OpenFiles(HWND hwnd) const for( i = 0; i < fileNum; i++ ){ // ファイル名差し替え wcscpy( fi.m_szPath, CCommandLine::getInstance()->GetFileName(i) ); - bool ret = CControlTray::OpenNewEditor2( GetProcessInstance(), hwnd, &fi, bViewMode ); + bool ret = CControlTray::OpenNewEditor2(GetProcessInstance(), hwnd, &fi, bViewMode, true); // 開くのを待つ if( ret == false ){ break; } From fce24344474754a051e2d7e906013544a7df9e4c Mon Sep 17 00:00:00 2001 From: berryzplus Date: Fri, 17 Jul 2026 23:14:05 +0900 Subject: [PATCH 14/19] =?UTF-8?q?=E3=82=A8=E3=83=87=E3=82=A3=E3=82=BF?= =?UTF-8?q?=E3=83=BC=E5=88=9D=E6=9C=9F=E5=8C=96=E5=AE=8C=E4=BA=86=E3=82=A4?= =?UTF-8?q?=E3=83=99=E3=83=B3=E3=83=88=E3=81=AE=E6=A4=9C=E5=87=BA=E3=82=92?= =?UTF-8?q?=E3=82=A2=E3=83=97=E3=83=AA=E5=81=B4=E3=81=AB=E7=B5=84=E3=81=BF?= =?UTF-8?q?=E8=BE=BC=E3=82=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sakura_core/_main/CControlTray.cpp | 62 ++++----- src/test/cpp/tests1/test-winmain.cpp | 191 ++++++++++++++++++--------- 2 files changed, 152 insertions(+), 101 deletions(-) diff --git a/sakura_core/_main/CControlTray.cpp b/sakura_core/_main/CControlTray.cpp index 83a8117f0c..4a2e09ccbf 100644 --- a/sakura_core/_main/CControlTray.cpp +++ b/sakura_core/_main/CControlTray.cpp @@ -1228,6 +1228,9 @@ bool CControlTray::OpenNewEditor( // May 30, 2003 genta カレントディレクトリ指定を可能に //エディタプロセスを起動 DWORD dwCreationFlag = CREATE_DEFAULT_ERROR_MODE; + + if (sync) dwCreationFlag |= CREATE_SUSPENDED; + #ifdef _DEBUG // dwCreationFlag |= DEBUG_PROCESS; //2007.09.22 kobake デバッグ用フラグ #endif @@ -1267,11 +1270,28 @@ bool CControlTray::OpenNewEditor( return false; } + // タブまとめ時は起動したプロセスが立ち上がるまでしばらくタイトルバーをアクティブに保つため、強制的に同期モードにする + if (pShareData->m_Common.m_sTabBar.m_bDispTabWnd && !pShareData->m_Common.m_sTabBar.m_bDispTabWndMultiWin && !sync){ + sync = true; + } + + // MYWM_FIRST_IDLE が届くまでちょっとだけ余分に待つ // 2008.04.19 ryoji + // Note. 起動先プロセスが初期化処理中に COM 関数(SHGetFileInfo API なども含む)を実行すると、 + // その時点で COM の同期機構が動いて WaitForInputIdle は終了してしまう可能性がある(らしい)。 bool bRet = true; - if( sync ){ - // 起動したプロセスが完全に立ち上がるまでちょっと待つ. - int nResult = WaitForInputIdle( p.hProcess, 10000 ); // 最大10秒間待つ - if( nResult != 0 ){ + if (sync) + { + // エディター初期化完了イベントを作成する + SFilePath initEventName{ std::format(GSTR_EVENT_SAKURA_EP_INITIALIZED, p.dwThreadId) }; + HANDLE hEvent = ::CreateEventW(nullptr, TRUE, FALSE, initEventName); + + // エディターのメインスレッドを再開する + ::ResumeThread(p.hThread); + + // エディター初期化完了を待つ + std::array handles{ hEvent, p.hProcess }; + const auto dwRet = ::WaitForMultipleObjects(DWORD(std::size(handles)), std::data(handles), FALSE, 15000); + if (WAIT_OBJECT_0 != dwRet) { ErrorMessage( hWndParent, LS(STR_TRAY_CREATEPROC2), @@ -1280,40 +1300,6 @@ bool CControlTray::OpenNewEditor( bRet = false; } } - else{ - // タブまとめ時は起動したプロセスが立ち上がるまでしばらくタイトルバーをアクティブに保つ // 2007.02.03 ryoji - if( pShareData->m_Common.m_sTabBar.m_bDispTabWnd && !pShareData->m_Common.m_sTabBar.m_bDispTabWndMultiWin ){ - WaitForInputIdle( p.hProcess, 3000 ); - sync = true; - } - } - - // MYWM_FIRST_IDLE が届くまでちょっとだけ余分に待つ // 2008.04.19 ryoji - // Note. 起動先プロセスが初期化処理中に COM 関数(SHGetFileInfo API なども含む)を実行すると、 - // その時点で COM の同期機構が動いて WaitForInputIdle は終了してしまう可能性がある(らしい)。 - if( sync && bRet ) - { - int i; - for( i = 0; i < 200; i++ ){ - MSG msg; - DWORD dwExitCode; - if( ::PeekMessage( &msg, nullptr, MYWM_FIRST_IDLE, MYWM_FIRST_IDLE, PM_REMOVE ) ){ - if( msg.message == WM_QUIT ){ // 指定範囲外でも WM_QUIT は取り出される - ::PostQuitMessage( int(msg.wParam) ); - break; - } - // 監視対象プロセスからのメッセージなら抜ける - // そうでなければ破棄して次を取り出す - if( msg.wParam == p.dwProcessId ){ - break; - } - } - if( ::GetExitCodeProcess( p.hProcess, &dwExitCode ) && dwExitCode != STILL_ACTIVE ){ - break; // 監視対象プロセスが終了した - } - ::Sleep(10); - } - } CloseHandle( p.hThread ); CloseHandle( p.hProcess ); diff --git a/src/test/cpp/tests1/test-winmain.cpp b/src/test/cpp/tests1/test-winmain.cpp index 3b0bc3a35a..331ca27bd3 100644 --- a/src/test/cpp/tests1/test-winmain.cpp +++ b/src/test/cpp/tests1/test-winmain.cpp @@ -838,10 +838,6 @@ TEST_P(WinMainTest, runEditorProcess) fs.close(); } - // コントロールプロセスを起動する - auto cp = testing::CreateControlProcess(profileName); - EXPECT_THAT(cp, NotNull()); - // 起動時実行マクロの中身を作る constexpr std::array macroCommands = { L"Down();"sv, @@ -970,7 +966,7 @@ TEST_P(WinMainTest, runEditorProcess) EXPECT_EXIT({ StartEditorProcess(command); }, ::testing::ExitedWithCode(0), ".*" ); // コントロールプロセスに終了指示を出して終了を待つ - testing::TerminateControlProcess(profileName, cp.dwProcessId); + testing::TerminateControlProcess(profileName); // コントロールプロセスが終了すると、INIファイルが作成される EXPECT_THAT(fexist(iniPath), IsTrue()); @@ -1074,6 +1070,89 @@ TEST_F(WinMainFuncTest, CreateControlProcess102) EXPECT_EXIT({ StartEditorProcess(std::format(LR"(-NOWIN -PROF="{:s}")", profileName)); }, ::testing::ExitedWithCode(0), ".*"); // たぶんバグです。エラー終了なのに0を返してる。 } +/*! + * @brief コントロールプロセス起動の失敗をテストする。(初期化完了イベント作成済み) + */ +TEST_F(WinMainFuncTest, CreateControlProcess103) +{ + // テスト用プロファイル名 + const auto profileName{ GetProfileName() }; + + // 初期化完了イベントの名前を決める + SFilePath initEventName{ GSTR_EVENT_SAKURA_CP_INITIALIZED }; + initEventName += profileName; + + // プロセス起動前に初期化完了イベントを作成する + cxx::HandleHolder hEvent = ::CreateEventW(nullptr, TRUE, FALSE, initEventName); + + // エディタープロセスを起動する + std::array args{}; + auto ep = testing::CreateEditorProcess(args, profileName, false); + EXPECT_THAT(ep, NotNull()); + + // プロセスが完全に終了するまで待つ + ep.lock(); +} + +/*! + * @brief WinMainを起動してみるテスト + * プログラムが起動する正常ルートに潜む障害を検出するためのもの。 + * 複数ファイルを開くパターンのテスト。 + */ +TEST_F(WinMainFuncTest, CreateEditorProcess001) +{ + // テスト用プロファイル名 + const auto profileName{ GetProfileName() }; + + // コマンドラインを組み立てる + std::array args{ + gm_TestDataPath.native(), // 1つ目のファイル + gm_TestDataPath.native(), // 2つ目のファイルはCNormalProcess::OpenFilesで処理される + gm_TestDataPath.native(), // 3つ目のファイルもCNormalProcess::OpenFilesで処理される + }; + + // エディタープロセスを起動する + auto ep = testing::CreateEditorProcess(args, profileName); + EXPECT_THAT(ep, NotNull()); + EXPECT_THAT(ep.hWnd, NotNull()); + + // 編集ウインドウにクローズを要求する + testing::RequestForeignWindowClose(ep.hWnd); + + // 編集ウインドウが閉じられた後、プロセスが完全に終了するまで待つ + ep.lock(); + + // コントロールプロセスに終了指示を出して終了を待つ + testing::TerminateControlProcess(profileName); +} + +/*! + * @brief WinMainを起動してみるテスト + * プログラムが起動する正常ルートに潜む障害を検出するためのもの。 + * エディター起動の失敗をテストする。 + */ +TEST_F(WinMainFuncTest, CreateEditorProcess101) +{ + // テスト用プロファイル名 + const auto profileName{ GetProfileName() }; + + // ミューテックスの名前を組み立てる + SFilePath szMutexName{ GSTR_MUTEX_SAKURA_CP }; + szMutexName += profileName; + + // ミューテックスを作成してロックする + cxx::HandleHolder hMutex{ ::CreateMutexW(nullptr, TRUE, szMutexName) }; + EXPECT_THAT(hMutex, NotNull()); + + // エディタープロセスを起動する + std::array args{}; + auto ep = testing::CreateEditorProcess(args, profileName, false); + EXPECT_THAT(ep, NotNull()); + + // プロセスが完全に終了するまで待つ + ep.lock(); +} + /*! * @brief WinMainを起動してみるテスト * プログラムが起動する正常ルートに潜む障害を検出するためのもの。 @@ -1131,28 +1210,22 @@ TEST_F(WinMainFuncTest, DoGrep001) */ TEST_F(WinMainFuncTest, OpenDebugWindow001) { - RunGuiTest([this] { - // テスト用プロファイル名 - const auto profileName{ GetProfileName() }; - - // コントロールプロセスを起動する - auto cp = testing::CreateControlProcess(profileName); - EXPECT_THAT(cp, NotNull()); + // テスト用プロファイル名 + const auto profileName{ GetProfileName() }; - // エディタープロセスを起動する - auto ep = testing::CreateEditorProcess(std::array{ LR"(-DEBUGMODE)" }, profileName); - EXPECT_THAT(ep, NotNull()); - EXPECT_THAT(ep.hWnd, NotNull()); + // エディタープロセスを起動する + auto ep = testing::CreateEditorProcess(std::array{ LR"(-DEBUGMODE)" }, profileName); + EXPECT_THAT(ep, NotNull()); + EXPECT_THAT(ep.hWnd, NotNull()); - // 編集ウインドウにクローズを要求する - testing::RequestForeignWindowClose(ep.hWnd); + // 編集ウインドウにクローズを要求する + testing::RequestForeignWindowClose(ep.hWnd); - // 編集ウインドウが閉じられた後、プロセスが完全に終了するまで待つ - ep.lock(); + // 編集ウインドウが閉じられた後、プロセスが完全に終了するまで待つ + ep.lock(); - // コントロールプロセスに終了指示を出して終了を待つ - testing::TerminateControlProcess(profileName, cp.dwProcessId); - }); + // コントロールプロセスに終了指示を出して終了を待つ + testing::TerminateControlProcess(profileName); } /*! @@ -1162,36 +1235,30 @@ TEST_F(WinMainFuncTest, OpenDebugWindow001) */ TEST_F(WinMainFuncTest, ShowDlgGrep101) { - RunGuiTest([this] { - // テスト用プロファイル名 - const auto profileName{ GetProfileName() }; - - // コントロールプロセスを起動する - auto cp = testing::CreateControlProcess(profileName); - EXPECT_THAT(cp, NotNull()); + // テスト用プロファイル名 + const auto profileName{ GetProfileName() }; - // 表示されたGrepダイアログを閉じるためのスレッドを起動する - std::jthread t = StartWindowCloser(L"Grep", [this] (HWND hWndDlg) { - EmulateInvokeButton(hWndDlg, L"キャンセル(X)"); - }); + // 表示されたGrepダイアログを閉じるためのスレッドを起動する + std::jthread t = StartWindowCloser(L"Grep", [this] (HWND hWndDlg) { + EmulateInvokeButton(hWndDlg, L"キャンセル(X)"); + }); - // エディタープロセスを起動する - auto ep = testing::CreateEditorProcess(std::array{ LR"(-GREPDLG)", LR"(-GREPMODE)" }, profileName); - EXPECT_THAT(ep, NotNull()); - EXPECT_THAT(ep.hWnd, NotNull()); + // エディタープロセスを起動する + auto ep = testing::CreateEditorProcess(std::array{ LR"(-GREPDLG)", LR"(-GREPMODE)" }, profileName); + EXPECT_THAT(ep, NotNull()); + EXPECT_THAT(ep.hWnd, NotNull()); - // Grepダイアログが表示されるのを待って閉じる - t.join(); + // Grepダイアログが表示されるのを待って閉じる + t.join(); - // 編集ウインドウにクローズを要求する - testing::RequestForeignWindowClose(ep.hWnd); + // 編集ウインドウにクローズを要求する + testing::RequestForeignWindowClose(ep.hWnd); - // 編集ウインドウが閉じられた後、プロセスが完全に終了するまで待つ - ep.lock(); + // 編集ウインドウが閉じられた後、プロセスが完全に終了するまで待つ + ep.lock(); - // コントロールプロセスに終了指示を出して終了を待つ - testing::TerminateControlProcess(profileName, cp.dwProcessId); - }); + // コントロールプロセスに終了指示を出して終了を待つ + testing::TerminateControlProcess(profileName); } /*! @@ -1201,26 +1268,24 @@ TEST_F(WinMainFuncTest, ShowDlgGrep101) */ TEST_F(WinMainFuncTest, ShowDlgProfileMgr101) { - RunGuiTest([this] { - // テスト用プロファイル名 - const auto profileName{ GetProfileName() }; + // テスト用プロファイル名 + const auto profileName{ GetProfileName() }; - // 表示されたプロファイルマネージャを閉じるためのスレッドを起動する - std::jthread t = StartWindowCloser(L"プロファイルマネージャ", [&] (HWND hWndDlg) { - // プロファイルマネージャを閉じる - EmulateInvokeButton(hWndDlg, L"閉じる(X)"); - }); + // 表示されたプロファイルマネージャを閉じるためのスレッドを起動する + std::jthread t = StartWindowCloser(L"プロファイルマネージャ", [this] (HWND hWndDlg) { + // プロファイルマネージャを閉じる + EmulateInvokeButton(hWndDlg, L"閉じる(X)"); + }); - // エディタープロセスを起動する - auto ep = testing::CreateEditorProcess(std::array{ LR"(-PROFMGR)" }, profileName, false); - EXPECT_THAT(ep, NotNull()); + // エディタープロセスを起動する + auto ep = testing::CreateEditorProcess(std::array{ LR"(-PROFMGR)" }, profileName, false); + EXPECT_THAT(ep, NotNull()); - // プロファイルマネージャが表示されるのを待って閉じる - t.join(); + // プロファイルマネージャが表示されるのを待って閉じる + t.join(); - // 編集ウインドウが閉じられた後、プロセスが完全に終了するまで待つ - ep.lock(); - }); + // 編集ウインドウが閉じられた後、プロセスが完全に終了するまで待つ + ep.lock(); } } // namespace winmain From 77d5852337fabad0f90ad3751100af7cd8502b30 Mon Sep 17 00:00:00 2001 From: berryzplus Date: Fri, 17 Jul 2026 23:14:20 +0900 Subject: [PATCH 15/19] =?UTF-8?q?SonarQube=E6=8C=87=E6=91=98=E5=AF=BE?= =?UTF-8?q?=E5=BF=9C=20This=20function=20should=20be=20declared=20"const".?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sakura_core/_main/CControlTray.cpp | 6 +++--- sakura_core/_main/CControlTray.h | 6 +++--- sakura_core/_main/CProcessFactory.cpp | 6 +++--- sakura_core/_main/CProcessFactory.h | 8 ++++---- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/sakura_core/_main/CControlTray.cpp b/sakura_core/_main/CControlTray.cpp index 4a2e09ccbf..68ab2b0957 100644 --- a/sakura_core/_main/CControlTray.cpp +++ b/sakura_core/_main/CControlTray.cpp @@ -320,7 +320,7 @@ bool CControlTray::CreateTrayIcon( [[maybe_unused]] HWND hWnd ) } /* メッセージループ */ -void CControlTray::MessageLoop( void ) +void CControlTray::MessageLoop() const { //複数プロセス版 MSG msg; @@ -338,7 +338,7 @@ void CControlTray::MessageLoop( void ) } /* タスクトレイのアイコンに関する処理 */ -BOOL CControlTray::TrayMessage( HWND hDlg, DWORD dwMessage, UINT uID, HICON hIcon, const WCHAR* pszTip ) +BOOL CControlTray::TrayMessage(HWND hDlg, DWORD dwMessage, UINT uID, HICON hIcon, const WCHAR* pszTip) const { BOOL res; NOTIFYICONDATA tnd; @@ -947,7 +947,7 @@ LRESULT CControlTray::DispatchEvent( } /* WM_COMMANDメッセージ処理 */ -void CControlTray::OnCommand( WORD wNotifyCode, [[maybe_unused]] WORD wID , [[maybe_unused]] HWND hwndCtl ) +void CControlTray::OnCommand(WORD wNotifyCode, [[maybe_unused]] WORD wID , [[maybe_unused]] HWND hwndCtl) const { switch( wNotifyCode ){ /* メニューからのメッセージ */ diff --git a/sakura_core/_main/CControlTray.h b/sakura_core/_main/CControlTray.h index 5b3de53e3b..69152ab63d 100644 --- a/sakura_core/_main/CControlTray.h +++ b/sakura_core/_main/CControlTray.h @@ -73,7 +73,7 @@ class CControlTray HWND Create(HINSTANCE hInstance); /* 作成 */ bool CreateTrayIcon(HWND hWnd); // 20010412 by aroka LRESULT DispatchEvent(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); /* メッセージ処理 */ - void MessageLoop( void ); /* メッセージループ */ + void MessageLoop() const; /* メッセージループ */ void OnDestroy( void ); /* WM_DESTROY 処理 */ // 2006.07.09 ryoji int CreatePopUpMenu_L( void ); /* ポップアップメニュー(トレイ左ボタン) */ int CreatePopUpMenu_R( void ); /* ポップアップメニュー(トレイ右ボタン) */ @@ -111,8 +111,8 @@ class CControlTray static void DoGrepCreateWindow(HINSTANCE hinst, HWND, CDlgGrep& cDlgGrep); protected: void DoGrep(); //Stonee, 2001/03/21 - BOOL TrayMessage(HWND hDlg, DWORD dwMessage, UINT uID, HICON hIcon, const WCHAR* pszTip); /*!< タスクトレイのアイコンに関する処理 */ - void OnCommand(WORD wNotifyCode, WORD wID, HWND hwndCtl); /*!< WM_COMMANDメッセージ処理 */ + BOOL TrayMessage(HWND hDlg, DWORD dwMessage, UINT uID, HICON hIcon, const WCHAR* pszTip) const; /*!< タスクトレイのアイコンに関する処理 */ + void OnCommand(WORD wNotifyCode, WORD wID, HWND hwndCtl) const; /*!< WM_COMMANDメッセージ処理 */ void OnNewEditor(bool bNewWindow); //!< 2003.05.30 genta 新規ウィンドウ作成処理を切り出し static INT_PTR CALLBACK ExitingDlgProc( /*!< 終了ダイアログ用プロシージャ */ // 2006.07.02 ryoji CControlProcess から移動 diff --git a/sakura_core/_main/CProcessFactory.cpp b/sakura_core/_main/CProcessFactory.cpp index 0c600d9385..8afc91e6a9 100644 --- a/sakura_core/_main/CProcessFactory.cpp +++ b/sakura_core/_main/CProcessFactory.cpp @@ -80,7 +80,7 @@ CProcess* CProcessFactory::Create( HINSTANCE hInstance, LPCWSTR lpCmdLine ) return process; } -bool CProcessFactory::ProfileSelect( HINSTANCE hInstance, LPCWSTR lpCmdLine ) +bool CProcessFactory::ProfileSelect(HINSTANCE hInstance, LPCWSTR lpCmdLine) const { // May 30, 2000 genta // 実行ファイル名をもとに漢字コードを固定する. @@ -112,7 +112,7 @@ bool CProcessFactory::ProfileSelect( HINSTANCE hInstance, LPCWSTR lpCmdLine ) @author aroka @date 2002/01/03 */ -bool CProcessFactory::IsValidVersion() +bool CProcessFactory::IsValidVersion() const { // Windowsバージョンは廃止。 // 動作可能バージョン(=windows7以降)でなければ起動できない。 @@ -125,7 +125,7 @@ bool CProcessFactory::IsValidVersion() @author aroka @date 2002/01/03 作成 2002/01/18 変更 */ -bool CProcessFactory::IsStartingControlProcess() +bool CProcessFactory::IsStartingControlProcess() const { return CCommandLine::getInstance()->IsNoWindow(); } diff --git a/sakura_core/_main/CProcessFactory.h b/sakura_core/_main/CProcessFactory.h index 68f96a5464..0db3cc1d79 100644 --- a/sakura_core/_main/CProcessFactory.h +++ b/sakura_core/_main/CProcessFactory.h @@ -39,11 +39,11 @@ class CProcessFactory { static bool StartControlProcess(); CProcess* Create( HINSTANCE hInstance, LPCWSTR lpCmdLine ); -protected: + private: - bool IsValidVersion(); - bool ProfileSelect(HINSTANCE hInstance, LPCWSTR lpCmdLine); - bool IsStartingControlProcess(); + bool IsValidVersion() const; + bool ProfileSelect(HINSTANCE hInstance, LPCWSTR lpCmdLine) const; + bool IsStartingControlProcess() const; }; #endif /* SAKURA_CPROCESSFACTORY_5006562F_7795_40FF_AA4C_FFB94842F7C5_H_ */ From 18bc85f336a5dab9f390a1a63857a585cea00bd3 Mon Sep 17 00:00:00 2001 From: berryzplus Date: Fri, 17 Jul 2026 23:14:34 +0900 Subject: [PATCH 16/19] =?UTF-8?q?SonarQube=E6=8C=87=E6=91=98=E5=AF=BE?= =?UTF-8?q?=E5=BF=9C=E3=81=A7CNormalProcess::OpenFiles=E3=82=92=E7=B5=84?= =?UTF-8?q?=E3=81=BF=E7=9B=B4=E3=81=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sakura_core/_main/CNormalProcess.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/sakura_core/_main/CNormalProcess.cpp b/sakura_core/_main/CNormalProcess.cpp index 2a124ebacc..c597c9d22c 100644 --- a/sakura_core/_main/CNormalProcess.cpp +++ b/sakura_core/_main/CNormalProcess.cpp @@ -529,19 +529,20 @@ void CNormalProcess::OpenFiles(HWND hwnd) const bool bViewMode = CCommandLine::getInstance()->IsViewMode(); if (auto fileNum = CCommandLine::getInstance()->GetFileNum(); - 0 < fileNum) { - int nDropFileNumMax = GetDllShareData().m_Common.m_sFile.m_nDropFileNumMax - 1; + 0 < fileNum) + { // ファイルドロップ数の上限に合わせる - if( fileNum > nDropFileNumMax ){ + if (const auto nDropFileNumMax = GetDllShareData().m_Common.m_sFile.m_nDropFileNumMax - 1; + nDropFileNumMax < fileNum) { fileNum = nDropFileNumMax; } - int i; - for( i = 0; i < fileNum; i++ ){ + for (int i = 0; i < fileNum; ++i) { // ファイル名差し替え - wcscpy( fi.m_szPath, CCommandLine::getInstance()->GetFileName(i) ); - bool ret = CControlTray::OpenNewEditor2(GetProcessInstance(), hwnd, &fi, bViewMode, true); // 開くのを待つ - if( ret == false ){ + ::wcscpy_s(fi.m_szPath, CCommandLine::getInstance()->GetFileName(i)); + + // 同期モードでファイルを開いていく + if (!CControlTray::OpenNewEditor2(GetProcessInstance(), hwnd, &fi, bViewMode, true)) { break; } } From 2409e20699211d465c02358bb9becfe7e4b32387 Mon Sep 17 00:00:00 2001 From: berryzplus Date: Fri, 17 Jul 2026 23:14:49 +0900 Subject: [PATCH 17/19] =?UTF-8?q?SonarQube=E6=8C=87=E6=91=98=E5=AF=BE?= =?UTF-8?q?=E5=BF=9C=20Make=20sure=20use=20of=20"wcscpy"=20is=20safe=20her?= =?UTF-8?q?e.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit WCHAR[260]同士のコピーなので実は「安全」と断言できるのだけど、面倒なので直してしまう。 --- sakura_core/_main/CControlTray.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sakura_core/_main/CControlTray.cpp b/sakura_core/_main/CControlTray.cpp index 68ab2b0957..4b9df4299a 100644 --- a/sakura_core/_main/CControlTray.cpp +++ b/sakura_core/_main/CControlTray.cpp @@ -277,7 +277,7 @@ HWND CControlTray::Create( HINSTANCE hInstance ) m_pcPropertyManager = new CPropertyManager(); m_pcPropertyManager->Create( GetTrayHwnd(), &m_hIcons, &m_cMenuDrawer ); - wcscpy(m_szLanguageDll, GetDllShareData().m_Common.m_sWindow.m_szLanguageDll); + m_szLanguageDll = GetDllShareData().m_Common.m_sWindow.m_szLanguageDll; return GetTrayHwnd(); } @@ -577,7 +577,7 @@ LRESULT CControlTray::DispatchEvent( } { bool bChangeLang = wcscmp( GetDllShareData().m_Common.m_sWindow.m_szLanguageDll, m_szLanguageDll ) != 0; - wcscpy( m_szLanguageDll, GetDllShareData().m_Common.m_sWindow.m_szLanguageDll ); + m_szLanguageDll = GetDllShareData().m_Common.m_sWindow.m_szLanguageDll; std::vector values; if( bChangeLang ){ CShareData::getInstance()->ConvertLangValues(values, true); From 66a00ccff9fbf2d6c718306e97145c393cada731 Mon Sep 17 00:00:00 2001 From: berryzplus Date: Fri, 17 Jul 2026 23:15:03 +0900 Subject: [PATCH 18/19] =?UTF-8?q?SonarQube=E6=8C=87=E6=91=98=E5=AF=BE?= =?UTF-8?q?=E5=BF=9C=20Use=20the=20init-statement?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sakura_core/_main/CControlTray.cpp | 20 ++++++++++++++++---- src/test/cpp/tests1/test-winmain.cpp | 10 ++-------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/sakura_core/_main/CControlTray.cpp b/sakura_core/_main/CControlTray.cpp index 4b9df4299a..0acf6efdcb 100644 --- a/sakura_core/_main/CControlTray.cpp +++ b/sakura_core/_main/CControlTray.cpp @@ -65,6 +65,18 @@ ///////////////////////////////////////////////////////////////////////// static LRESULT CALLBACK CControlTrayWndProc( HWND, UINT, WPARAM, LPARAM ); +namespace cxx { + +/*! + * @brief トップレベルウインドウを検索する + */ +HWND FindWindowW(std::wstring_view className, const std::optional& optWindowName = std::nullopt) +{ + return ::FindWindowW(std::data(std::wstring(className)), optWindowName.has_value() ? std::data(*optWindowName) : nullptr); +} + +} // namespace cxx + //Stonee, 2001/03/21 //Stonee, 2001/07/01 多重起動された場合は前回のダイアログを前面に出すようにした。 void CControlTray::DoGrep() @@ -221,8 +233,7 @@ HWND CControlTray::Create( HINSTANCE hInstance ) const auto pszProfileName = GetProfileName(); std::wstring strCEditAppName = GSTR_CEDITAPP; strCEditAppName += pszProfileName; - HWND hwndWork = ::FindWindow( strCEditAppName.c_str(), strCEditAppName.c_str() ); - if( nullptr != hwndWork ){ + if (const auto hWndTray = cxx::FindWindowW(strCEditAppName, strCEditAppName); hWndTray){ return nullptr; } @@ -1235,7 +1246,8 @@ bool CControlTray::OpenNewEditor( // dwCreationFlag |= DEBUG_PROCESS; //2007.09.22 kobake デバッグ用フラグ #endif WCHAR szCmdLine[1024]; wcscpy_s(szCmdLine, std::size(szCmdLine), cCmdLineBuf.c_str()); - BOOL bCreateResult = CreateProcess( + + if (const auto bCreateResult = ::CreateProcessW( szEXE, // 実行可能モジュールの名前 szCmdLine, // コマンドラインの文字列 nullptr, // セキュリティ記述子 @@ -1247,7 +1259,7 @@ bool CControlTray::OpenNewEditor( &s, // スタートアップ情報 &p // プロセス情報 ); - if( !bCreateResult ){ + !bCreateResult) { // 失敗 WCHAR* pMsg; FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | diff --git a/src/test/cpp/tests1/test-winmain.cpp b/src/test/cpp/tests1/test-winmain.cpp index 331ca27bd3..35b471adcb 100644 --- a/src/test/cpp/tests1/test-winmain.cpp +++ b/src/test/cpp/tests1/test-winmain.cpp @@ -42,6 +42,8 @@ void extract_zip_resource(WORD id, const std::optional& o namespace cxx { +HWND FindWindowW(std::wstring_view className, const std::optional& optWindowName = std::nullopt); + /*! * @brief システムエラーを例外として発生させる * @@ -53,14 +55,6 @@ NORETURN void raise_system_error(const std::string& message) { throw std::system_error(int(::GetLastError()), std::system_category(), message); } -/*! - * @brief トップレベルウインドウを検索する - */ -HWND FindWindowW(std::wstring_view className, const std::optional& optWindowName = std::nullopt) -{ - return ::FindWindowW(std::data(std::wstring(className)), optWindowName.has_value() ? std::data(*optWindowName) : nullptr); -} - /*! * @brief システムディレクトリのパスを取得する * From e0e0fe78cc8c70ede38f4a2a40919741b706c7ec Mon Sep 17 00:00:00 2001 From: berryzplus Date: Fri, 17 Jul 2026 23:15:17 +0900 Subject: [PATCH 19/19] =?UTF-8?q?SonarQube=E6=8C=87=E6=91=98=E5=AF=BE?= =?UTF-8?q?=E5=BF=9C=20Make=20the=20type=20of=20this=20variable=20a=20poin?= =?UTF-8?q?ter-to-const.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sakura_core/_main/CControlTray.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/sakura_core/_main/CControlTray.cpp b/sakura_core/_main/CControlTray.cpp index 0acf6efdcb..22236d2c19 100644 --- a/sakura_core/_main/CControlTray.cpp +++ b/sakura_core/_main/CControlTray.cpp @@ -1125,10 +1125,10 @@ bool CControlTray::OpenNewEditor( ) { /* 共有データ構造体のアドレスを返す */ - DLLSHAREDATA* pShareData = &GetDllShareData(); + const auto pShareData = &GetDllShareData(); /* 編集ウィンドウの上限チェック */ - if( pShareData->m_sNodes.m_nEditArrNum >= MAX_EDITWINDOWS ){ //最大値修正 //@@@ 2003.05.31 MIK + if (MAX_EDITWINDOWS <= pShareData->m_sNodes.m_nEditArrNum) { OkMessage( nullptr, LS(STR_MAXWINDOW), MAX_EDITWINDOWS ); return false; } @@ -1333,13 +1333,9 @@ bool CControlTray::OpenNewEditor2( bool bNewWindow //!< [in] 新規エディタを新しいウインドウで開く ) { - DLLSHAREDATA* pShareData; - - /* 共有データ構造体のアドレスを返す */ - pShareData = &GetDllShareData(); - /* 編集ウィンドウの上限チェック */ - if( pShareData->m_sNodes.m_nEditArrNum >= MAX_EDITWINDOWS ){ //最大値修正 //@@@ 2003.05.31 MIK + if (const auto pShareData = &GetDllShareData(); + MAX_EDITWINDOWS <= pShareData->m_sNodes.m_nEditArrNum) { OkMessage( nullptr, LS(STR_MAXWINDOW), MAX_EDITWINDOWS ); return false; }