问题描述
问题:为了避免DLL_attach崩溃,我在一个线程中启动了MS Text-to-speech引擎.它开始正常,并且语音引擎的文本已初始化,但是我无法在线程外访问ISpVoice.如何在线程外访问ISpVoice?毕竟这是一个全局变量...
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...
您可以在这里找到XPThreads: http://www.codeproject.com/KB/threads/XPThreads.aspx
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;
}
推荐答案
首先,您的问题是您期望在DllMain()运行时文件静态初始化程序运行时启动的线程已经完成.您无所事事与它同步.当然,如果您正在做一些与之同步的操作,那么您将对我针对您的其他问题 ...
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...
第二,COM接口指针通常是特定于线程的.通常,您无法通过CoCreateInstance()
或QueryInterface()
获得一个线程,然后仅在另一个线程中使用它.为了能够在另一个线程中使用接口指针,您需要使用诸如CoMarshalInterface()
之类的代码将其编组到该线程(请参阅此处).但是在执行此操作之前,您需要确保已在线程上初始化了COM,并且由于我为回答上一个问题而提出的所有原因,您不能这样做.
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.
第三,您没有理由在DllMain()
中调用CoUninitialize()
,因为a)您不知道正在调用哪个线程,b)您不负责在该线程上调用CoInitialize()
该应用程序拥有的随机线程.
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.
第四,由于LoadLibrary()的调用非常糟糕="nofollow noreferrer">此链接,是我为回答您先前的问题而发布的.
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.
因此,总而言之,正如我在回答您的另一个问题时所说的那样,您无法在DllMain()
中做您想做的事情.这不是这样做的地方.如前所述,您可以做的是在收到DLL_PROCESS_ATTACH
通知时运行一个线程,但这样做时要遵守规则,以免死锁并在其中加载COM对象.然后,您只能从该线程访问接口指针,并且必须进行自己的编组以将来自调用DLL的线程的值传递到COM线程.即使那样,也可能有更好的方法来做您正在做的事情(例如,将您正在构建的全部内容公开为OWN COM对象),但是您没有为任何人提供足够的上下文来给出答案解决您遇到的真正问题.
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.
哦,最后...您使用的XPThreads是基于这样一个错误的假设,即您必须等待从CreateThread()
返回的线程句柄,而不必,只需关闭它即可创建线程后,因为您对等待线程不感兴趣.您可能想看一下此问题以查看为什么您可能不应该使用CreateThread()
而应该使用_beginthreadex()
.
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()
.
这篇关于如何在线程外访问线程数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!