@@ -20,43 +20,79 @@ namespace FlashpointSecurePlayer {
2020 public class SingleInstance : Modifications {
2121 public SingleInstance ( EventHandler importStart , EventHandler importStop ) : base ( importStart , importStop ) { }
2222
23- // function to create a real MessageBox which
24- // automatically closes upon completion of tasks
25- private DialogResult ? ShowClosableMessageBox ( Task [ ] tasks , string text , string caption , MessageBoxButtons messageBoxButtons , MessageBoxIcon messageBoxIcon ) {
26- Form closableForm = new Form ( ) {
27- Size = new Size ( 0 , 0 )
28- } ;
29-
30- closableForm . BringToFront ( ) ;
31-
32- // need this because dialogResult defaults to Cancel when
33- // we want it to default to null
34- bool dialogResultSet = true ;
35-
36- Task . WhenAll ( tasks ) . ContinueWith ( delegate ( Task antecedentTask ) {
37- HandleAntecedentTask ( antecedentTask ) ;
38-
39- // this closes the form hosting the Message Box and
40- // causes it to stop blocking
41- closableForm . Close ( ) ;
42- dialogResultSet = false ;
43- } , TaskScheduler . FromCurrentSynchronizationContext ( ) ) ;
44-
45- // this line blocks execution, but the task above causes it to stop blocking
46- DialogResult dialogResult = MessageBox . Show ( closableForm , text , caption , messageBoxButtons , messageBoxIcon ) ;
23+ private delegate Task [ ] ClosableMessageBoxTasksDelegate ( CancellationToken token ) ;
24+ private delegate Task ClosableMessageBoxTaskDelegate ( CancellationToken token ) ;
25+
26+ // function to create a MessageBox which
27+ // automatically closes upon completion of multiple tasks
28+ private DialogResult ? MessageBoxShowClosable ( Form owner , string text , string caption , MessageBoxButtons buttons , MessageBoxIcon icon , ClosableMessageBoxTasksDelegate tasksDelegate ) {
29+ if ( owner == null ) {
30+ throw new ArgumentNullException ( "The owner is null." ) ;
31+ }
4732
48- if ( ! dialogResultSet ) {
49- return null ;
33+ Form closable = null ;
34+
35+ // owner is required for this invoke
36+ owner . InvokeIfRequired ( new MethodInvoker ( delegate ( ) {
37+ // the form is created, but not shown
38+ // its owner is set so the Message Box will act as an owned window
39+ closable = new Form {
40+ Owner = owner
41+ } ;
42+ } ) ) ;
43+
44+ using ( CancellationTokenSource cancellationTokenSource = new CancellationTokenSource ( ) ) {
45+ try {
46+ Task task = Task . WhenAll (
47+ tasksDelegate (
48+ cancellationTokenSource . Token
49+ )
50+ ) . ContinueWith (
51+ delegate ( Task antecedentTask ) {
52+ HandleAntecedentTask ( antecedentTask ) ;
53+
54+ // this closes the form hosting the Message Box and
55+ // causes it to stop blocking
56+ closable . InvokeIfRequired ( new MethodInvoker ( delegate ( ) {
57+ closable . Close ( ) ;
58+ } ) ) ;
59+ } ,
60+
61+ TaskScheduler . FromCurrentSynchronizationContext ( )
62+ ) ;
63+
64+ DialogResult ? dialogResult = null ;
65+
66+ // this blocks execution, but the task above causes it to stop blocking
67+ // if the dialog was closed upon completion of the tasks, we return null
68+ // faulted/cancelled task status doesn't count
69+ // (because it happens last, the task must run to completion to close the dialog)
70+ closable . InvokeIfRequired ( new MethodInvoker ( delegate ( ) {
71+ dialogResult = MessageBox . Show ( closable , text , caption , buttons , icon ) ;
72+ } ) ) ;
73+ return task . Status == TaskStatus . RanToCompletion ? null : dialogResult ;
74+ } finally {
75+ cancellationTokenSource . Cancel ( ) ;
76+ }
5077 }
51- return dialogResult ;
5278 }
5379
54- // single task version
55- private DialogResult ? ShowClosableMessageBox ( Task task , string text , string caption , MessageBoxButtons messageBoxButtons , MessageBoxIcon messageBoxIcon ) {
56- return ShowClosableMessageBox ( new Task [ ] { task } , text , caption , messageBoxButtons , messageBoxIcon ) ;
80+ // overload for a single task
81+ private DialogResult ? MessageBoxShowClosable ( Form owner , string text , string caption , MessageBoxButtons buttons , MessageBoxIcon icon , ClosableMessageBoxTaskDelegate taskDelegate ) {
82+ return MessageBoxShowClosable (
83+ owner ,
84+ text ,
85+ caption ,
86+ buttons ,
87+ icon ,
88+
89+ delegate ( CancellationToken token ) {
90+ return new Task [ ] { taskDelegate ( token ) } ;
91+ }
92+ ) ;
5793 }
5894
59- public override void Activate ( string templateName ) {
95+ public void Activate ( string templateName , Form owner ) {
6096 base . Activate ( templateName ) ;
6197
6298 if ( String . IsNullOrEmpty ( TemplateName ) ) {
@@ -97,6 +133,10 @@ public override void Activate(string templateName) {
97133 return ;
98134 }
99135
136+ if ( owner == null ) {
137+ throw new ArgumentNullException ( "The owner is null." ) ;
138+ }
139+
100140 const int PROCESS_BY_NAME_STRICT_WAIT_FOR_EXIT_MILLISECONDS = 1000 ;
101141
102142 Process [ ] processesByName = null ;
@@ -108,19 +148,22 @@ public override void Activate(string templateName) {
108148
109149 do {
110150 if ( processesByNameStrict != null ) {
111- // for future versions: would be nice if ShowClosableMessageBox created this token source
112- // and passed it as an argument to the tasks that we give to it
113- // (since you'll probably always want it)
114- // but unsure how to make the WhenAll in ShowClosableMessageBox pass the arguments
115- // to the task, or how to make the task itself recieve them
116- // maybe ThreadPool.QueueUserWorkItem does this?
117- using ( CancellationTokenSource cancellationTokenSource = new CancellationTokenSource ( ) ) {
118- CancellationToken token = cancellationTokenSource . Token ;
119-
120- // don't allow preceding further until
121- // all processes with the same name have been killed
122- DialogResult ? dialogResult = ShowClosableMessageBox (
123- Task . Run ( delegate ( ) {
151+ // don't allow preceding further until
152+ // all processes with the same name have been killed
153+ DialogResult ? dialogResult = MessageBoxShowClosable (
154+ owner ,
155+
156+ String . Format (
157+ Properties . Resources . ProcessCompatibilityConflict ,
158+ activeProcessName
159+ ) ,
160+
161+ Properties . Resources . FlashpointSecurePlayer ,
162+ MessageBoxButtons . OKCancel ,
163+ MessageBoxIcon . Warning ,
164+
165+ delegate ( CancellationToken token ) {
166+ return Task . Run ( delegate ( ) {
124167 // copy this, so it doesn't get set to null upon hitting OK
125168 Stack < Process > _processesByNameStrict = new Stack < Process > ( processesByNameStrict ) ;
126169
@@ -130,7 +173,8 @@ public override void Activate(string templateName) {
130173 if ( processByNameStrict != null ) {
131174 // test for cancellation before waiting
132175 // (so we don't wait unnecessarily for the next processes after this one)
133- while ( ! token . IsCancellationRequested ) {
176+ while ( token == null
177+ || ! token . IsCancellationRequested ) {
134178 if ( processByNameStrict . WaitForExit ( PROCESS_BY_NAME_STRICT_WAIT_FOR_EXIT_MILLISECONDS ) ) {
135179 break ;
136180 }
@@ -144,26 +188,13 @@ public override void Activate(string templateName) {
144188 }
145189
146190 _processesByNameStrict = null ;
147- } ) ,
148-
149- String . Format (
150- Properties . Resources . ProcessCompatibilityConflict ,
151- activeProcessName
152- ) ,
153-
154- Properties . Resources . FlashpointSecurePlayer ,
155- MessageBoxButtons . OKCancel ,
156- MessageBoxIcon . Warning
157- ) ;
158-
159- // end the task passed to the Closable Message Box
160- // we'll be creating a new one on the next loop as necessary
161- cancellationTokenSource . Cancel ( ) ;
162-
163- if ( dialogResult == DialogResult . Cancel ) {
164- Application . Exit ( ) ;
165- throw new InvalidModificationException ( "The operation was aborted by the user." ) ;
191+ } ) ;
166192 }
193+ ) ;
194+
195+ if ( dialogResult == DialogResult . Cancel ) {
196+ Application . Exit ( ) ;
197+ throw new InvalidModificationException ( "The operation was aborted by the user." ) ;
167198 }
168199
169200 processesByNameStrict = null ;
0 commit comments