3

これは Windows の質問のように思えますが、私の質問の本質は実際には C++11 (または MS C++0x TR1) です。std::functionそれは、オブジェクトとそのライフサイクルを渡すことです。

Windows PostMessageAPI を介した非同期実行用の汎用フレームワークが必要です。これは、状況により、現在のメッセージ処理を終了し、タスクをメッセージ キューに登録する必要がある場合です。

フレームワークは、静的関数へのポインター ( 内WPARAM) とコンテキストへのポインター( 内)LPARAMで正常に動作しています。これには、「this」ポインターと他のコンテキストが含まれています。

私はそれを次のレベルに引き上げ、バインドと関数構造を使用したいと考えています。私はVisual Studio 2010を使用しています(つまりstd::function、存在しますがTR1です)。

これまでのところ、これは私が持っているものです:

私はWM_ASYNC_TASK教科書で a を登録していますが、Windows の手順 (私は実際に WTL を使用しています) は正常に動作しています。PostMessage正常に動作し、メッセージは最終的にandで正しく、期待どおりに到達onAsyncTaskします。wparamlparam

const UINT WM_ASYNC_TASK = RegisterWindowMessage(L"Async-Task");

これCPluginは、タスクを送信する場所です。メッセージをCHiddenWindow受け取るウィンドウの実装はどれですか。onAsyncTaskに関数を に転送してもらいたいのですがCPluginhwndウィンドウのハンドルです。

CPlugin私は:

void CPlugin::ShowMessageBox( void* arg ) {
    wchar_t* text = (wchar_t*)arg;
    MessageBox( NULL, text, L"Title", MB_OK )
}

void CPlugin::Sender( ) {
    std::function<void(void*)> f = std::bind( &CPlug::ShowMessagebox, 
                                              this, 
                                              std::placeholders::_1 );
    PostMessage( hwnd, WM_ASYNC_TASK, (WPARAM)f, (LPARAM)"Hello!!" );
}

CHiddenWindow の WM_ASYNC_TASK メッセージのハンドラーで次のことを行いました。

void CHiddinWindow::onAsyncTask( WPARAM wparam, LPARAM lparam, /* more arguments */ )
{
    std::function<void(void*)> f = (std::function<void(void*)>)wparam;
    void* arg = lparam;
    f( arg );
}

質問:

  1. 現在、f を WPARAM にキャストしようとすると、PostMessage(WPARAMは基本的には long です) 内でコンパイラが文句を言います。
  2. f:のアドレスを取得していれば問題ありません(WPARAM)&f
  3. これは、f のライフサイクルについて別の問題を提起します。どのように保管すればよいですか?
  4. 渡すとき(私の欲求ではなく、外に置いて&fおく)、逆参照しようとすると、でアクセス違反が発生します。fSender()onAsyncHandlerf
  5. ライフサイクルについて、どうにかして f を制御下に置くことはできstd::shared_ptrますか?
  6. wparam で関数 (私の場合はテキスト) に引数を渡す本当の理由はありません。オブジェクトのテキスト部分を持つ方法があるべきだと思いますがstd::function、構文で失敗します。

私が言ったように、私は可変個引数テンプレートの前に VS 2010 を使用しています。Microsoft には、C++0x の TR1 実装があります。現在利用できないため、ブーストを含まないソリューションを探しています。

ありがとうございました!

4

1 に答える 1

2
  1. コンパイラは正しいです。実際には にstd::functionキャストできなかった自明でないオブジェクトです。WPARAMunsigned int
  2. はい、これは可能です (そして実際には唯一の正しい方法です) が、正しい型キャストとライフ タイム管理の問題が発生します。
  3. の寿命fは によって管理されCPluginます。メンバー関数CPluginにバインドすると、寿命thisと密接に関連しています。CPlugin
  4. もちろん、それは関数fの範囲外に出たとき(戻ってきたとき)に破棄されるからです。Sender()また、非同期メッセージングのため、これCHiddenWindowはメッセージを処理する前に発生します。In CHiddenWindow::onAsyncTaskpointer tofは既に無効です。
  5. shared_ptr共有所有権を強制するための優れたツールです(これはあなたの場合です)が、それでもそれを経由して渡すことはできませんWPARAM
  6. ; の何が問題になっていstd::bind(&CPlugin::ShowMessagebox, this, "Hello!")ますか?

このようなフレームワークの可能な解決策:

  1. グローバル非同期タスク キューを作成し、CPluginそれにCHiddenWindowアクセスできる必要があります
  2. キュー内の各タスクには、カウントスルー数などの一意の識別子があります
  3. CPlugin::Sender()はタスクをキューに入れCHiddenWindow、タスク ID を として非同期メッセージをポストしますWPARAM/LPARAMSender()また、このタスク ID をインスタンスに保持します。ピアがすべてのメッセージを処理する前にインスタンスが破棄さCPluginれた場合に備えて、キューから削除されます。CPlugin
  4. CHiddenWindow::onAsyncTaskタスク ID を取得し、キュー内の対応するタスクを検索し、タスクが見つかった場合はタスク ファンクターを実行します。次に、キューからタスクを削除します。

もちろん、次のような単純な (そして間違った) ソリューションを使用することもできます。

typedef std::function<void(void*)> Func;

void CPlugin::Sender()
{
    Func* f = new Func(std::bind(&CPlugin::ShowMessagebox, this, "Hello!"));
    PostMessage(hwnd, WM_ASYNC_TASK, (WPARAM)f, 0);
}

void CHiddenWindow::onAsyncTask(WPARAM wparam, LPARAM lparam)
{
    Func* f = (Func*)wparam;
    (*f)();
    delete f;
}

しかし、このソリューションには 2 つの欠点があります。

  1. 投稿されたメッセージが受信者によって処理されない場合、たとえば何らかの理由で閉じられる場合はどうなるでしょうか? この場合、Funcオブジェクトがリークされ、メモリが無駄になります。
  2. CPlugin受信者がメッセージを処理する前に、メッセージを送信したインスタンスが破棄された場合はどうなりますか? この場合、f()内部で呼び出すと未定義の動作 (おそらくアクセス違反/クラッシュ) が発生しCHiddenWindow::onAsyncTaskます。

したがって、投稿されたすべてのメッセージが受信者によって処理され、投稿されたすべてのメッセージが処理される前に送信者が破棄されないという強い保証がある場合にのみ、このようなソリューションを検討する必要があります。

于 2012-11-01T13:39:12.050 に答える