Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JetBrains Rider でデバッグ実行が出来ない #39

Open
sashi0034 opened this issue Jul 7, 2024 · 16 comments
Open

JetBrains Rider でデバッグ実行が出来ない #39

sashi0034 opened this issue Jul 7, 2024 · 16 comments

Comments

@sashi0034
Copy link
Member

v0.6 世代の Siv3D では JetBrains Rider を使用して開発およびエディタからのデバッグ実行が可能でした。しかし、現時点での v0.8 では Failed to load a engine shader というエラーダイアログが表示され、初期化に失敗します。

詳しく調査したところ、初期化時の SetWorkingDirectory で Visual Studio 実行時かどうかを判定する条件分岐が、Rider実行時には適用されていないことが原因であることが分かりました。そのため、デバッグ時の実行ディレクトリが siv8/WindowsDesktop/App/ではなく siv8/WindowsDesktop/Intermediate/Siv3D-Test/Debug/ になってしまいます。

@Reputeless
Copy link
Member

Siv3D 側で回避するためには、プログラムが JetBrains Rider によって実行されているかを判定できる必要があります。

次のコードはプログラムの環境変数を出力するコードです。

#include <iostream>
#include <cstdlib>

int main()
{
	extern char** environ;
   
	for (int i = 0; environ[i] != nullptr; ++i)
	{
		std::cout << environ[i] << '\n';
	}
}

Visual Studio によって実行した場合、システム環境変数とは別に VisualStudioVersion などの環境変数が表示され、Visual Studio から起動されたことがわかります。

同様に JetBrains Rider で実行したときにだけ設定される環境変数があれば、System::IsRunningInJetBrainsRider() を実装でき、問題を回避できます。

なお、v0.6 世代でも v0.6.15 からは v8 と同じ仕様になっています。engine フォルダの内容を Resource.rc で実行ファイルに埋め込んでいるので、カレントディレクトリの影響を受けず、エンジン初期化には成功します。

@sashi0034
Copy link
Member Author

ありがとうございます。頂いたコードをもとに JetBrains Rider で実行してみましたが、Visual Studio とは異なり、デバッグ実行時に実行環境を表す特有の環境変数が存在しないことが分かりました。

@sashi0034
Copy link
Member Author

参考までに、ユーザー側で可能な回避策がありました。あらかじめ Run Configuration の Environment variables に VisualStudioVersion のダミーの値を入れておくという方法です。

image

@Reputeless
Copy link
Member

Reputeless commented Jul 7, 2024

.vcxproj ファイル内に環境変数 SIV3D_APP_LAUNCHED_FROM_IDE=1 を追加し、VisualStudioVersion ではなく SIV3D_APP_LAUNCHED_FROM_IDE の有無で判定するよう仕様を変更してみました。

JetBrains Rider にも上記設定がインポートされるかはわかりませんが、今後は SIV3D_APP_LAUNCHED_FROM_IDE=1 で対応する予定です。

@sashi0034
Copy link
Member Author

ご対応ありがとうございます。
しかしながら、Rider では LocalDebuggerCommandArgumentsLocalDebuggerWorkingDirectory などは反映が可能であるのにも関わらず、LocalDebuggerEnvironment は扱えないようでデバッグ実行時に SIV3D_APP_LAUNCHED_FROM_IDE=1 が設定されませんでした。

@Reputeless
Copy link
Member

MSVC においても、最初 LocalDebuggerEnvironment は反映されなかったのですが、
MSVC を起動したときに Siv3D-Test.vcxproj と同じフォルダに自動的に生成されるローカルファイル Siv3D-Test.vcxproj.user を一旦削除して再起動したところ反映されました。設定が .user によってオーバーライドされていたようです。
もしかしたら Rider でも Siv3D-Test.vcxproj.user が悪さをしているかもしれません。

image

@sashi0034
Copy link
Member Author

確かに、ローカル環境のキャッシュの問題であるという可能性は盲点でした

しかし、改めてリポジトリを別フォルダにクローンし直してブランチ v8_develop_0706 に切り替えてから起動・実行してみたのですが、やはり依然として環境変数が設定されないようでした

どうにも、Rider は VisualStudio で設定された LocalDebuggerEnvironment を取り込むことが出来なさそうです

@Reputeless
Copy link
Member

確認ありがとうございます。そうでしたか。
何か別の良い対処法が見つかった場合は対応できるのでお知らせください。

@sashi0034
Copy link
Member Author

Win32 API で IsDebuggerPresent という関数があることが分かりました。これを使用すれば、VisualStudio でも Rider でもデバッグ実行時かどうかを実際に検出できました

https://learn.microsoft.com/ja-jp/windows/win32/api/debugapi/nf-debugapi-isdebuggerpresent

@yaito3014
Copy link
Contributor

標準機能で言うならば std::is_debugger_present が C++26 に採択されるみたいですね
提案書 にはどういったものが現在利用可能か書いてあるので参考までにどうぞ

@Reputeless
Copy link
Member

IsDebuggerPresent() は、MSVC で「デバッグなしで開始」から実行すると false になるため、完全な判定には使えなかったと記憶しています。Rider の場合はどうでしょうか。
でも無策よりは Rider での実行に役立つので、IDE 利用判定条件に追加したいと思います。

@sashi0034
Copy link
Member Author

確かに、Rider でも「(デバッグ無しの) Run」をすると IsDebuggerPresent() が false になってしまいました...難しいですね
ひとまず対応ありがとうございます

@Reputeless
Copy link
Member

親プロセスの名前を調べることで、Rider からの起動を判定できそうでした。
ただ、この処理にはかなりの時間(10 ms 程度)がかかります。
エンジン初期化が遅くなってしまうため、Siv3D 本体への採用は不可能です。

# include <Siv3D.hpp> // Siv3D v0.8.0
# include <Siv3D/Windows/Windows.hpp>
# include <tlhelp32.h>

struct ProcessInfo
{
	DWORD pid = 0;
	String name;
};

Optional<ProcessInfo> GetParentProcess(DWORD processID)
{
	HANDLE hSnapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

	if (hSnapshot == INVALID_HANDLE_VALUE)
	{
		return none;
	}

	PROCESSENTRY32W pe32
	{
		.dwSize = sizeof(PROCESSENTRY32W)
	};

	ProcessInfo parentInfo;

	if (::Process32FirstW(hSnapshot, &pe32))
	{
		do
		{
			if (pe32.th32ProcessID == processID)
			{
				parentInfo.pid = pe32.th32ParentProcessID;
				break;
			}

		} while (::Process32NextW(hSnapshot, &pe32));

		// 親プロセスの名前を取得
		if (parentInfo.pid != 0)
		{
			::Process32FirstW(hSnapshot, &pe32);

			do
			{
				if (pe32.th32ProcessID == parentInfo.pid)
				{
					parentInfo.name = Unicode::FromWstring(pe32.szExeFile);
					break;
				}

			} while (::Process32NextW(hSnapshot, &pe32));
		}
	}

	::CloseHandle(hSnapshot);
	return parentInfo;
}

void Main()
{
	Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });

	MicrosecClock clock;

	if (const Optional<ProcessInfo> parentInfo = GetParentProcess(::GetCurrentProcessId()))
	{
		clock.console();
		Console << parentInfo->pid;
		Console << parentInfo->name;
	}
	else
	{
		Console << U"Parent process not found";
	}

	while (System::Update())
	{

	}
}

Reputeless added a commit that referenced this issue Jul 11, 2024
@sashi0034
Copy link
Member Author

実行中のファイルを含むフォルダの中に実行ファイルに対応する .pdb ファイルが存在するか否かを調べることにより、中間ディレクトリで実行中か否かを判断すると効果的なのではないかと考えました。この方法についてご意見をお聞かせいただけますでしょうか

@Reputeless
Copy link
Member

漏れていたケースをおおよそカバーできる、低コストで良い方法だと思います。
PDB を生成しないビルドオプションもあるので 100% にはなりませんが、採用してみたいと思います。
この先実装したらお知らせします。Rider で検証していただけると幸いです。

@sashi0034
Copy link
Member Author

ありがとうございます!検証の件承知いたしました

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants