2011-07-15 4 views
1

接続ハンドラ(ユーザ名とパスワードを要求するダイアログ)を作成しています。コードは、ダイアログを表示するハンドラです。このコードはスレッドから呼び出すことができるので、Invoke()の場合はInvokeRequiredが必要です。コントロールを使用できないときに呼び出す方法()

Controlでハンドラを初期化してInvokeRequiredにすることができますが、Controlがnullになることがあります。出来ますか?コードをどのように実装できますか?次は正しいですか?

public class GuiCredentialsHandler 
{ 
    // control used to invoke if needed 
    private static Control mInvokeControl; 

    /// <summary> 
    /// Initialize a GetCredentials handler for current process. 
    /// This method should be always called from the main thread, for 
    /// a correctly handling for invokes (when the handler is called 
    /// from a thread). 
    /// </summary> 
    /// <param name="parentControl">Application top form. 
    /// Can be null if unknown</param> 
    public static void Initialize(Control parentControl) 
    { 
     if (parentControl != null) 
     { 
      mInvokeControl = parentControl; 
     } 
     else 
     { 
      mInvokeControl = new Control(); 
      // force to create window handle 
      mInvokeControl.CreateControl(); 
     } 
    } 

    public static Credentials GetCredentials() 
    { 
     if (mInvokeControl.InvokeRequired) 
     { 
      return mInvokeControl.Invoke(
       new GetCredentialsDelegate(DoGetCredentials), null) 
       as Credentials; 
     } 
     else 
     { 
      return DoGetCredentials(); 
     } 
    } 

    private static Credentials DoGetCredentials() 
    { 
     // the code stuff goes here 
    } 

}

私の質問は以下のとおりです。

  1. 初期化()メソッドがUIThreadで実行されている場合は、私がInitializeMethod()
  2. にヌル制御を渡すとどうなりますか、しますコードは後で作業しますか?
  3. InvokeRequiredをテストするコントロールがない場合は、どのようなパターンを推奨しますか?事前に

おかげで


はEDIT:いくつかのテストをやって、私はInitialize()にnullを渡すと、InvokeRequiredが返すように見えるので、コントロールは、UIスレッドで実行されていないことを認識しています偽です。常に。だから私の質問は、私は実際に(起きる)Invokeを実行することができます私はコントロールを持っていないですか?


EDIT2mInvokeControl.CreateControl() fixs問題を行います。

+0

ログイン資格情報を取得するためのダイアログボックス/フォームを作成している場合は、それを呼び出しターゲットとして使用できるはずです。それ以外の場合、資格情報はどこから来ますか? –

+0

@David Lively:はい、DoGetCredentialsコールでフォームを作成しています –

答えて

1

単純な解決策は、ワーカースレッドがInvokeを呼び出すことができるメインスレッドに非表示のコントロールを作成することです。

+0

これを試しましたが、あなたは 'control.CreateControl()'を呼び出す必要があります。それ以外の場合は動作しません。私はリフレクタで 'Control'クラスを再検討し、' InvokeRequired'は、現在のスレッドを再びコントロールハンドルを作成したスレッドと比較します。 'CreateControl()'を呼び出さなければ、ハンドルは決して生成されず、常にfalseを返します。 –

+0

@ダニエルそれは完璧な意味があります。 'Invoke'の背後にある全体的な理由は、ウィンドウを受け取った関数がウィンドウを作成したスレッドで作成しなければならないWin32の要件を満たすことです。あなたは、アプリケーションの起動時にハンドルがUIスレッド上に作成されていることを確認する必要があります。 –

3

代わりにそのクラスにISynchronizeInvokeを実装してください。

public class GuiCredentialsHandler : ISynchronizeInvoke 
{ 
     //.... 

     private readonly System.Threading.SynchronizationContext _currentContext = System.Threading.SynchronizationContext.Current; 

     private readonly System.Threading.Thread _mainThread = System.Threading.Thread.CurrentThread; 

     private readonly object _invokeLocker = new object(); 
     //.... 


     #region ISynchronizeInvoke Members 

     public bool InvokeRequired 
     { 
      get 
      { 
       return System.Threading.Thread.CurrentThread.ManagedThreadId != this._mainThread.ManagedThreadId; 
      } 
     } 

     /// <summary> 
     /// This method is not supported! 
     /// </summary> 
     /// <param name="method"></param> 
     /// <param name="args"></param> 
     /// <returns></returns> 
     [Obsolete("This method is not supported!", true)] 
     public IAsyncResult BeginInvoke(Delegate method, object[] args) 
     { 
      throw new NotSupportedException("The method or operation is not implemented."); 
     } 

     /// <summary> 
     /// This method is not supported! 
     /// </summary> 
     /// <param name="method"></param> 
     /// <param name="args"></param> 
     /// <returns></returns> 
     [Obsolete("This method is not supported!", true)] 
     public object EndInvoke(IAsyncResult result) 
     { 
      throw new NotSupportedException("The method or operation is not implemented."); 
     } 

     public object Invoke(Delegate method, object[] args) 
     { 
      if (method == null) 
      { 
       throw new ArgumentNullException("method"); 
      } 

      lock (_invokeLocker) 
      { 
       object objectToGet = null; 

       SendOrPostCallback invoker = new SendOrPostCallback(
       delegate(object data) 
       { 
        objectToGet = method.DynamicInvoke(args); 
       }); 

       _currentContext.Send(new SendOrPostCallback(invoker), method.Target); 

       return objectToGet; 
      } 
     } 

     public object Invoke(Delegate method) 
     { 
      return Invoke(method, null); 
     } 

     #endregion//ISynchronizeInvoke Members 

} 

注:ここでは例があるクラスの実装のそれはSystem.Threading.SynchronizationContext.Currentを使用しているためSystem.Threading.SynchronizationContext.Currentがnullであるので、あなたはWindowsFormswpfではなく、コンソールアプリケーションでそれを使用することができます。

+0

あなたの答えをありがとう。 UIスレッドにない場合、このISynchronizeInvokeクラスのインスタンスを作成するとどうなりますか?たとえば、Windowsエクスプローラのシェル拡張スレッドからクラスを作成する場合です。 –

+0

@Daniel:あなたは大歓迎です、 'ISynchronizeInvoke'はインターフェイスです。インスタンスを作成することはできません。それを実装するクラスからインスタンスを作成することはできますが、UIスレッドになく、 'Invoke'を呼び出すと、別のスレッドにあってもUIスレッドのメソッドが呼び出されます。 Windowsフォームやwpfを使用していない場合は、独自の 'SynchronizationContext'を実装して、必要なスレッドで実行をマーシャリングする必要があります。 –

関連する問題