0

ObserverクラスとSubscriberクラスがあります。
テストの目的で、オブザーバーは偽のメッセージと呼び出しを生成するスレッドを作成しますCServerCommandObserver::NotifySubscribers()。これは次のようになります。

void CServerCommandObserver::NotifySubscribers(const Command cmd, void const * const pData)
{
    // Executed in worker thread //

    for (Subscribers::const_iterator it = m_subscribers.begin(); it != m_subscribers.end(); ++it)
    {
        const CServerCommandSubscriber * pSubscriber = *it;

        const HWND hWnd = pSubscriber->GetWindowHandle();
        if (!IsWindow(hWnd)) { ASSERT(FALSE); continue; }

        SendMessage(hWnd, WM_SERVERCOMMAND, cmd, reinterpret_cast<LPARAM>(pData));
    }
}

サブスクライバーはCDialog派生クラスであり、からも継承しCServerCommandSubscriberます。

派生クラスに、サーバーコマンドをサブスクライバークラスハンドラーにルーティングするメッセージマップエントリを追加しました。

// Derived dialog class .cpp
ON_REGISTERED_MESSAGE(CServerCommandObserver::WM_SERVERCOMMAND, HandleServerCommand)

// Subscriber base class .cpp
void CServerCommandSubscriber::HandleServerCommand(const WPARAM wParam, const LPARAM lParam)
{
    const Command cmd = static_cast<Command>(wParam);

    switch (cmd)
    {
    case something:
        OnSomething(SomethingData(lParam)); // Virtual method call
        break;
    case // ...
    };
}

問題は、HandleServerCommand()メソッドで奇妙なクラッシュが発生することです。

これは次のようになります。

デバッグエラー!

プログラム:c:\ myprogram.exe
モジュール:
ファイル:i386 \ chkesp.c
行:42

ESPの値は、関数呼び出し全体で適切に保存されませんでした。これは通常、ある呼び出し規約で宣言された関数を、別の呼び出し規約で宣言された関数ポインターで呼び出した結果です。

AfxBeginThread()が必要とする関数ポインターを確認しました。

typedef UINT (AFX_CDECL *AFX_THREADPROC)(LPVOID); // AFXWIN.H

static UINT AFX_CDECL MessageGeneratorThread(LPVOID pParam); // My thread function

私には、これは互換性があるように見えますね。

他に何を探す必要があるのか​​わかりません。何か案は?

私は別の奇妙な観察を行いました。これは関連している可能性があります。メソッドでは、ハンドルが指すウィンドウが存在するかどうかを確認するためNotifySubscribersに呼び出します。IsWindow()どうやらそうです。ただし、呼び出しCWnd::FromHandlePermanent()はNULLポインターを返します。

4

3 に答える 3

2

からafxmsg_.h:

// for Registered Windows messages
#define ON_REGISTERED_MESSAGE(nMessageVariable, memberFxn) \
    { 0xC000, 0, 0, 0, (UINT_PTR)(UINT*)(&nMessageVariable), \
        /*implied 'AfxSig_lwl'*/ \
        (AFX_PMSG)(AFX_PMSGW) \
        (static_cast< LRESULT (AFX_MSG_CALL CWnd::*)(WPARAM, LPARAM) > \
        (memberFxn)) },

したがって、署名はLRESULT ClassName::FunctionName(WPARAM, LPARAM)ですが、あなた の署名は ですvoid ClassName::FunctionName(const WPARAM, const LPARAM)。これはコンパイルされるべきではありません。少なくとも VS2008 ではコンパイルされません。

CServerCommandSubscriber クラス (ヘッダー ファイル内) の HandleServerCommand 宣言は何ですか?

于 2010-06-23T14:21:21.127 に答える
1

私は最終的にウィンドウメッセージなしでそれを行うことに決め、現在私の回避策をここに投稿しています. 多分それは他の誰かを助けるでしょう。

オブザーバーがサブスクライバーにウィンドウ メッセージを投稿できるようにする代わりに、オブザーバーがデータを同期されたサブスクライバー バッファーに入れられるようにします。ダイアログ クラスのサブスクライバーは、タイマーを使用して定期的にバッファーをチェックし、それらが空でない場合は適切なハンドラーを呼び出します。
いくつかの欠点があります。

  • データ型ごとに、サブスクライバーにバッファー メンバーを追加する必要があるため、コーディングの手間がかかります。
  • データは通話中に 1 回だけではなく、加入者ごとに存在するため、より多くのスペースを消費しSendMessage()ます。
  • また、メッセージの処理中にオブザーバ スレッドが中断されることに頼るのではなく、手動で同期を行う必要があります。

A - IMO - 大きな利点は、型安全性が優れていることです。lParamの値に応じて、一部の値をポインターにキャストする必要はありませんwParam。このため、この回避策は、元のアプローチよりも優れていなくても、非常に受け入れられると思います。

于 2010-06-23T16:41:49.890 に答える
1

私には、これは互換性があるように見えますね。

構文的にはそのように見えます。

他に何を探せばいいのかわからない。何か案は?

はい: プラグイン ライブラリをデバッグ設定でコンパイルし、リリース コンパイルされたアプリケーションで使用するときに、同じ問題が発生しました。

基本的に、問題はスタックの破損のように見えます。

NotifySubscribers別のスレッドで実行しているため、 の代わりにPostMessage(または) を使用することを検討してください。PostThreadMessageSendMessage

これはクラッシュの実際の原因ではないかもしれませんが、とにかく変更を行う必要があります (SendMessageデータをまったく保護せずに使用してスレッド化コンテキストを切り替えているためです。

于 2010-06-23T13:47:24.980 に答える