2 using System.Collections.Generic;
3 using System.ComponentModel;
4 using System.Diagnostics;
6 using System.Threading;
7 using System.Threading.Tasks;
8 using System.Windows.Forms;
9 using Microsoft.VisualBasic.ApplicationServices;
14 /// アプリケーションフォームの基礎クラス。
16 public class ApplicationBase : Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase
19 /// 基本的に、このインスタンスを使用する任意のスレッドはこれを lock してからアクセスすること。
21 protected readonly object スレッド間同期 = new object();
28 public bool ウィンドウモードである
30 get { return !this.全画面モードである; }
31 protected set { this.全画面モードである = !value; }
34 public ApplicationBase() : base()
36 this.EnableVisualStyles = true;
37 this.IsSingleInstance = true; // 二重起動を禁止する
38 this.MainForm = new Form();
39 this.MainForm.Load += OnLoad;
40 this.MainForm.FormClosing += OnClosing;
41 this.MainForm.ClientSizeChanged += OnClientSizeChanged;
44 protected SharpDX.Size2F 設計画面サイズdpx = SharpDX.Size2F.Empty; // 初期化する() 内で設定すること。
45 protected FDK.メディア.デバイスリソース デバイスリソース = null;
46 protected System.Threading.Thread 進行描画スレッド = null;
47 protected AutoResetEvent 進行描画スレッドのキャンセル = null;
48 protected System.Threading.AutoResetEvent 進行描画スレッドの起動を完了した = new System.Threading.AutoResetEvent( false );
50 protected virtual void 初期化する()
57 Debug.Assert( null == this.デバイスリソース, "デバイスリソースの作成前であること。" );
58 this.設計画面サイズdpx = new SharpDX.Size2F( 640, 480 );
61 protected virtual void 終了する()
68 Debug.Assert( null != this.デバイスリソース, "デバイスリソースが解放される前であること。" );
71 protected virtual void シーンを描画する()
75 // このメソッドはGUIスレッドではなく進行描画スレッドから呼び出されるので注意する。
89 // 表示する。垂直帰線待ちなどで時間がかかるので、lock しないこと。
90 this.デバイスリソース.SwapChain1.Present( 0, SharpDX.DXGI.PresentFlags.None );
92 protected virtual void デバイス依存リソースを解放する()
96 Debug.Assert( null != this.デバイスリソース ); // 解放前であること。
98 // ここで自分のデバイス依存リソースを解放する。
102 protected virtual void デバイス依存リソースを再構築する()
106 Debug.Assert( null != this.デバイスリソース ); // 再生成済みであること。
108 // ここで自分のデバイス依存リソースを解放する。
113 protected void 全画面モードとウィンドウモードを切り替える()
119 this.デバイスリソース.SwapChain1.SetFullscreenState( false, null ); // ウィンドウモードへ。
120 this.ウィンドウモードである = true;
124 this.デバイスリソース.SwapChain1.SetFullscreenState( true, null ); // 全画面モードへ。
125 this.全画面モードである = true;
130 /// アプリが二重起動されたときに発生するイベント。
133 /// 後続のインスタンスは起動せず、既存のインスタンスに対してこのイベントが発生する。
134 /// eventArg.CommandLine で、後続のインスタンスのコマンドライン引数を確認することができる。
136 protected override void OnStartupNextInstance( StartupNextInstanceEventArgs eventArgs )
138 // 必要がれば、派生クラスで実装すること。
141 private bool Closing中 = false;
143 private void OnLoad( object sender, EventArgs e )
145 FDK.Log.現在のスレッドに名前をつける( "GUI" );
146 FDK.Log.BeginInfo( $"{FDK.Utilities.現在のメソッド名}" );
148 #region " アプリケーションを初期化する。"
150 FDK.Log.BeginInfo( "派生クラスを初期化します。" );
153 Debug.Assert( SharpDX.Size2F.Empty != this.設計画面サイズdpx, "初期化メソッド内で設計画面サイズを設定してあること。" );
155 FDK.Log.Info( $"設計画面サイズ: {this.設計画面サイズdpx}" );
156 FDK.Log.Info( $"物理画面サイズ: {this.MainForm.ClientSize}" );
158 FDK.Log.EndInfo( "派生クラスを初期化しました。" );
161 #region " デバイスリソースを作成する。"
163 FDK.Log.BeginInfo( "デバイスリソースを作成します。" );
166 this.デバイスリソース = new メディア.デバイスリソース();
167 this.デバイスリソース.設計画面サイズdpx = this.設計画面サイズdpx;
168 this.デバイスリソース.すべてのリソースを作成する( this.MainForm.ClientSize, this.MainForm.Handle );
170 FDK.Log.EndInfo( "デバイスリソースを作成しました。" );
173 #region " 進行描画スレッドを開始する。"
175 FDK.Log.BeginInfo( "進行描画スレッドを開始します。" );
178 this.進行描画スレッドのキャンセル = new AutoResetEvent( false );
179 this.進行描画スレッド = new Thread( this.進行描画スレッドエントリ );
180 //this.進行描画スレッド.Priority = ThreadPriority.AboveNormal;
181 this.進行描画スレッド.Start();
182 this.進行描画スレッドの起動を完了した.WaitOne(); // スレッドの起動完了通知を待つ。
187 FDK.Log.EndInfo( $"{FDK.Utilities.現在のメソッド名}" );
189 private void OnClosing( object sender, FormClosingEventArgs e )
191 FDK.Log.BeginInfo( $"{FDK.Utilities.現在のメソッド名}" );
193 this.Closing中 = true;
196 if( ( null != this.進行描画スレッドのキャンセル ) &&
197 ( null != this.進行描画スレッド ) )
201 this.進行描画スレッドのキャンセル.Set(); // lock 内で呼び出したら絶対タイムアウトになるので注意。
202 FDK.Log.Info( "進行描画スレッドにキャンセルを発行しました。" );
204 bool done = this.進行描画スレッド.Join( millisecondsTimeout: 5000 ); // タイムアウトは保険。
206 FDK.Log.Info( "進行描画スレッドの完了を確認しました。" );
208 FDK.Log.ERROR( "進行描画スレッドの完了を時間内に確認できませんでした。" );
210 catch( AggregateException ex )
212 if( ex.InnerExceptions.Any( ( 内部例外 ) => ( 内部例外 is OperationCanceledException ) ) )
221 catch( ObjectDisposedException )
233 this.デバイスリソース?.Dispose();
234 this.デバイスリソース = null;
237 FDK.Log.EndInfo( $"{FDK.Utilities.現在のメソッド名}" );
239 private void OnClientSizeChanged( object sender, EventArgs e )
241 FDK.Log.BeginInfo( $"{FDK.Utilities.現在のメソッド名}" );
242 FDK.Log.Info( $"新しいクライアントサイズ = {this.MainForm.ClientSize}" );
248 if( null == this.デバイスリソース )
250 FDK.Log.Info( " まだ初期化されてないので、何もしません。" );
251 FDK.Log.EndInfo( $"{FDK.Utilities.現在のメソッド名}" );
254 if( this.MainForm.WindowState == FormWindowState.Minimized )
256 FDK.Log.Info( "最小化されました。" );
257 FDK.Log.EndInfo( $"{FDK.Utilities.現在のメソッド名}" );
262 FDK.Log.Info( " 終了処理中なので、何もしません。" );
263 FDK.Log.EndInfo( $"{FDK.Utilities.現在のメソッド名}" );
269 var dr = this.デバイスリソース;
270 Debug.Assert( null != dr, "デバイスリソースが作成済みであること。" );
272 // 現在の画面モードを取得しておく。(Alt+TABなど、勝手に全画面を解除されることもあるので。)
273 SharpDX.Mathematics.Interop.RawBool fullscreen;
274 SharpDX.DXGI.Output outputTarget;
275 dr.SwapChain1.GetFullscreenState( out fullscreen, out outputTarget );
276 this.全画面モードである = fullscreen;
277 outputTarget?.Dispose();
278 FDK.Log.Info( $"現在、全画面モードである = {this.全画面モードである}" );
281 this.デバイス依存リソースを解放する();
282 dr.サイズに依存するリソースを解放する();
285 dr.物理画面サイズpx = new SharpDX.Size2F( this.MainForm.ClientSize.Width, this.MainForm.ClientSize.Height );
288 dr.サイズに依存するリソースを作成する();
289 this.デバイス依存リソースを再構築する();
292 FDK.Log.EndInfo( $"{FDK.Utilities.現在のメソッド名}" );
295 private void 進行描画スレッドエントリ()
297 FDK.Log.現在のスレッドに名前をつける( "Main" );
298 FDK.Log.Info( "進行描画スレッドを起動しました。" );
300 this.進行描画スレッドの起動を完了した.Set(); // 生成元へ起動完了を通知する。
304 bool アプリを終了せよ = false;
308 // 別スレッドからキャンセル要求があれば、終了フラグを立てる。
309 if( this.進行描画スレッドのキャンセル.WaitOne( 0 ) )
315 // D3Dデバイスが消失していれば再構築する。
317 this.デバイスリソース.D3Dデバイスが消失していれば再構築する( out 異常発生 );
323 // 終了フラグがセットされていれば、ループを抜けてスレッドを終了する。
327 // それ以外なら、シーンを進行・描画する。
331 FDK.Log.Info( "進行描画スレッドを終了します。" );