vb编译环境中正常运行,生成可执行文件后运行出错。求救!

ylgm44 2010-08-11 10:02:17
vb6 调用vs2008写的Com组件,通过连接点接收事件消息。具体实现如下:

Com接口:

import "oaidl.idl";
import "ocidl.idl";

[
object,
uuid(39570840-2098-4803-9150-A49F98530CC7),
dual,
nonextensible,
helpstring("ISendEvent 接口"),
pointer_default(unique)
]
interface ISendEvent : IDispatch{
[id(1), helpstring("方法Start")] HRESULT Start(void);
};
[
uuid(7F0E1638-858D-4806-A869-37EC37028B1B),
version(1.0),
helpstring("EventSender 1.0 类型库")
]
library EventSenderLib
{
importlib("stdole2.tlb");
[
uuid(C40F0451-A58C-4400-A9EE-D66F4C2E2F7D),
helpstring("_ISendEventEvents 接口")
]
dispinterface _ISendEventEvents
{
properties:
methods:
[id(1), helpstring("方法OnEvent")] HRESULT OnEvent(void);
};
[
uuid(8716FB51-3B68-4D2C-9E1C-53988C2151F0),
helpstring("SendEvent Class")
]
coclass SendEvent
{
[default] interface ISendEvent;
[default, source] dispinterface _ISendEventEvents;
};
};


接口中,正常接口只有一个:Start()
事件接口一个:OnEvent()

实现:(调用接口Start,不做任何处理,直接激发事件)

// CSendEvent
STDMETHODIMP CSendEvent::Start(void)
{
Fire_OnEvent();
return S_OK;
}


连接点实现:(采用vs2008自动生成代码,没有进行任何改动)

template<class T>
class CProxy_ISendEventEvents :
public IConnectionPointImpl<T, &__uuidof(_ISendEventEvents)>
{
public:
HRESULT Fire_OnEvent()
{
HRESULT hr = S_OK;
T * pThis = static_cast<T *>(this);
int cConnections = m_vec.GetSize();

for (int iConnection = 0; iConnection < cConnections; iConnection++)
{
pThis->Lock();
CComPtr<IUnknown> punkConnection = m_vec.GetAt(iConnection);
pThis->Unlock();

IDispatch * pConnection = static_cast<IDispatch *>(punkConnection.p);

if (pConnection)
{
CComVariant varResult;

DISPPARAMS params = { NULL, NULL, 0, 0 };
hr = pConnection->Invoke(1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, ¶ms, &varResult, NULL, NULL);
}
}
return hr;
}
};


VB端调用:

Dim WithEvents evt as EventSenderLib.SendEvent

Private Sub Command1_Click()
Evt.Start
End Sub

Private Sub evt_OnEvent()
MsgBox "OnEvent"
End Sub

Private Sub Form_Load()
Set evt = New EventSenderLib.SendEvent
End Sub


在vb6编译环境中,运行征程,点击按钮,能够弹出对话框。
用File->Make Project1.exe生成可执行程序,然后到文件夹中执行可执行文件,就会出现错误,不能运行。

把Projext1.exe加入到EventSender工程中,进行调试,发现错误出现在连接点代码处。

DISPPARAMS params = { NULL, NULL, 0, 0 };
hr = pConnection->Invoke(1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, ¶ms, &varResult, NULL, NULL);


就出现在invoke这一行。
错误信息如下:

Project1.exe 中的0x73459a49 处未处理的异常:0xC0000005: 读取位置 0x00000034 时发生访问冲突

有哪位知道怎么回事,希望能告诉我一声。先谢谢大家。
...全文
391 5 打赏 收藏 转发到动态 举报
写回复
用AI写文章
5 条回复
切换为时间正序
请发表友善的回复…
发表回复
jack18jackandjack 2011-10-10
  • 打赏
  • 举报
回复
各位高手,我现在也是在第二线程中促发事件。我也按照上面的代码改了,我用的是vs2005。clock这个事件是在第二线程中激发的,CComPtr<IUnknown> punkConnection = m_vec.GetAt(iConnection);也正确返回了。
HRESULT Fire_Clock()
{
HRESULT hr = S_OK;
T * pThis = static_cast<T *>(this);
int cConnections = m_vec.GetSize();

for (int iConnection = 0; iConnection < cConnections; iConnection++)
{
pThis->Lock();
CComPtr<IUnknown> punkConnection = m_vec.GetAt(iConnection);
pThis->Unlock();

CComQIPtr< IDispatch > pConnection( punkConnection );

if (pConnection)
{
CComVariant varResult;

DISPPARAMS params = { NULL, NULL, 0, 0 };
hr = pConnection->Invoke(3, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, ¶ms, &varResult, NULL, NULL);
}
}
return hr;
}

运行到CComQIPtr< IDispatch > pConnection( punkConnection );这里时,跟进去发现在 CComQIPtr(__in_opt IUnknown* lp) throw()
{
if (lp != NULL)
lp->QueryInterface(*piid, (void **)&p);
}
中,lp->QueryInterface(*piid, (void **)&p);跑了这句之后直接线程退出了,请问是什么原因呢???
ylgm44 2010-08-12
  • 打赏
  • 举报
回复
已经解决了。参考:ATL: Firing Events from Worker Threads
http://www.codeguru.com/cpp/com-tech/atl/atl/print.php/c75

就是说com中的辅助线程激发事件,就会导致这个问题。必须是主线程激发事件。
按照上面链接中所描述的方法修改事件函数Fire_XXX,可以解决此问题。
思路就是先在主线程中把主线程的接口指针注册到全局接口表中,辅助线程激发事件时,从全局接口表中取得主线程接口指针,这样就相当于主线程激发的事件而不是辅助线程激发事件了。

上面链接中带代码有点错误,我把这个错误修改了一下,可以正确运行了。下面把正确代码贴一下,有需要的自己对照一下哪些地方修改了。

另外,使用这个代码,需要在每个激发事件的辅助线程添加CoInitialize(NULL)和CoUnInitialize()函数。要不然出错。

下面是修改后的代码:注释头表明了那些地方我做了修改。

/*
Michael Lindig
July 16, 2000

In most cases of my work I develop ATL objects with worker threads.
In the worker threads there must be often fire events for signalling
thread states. The problem of firing events from worker thread is
that they must enter another apartment. In this apartment the sink
interfaces are not valid and thats why some clients would not be receive
the events (eg VB). I found a simple solution for that. (A solution
without PostMessage.)

Only the proxy code of wizard must be changed, to get the firing of
events right. I use the global interface table (GIT) for my solution.
"The GIT holds a list of marshaled interface pointers that, on request
can be unmarshaled any number of times to any number of apartment in
your process, regardless of whether the pointer is for an object or a
proxy."(Professional ATL COM Programming, Dr Richard Grimes).

I wrote a new specialized class for CComDynamicUnkArray which use the
GIT for accessing the sink interfaces.

====================================================================
2010.8.12 modified by yangleigang

original:
//The proxy code use this function to get the interface !
CComPtr GetAt(int nIndex)
{
DWORD dwCookie = (DWORD)CComDynamicUnkArray::GetAt( nIndex );

if( dwCookie == 0 )
return NULL;

if( CookieMap.find( dwCookie ) == CookieMap.end() )
{
return (IUnknown*)dwCookie;
}

if( GIT != NULL )
{
CComPtr ppv;

HRESULT hr = GIT->GetInterfaceFromGlobal(
CookieMap[dwCookie], //Cookie identifying the desired global
//interface and its object
__uuidof(IUnknown), //IID of the registered global interface
reinterpret_cast< void** >(&ppv) //Indirect pointer
//to the desired interface
);

if( hr == S_OK )
{
return ppv;
}

//Should never be reached, a ASSERT or exception is possible
}
return (IUnknown*)dwCookie;
}
after modified:
//The proxy code use this function to get the interface !
CComPtr<IUnknown> GetAt(int nIndex)
{
IUnknown* pUnknown = CComDynamicUnkArray::GetAt( nIndex );
if(NULL == pUnknown)
{
return NULL;
}

DWORD dwOrgCookie = CComDynamicUnkArray::GetCookie(&pUnknown);

if( dwOrgCookie == 0 )
return NULL;

if( CookieMap.find( dwOrgCookie ) == CookieMap.end() )
{
return (IUnknown*)pUnknown;
}

if( GIT != NULL )
{
CComPtr<IUnknown> ppv;

HRESULT hr = GIT->GetInterfaceFromGlobal(
CookieMap[dwOrgCookie], //Cookie identifying the desired global interface and its object
__uuidof(IUnknown), //IID of the registered global interface
reinterpret_cast< void** >(&ppv) //Indirect pointer to the desired interface
);

if( hr == S_OK )
{
return ppv;
}

//Should never be reached, a ASSERT or exception is possible
}
return (IUnknown*)pUnknown;
}

reason:
1. CComDynamicUnkArray::GetAt( nIndex ).
此函数返回Iunknown接口指针而不是Cookie。原来代码中错误的把这个指针
直接当成Cookie来使用,导致程序运行不正确。

2. 在CComDynamicUnkArray::Add()函数中,CComDynamicUnkArray类默认给m_vec
分配4个空间。即使只Add了一个接口指针,m_vec的size也是4。如果我们
只有一个接口,也会调用GetAt函数4次。而且第2/3/4此得到的IUnknown指针
均为NULL。因此,我们需要添加是否为NULL的判断。

3. CComDynamicUnkArray::GetCookie返回的是CComDynamicUnkArray中的Cookie。
此Cookie和CookieMap中存放的Cookie不是一个东西。CComDynamicUnkArray的
Cookie刚好是CookieMap中的键值。因此,为了避免混淆,将CComDynamicUnkArray::GetCookie
返回的Cookie命名为orgCookie。
*/

class CComDynamicUnkArray_GIT : public CComDynamicUnkArray
{
private:
IGlobalInterfaceTable* GIT;

public:
CComDynamicUnkArray_GIT() : CComDynamicUnkArray()
{
GIT = NULL;
::CoInitialize(NULL);

CoCreateInstance(CLSID_StdGlobalInterfaceTable,
NULL,
CLSCTX_INPROC_SERVER,
__uuidof(IGlobalInterfaceTable),
reinterpret_cast< void** >(&GIT) );
}

~CComDynamicUnkArray_GIT()
{
//clean up the class
clear();
if( GIT != NULL )
{
GIT->Release();
}
::CoUninitialize();
}

DWORD Add(IUnknown* pUnk);
BOOL Remove(DWORD dwCookie);

//The proxy code use this function to get the interface !
CComPtr<IUnknown> GetAt(int nIndex)
{
IUnknown* pUnknown = CComDynamicUnkArray::GetAt( nIndex );
if(NULL == pUnknown)
{
return NULL;
}

DWORD dwOrgCookie = CComDynamicUnkArray::GetCookie(&pUnknown);

if( dwOrgCookie == 0 )
return NULL;

if( CookieMap.find( dwOrgCookie ) == CookieMap.end() )
{
return (IUnknown*)pUnknown;
}

if( GIT != NULL )
{
CComPtr<IUnknown> ppv;

HRESULT hr = GIT->GetInterfaceFromGlobal(
CookieMap[dwOrgCookie], //Cookie identifying the desired global interface and its object
__uuidof(IUnknown), //IID of the registered global interface
reinterpret_cast< void** >(&ppv) //Indirect pointer to the desired interface
);

if( hr == S_OK )
{
return ppv;
}

//Should never be reached, a ASSERT or exception is possible
}
return (IUnknown*)pUnknown;
}

//clean up the GIT
void clear()
{
CComDynamicUnkArray::clear();

if( GIT != NULL )
{
map< DWORD, DWORD >::iterator iter;
for (iter = CookieMap.begin();
iter != CookieMap.end();
++iter )
{
GIT->RevokeInterfaceFromGlobal(
iter->second //Cookie that was returned from RegisterInterfaceInGlobal
);
}
}
CookieMap.clear();
}

protected:
map< DWORD, DWORD > CookieMap;
};

inline DWORD CComDynamicUnkArray_GIT::Add(IUnknown* pUnk)
{
DWORD Result = CComDynamicUnkArray::Add( pUnk );

HRESULT hr;
DWORD pdwCookie = 0;
if( GIT != NULL )
{
hr = GIT->RegisterInterfaceInGlobal(
pUnk, //Pointer to interface of type riid of object containing global interface
__uuidof(IUnknown), //IID of the interface to be registered
&pdwCookie //Supplies a pointer to the cookie that provides a caller in another apartment access to the interface pointer
);
}
if( hr == S_OK )
{
CookieMap[Result] = pdwCookie;
}

return Result;
}

inline BOOL CComDynamicUnkArray_GIT::Remove(DWORD dwCookie)
{
BOOL Result = CComDynamicUnkArray::Remove( dwCookie );

if( GIT != NULL )
{
if( CookieMap.find( dwCookie ) != CookieMap.end() )
{
GIT->RevokeInterfaceFromGlobal(
CookieMap[dwCookie] //Cookie that was returned from RegisterInterfaceInGlobal
);

CookieMap.erase(dwCookie);
}
}
return Result;
}


在线程函数的开始末尾加CoInitialize和CoUnInitialize

UINT WINAPI EventProc(LPVOID pParam)
{
::CoInitialize(NULL);
CEvent* pEvent = reinterpret_cast<CEvent*>(pParam);

while(1)
{
pEvent->OnEvent();
::Sleep(1000);
}
::CoUninitialize();
}
ylgm44 2010-08-11
  • 打赏
  • 举报
回复
后来随便动了一下,就不出错误了。改动地方:
hr = pConnection->Invoke(1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, ¶ms, &varResult, NULL, NULL);
这句话中,吧Invoke(1, 改为 Invoke(0x1就不出问题了。再改回来也不出了。

很奇怪。但是这只是个例子程序,远程序中还是出错。原程序中有多线程。于是我把这个程序改成多线程的。果然,每次都出错。

改后的代码:

class CEvent
{
public:
CEvent(CSendEvent* pParent) : m_pParent(pParent)
{}

void OnEvent()
{
m_pParent->OnEvent();
}

private:

CSendEvent* m_pParent;
};

UINT WINAPI EventProc(LPVOID pParam)
{
CEvent* pEvent = reinterpret_cast<CEvent*>(pParam);

while(1)
{
//pEvent->OnEvent();
::Sleep(1000);
}
}

// CSendEvent
STDMETHODIMP CSendEvent::Start(void)
{
CEvent* pEvent = new CEvent(this);
HANDLE hThread;
UINT uiThreadId = 0;

//启动线程,使用_beginthreadex
hThread = (HANDLE)_beginthreadex(NULL, // 安全参数
0, // 堆栈
EventProc,// 线程程序
pEvent, // 线程参数
CREATE_SUSPENDED, //创建模式
&uiThreadId); // 线程ID

//继续线程
ResumeThread( hThread );

OnEvent();
return S_OK;
}

void CSendEvent::OnEvent()
{
Fire_OnEvent();
}

其他地方代码不变。每次都出错。错误一样。

说明这个错误和多线程有关。希望有知情人帮忙一把。。。着急啊。。
ylgm44 2010-08-11
  • 打赏
  • 举报
回复
是多线程导致的。跟线程模式有关。vb只支持STA模式。com中worker线程激发事件,就会导致这个问题。
正在调查怎么解决。还没有彻底搞明白线程模型。谁要是明白,结合这个例子给讲讲。
jhone99 2010-08-11
  • 打赏
  • 举报
回复
路径有问题吗?

是否因为运行后某些释放没有做导致的?重启直接运行exe如何?

864

社区成员

发帖
与我相关
我的任务
社区描述
VB COM/DCOM/COM+
c++ 技术论坛(原bbs)
社区管理员
  • COM/DCOM/COM+社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧