名前付きパイプからメッセージを読み取るスレッドがあります。これはブロッキング読み取りであるため、独自のスレッドにあります。このスレッドがメッセージを読み取るときに、メイン スレッドで実行されている Windows フォーム メッセージ ループに、メッセージの準備ができたことを通知する必要があります。どうやってやるの?win32 では PostMessage を実行しますが、その関数は .Net には存在しないようです (または、少なくとも見つけることができませんでした)。
3 に答える
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());
}
}
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);
}
}
実際にメッセージ ループにメッセージを投稿したいですか、それとも単にフォーム内のコントロールを更新したり、メッセージ ボックスを表示したりしたいだけですか? 前者の場合は、@Noldorin の応答を参照してください。後者の場合は、Control.Invoke() メソッドを使用して、「読み取り」スレッドからメイン UI スレッドへの呼び出しをマーシャリングする必要があります。これは、コントロールが作成されたスレッドによってのみコントロールを更新できるためです。
これは、.NET ではかなり標準的なことです。基本については、次の MSDN の記事を参照してください。
- Control.Invoke メソッド
- Control.InvokeRequired プロパティ(コミュニティ コンテンツの最初の例を参照)
これを行う方法を理解したら、Peter Duniho のブログで正規の手法を改善する方法を参照してください。