Microsoft DirectX 8.0

エラーのログ

Microsoft® DirectShow® 編集サービス (DES) では、DES プロジェクトの読み込み、構築、またはレンダリング時に発生するエラーを、ログに記録するための組み込みメカニズムが提供される。ここでは、XML プロジェクト ファイルを読み込んでレンダリングを試みるサンプル コンソール アプリケーションについて説明する。エラーが起きた場合、このアプリケーションはエラー メッセージをコンソール ウィンドウに表示する。ここで紹介するサンプル コードは、「プロジェクトのロードとプレビュー」に示された例に基づいて構築されている。

注 :  ユーザー アプリケーションでは、エラー ログを実装する必要はない。DES は、明示的に要求しなければエラーをログに記録しない。

ここでは、ユーザーが COM クライアント プログラミングおよび DES タイムライン モデルについて理解していることを想定している。また、ユーザーは COM オブジェクト プログラミングの基本事項を理解している必要がある。DES におけるタイムラインの詳細については、「タイムライン モデル」を参照すること。

ここには以下のセクションが含まれる。

エラー ログの概要

エラー処理に関して最大の柔軟性をアプリケーションに与えるために、DES はコールバック メカニズムを使用している。ユーザー アプリケーションでは、エラー ログをとるためのメソッドを実装する。実行時にエラーが発生した場合、DES はユーザーによって提供されたメソッドを呼び出す。このメソッドは、エラーの詳細を示すパラメータをとる。メソッドがこの情報をどのように処理するかはプログラマに任せられる。ただし、プログラムの実行を妨害する可能性があるので、可能な限り早く復帰する必要がある。

エラーをログに記録するコールバック メソッドは、COM インターフェイス IAMErrorLog に含まれている。ユーザー アプリケーションは、このインターフェイスを実装しなければならない。すべての COM インターフェイスと同様に、IAMErrorLogIUnknown インターフェイスを継承するので、ユーザー アプリケーションは、このインターフェイスも実装しなければならない。

これらの COM インターフェイスの実装には、いくつかの方法が選択できる。IUnknown メソッドの標準の実装を提供する、Active Template Library (ATL) を使用できる。DirectShow は C++ 基底クラス CUnknown も提供しており、これによって COM インターフェイスの実装が容易になる。CUnknown の使い方については、「IUnknown の実装方法」を参照すること。

後のサンプル コードでは、コード内に含まれる C++ クラスを定義している。このクラスは、IUnknownIAMErrorLog の両方を実装する。ただし、CoCreateInstance をサポートしていないため、真の COM オブジェクトは生成されない。しかし、この手法は、サンプル コードの目的としては妥当なものである。

エラー ログ クラスの作成

最初に、エラー ログ記録を実装するクラスを宣言する。このクラスは、IAMErrorLog インターフェイスを継承する。これには、3 つの IUnknown メソッドと、IAMErrorLog の 1 つのメソッドの宣言が含まれる。このクラスの宣言を次に示す。

class CErrReporter : public IAMErrorLog
{
protected:
    long    m_lRef; // Reference count.

public:
    CErrReporter() { m_lRef = 0; }

    // IUnknown
    STDMETHOD(QueryInterface(REFIID, void**));
    STDMETHOD_(ULONG, AddRef());
    STDMETHOD_(ULONG, Release());

    // IAMErrorLog
    STDMETHOD(LogError(LONG, BSTR, LONG, HRESULT, VARIANT*));
};

このクラスの唯一のメンバ変数は、オブジェクトの参照カウントを保持する _lRef である。

次に、IUnknown のメソッドを定義する。詳細については、後のサンプル コードを参照すること。これで、COM フレームワークを使うことにより IAMErrorLog インターフェイスを実装することができる。以下では、その方法を説明している。

IAMErrorLog の実装

IAMErrorLog インターフェイスには、単一のメソッド LogError がある。このメソッドのパラメータには、発生したエラーに関する情報が含まれる。

STDMETHODIMP LogError(
    LONG Severity,                   // 予約済み。使用しないこと。
    BSTR ErrorString,                // 詳細。
    LONG ErrorCode,                  // エラー コード。
    HRESULT hresult,                 // エラーを発生させた HRESULT。
    VARIANT *pExtraInfo);            // エラーに関する追加情報。

エラー コードとエラー文字列は、DirectShow 編集サービスで定義されている。エラーの一覧については、「レンダリング エラー」を参照すること。

パラメータ pExtraInfo には、エラーに関する追加情報を保持する VARIANT 型へのポインタが入る。この VARIANT 型のデータ型と内容は、発生した個々のエラーによって異なる。

たとえば、不正なファイル名によってエラーが発生した場合、VARIANT は不正なファイル名の文字列となる。エラーには追加情報がないものがあるので、pExtraInfo が NULL であることもある。次のコードは、この VARIANT の vt メンバをテストする方法を示している。このメンバは、データ型を指定し、それに応じてメッセージの書式を設定する。

if( pExtraInfo )    // もしあれば、追加情報をレポートする。
{                           
    printf("\tExtra info: ");
    if( pExtraInfo->vt == VT_BSTR )      // 追加情報は BSTR である。
    {
        char szExtra[256];
        WideCharToMultiByte(CP_ACP, 0, pExtraInfo->bstrVal, -1, szExtra, 256, 0, 0);
        printf("%s\n", szExtra);
    } 
    else if( pExtraInfo->vt == VT_I4 )   // 追加情報は整数である。
        printf("%d\n", pExtraInfo->lVal);

    else if( pExtraInfo->vt == VT_R8 )   // 追加情報は浮動小数点である。
        printf("%f\n", pExtraInfo->dblVal);
}

注 :  pExtraInfo が指す VARIANT を解放してはならない。また、VARIANT はメソッドが復帰した後は無効になるので、後で参照してはならない。

エラー ログの設定

エラー ログ クラスを実装したら、そのクラスの新しいインスタンスを作成する。次に、タイムライン上で IAMSetErrorLog::put_ErrorLog メソッドを呼び出すことにより、そのインスタンスを指すポインタを DES に与える。タイムラインに IAMSetErrorLog インターフェイスについて照会する。すべてのエラーがログに記録されるようにするには、タイムラインの読み込み、保存、およびレンダリングを行う前に、このメソッドを呼び出す必要がある。

IAMSetErrorLog  *pSetLog = NULL;
IAMErrorLog     *pLog = new CErrReporter();

pTL->QueryInterface(IID_IAMSetErrorLog, (void **)&pSetLog);
pSetLog->put_ErrorLog(pLog);
pSetLog->Release();

エラー ログによって、アプリケーションでメソッドを呼び出すときに受け取る戻り値には影響はない。エラー ログは、通常のエラー処理手法を補完するが、置き換えるわけではない。堅牢なアプリケーションを作成するためには、常に HRESULT 値をチェックしなければならない。

サンプル コード

次のサンプル コードは、ここで説明したエラー ログ クラスを使用し、XML プロジェクト ファイルを読み込んでプレビューする、完全なコンソール アプリケーションである。プロジェクト ファイルの名前は、アプリケーションにハード コーディングされる。

コードを簡潔にするため、コンソール アプリケーションは ATL スマート ポインタを使用する。これによって、QueryInterfaceRelease を呼び出さなくても済む。必要な場合は、「プロジェクトのロードとプレビュー」でサンプル アプリケーションを変更できる。前に示したコードを追加するだけでよい。

#include <atlbase.h>
#include <dshow.h>
#include <qedit.h>
#include <stdio.h>

// エラー ログ クラス
class CErrReporter : public IAMErrorLog
{
protected:
    long    m_lRef; // Reference count.

public:
    CErrReporter() { m_lRef = 0; }

    // IUnknown
    STDMETHOD(QueryInterface(REFIID, void**));
    STDMETHOD_(ULONG, AddRef());
    STDMETHOD_(ULONG, Release());

    // IAMErrorLog
    STDMETHOD(LogError(LONG, BSTR, LONG, HRESULT, VARIANT*));
};

STDMETHODIMP CErrReporter::QueryInterface(REFIID riid, void **ppv)
{
    if (ppv == NULL)
        return E_POINTER;
    *ppv = NULL;

    if (riid == IID_IUnknown)
        *ppv = (IUnknown*)this;
    else if (riid == IID_IAMErrorLog)
        *ppv = (IAMErrorLog*)this;
        
    if (*ppv) 
    {
        AddRef();
        return S_OK;
    }
    return E_NOINTERFACE;
}

STDMETHODIMP_(ULONG) CErrReporter::AddRef()
{
    return InterlockedIncrement(&m_lRef);
}

STDMETHODIMP_(ULONG) CErrReporter::Release()
{
    if (InterlockedDecrement(&m_lRef) == 0)
    {
        delete this;
        return 0;
    }
    return m_lRef;
}

STDMETHODIMP CErrReporter::LogError(
        LONG Severity, 
        BSTR ErrorString,
        LONG ErrorCode, 
        HRESULT hresult, 
        VARIANT *pExtraInfo)
{
    char szError[256];
    WideCharToMultiByte(CP_ACP, 0, ErrorString, -1, szError, 256, 0, 0);
    printf("Error %d: %s\n", ErrorCode, szError);
    
    if( pExtraInfo )    // もしあれば、追加情報をレポートする。
    {                           
        printf("\tExtra info: ");
        if( pExtraInfo->vt == VT_BSTR )      // 追加情報は BSTR である。
        {
            char szExtra[256];
            WideCharToMultiByte(CP_ACP, 0, pExtraInfo->bstrVal, -1, szExtra, 256, 0, 0);
            printf("%s\n", szExtra);
        } 
        else if( pExtraInfo->vt == VT_I4 )   // 追加情報は整数である。
            printf("%d\n", pExtraInfo->lVal);

        else if( pExtraInfo->vt == VT_R8 )   // 追加情報は浮動小数点である。
            printf("%f\n", pExtraInfo->dblVal);
    }
    return hresult;
};

void __cdecl main(void)
{
    CoInitialize(NULL);
    {
    HRESULT hr;
    CComPtr< IAMTimeline >   pTL;
    CComPtr< IRenderEngine > pRenderEngine; 
    CComPtr< IXml2Dex >      pXML; 
    CComPtr< IGraphBuilder > pGraph;

    hr = CoCreateInstance(CLSID_AMTimeline, NULL, CLSCTX_INPROC_SERVER, 
                IID_IAMTimeline, (void**) &pTL);

    hr = CoCreateInstance(CLSID_Xml2Dex, NULL, CLSCTX_INPROC_SERVER, 
                IID_IXml2Dex, (void**) &pXML);

    hr = CoCreateInstance(CLSID_RenderEngine, NULL, CLSCTX_INPROC_SERVER,
                IID_IRenderEngine, (void**) &pRenderEngine);

    // エラー ログを設定する。
    CComQIPtr<IAMSetErrorLog, &IID_IAMSetErrorLog> pSetLog(pTL);
    if (pSetLog)
    {
        IAMErrorLog *pLog = new CErrReporter;    
        pSetLog->put_ErrorLog(pLog);
    }
   
    // プロジェクトを読み込んでプレビューする。
    hr = pXML->ReadXMLFile(pTL, L"C:\\Example.xtl"); 
    if (SUCCEEDED(hr))
    {
        hr = pRenderEngine->SetTimelineObject(pTL);
        hr = pRenderEngine->ConnectFrontEnd( );
        hr = pRenderEngine->RenderOutputPins( );
        hr = pRenderEngine->GetFilterGraph(&pGraph);

        CComQIPtr<IMediaControl, &IID_IMediaControl> pControl(pGraph);
        CComQIPtr<IMediaEvent, &IID_IMediaEvent> pEvent(pGraph);
        pControl->Run();

        long evCode;
        hr = pEvent->WaitForCompletion(INFINITE, &evCode);
        pControl->Stop();
    }
    // クリーン アップする。
    pRenderEngine->ScrapIt();
    }
    CoUninitialize();
}