4

IShellDispatch COM インターフェイス、より具体的にはFolderItemVerbsオブジェクトに非常に奇妙な問題があり、気が狂いそうになります!

FolderItemVerbs ::Release()の後にCoUninitialze()を呼び出すと、クラッシュが発生します。明らかに再現可能ですが、10 回に 1 回しか発生しません。

クラッシュは「0xC0000005: アクセス違反」エラーです。問題のあるコードをループで 100% 実行すると、遅かれ早かれクラッシュが再現されます :-(

サンプル プログラムを参照してください。

static int TestProc(const TCHAR *pcDirectoryName, const TCHAR *pcFileName)
{
    int iSuccess = 0;

    IShellDispatch *pShellDispatch = NULL;
    Folder *pFolder = NULL; FolderItem *pItem = NULL;
    FolderItemVerbs *pVerbs = NULL;

    HRESULT hr = CoCreateInstance(CLSID_Shell, NULL, CLSCTX_INPROC_SERVER, IID_IShellDispatch, (void**)&pShellDispatch);
    if(FAILED(hr) || (pShellDispatch ==  NULL))
    {
        iSuccess = -3;
        return iSuccess;
    }

    variant_t vaDirectory(pcDirectoryName);
    hr = pShellDispatch->NameSpace(vaDirectory, &pFolder);
    if(FAILED(hr) || (pFolder == NULL))
    {
        iSuccess = -4;
        pShellDispatch->Release();
        return iSuccess;
    }

    variant_t vaFileName(pcFileName);
    hr = pFolder->ParseName(vaFileName, &pItem);
    if(FAILED(hr) || (pItem == NULL))
    {
        iSuccess = -5;
        pFolder->Release();
        pShellDispatch->Release();
        return iSuccess;
    }

    hr = pItem->Verbs(&pVerbs);
    if(FAILED(hr) || (pVerbs == NULL))
    {
        iSuccess = -6;
        pItem->Release();
        pFolder->Release();
        pShellDispatch->Release();
        return iSuccess;
    }

    /* Here we would do something with the FolderItemVerbs */

    pVerbs->Release(); pVerbs = NULL; //If this line is commented out, we don't get a crash, but a massive memory leak!
    pItem->Release(); pItem = NULL;
    pFolder->Release(); pFolder = NULL;
    pShellDispatch->Release(); pShellDispatch = NULL;

    iSuccess = 1;
    return iSuccess;
}

//-----------------------------------------------------------------------------

static unsigned __stdcall ThreadProc(void* pArguments)
{
    HRESULT hr = CoInitialize(NULL);
    if((hr == S_OK) || (hr == S_FALSE))
    {
        threadParam_t *params = (threadParam_t*) pArguments;
        params->returnValue = TestProc(params->pcDirectoryName, params->pcFileName);
        CoUninitialize();
    }
    else
    {
        if(threadParam_t *params = (threadParam_t*) pArguments)
        {
            params->returnValue = -10;
        }
    }

    return EXIT_SUCCESS;
}  

完全なサンプル コードを ダウンロードしてください: http://pastie.org/private/0xsnajpia9lsmgnlf2afa

FolderItemVerbsオブジェクトを作成しないと、クラッシュはすぐになくなるため、FolderItemVerbsにクラッシュすることを明確に追跡したことにも注意してください。

また、 CoUninitialize()の前に「pVerbs->Release()」を呼び出さないと、クラッシュも発生しなくなりますが、明らかに大量のメモリリークが発生します。

もう 1 つの奇妙な点は、デバッガーでプログラムを実行すると、クラッシュが発生しないことです。しかし、プログラムを実行し、クラッシュを待ってから、デバッガーにクラッシュを処理させることができます。

残念ながら、取得したスタック トレースはあまり役に立ちません: http://pastie.org/private/cuwunlun2t5dc5lembpw

ここで何か間違ったことをしているとは思いません。この 2 日間、コードを何度も確認しました。したがって、これはすべて FolderItemVerbs のバグのようです!

以前にこれに遭遇した人はいますか、またはこれが FolderItemVerbs のバグであることを確認できますか? また、問題の回避策はありますか?

前もって感謝します !!!

4

1 に答える 1

0

みんなありがとう!

以下は、明示的なメッセージ ディスパッチを実行する「修正済み」コードです。

void DispatchPendingMessages(void)
{
    const DWORD uiTimeout = GetTickCount() + 10000;
    const HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    unsigned int counter = 0;
    if(hEvent)
    {
        for(;;)
        {
            MSG Message;
            while(PeekMessage(&Message, NULL, WM_NULL, WM_NULL, PM_REMOVE))
            {
                TranslateMessage(&Message);
                DispatchMessage(&Message);
            }
            const DWORD nWaitResult = MsgWaitForMultipleObjects(1, &hEvent, FALSE, 250, QS_ALLINPUT | QS_ALLPOSTMESSAGE);
            if((nWaitResult == WAIT_TIMEOUT) || (nWaitResult == WAIT_FAILED) || (GetTickCount() >= uiTimeout)) break;
        }
        CloseHandle(hEvent);
    }
}

//-----------------------------------------------------------------------------

static int TestProc(const TCHAR *pcDirectoryName, const TCHAR *pcFileName)
{
    int iSuccess = 0;

    IShellDispatch *pShellDispatch = NULL;
    Folder *pFolder = NULL; FolderItem *pItem = NULL;
    FolderItemVerbs *pVerbs = NULL;

    HRESULT hr = CoCreateInstance(CLSID_Shell, NULL, CLSCTX_INPROC_SERVER, IID_IShellDispatch, (void**)&pShellDispatch);
    if(FAILED(hr) || (pShellDispatch ==  NULL))
    {
        iSuccess = -3;
        return iSuccess;
    }

    variant_t vaDirectory(pcDirectoryName);
    hr = pShellDispatch->NameSpace(vaDirectory, &pFolder);
    if(FAILED(hr) || (pFolder == NULL))
    {
        iSuccess = -4;
        pShellDispatch->Release();
        return iSuccess;
    }

    variant_t vaFileName(pcFileName);
    hr = pFolder->ParseName(vaFileName, &pItem);
    if(FAILED(hr) || (pItem == NULL))
    {
        iSuccess = -5;
        pFolder->Release();
        pShellDispatch->Release();
        return iSuccess;
    }

    hr = pItem->Verbs(&pVerbs);
    if(FAILED(hr) || (pVerbs == NULL))
    {
        iSuccess = -6;
        pItem->Release();
        pFolder->Release();
        pShellDispatch->Release();
        return iSuccess;
    }

    /* Here we would do something with the FolderItemVerbs */

    pVerbs->Release(); pVerbs = NULL;
    pItem->Release(); pItem = NULL;
    pFolder->Release(); pFolder = NULL;
    pShellDispatch->Release(); pShellDispatch = NULL;

    iSuccess = 1;
    return iSuccess;
}

//-----------------------------------------------------------------------------

static unsigned __stdcall ThreadProc(void* pArguments)
{
    HRESULT hr = CoInitialize(NULL);
    if((hr == S_OK) || (hr == S_FALSE))
    {
        threadParam_t *params = (threadParam_t*) pArguments;
        params->returnValue = TestProc(params->pcDirectoryName, params->pcFileName);
        DispatchPendingMessages(); //This is required before CoUninitialize() to avoid crash with certain Shell Extensions !!!
        CoUninitialize();
    }
    else
    {
        if(threadParam_t *params = (threadParam_t*) pArguments)
        {
            params->returnValue = -10;
        }
    }

    return EXIT_SUCCESS;
}

これまでのところ、そのコードでクラッシュを再現できませんでした:-)

于 2012-10-22T23:52:28.507 に答える