Как получить доступ к данным потока вне потока - PullRequest
1 голос
/ 21 апреля 2010

Вопрос: Я запускаю механизм преобразования текста в речь в потоке, чтобы избежать сбоя DLL_attach. Он запускается нормально, и механизм преобразования текста в речь инициализируется, но я не могу получить доступ к ISpVoice вне потока. Как я могу получить доступ к ISpVoice вне потока? В конце концов, это глобальная переменная ...

Вы найдете XPThreads здесь: http://www.codeproject.com/KB/threads/XPThreads.aspx

#include <windows.h>
#include <sapi.h>
#include "XPThreads.h"


ISpVoice * pVoice = NULL;

unsigned long init_engine_thread(void* param)
{
Sleep(5000);
    printf("lolthread\n");



    //HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
    HRESULT hr = CoInitialize(NULL);

    if(FAILED(hr) )
    {
        MessageBox(NULL, TEXT("Failed To Initialize"), TEXT("Error"), 0);
        char buffer[2000] ;
        sprintf(buffer, "An error occured: 0x%08X.\n", hr);
        FILE * pFile = fopen ( "c:\\temp\\CoInitialize_dll.txt" , "w" );
        fwrite (buffer , 1 , strlen(buffer) , pFile );
        fclose (pFile);
    }
    else
    {   
        printf("trying to create instance.\n");
        //HRESULT hr = CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_ALL, IID_ISpVoice, (void **) &pVoice);
        //hr = CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_ALL, IID_ISpVoice, (void **) &pVoice);
        //HRESULT hr = CoCreateInstance(__uuidof(ISpVoice), NULL, CLSCTX_INPROC_SERVER, IID_ISpVoice, (void **) &pVoice);
        HRESULT hr = CoCreateInstance(__uuidof(SpVoice), NULL, CLSCTX_ALL, IID_ISpVoice, (void **) &pVoice);
        if( SUCCEEDED( hr ) )
        {
            printf("Succeeded\n");
            hr = pVoice->Speak(L"The text to speech engine has been successfully initialized.", 0, NULL);
        }
        else
        {
            printf("failed\n");
            MessageBox(NULL, TEXT("Failed To Create COM instance"), TEXT("Error"), 0);
            char buffer[2000] ;
            sprintf(buffer, "An error occured: 0x%08X.\n", hr);
            FILE * pFile = fopen ( "c:\\temp\\CoCreateInstance_dll.txt" , "w" );
            fwrite (buffer , 1 , strlen(buffer) , pFile );
            fclose (pFile);
        }
    }






return NULL;
}


XPThreads* ptrThread = new XPThreads(init_engine_thread);


BOOL APIENTRY DllMain( HMODULE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
    //init_engine();
    LoadLibrary(TEXT("ole32.dll"));
    ptrThread->Run();
    break;
case DLL_THREAD_ATTACH:
    break;
case DLL_THREAD_DETACH:
    break;
case DLL_PROCESS_DETACH:
    if(pVoice != NULL)
    {
        pVoice->Release();
        pVoice = NULL;
    }
    CoUninitialize();
    break;
}
return TRUE;
}

1 Ответ

5 голосов
/ 21 апреля 2010

Во-первых, ваша проблема в том, что вы ожидаете, что поток, который вы запускаете при запуске статических инициализаторов файла, завершится во время работы DllMain (), и все же вы ничего не делаете для синхронизации с ним. Конечно, если вы делаете что-то для синхронизации с этим, то вы бы не справились с проблемами, которые были подробно описаны в ссылке, которую я разместил в ответ на ваш другой вопрос ...

Во-вторых, указатели интерфейса COM, как правило, зависят от потока. Обычно вы не можете получить один в одном потоке через CoCreateInstance() или QueryInterface(), а затем просто использовать его в другом потоке. Чтобы иметь возможность использовать указатель интерфейса в другом потоке, вам нужно перенаправить его в этот поток, используя что-то вроде CoMarshalInterface() (см. здесь ). Но прежде чем вы сможете это сделать, вам нужно убедиться, что вы инициализировали COM в потоке, и вы не можете сделать это по всем причинам, которые я привел в ответ на ваш предыдущий вопрос.

В-третьих, у вас нет оснований для вызова CoUninitialize() в вашем DllMain() как a) вы не знаете, к какому потоку вы вызываетесь, и b) вы не несете ответственности за вызов CoInitialize() для этого случайный поток, которым владеет приложение.

В-четвертых, звонок на LoadLibrary() ОЧЕНЬ ПЛОХО по причинам, указанным в этой ссылке , которую я разместил в ответ на ваш предыдущий вопрос.

Итак, в заключение, как я сказал в ответ на ваш другой вопрос, вы не можете делать то, что вы хотите сделать в DllMain(). Это не место, чтобы сделать это. Как я уже говорил ранее, вы МОЖЕТЕ выполнить поток, когда получите уведомление DLL_PROCESS_ATTACH, но при этом соблюдаете правила, чтобы не блокировать и не загружать COM-объект там. После этого вы можете ТОЛЬКО получить доступ к указателю на интерфейс из этого потока, и вам придется выполнить собственный маршаллинг для передачи значений из потоков, которые вызывают в вашу DLL, в ваш поток COM. Даже тогда, возможно, есть лучший способ сделать то, что вы делаете (например, выставить все, что вы создаете, так как это СОБСТВЕННЫЙ COM-объект), но вы не даете достаточно контекста, чтобы кто-нибудь смог найти ответ к реальной проблеме, которая у вас есть.

Да, и наконец ... Используемая вами вещь XPThreads основана на ошибочном допущении, что вам НЕОБХОДИМО дождаться дескриптора потока, который вы получите от CreateThread(), нет, вы можете просто закрыть его после того, как вы создали свою тему, так как вы не заинтересованы в ее ожидании. Возможно, вы захотите взглянуть на этот вопрос , чтобы понять, почему вам, вероятно, не следует использовать CreateThread(), а вместо этого следует использовать _beginthreadex().

...