Problem firing an event from ATL to VB which passes a SAFEARRAY
Author |
Message |
Jon Tayl #1 / 9
|
Problem firing an event from ATL to VB which passes a SAFEARRAY
I am writing an ATL object (an ActiveX control) which I want to pass a buffer of bytes (of variable length), to a VB application. I am using a SAFEARRAY to do this. My problem is that although the Event fires, VB never seems to receive the event. In my ATL project (using VC6), I declare the callback event in the IDL file as follows: dispinterface _ITestSafeArrayEvents { properties: methods: [id(2), helpstring("method Callback")] HRESULT CallBack ([in] SAFEARRAY *Array); Quote: };
Incidently, I found that if I declare "CallBack ([in] SAFEARRAY Array)" as suggested by other posters in the past, that the IDL compiler complains about Array not being an oleautomation type, and VB refuses to use that as an event. Having implemented the connection point (using the Dev Studio wizard), it generates the code below. My application code generates a safearray (using code I know works from another project), then calls Fire_CallBack passing it a pointer to that safearray. Stepping into the de{*filter*}, pDispatch->Invoke (...) definately gets called. However VB just doesn't receive this event (other events do work OK). Should I modify my event handler, or have I completed missed something? Thanks Jon HRESULT Fire_CallBack(tagSAFEARRAY * Array) { CComVariant varResult; T* pT = static_cast<T*>(this); int nConnectionIndex; CComVariant* pvars = new CComVariant[1]; int nConnections = m_vec.GetSize(); for (nConnectionIndex = 0; nConnectionIndex < nConnections; nConnectionIndex++) { pT->Lock(); CComPtr<IUnknown> sp = m_vec.GetAt(nConnectionIndex); pT->Unlock(); IDispatch* pDispatch = reinterpret_cast<IDispatch*>(sp.p); if (pDispatch != NULL) { VariantClear(&varResult); pvars[0] = Array; DISPPARAMS disp = { pvars, NULL, 1, 0 }; pDispatch->Invoke(0x2, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, &varResult, NULL, NULL); } } delete[] pvars; return varResult.scode; }
|
Mon, 19 Jul 2004 18:41:47 GMT |
|
|
Jon Tayl #2 / 9
|
Problem firing an event from ATL to VB which passes a SAFEARRAY
There was a typo in my question... the IDL looks like this: dispinterface _ITestSafeArrayEvents { properties: methods: [id(2), helpstring("method Callback")] HRESULT CallBack ([in] SAFEARRAY(BYTE) *D2BMsg); }; Quote:
> I am writing an ATL object (an ActiveX control) which I want to pass a > buffer of bytes (of variable length), to a VB application. I am using > a SAFEARRAY to do this. My problem is that although the Event fires, > VB never seems to receive the event. > In my ATL project (using VC6), I declare the callback event in the IDL > file as follows: > dispinterface _ITestSafeArrayEvents > { > properties: > methods: > [id(2), helpstring("method Callback")] HRESULT CallBack ([in] > SAFEARRAY *Array); > }; > Incidently, I found that if I declare "CallBack ([in] SAFEARRAY > Array)" as suggested by other posters in the past, that the IDL > compiler complains about Array not being an oleautomation type, and VB > refuses to use that as an event. > Having implemented the connection point (using the Dev Studio wizard), > it generates the code below. My application code generates a > safearray (using code I know works from another project), then calls > Fire_CallBack passing it a pointer to that safearray. Stepping into > the de{*filter*}, pDispatch->Invoke (...) definately gets called. However > VB just doesn't receive this event (other events do work OK). > Should I modify my event handler, or have I completed missed > something? > Thanks > Jon > HRESULT Fire_CallBack(tagSAFEARRAY * Array) > { > CComVariant varResult; > T* pT = static_cast<T*>(this); > int nConnectionIndex; > CComVariant* pvars = new CComVariant[1]; > int nConnections = m_vec.GetSize(); > for (nConnectionIndex = 0; nConnectionIndex < nConnections; > nConnectionIndex++) > { > pT->Lock(); > CComPtr<IUnknown> sp = m_vec.GetAt(nConnectionIndex); > pT->Unlock(); > IDispatch* pDispatch = reinterpret_cast<IDispatch*>(sp.p); > if (pDispatch != NULL) > { > VariantClear(&varResult); > pvars[0] = Array; > DISPPARAMS disp = { pvars, NULL, 1, 0 }; > pDispatch->Invoke(0x2, IID_NULL, LOCALE_USER_DEFAULT, > DISPATCH_METHOD, &disp, &varResult, NULL, NULL); > } > } > delete[] pvars; > return varResult.scode; > }
|
Mon, 19 Jul 2004 21:35:34 GMT |
|
|
Alexander Nickolo #3 / 9
|
Problem firing an event from ATL to VB which passes a SAFEARRAY
This is not going to work, because the wizard does not support references. You'll have to set up the VARIANT as a reference manually: Quote: > > pvars[0] = Array;
becomes V_VT(pvars) = VT_BYREF | VT_ARRAY | VT_<arrayelemtype>; V_ARRAYREF(pvars) = Array; Change the array to have double indirection in the method too: HRESULT Fire_CallBack(SAFEARRAY **Array) Also, get in the habit of specifying void in dispinterfaces: [id(2), helpstring("method Callback")] void CallBack ([in] SAFEARRAY(BYTE) *D2BMsg); -- ===================================== Alexander Nickolov Microsoft MVP [VC], MCSD
MVP VC FAQ: http://www.*-*-*.com/ ===================================== Quote:
> There was a typo in my question... the IDL looks like this: > dispinterface _ITestSafeArrayEvents > { > properties: > methods: > [id(2), helpstring("method Callback")] HRESULT CallBack ([in] > SAFEARRAY(BYTE) *D2BMsg); > };
> > I am writing an ATL object (an ActiveX control) which I want to pass a > > buffer of bytes (of variable length), to a VB application. I am using > > a SAFEARRAY to do this. My problem is that although the Event fires, > > VB never seems to receive the event. > > In my ATL project (using VC6), I declare the callback event in the IDL > > file as follows: > > dispinterface _ITestSafeArrayEvents > > { > > properties: > > methods: > > [id(2), helpstring("method Callback")] HRESULT CallBack ([in] > > SAFEARRAY *Array); > > }; > > Incidently, I found that if I declare "CallBack ([in] SAFEARRAY > > Array)" as suggested by other posters in the past, that the IDL > > compiler complains about Array not being an oleautomation type, and VB > > refuses to use that as an event. > > Having implemented the connection point (using the Dev Studio wizard), > > it generates the code below. My application code generates a > > safearray (using code I know works from another project), then calls > > Fire_CallBack passing it a pointer to that safearray. Stepping into > > the de{*filter*}, pDispatch->Invoke (...) definately gets called. However > > VB just doesn't receive this event (other events do work OK). > > Should I modify my event handler, or have I completed missed > > something? > > Thanks > > Jon > > HRESULT Fire_CallBack(tagSAFEARRAY * Array) > > { > > CComVariant varResult; > > T* pT = static_cast<T*>(this); > > int nConnectionIndex; > > CComVariant* pvars = new CComVariant[1]; > > int nConnections = m_vec.GetSize(); > > for (nConnectionIndex = 0; nConnectionIndex < nConnections; > > nConnectionIndex++) > > { > > pT->Lock(); > > CComPtr<IUnknown> sp = m_vec.GetAt(nConnectionIndex); > > pT->Unlock(); > > IDispatch* pDispatch = reinterpret_cast<IDispatch*>(sp.p); > > if (pDispatch != NULL) > > { > > VariantClear(&varResult); > > pvars[0] = Array; > > DISPPARAMS disp = { pvars, NULL, 1, 0 }; > > pDispatch->Invoke(0x2, IID_NULL, LOCALE_USER_DEFAULT, > > DISPATCH_METHOD, &disp, &varResult, NULL, NULL); > > } > > } > > delete[] pvars; > > return varResult.scode; > > }
|
Tue, 20 Jul 2004 04:37:54 GMT |
|
|
Michai #4 / 9
|
Problem firing an event from ATL to VB which passes a SAFEARRAY
for me best way is to you VARIANT wich will contain SAFEARRAY HRESULT Fire_CallBack(VARIANT Array) michail
This is not going to work, because the wizard does not support references. You'll have to set up the VARIANT as a reference manually: Quote: > > pvars[0] = Array;
becomes V_VT(pvars) = VT_BYREF | VT_ARRAY | VT_<arrayelemtype>; V_ARRAYREF(pvars) = Array; Change the array to have double indirection in the method too: HRESULT Fire_CallBack(SAFEARRAY **Array) Also, get in the habit of specifying void in dispinterfaces: [id(2), helpstring("method Callback")] void CallBack ([in] SAFEARRAY(BYTE) *D2BMsg); -- ===================================== Alexander Nickolov Microsoft MVP [VC], MCSD
MVP VC FAQ: http://www.*-*-*.com/ =====================================
Quote: > There was a typo in my question... the IDL looks like this: > dispinterface _ITestSafeArrayEvents > { > properties: > methods: > [id(2), helpstring("method Callback")] HRESULT CallBack ([in] > SAFEARRAY(BYTE) *D2BMsg); > };
Quote: > > I am writing an ATL object (an ActiveX control) which I want to pass a > > buffer of bytes (of variable length), to a VB application. I am using > > a SAFEARRAY to do this. My problem is that although the Event fires, > > VB never seems to receive the event. > > In my ATL project (using VC6), I declare the callback event in the IDL > > file as follows: > > dispinterface _ITestSafeArrayEvents > > { > > properties: > > methods: > > [id(2), helpstring("method Callback")] HRESULT CallBack ([in] > > SAFEARRAY *Array); > > }; > > Incidently, I found that if I declare "CallBack ([in] SAFEARRAY > > Array)" as suggested by other posters in the past, that the IDL > > compiler complains about Array not being an oleautomation type, and VB > > refuses to use that as an event. > > Having implemented the connection point (using the Dev Studio wizard), > > it generates the code below. My application code generates a > > safearray (using code I know works from another project), then calls > > Fire_CallBack passing it a pointer to that safearray. Stepping into > > the de{*filter*}, pDispatch->Invoke (...) definately gets called. However > > VB just doesn't receive this event (other events do work OK). > > Should I modify my event handler, or have I completed missed > > something? > > Thanks > > Jon > > HRESULT Fire_CallBack(tagSAFEARRAY * Array) > > { > > CComVariant varResult; > > T* pT = static_cast<T*>(this); > > int nConnectionIndex; > > CComVariant* pvars = new CComVariant[1]; > > int nConnections = m_vec.GetSize(); > > for (nConnectionIndex = 0; nConnectionIndex < nConnections; > > nConnectionIndex++) > > { > > pT->Lock(); > > CComPtr<IUnknown> sp = m_vec.GetAt(nConnectionIndex); > > pT->Unlock(); > > IDispatch* pDispatch = reinterpret_cast<IDispatch*>(sp.p); > > if (pDispatch != NULL) > > { > > VariantClear(&varResult); > > pvars[0] = Array; > > DISPPARAMS disp = { pvars, NULL, 1, 0 }; > > pDispatch->Invoke(0x2, IID_NULL, LOCALE_USER_DEFAULT, > > DISPATCH_METHOD, &disp, &varResult, NULL, NULL); > > } > > } > > delete[] pvars; > > return varResult.scode; > > }
|
Tue, 20 Jul 2004 07:09:35 GMT |
|
|
Debabrata Sarma[M #5 / 9
|
Problem firing an event from ATL to VB which passes a SAFEARRAY
You may have to do this in the following way. HRESULT Fire_CallBack(VARIANT *pv) { SAFEARRAY *sa; sa = SafeArrayCreate(...); VARIANT v; VariantInit(&v); v.vt = VT_ARRAY | VT_UI1; v.pparray = &sa; *pv = v; Quote: }
The following article has relevant information. How To Pass Binary Data Between an ActiveX Control and VB ID: Q154172 Deba Sarma[MSFT] This posting is provided AS IS with no warranties, and confers no rights.
|
Tue, 27 Jul 2004 02:24:29 GMT |
|
|
Igor Tandetni #6 / 9
|
Problem firing an event from ATL to VB which passes a SAFEARRAY
That's nonsense. As soon as the function returns, sa goes out of scope, so the pointer you stuff into VARIANT becomes invalid. Also, if you assign to pparray, the type should be VT_UI1 | VT_ARRAY | VT_BYREF. If you specify the type as VT_UI1 | VT_ARRAY, you should put SAFEARRAY* pointer into parray. -- With best wishes, Igor Tandetnik "For every complex problem, there is a solution that is simple, neat, and wrong." H.L. Mencken
Quote: > You may have to do this in the following way. > HRESULT Fire_CallBack(VARIANT *pv) > { > SAFEARRAY *sa; > sa = SafeArrayCreate(...); > VARIANT v; > VariantInit(&v); > v.vt = VT_ARRAY | VT_UI1; > v.pparray = &sa; > *pv = v; > } > The following article has relevant information. > How To Pass Binary Data Between an ActiveX Control and VB > ID: Q154172 > Deba Sarma[MSFT] > This posting is provided AS IS with no warranties, and confers no rights.
|
Tue, 27 Jul 2004 02:37:17 GMT |
|
|
James Ingra #7 / 9
|
Problem firing an event from ATL to VB which passes a SAFEARRAY
On Thu, 7 Feb 2002 13:37:17 -0500, "Igor Tandetnik" Quote:
>That's nonsense. As soon as the function returns, sa goes out of scope, >so the pointer you stuff into VARIANT becomes invalid. Also, if you
I'd disagree - sa is a pointer. The SAFEARRAY created will not go out of scope, even when sa is abandoned. I've done it this way myself, and never had any kind of problem.. I'm more worried about using the VARIANT v - surely the parameter pv can be used more easily? v seems surplus to requirements to me... now that WILL go out of scope. Course if anyone knows different... James
|
Tue, 27 Jul 2004 03:18:42 GMT |
|
|
Jack Ji #8 / 9
|
Problem firing an event from ATL to VB which passes a SAFEARRAY
I usually do it this way: V_VT(pv) = VT_ARRAY | VT_UI1; V_ARRAY(pv) = sa; or V_VT(&v) = VT_ARRAY | VT_UI1; V_ARRAY(&v) = sa; VariantInit(pv); *pv = v; -- Jack
Quote: > On Thu, 7 Feb 2002 13:37:17 -0500, "Igor Tandetnik"
> >That's nonsense. As soon as the function returns, sa goes out of scope, > >so the pointer you stuff into VARIANT becomes invalid. Also, if you > I'd disagree - sa is a pointer. The SAFEARRAY created will not go out > of scope, even when sa is abandoned. > I've done it this way myself, and never had any kind of problem.. > I'm more worried about using the VARIANT v - surely the parameter pv > can be used more easily? v seems surplus to requirements to me... > now that WILL go out of scope. > Course if anyone knows different... > James
|
Tue, 27 Jul 2004 03:37:47 GMT |
|
|
Igor Tandetni #9 / 9
|
Problem firing an event from ATL to VB which passes a SAFEARRAY
Quote: > On Thu, 7 Feb 2002 13:37:17 -0500, "Igor Tandetnik"
> >That's nonsense. As soon as the function returns, sa goes out of scope, > >so the pointer you stuff into VARIANT becomes invalid. Also, if you > I'd disagree - sa is a pointer. The SAFEARRAY created will not go out > of scope, even when sa is abandoned.
Yeah, but the code stuffs pointer to a pointer into the VARIANT (note pparray = &sa). The intermediate pointer is a local variable that goes out of scope, after which a) VARIANT holds a dangling SAFEARRAY** pointer and b) there's no way to access original SAFEARRAY* to free it, thus we have a memory leak. Note also the inconsistency between what is put into variant and what type it is given. Quote: > I've done it this way myself, and never had any kind of problem..
You very likely did something different. The code as written has absolutely no chance of working. Quote: > I'm more worried about using the VARIANT v - surely the parameter pv > can be used more easily? v seems surplus to requirements to me... > now that WILL go out of scope.
When one structure is assigned to another (as in *pv = v) the compiler performs bitwise copy. So *pv will be set up exactly as v. Not terribly efficient, but kosher. -- With best wishes, Igor Tandetnik "For every complex problem, there is a solution that is simple, neat, and wrong." H.L. Mencken
|
Tue, 27 Jul 2004 03:42:22 GMT |
|
|
|