static void Main(string[] args) { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); try { Trace.AutoFlush = true; #region " %USERPROFILE%/AppData/DTXMania2 フォルダがなければ作成する。" //---------------- var AppDataフォルダ名 = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData, Environment.SpecialFolderOption.Create), @"DTXMania2\"); // なければ作成。 if (!(Directory.Exists(AppDataフォルダ名))) { Directory.CreateDirectory(AppDataフォルダ名); } //---------------- #endregion #region " ログファイルへのログの複製出力開始。" //---------------- Program.ログファイル名 = Log.ログファイル名を生成する(Path.Combine(AppDataフォルダ名, "Logs"), "Log.", TimeSpan.FromDays(ログファイルの最大保存日数)); // ログファイルをTraceリスナとして追加。 // 以降、Trace(ならびにFDK.Logクラス)による出力は、このリスナ(=ログファイル)にも出力される。 Trace.Listeners.Add(new TraceLogListener(new StreamWriter(Program.ログファイル名, false, Encoding.GetEncoding("utf-8")))); //---------------- #endregion Log.現在のスレッドに名前をつける("描画"); Log.WriteLine(Application.ProductName + " " + App.リリース番号.ToString("000")); // アプリ名とバージョン Log.システム情報をログ出力する(); Log.WriteLine(""); var options = new CommandLineOptions(); #region " コマンドライン引数を解析する。" //---------------- if (!options.解析する(args)) // 解析に失敗すればfalse { // 利用法を表示して終了。 Log.WriteLine(options.Usage); // ログと using (var console = new FDK.Console()) console.Out?.WriteLine(options.Usage); // 標準出力の両方へ return; } //---------------- #endregion bool ビュアーモードである = (options.再生開始 || options.再生停止); if (ビュアーモードである) { // (A) ビュアーモードの場合、DTXManiaのWCFサービスがすでに存在しているか否かで処理分岐。 if (_WCFサービスを取得する(1, out var factory, out var service, out var serviceChannel)) { // (A-a) 取得できた → すでに起動しているアプリへ処理を委託し、自分は終了。 _起動済みのアプリケーションに処理を委託する(service, options); _WCFサービスを解放する(factory, service, serviceChannel); } else { // (A-b) 取得失敗 → 自分がビュアーとして起動し、処理を自分へ委託。 _アプリケーションをビュアーモードで起動する(options); } } else { // (B) 通常起動する。 _アプリケーションを通常起動する(); } }
static void Main(string[] args) { try { // 初期化 timeBeginPeriod(1); Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); // .NET Core で Shift-JIS 他を利用可能にする #region " コマンドライン引数を解析する。" //---------------- Global.Options = new CommandLineOptions(); if (!Global.Options.解析する(args)) // 解析に失敗すればfalse { // 利用法を表示して終了。 Trace.WriteLine(Global.Options.Usage); // Traceと using (var console = new FDK.Console()) console.Out?.WriteLine(Global.Options.Usage); // 標準出力の両方へ return; } //---------------- #endregion #region " AppData に DTXMania2 フォルダがなければ作成する。" //---------------- var AppDataフォルダ名 = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData, Environment.SpecialFolderOption.Create), "DTXMania2"); // %USERPROFILE%/AppData/local/DTXMania2/ if (!(Directory.Exists(AppDataフォルダ名))) { Directory.CreateDirectory(AppDataフォルダ名); } //---------------- #endregion #region " ログファイルへのログの複製出力開始。" //---------------- { const int ログファイルの最大保存日数 = 30; Trace.AutoFlush = true; var ログファイル名 = Log.ログファイル名を生成する( ログフォルダパス: Path.Combine(AppDataフォルダ名, "Logs"), ログファイルの接頭辞: "Log.", 最大保存期間: TimeSpan.FromDays(ログファイルの最大保存日数)); // ログファイルをTraceリスナとして追加。 // 以降、Trace(ならびにLogクラス)による出力は、このリスナ(=ログファイル)にも出力される。 Trace.Listeners.Add(new TraceLogListener(new StreamWriter(ログファイル名, false, Encoding.GetEncoding("utf-8")))); Log.現在のスレッドに名前をつける("Form"); } //---------------- #endregion #region " 二重起動チェックまたはオプション送信。" //---------------- using (var pipeToViewer = new NamedPipeClientStream(".", _ビュアー用パイプライン名, PipeDirection.Out)) { try { // パイプラインサーバへの接続を試みる。 pipeToViewer.Connect(100); // (A) サービスが立ち上がっている場合 → ビュアーモードかどうかでさらに分岐 if (Global.Options.ビュアーモードである) { #region " (A-a) ビュアーモードである → オプション内容をパイプラインサーバへ送信して正常終了。" //---------------- var ss = new StreamStringForNamedPipe(pipeToViewer); var yamlText = Global.Options.ToYaml(); // YAML化 ss.WriteString(yamlText); return; //---------------- #endregion } else { #region " (A-b) 通常モードである → 二重起動としてエラー終了。" //---------------- var ss = new StreamStringForNamedPipe(pipeToViewer); ss.WriteString("ping"); var msg = Properties.Resources.TXT_二重起動はできません; Trace.WriteLine(msg); // Traceと MessageBox.Show(msg, "DTXMania2 error"); // ダイアログに表示。 return; //---------------- #endregion } } catch (TimeoutException) { // (B) サービスが立ち上がっていない場合 → そのまま起動 } } //---------------- #endregion #region " タイトル、著作権、システム情報をログ出力する。" //---------------- Log.WriteLine($"{Application.ProductName} Release {int.Parse( Application.ProductVersion.Split( '.' ).ElementAt( 0 ) ):000}"); var copyrights = (AssemblyCopyrightAttribute[])Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyCopyrightAttribute), false); Log.WriteLine($"{copyrights[ 0 ].Copyright}"); Log.WriteLine(""); #region " Windows 情報 " //---------------- using (var hklmKey = Microsoft.Win32.Registry.LocalMachine) using (var subKey = hklmKey.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion")) // キーがなかったら null が返される { if (null != subKey) { var os_product = subKey.GetValue("ProductName")?.ToString() ?? "Unknown OS"; var os_release = subKey.GetValue("ReleaseId")?.ToString() ?? "Unknown Release"; var os_build = subKey.GetValue("CurrentBuild")?.ToString() ?? "Unknown Build"; var os_bit = Environment.Is64BitOperatingSystem ? "64bit" : "32bit"; var process_bit = Environment.Is64BitProcess ? "64bit" : "32bit"; Log.WriteLine($"{os_product} {os_release}.{os_build} ({os_bit} OS, {process_bit} process)"); } } //---------------- #endregion #region " メモリ情報 " //---------------- { var output = ""; var info = new ProcessStartInfo(); info.FileName = "wmic"; info.Arguments = "OS get FreePhysicalMemory,TotalVisibleMemorySize /Value"; info.RedirectStandardOutput = true; using (var process = Process.Start(info)) { if (process is not null) { output = process.StandardOutput.ReadToEnd(); var lines = output.Trim().Split("\n"); var freeMemoryParts = lines[0].Split("=", StringSplitOptions.RemoveEmptyEntries); var totalMemoryParts = lines[1].Split("=", StringSplitOptions.RemoveEmptyEntries); var Total = Math.Round(double.Parse(totalMemoryParts[1]) / 1024 / 1024, 0); var Free = Math.Round(double.Parse(freeMemoryParts[1]) / 1024 / 1024, 0); Log.WriteLine($"{Total}GB Total physical memory, {Free}GB Free"); } else { Log.ERROR("Proces.Start() failed."); } } } //---------------- #endregion #region " .NET ランタイム情報 " //---------------- Log.WriteLine($".NET version {Environment.Version}"); //---------------- #endregion Log.WriteLine(""); //---------------- #endregion #region " フォルダ変数を設定する。" //---------------- { var exePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? ""; Folder.フォルダ変数を追加または更新する("Exe", exePath); Folder.フォルダ変数を追加または更新する("ResourcesRoot", Path.Combine(exePath, "Resources")); Folder.フォルダ変数を追加または更新する("DrumSounds", Path.Combine(exePath, @"Resources\Default\DrumSounds")); Folder.フォルダ変数を追加または更新する("SystemSounds", Path.Combine(exePath, @"Resources\Default\SystemSounds")); Folder.フォルダ変数を追加または更新する("Images", Path.Combine(exePath, @"Resources\Default\Images")); Folder.フォルダ変数を追加または更新する("AppData", AppDataフォルダ名); Folder.フォルダ変数を追加または更新する("UserProfile", Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)); } //---------------- #endregion #region " スレッドプールのオンラインスレッド数を変更する。" //---------------- { const int 望オンラインスレッド数 = 32; // 既定の数はCPUコア数。 // .NET の仕様により、Taskの同時利用数が最小値を超えると、それ以降の Task.Run での起動には最大2回/秒もの制限がかかる。 // https://docs.microsoft.com/ja-jp/dotnet/api/system.threading.threadpool.getminthreads ThreadPool.GetMaxThreads(out int workMax, out int compMax); ThreadPool.GetMinThreads(out int workMin, out int compMin); ThreadPool.SetMinThreads( Math.Clamp(望オンラインスレッド数, min: workMin, max: workMax), // workMin 〜 workMax の範囲を越えない Math.Clamp(望オンラインスレッド数, min: compMin, max: compMax)); // compMin 〜 compMax の範囲を越えない } //---------------- #endregion // メインループ Application.SetHighDpiMode(HighDpiMode.SystemAware); Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); App appForm; do { using (appForm = new App()) { // アプリのメインループを実行する。 // アプリが終了するまでこのメソッドからは戻ってこない。 Application.Run(appForm); } } while(appForm.再起動が必要); // 戻ってきた際、再起動フラグが立っていたらここでアプリを再起動する。 #region " 備考: 再起動について " //---------------- // .NET Core 3 で Application.Restart() すると、「起動したプロセスじゃないので却下」と言われる。 // おそらく起動プロセスが dotnet であるため? // // ↓没コード // if( appForm.再起動が必要 ) // { // // 注意:Visual Sutdio のデバッグ>例外設定で Common Language Runtime Exceptions にチェックを入れていると、 // // ここで InvalidDeploymentException が発生してデバッガが一時停止するが、これは「ファーストチャンス例外」なので、 // // 単に無視すること。 // Application.Restart(); // } //---------------- #endregion // 終了 timeEndPeriod(1); Log.WriteLine(""); Log.WriteLine("遊んでくれてありがとう!"); } #if !DEBUG // Release 時には、未処理の例外をキャッチしたらダイアログを表示する。 catch (Exception) { } #else // Debug 時には、未処理の例外が発出されても無視。(デバッガでキャッチすることを想定。) finally { } #endif }
static void Main(string[] args) { try { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); #region " SharpDX のデバッグパラメータを設定する。" //---------------- #if DEBUG SharpDX.Configuration.EnableReleaseOnFinalizer = true; // ファイナライザの実行中、未解放のCOMを見つけたら解放を試みる。 SharpDX.Configuration.EnableTrackingReleaseOnFinalizer = true; // その際には Trace にメッセージを出力する。 #endif //---------------- #endregion #region " フォルダ変数を設定する。" //---------------- var exePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); VariablePath.フォルダ変数を追加または更新する("Exe", $@"{exePath}\"); VariablePath.フォルダ変数を追加または更新する("System", Path.Combine(exePath, @"System\")); VariablePath.フォルダ変数を追加または更新する("AppData", Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData, Environment.SpecialFolderOption.Create), @"DTXMania2\")); VariablePath.フォルダ変数を追加または更新する("UserProfile", Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + @"\"); //---------------- #endregion #region " %USERPROFILE%/AppData/DTXMania2 フォルダがなければ作成する。" //---------------- var AppDataフォルダ名 = new VariablePath("$(AppData)"); if (!(Directory.Exists(AppDataフォルダ名.数なしパス))) { Directory.CreateDirectory(AppDataフォルダ名.数なしパス); } //---------------- #endregion #region " ログファイルへのログの複製出力開始。" //---------------- Trace.AutoFlush = true; ログファイル名 = Log.ログファイル名を生成する(Path.Combine(AppDataフォルダ名.数なしパス, "Logs"), "Log.", TimeSpan.FromDays(ログファイルの最大保存日数)); // ログファイルをTraceリスナとして追加。 // 以降、Trace(ならびにFDK.Logクラス)による出力は、このリスナ(=ログファイル)にも出力される。 Trace.Listeners.Add(new TraceLogListener(new StreamWriter(ログファイル名, false, Encoding.GetEncoding("utf-8")))); // 最初の出力。 Log.現在のスレッドに名前をつける("UI"); Log.WriteLine(Application.ProductName + " " + AppForm.リリース番号.ToString("000")); // アプリ名とバージョン Log.システム情報をログ出力する(); Log.WriteLine(""); //---------------- #endregion #region " コマンドライン引数を解析する。" //---------------- var options = new CommandLineOptions(); if (!options.解析する(args)) // 解析に失敗すればfalse { // 利用法を表示して終了。 Log.WriteLine(options.Usage); // ログと using (var console = new FDK.Console()) console.Out?.WriteLine(options.Usage); // 標準出力の両方へ return; } //---------------- #endregion #region " アプリ起動。" //---------------- // Appインスタンスを生成、初期化。 using (var app = new AppForm(options)) { if (app.WCFサービスをチェックする(options)) { // アプリを起動。 // アプリが終了するまで、このメソッドからは戻ってこない。 Application.Run(app); // 戻ってきた際、再起動フラグが立っていたらここでアプリを再起動する。 if (app.再起動が必要) { Application.Restart(); } } else { // false ならアプリを起動しない。 } } //---------------- #endregion } // Release 時には、未処理の例外をキャッチしたらダイアログを表示する。 #if !DEBUG catch (Exception e) { using (var dlg = new 未処理例外検出ダイアログ()) { Trace.WriteLine(""); Trace.WriteLine("====> 未処理の例外が検出されました。"); Trace.WriteLine(""); Trace.WriteLine(e.ToString()); dlg.ShowDialog(); } } #else // DEBUG 時には、未処理の例外が発出されても無視。(デバッガでキャッチすることを想定。) finally { } #endif System.Threading.Tasks.Task.Delay(1000).Wait(); Log.WriteLine(""); Log.WriteLine("遊んでくれてありがとう!"); }