none
NotifyIpInterfaceChange によるコールバック通知直後にAccess violation が発生する RRS feed

  • 質問

  • Windows 7 の環境で、ネットワークインタフェースの状態変化を検出するために NotifyIpInterfaceChange を使用してみましたが、状態変化検出直後に Access violation が発生します。ソースコードには問題内容に思えますが、不備などあればご指摘ください

    下記コードで、インタフェース状態の変化通知のコールバックが呼び出され、コールバック関数終了後にAccess violation が発生します。

    ::NotifyIpInterfaceChange( AF_UNSPEC , (PIPINTERFACE_CHANGE_CALLBACK)InterfaceChangeCallback , this , TRUE, &NotifyHandle ) ;

    VOID CNetworkStatusChange::InterfaceChangeCallback( IN PVOID CallerContext , IN PMIB_IPINTERFACE_ROW Row OPTIONAL , IN MIB_NOTIFICATION_TYPE NotificationType )
    {
     if( NotificationType != MibInitialNotification )
      ::SetEvent( CNetworkStatusChange::getInstance().m_hNotifyEvent ) ;
    }

    2010年5月24日 1:48

回答

  • 念のための確認ですが、呼び出し規約はあっているんですよね?
    コールバック関数としては NETIOAPI_API_ で、定義をたどっていくと、WINAPI、stdcall になります。

    VC のデフォルトは cdecl なので、呼び出し規約を変更するキーワードをつけてないと、ひどいことになります。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    • 回答としてマーク 太田章 2010年5月24日 14:07
    2010年5月24日 13:49
    モデレータ

すべての返信

  • ぱっと見で、気になるのは、この callback は static なんですよね?
    2010年5月24日 3:36
  • NotifyIpInterfaceChange() 3rd パラメータの CallerContext に指定している "this" ポインタが問題なのでは...
    提示されている Callback Routine の実装を見る限り、とりあえず CallerContext は必要なさそうなので、
    NotifyIpInterfaceChange() 3rd パラメータに NULL をセットしたらどうなるか、試してみてはいかがでしょう?
    2010年5月24日 9:36
  •   ::SetEvent( CNetworkStatusChange::getInstance().m_hNotifyEvent ) ;

    この部分が少し引っかかります。セオリーから行けば

     ::SetEvent( CNetworkStatusChange::getInstance()->m_hNotifyEvent ) ;

    だと思うのですが、上記のコードは転載する時にすり替わったとかしたのでしょうか?(getInstance() の後ろは、「.(ピリオド)」なくて「->」とするのが通例)

    この部分が転載漏れだったとして、getInstance() が返すインスタンスが正しいものなのかどうかが気になりますね。
    getInstance() の実装内容だとか、コールバック時にもインスタンスがちゃんと存在しているかとかは、大丈夫でしょうか?

    ちなみに、SetEvent を外すと動きはどうなるでしょうか?
    2010年5月24日 9:58
  • セオリーから行けば

     ::SetEvent( CNetworkStatusChange::getInstance()->m_hNotifyEvent ) ;

    だと思うのですが、上記のコードは転載する時にすり替わったとかしたのでしょうか?(getInstance() の後ろは、「.(ピリオド)」なくて「->」とするのが通例)

    -> 演算子でなければならない理由は何でしょうか?(ミス予防などでしょうか?)
    T& getInstance(); なら、. 演算子でも問題ないと思います。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    2010年5月24日 13:31
    モデレータ
  • はい、staticです

    2010年5月24日 13:36
  • NULLでも変わりませんでした。

    thisポインタを渡しているのは、当初thisポインタを使用してイベントハンドルを利用していたのですが、問題切り分けのためにthisポインタを使用しないようにした残骸です

    2010年5月24日 13:37
  • getinstance() はクラスの参照を返しているのでコーディングに問題はりません(コンパイル時もエラーやワーニングは出ていません)
    2010年5月24日 13:38
  • ちなみにSetEventを外しても変わりません。というか、::NotifyIpInterfaceChangeの第4引数にTRUEを指定しているので、初回は if( NotificationType != MibInitialNotification )の条件が成立せず、SetEventをせずにそのままコールバック関数は無処理で終了します。その直後にAccess violation が発生知るので原因がつかめていません
    2010年5月24日 13:41
  • 念のための確認ですが、呼び出し規約はあっているんですよね?
    コールバック関数としては NETIOAPI_API_ で、定義をたどっていくと、WINAPI、stdcall になります。

    VC のデフォルトは cdecl なので、呼び出し規約を変更するキーワードをつけてないと、ひどいことになります。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    • 回答としてマーク 太田章 2010年5月24日 14:07
    2010年5月24日 13:49
    モデレータ
  • 呼び出し規約が違っていました。

    netioapi.h の定義が以下のようになっていたのでそのまま使用していました。

    typedef
    VOID
    (*PIPINTERFACE_CHANGE_CALLBACK) (
        IN PVOID CallerContext,
        IN PMIB_IPINTERFACE_ROW Row OPTIONAL,
        IN MIB_NOTIFICATION_TYPE NotificationType
        );

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

    2010年5月24日 14:25