MFC DLL中的多线程 + com初始化问题

时间:2021-11-30 19:49:33
我用ATL做的windows服务,调用MFC写的DLL,这个dll中有三个线程,主线程和其中一个自线程都需要初始化com来解析XML文件,得知每个线程都需要初始化com也就是调用coinitiliza(NULL)函数,问题是在子线程初始化com后,再调用_com_util::convertBTRToString()时报内存错误:内存0x000000处不能为Read,其实是_bstr_t转为char*时发生的,请教大家了

18 个解决方案

#1


com初始化是线程相关的,每个线程都独立的初始化和释放 .

试一试:CoinitilizeEx和oleinitialize ,这三个方法还是有些区别. 

#2


刚到公司就按照你的方法试了一下,错误照旧,其实CoinitilizeEx这个函数我早就试过了,就是解决不了啊,十分郁闷。

#3



这个是个套间问题,LZ你看看你的代码结构是不是这样,我简单描述
主线程
{
coinitiliza(NULL)
子线程
{
coinitiliza(NULL)
uncoinitiliza()

convertBTRToString
uncoinitiliza()
}
如果是如上面,那就有可能是,主线程运行时把子线程起来了,但子线程结束时调用了uncoinitiliza()所以com库就已经被释放了。所以再调用和一些COM相关的函数就不行了。LZ可以这样式,把子线程对COM库的注册和返注册注释掉。

#4


我确实是在主线程启东市,立刻启动了子线程,等待主线程的消息,子线程开始工作,现在顺序是这样的,主线程先初始化com,启动子线程,子线程com口初始化,等待消息,到这里都没有问题,子线程解析XML也没有问题(如果注释掉注册,就会CreateInstance失败(尚未调用coinitiliza)),但是因为IXMLDOMNodePtr::text是_bstr_t类型的,而我的相关结构里的属性是char *的,我跟踪了一下,运行到_bstr_t::m_Data::getString()的时候,需要调用_com::convertBTRToString()函数,一旦到这里,就会包内存错误,我都没辙了

#5



建议LZ:1,调用GetLastError看返回的错误码;
2,LZ能不能把convertBTRToString句左右的代码帖出来,我们也好帮你分析一下是否是API的用法出现了问题?

#6


多谢SiGoYi,错误就是在这个函数里发生的,奇怪的是主线程有一个函数和这个差不多,却没有出问题

bool Download::XmlToLinkTable(const char *fileConent)
{
//::CoInitialize(NULL);   //这句话导致了我的内存问题

MSXML::IXMLDOMDocumentPtr pDoc;
MSXML::IXMLDOMElementPtr pEl;
HRESULT hr = pDoc.CreateInstance(MSXML::CLSID_DOMDocument);
if (FAILED(hr))
{
_com_error er(hr);
return false;
}
variant_t vResult;
vResult = pDoc->loadXML(fileConent);
//_bstr_t sPath("new.xml");
//vResult = pDoc->load(sPath);
if ((BOOL)vResult == FALSE){
WriteLog("载入XML时发生了错误, 请检查XML文档路径", logPath);
return false;
}

if (pDoc->parseError->errorCode != 0) {
WriteLog("XML解析时发生了错误, 请检查XML文档", logPath);
return false;
}
WriteLog("远程XML配置文件解析成功!", logPath);
pEl = pDoc->documentElement;

for (MSXML::IXMLDOMNodePtr pChild = pEl->firstChild; NULL != pChild; pChild = pChild->nextSibling) {
DATAFILENODE *node = new DATAFILENODE;
strcpy_s(node->url, MAX_PATH, pChild->text);
/*
上面这句话调用strcpy_s,最后一个参数是char*的,我把_bstr_t 的字符串给进去后,会调用隐式的转换,
就是_bstr_t::m_Data::getString()函数,这个是微软的函数,他会调用_com::convertBTRToString()函数,
跟踪到这里再下一步就会报内存错误了。
*/
splitPath(node->url, node->name, node->fullPath);
node->next = NULL;
InsertNode(node);
}
WriteLog("到达任务时间,远程配置文件加载成功,开始下载...", logPath);
return true;


//采用tinyxml运行库解析xml
//date:2010-3-2
}

#7


请问LZ,bool Download::XmlToLinkTable(const char *fileConent)这个函数应该是你子线程调用的吧!如果是你把::CoInitialize(NULL);加在线程函数的开头式式,明白什么意思吧!就是在你的创建子线程时那个子线程入口函数,在那个函数头加CoInitialize(NULL);在退出函数之前再调用UnConInitialize();其余的在子线程函数里调用的都去掉。这样应该差不多。如果好用了你再回复一下,我再帮你看看代码。

#8


学习

mark

#9



楼主我好像知道你的问题所在了,DATAFILENODE *node = new DATAFILENODE;看看DATAFILENODE结构体应该是你自己定义的吧,再看DATAFILENODE中的url成员,应该是个char*吧!你是不是没有分配内存啊!是不是应该先new一下啊!因为下面的API strcpy_s要往DATAFILENODE的成员url中拷贝,如果你不先分配内存是不行的。

#10


谢谢SiGoYi,但是我的结构属性有关字符串的都是这么定义的
typedef struct pDATAFILENODE {
     char url[260];
     char otherProperty[size];
}DATAFILENODE;
所以应该不是分配内存的问题,还有,这个函数的开头原来确实是调用::CoInitialize(NULL)的,但是后来已经改成在函数入口点调用了,问题并没有解决啊。
入口看书如下:
unsigned __stdcall ThreadMain(LPVOID pParam)
{
::CoInitialize(NULL);
//监视者线程
MSG msg;//该线程接收到的消息
Download d; //任务对象,当线程接收到不同的消息时有不同的工作要完成
d.GetConfig(&theApp.conf);
d.GetLogPath(theApp.logPath);
d.GetDlThreadId(nDlThreadId);
d.GetRsThreadId(nRsThreadId);
while(true){
if(::GetMessage(&msg, NULL, NULL, NULL)) {
switch(msg.message){
case WM_TIMEISUP:
{
d.StartDownload();
break;
}
case WM_DOWNLOADISCOMPLETE:
{
/*2010-2-24 下载完成的消息发送到这里,指示Downlaod d对象遍历链表*/
d.GetRemoteFile(NULL, msg.lParam);
break;
}
default:
break;
}
}
};
return 0;
}

#11


这个错误应该和CoInitialize没什么关系,CoInitialize只是初始化线程Com环境,是个API函数.你仔细跟踪一下convertBTRToString函数,看看为什么出错吧,如果实在找不出什么错误,就用WideCharToMultiByte函数算了

#12



我在我本地写了这样的代码
//CoInitialize(NULL);
typedef struct pDATAFILENODE { 
char url[260]; 
char otherProperty[10]; 
}DATAFILENODE; 

DATAFILENODE* p = new DATAFILENODE;
_bstr_t a = SysAllocString(_T("abc"));
strcpy_s(p->url, 5, a);
感觉CoInitialize调用或不调用下面的strcpy_s都能成功啊!真想不出来为什么了!

#13


LZ也可以把url这个成员的类型改成_bstr_t之后用_bstr_t的copy()函数来实现拷贝。

#14


谢谢skybblue,我试过改用WideCharToMultiByte函数,但是依然有内存问题,google了一些帖子,好像说convertBTRToString内部也调用WideCharToMultiByte函数的。

#15


引用 12 楼 sigoyi 的回复:
我在我本地写了这样的代码
  //CoInitialize(NULL);
  typedef struct pDATAFILENODE {
  char url[260];
  char otherProperty[10];
  }DATAFILENODE;

  DATAFILENODE* p = new DATAFILENODE;
  _bstr_t a = SysAllocString(_T("abc"));
  strcpy_s(p->url, 5, a);
 感觉CoInitialize调用或不调用下面的strcpy_s都能成功啊!真想不出来为什么了!

说的是啊,在主线程做同样的事情,没有任何问题,而且我还测试过,在CoInitialize函数的前面和后面都做wchar_t到char的转换,结果在调用之前成功了,而之后那个,失败了

#16


关注中~~~~~~~~~~~~~~求高手指导啊!

#17


线程套间的问题
可参考http://www.codeproject.com/KB/COM/CCOMThread2.aspx

#18


问题找到了,是我三个同时启动的线程中的一个线程中,一个char 字符串 在memset过程中size设定超过了char字符串本尊的长度,现在没问题了,还是谢谢大家

#1


com初始化是线程相关的,每个线程都独立的初始化和释放 .

试一试:CoinitilizeEx和oleinitialize ,这三个方法还是有些区别. 

#2


刚到公司就按照你的方法试了一下,错误照旧,其实CoinitilizeEx这个函数我早就试过了,就是解决不了啊,十分郁闷。

#3



这个是个套间问题,LZ你看看你的代码结构是不是这样,我简单描述
主线程
{
coinitiliza(NULL)
子线程
{
coinitiliza(NULL)
uncoinitiliza()

convertBTRToString
uncoinitiliza()
}
如果是如上面,那就有可能是,主线程运行时把子线程起来了,但子线程结束时调用了uncoinitiliza()所以com库就已经被释放了。所以再调用和一些COM相关的函数就不行了。LZ可以这样式,把子线程对COM库的注册和返注册注释掉。

#4


我确实是在主线程启东市,立刻启动了子线程,等待主线程的消息,子线程开始工作,现在顺序是这样的,主线程先初始化com,启动子线程,子线程com口初始化,等待消息,到这里都没有问题,子线程解析XML也没有问题(如果注释掉注册,就会CreateInstance失败(尚未调用coinitiliza)),但是因为IXMLDOMNodePtr::text是_bstr_t类型的,而我的相关结构里的属性是char *的,我跟踪了一下,运行到_bstr_t::m_Data::getString()的时候,需要调用_com::convertBTRToString()函数,一旦到这里,就会包内存错误,我都没辙了

#5



建议LZ:1,调用GetLastError看返回的错误码;
2,LZ能不能把convertBTRToString句左右的代码帖出来,我们也好帮你分析一下是否是API的用法出现了问题?

#6


多谢SiGoYi,错误就是在这个函数里发生的,奇怪的是主线程有一个函数和这个差不多,却没有出问题

bool Download::XmlToLinkTable(const char *fileConent)
{
//::CoInitialize(NULL);   //这句话导致了我的内存问题

MSXML::IXMLDOMDocumentPtr pDoc;
MSXML::IXMLDOMElementPtr pEl;
HRESULT hr = pDoc.CreateInstance(MSXML::CLSID_DOMDocument);
if (FAILED(hr))
{
_com_error er(hr);
return false;
}
variant_t vResult;
vResult = pDoc->loadXML(fileConent);
//_bstr_t sPath("new.xml");
//vResult = pDoc->load(sPath);
if ((BOOL)vResult == FALSE){
WriteLog("载入XML时发生了错误, 请检查XML文档路径", logPath);
return false;
}

if (pDoc->parseError->errorCode != 0) {
WriteLog("XML解析时发生了错误, 请检查XML文档", logPath);
return false;
}
WriteLog("远程XML配置文件解析成功!", logPath);
pEl = pDoc->documentElement;

for (MSXML::IXMLDOMNodePtr pChild = pEl->firstChild; NULL != pChild; pChild = pChild->nextSibling) {
DATAFILENODE *node = new DATAFILENODE;
strcpy_s(node->url, MAX_PATH, pChild->text);
/*
上面这句话调用strcpy_s,最后一个参数是char*的,我把_bstr_t 的字符串给进去后,会调用隐式的转换,
就是_bstr_t::m_Data::getString()函数,这个是微软的函数,他会调用_com::convertBTRToString()函数,
跟踪到这里再下一步就会报内存错误了。
*/
splitPath(node->url, node->name, node->fullPath);
node->next = NULL;
InsertNode(node);
}
WriteLog("到达任务时间,远程配置文件加载成功,开始下载...", logPath);
return true;


//采用tinyxml运行库解析xml
//date:2010-3-2
}

#7


请问LZ,bool Download::XmlToLinkTable(const char *fileConent)这个函数应该是你子线程调用的吧!如果是你把::CoInitialize(NULL);加在线程函数的开头式式,明白什么意思吧!就是在你的创建子线程时那个子线程入口函数,在那个函数头加CoInitialize(NULL);在退出函数之前再调用UnConInitialize();其余的在子线程函数里调用的都去掉。这样应该差不多。如果好用了你再回复一下,我再帮你看看代码。

#8


学习

mark

#9



楼主我好像知道你的问题所在了,DATAFILENODE *node = new DATAFILENODE;看看DATAFILENODE结构体应该是你自己定义的吧,再看DATAFILENODE中的url成员,应该是个char*吧!你是不是没有分配内存啊!是不是应该先new一下啊!因为下面的API strcpy_s要往DATAFILENODE的成员url中拷贝,如果你不先分配内存是不行的。

#10


谢谢SiGoYi,但是我的结构属性有关字符串的都是这么定义的
typedef struct pDATAFILENODE {
     char url[260];
     char otherProperty[size];
}DATAFILENODE;
所以应该不是分配内存的问题,还有,这个函数的开头原来确实是调用::CoInitialize(NULL)的,但是后来已经改成在函数入口点调用了,问题并没有解决啊。
入口看书如下:
unsigned __stdcall ThreadMain(LPVOID pParam)
{
::CoInitialize(NULL);
//监视者线程
MSG msg;//该线程接收到的消息
Download d; //任务对象,当线程接收到不同的消息时有不同的工作要完成
d.GetConfig(&theApp.conf);
d.GetLogPath(theApp.logPath);
d.GetDlThreadId(nDlThreadId);
d.GetRsThreadId(nRsThreadId);
while(true){
if(::GetMessage(&msg, NULL, NULL, NULL)) {
switch(msg.message){
case WM_TIMEISUP:
{
d.StartDownload();
break;
}
case WM_DOWNLOADISCOMPLETE:
{
/*2010-2-24 下载完成的消息发送到这里,指示Downlaod d对象遍历链表*/
d.GetRemoteFile(NULL, msg.lParam);
break;
}
default:
break;
}
}
};
return 0;
}

#11


这个错误应该和CoInitialize没什么关系,CoInitialize只是初始化线程Com环境,是个API函数.你仔细跟踪一下convertBTRToString函数,看看为什么出错吧,如果实在找不出什么错误,就用WideCharToMultiByte函数算了

#12



我在我本地写了这样的代码
//CoInitialize(NULL);
typedef struct pDATAFILENODE { 
char url[260]; 
char otherProperty[10]; 
}DATAFILENODE; 

DATAFILENODE* p = new DATAFILENODE;
_bstr_t a = SysAllocString(_T("abc"));
strcpy_s(p->url, 5, a);
感觉CoInitialize调用或不调用下面的strcpy_s都能成功啊!真想不出来为什么了!

#13


LZ也可以把url这个成员的类型改成_bstr_t之后用_bstr_t的copy()函数来实现拷贝。

#14


谢谢skybblue,我试过改用WideCharToMultiByte函数,但是依然有内存问题,google了一些帖子,好像说convertBTRToString内部也调用WideCharToMultiByte函数的。

#15


引用 12 楼 sigoyi 的回复:
我在我本地写了这样的代码
  //CoInitialize(NULL);
  typedef struct pDATAFILENODE {
  char url[260];
  char otherProperty[10];
  }DATAFILENODE;

  DATAFILENODE* p = new DATAFILENODE;
  _bstr_t a = SysAllocString(_T("abc"));
  strcpy_s(p->url, 5, a);
 感觉CoInitialize调用或不调用下面的strcpy_s都能成功啊!真想不出来为什么了!

说的是啊,在主线程做同样的事情,没有任何问题,而且我还测试过,在CoInitialize函数的前面和后面都做wchar_t到char的转换,结果在调用之前成功了,而之后那个,失败了

#16


关注中~~~~~~~~~~~~~~求高手指导啊!

#17


线程套间的问题
可参考http://www.codeproject.com/KB/COM/CCOMThread2.aspx

#18


问题找到了,是我三个同时启动的线程中的一个线程中,一个char 字符串 在memset过程中size设定超过了char字符串本尊的长度,现在没问题了,还是谢谢大家