views:

156

answers:

1

Question: I start the MS Text-to-speech engine in a thread, in order to avoid a crash on DLL_attach. It starts fine, and the text to speech engine gets initialized, but I can't access ISpVoice outside the thread. How can I access ISpVoice outside the thread ? It's a global variable after all...

You find XPThreads here: 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;
}
+4  A: 

Firstly, your problem is that you are expecting the thread that you start when the file's static initialisers are run to have completed whilst your DllMain() is running and yet you're doing nothing to synchronise with it. Of course if you WERE doing something to synchronise with it then you would be falling foul of the problems that were detailed in the link that I posted in response to your other question...

Secondly, COM interface pointers are, generally, thread specific. You can't, generally, obtain one in one thread via CoCreateInstance() or QueryInterface() and then just use it in another thread. To be able to use a use an interface pointer in another thread you need to marshal it to that thread using something like CoMarshalInterface() (see here). But before you can do that you need to make sure that you have initialised COM on the thread and you can't do that for all the reasons that I raised in answer to your previous question.

Thirdly, you have no reason to be calling CoUninitialize() in your DllMain() as a) you have no idea what thread you're being called on and b) you weren't responsible for calling CoInitialize() on that random thread that is owned by the application.

Fourthly, the call to LoadLibrary() is VERY BAD for the reasons pointed out in this link that I posted in answer to your earlier question.

So, in summary, as I said in reply to your other question, you can not do what you want to do in DllMain(). It's not the place to do it. As I stated before, what you COULD do is run up a thread when you get your DLL_PROCESS_ATTACH notification but abide by the rules when you do so so that you don't deadlock and load your COM object in there. You can then ONLY access the interface pointer from that thread and you'll have to do your own marshalling to pass values from the threads that call into your DLL across to your COM thread. Even then there's probably a better way to do what you're doing (such as exposing the whole of whatever it is you're building as it's OWN COM object) but you're not giving enough context for anyone to come up with an answer to the REAL problem that you have.

Oh and finally... The XPThreads thing that you are using is based on the flawed assumption that you HAVE to wait for the thread handle that you get back from CreateThread(), you don't, you could just close it after you create your thread as you're not interested in waiting for it. You might like to take a look at this question to see why you probably shouldn't be using CreateThread() and should, instead, be using _beginthreadex().

Len Holgate