16

名前付きパイプからメッセージを読み取るスレッドがあります。これはブロッキング読み取りであるため、独自のスレッドにあります。このスレッドがメッセージを読み取るときに、メイン スレッドで実行されている Windows フォーム メッセージ ループに、メッセージの準備ができたことを通知する必要があります。どうやってやるの?win32 では PostMessage を実行しますが、その関数は .Net には存在しないようです (または、少なくとも見つけることができませんでした)。

4

3 に答える 3

18

PostMessage(および同様SendMessageに) はWin32 API 関数であるため、.NET とは直接関連付けられていません。ただし、.NET は、P/Invoke 呼び出しを使用して、Win32 API との相互運用を適切にサポートしています。

あなたは Win32 プログラミング .NET を初めて使用するようですが、この MSDN Magazine の記事では、このトピックに関する確かな紹介が提供されています。

pinvoke.net の優れた Web サイトには、C#/VB.NET からこれらの API 関数の多くを使用する方法が詳しく説明されています。具体的にはこちらのページをご覧くださいPostMessage

標準宣言は次のとおりです。

[DllImport("user32.dll", SetLastError = true)]
static extern bool PostMessage(HandleRef hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

しかし、ページに示されているように、この関数をラップして Win32 エラーを適切に処理することをお勧めします。

void PostMessageSafe(HandleRef hWnd, uint msg, IntPtr wParam, IntPtr lParam)
{
    bool returnValue = PostMessage(hWnd, msg, wParam, lParam);
    if(!returnValue)
    {
        // An error occured
        throw new Win32Exception(Marshal.GetLastWin32Error());
    }
}        
于 2009-09-30T13:03:10.427 に答える
17

WinForms では、これをControl.BeginInvokeで実現できます。例:

public class SomethingReadyNotifier
{
   private readonly Control synchronizer = new Control();

   /// <summary>
   /// Event raised when something is ready. The event is always raised in the
   /// message loop of the thread where this object was created.
   /// </summary>
   public event EventHandler SomethingReady;

   protected void OnSomethingReady()
   {
       SomethingReady?.Invoke(this, EventArgs.Empty);
   }

   /// <summary>
   /// Causes the SomethingReady event to be raised on the message loop of the
   /// thread which created this object.
   /// </summary>
   /// <remarks>
   /// Can safely be called from any thread. Always returns immediately without
   /// waiting for the event to be handled.
   /// </remarks>
   public void NotifySomethingReady()
   {
      this.synchronizer.BeginInvoke(new Action(OnSomethingReady));
   }
}

WinForms に依存しない上記のよりクリーンなバリアントは、 を使用することSynchronizationContextです。メイン スレッドでSynchronizationContext.Currentを呼び出し、その参照を以下に示すクラスのコンストラクターに渡します。

public class SomethingReadyNotifier
{
    private readonly SynchronizationContext synchronizationContext;

    /// <summary>
    /// Create a new <see cref="SomethingReadyNotifier"/> instance. 
    /// </summary>
    /// <param name="synchronizationContext">
    /// The synchronization context that will be used to raise
    /// <see cref="SomethingReady"/> events.
    /// </param>
    public SomethingReadyNotifier(SynchronizationContext synchronizationContext)
    {
        this.synchronizationContext = synchronizationContext;
    }

    /// <summary>
    /// Event raised when something is ready. The event is always raised
    /// by posting on the synchronization context provided to the constructor.
    /// </summary>
    public event EventHandler SomethingReady;

    private void OnSomethingReady()
    {
        SomethingReady?.Invoke(this, EventArgs.Empty);
    }

    /// <summary>
    /// Causes the SomethingReady event to be raised.
    /// </summary>
    /// <remarks>
    /// Can safely be called from any thread. Always returns immediately without
    /// waiting for the event to be handled.
    /// </remarks>
    public void NotifySomethingReady()
    {
        this.synchronizationContext.Post(
                state => OnSomethingReady(),
                state: null);
        }
    }
于 2009-09-30T13:55:14.273 に答える
5

実際にメッセージ ループにメッセージを投稿したいですか、それとも単にフォーム内のコントロールを更新したり、メッセージ ボックスを表示したりしたいだけですか? 前者の場合は、@Noldorin の応答を参照してください。後者の場合は、Control.Invoke() メソッドを使用して、「読み取り」スレッドからメイン UI スレッドへの呼び出しをマーシャリングする必要があります。これは、コントロールが作成されたスレッドによってのみコントロールを更新できるためです。

これは、.NET ではかなり標準的なことです。基本については、次の MSDN の記事を参照してください。

これを行う方法を理解したら、Peter Duniho のブログで正規の手法を改善する方法を参照してください。

于 2009-09-30T13:49:03.513 に答える