Safearray as Variant parameter to an Event
Author |
Message |
Dale Elsto #1 / 12
|
Safearray as Variant parameter to an Event
Is a safearray a valid event parameter? My event works without the last two parameters listed below. With those parameters, the compiler fails with the list of errors below. How can I do this? thanks interface definition for event: [id(1), helpstring("method OnNewMessage")] HRESULT OnNewMessage([in] short nBytes, [in] BYTE cDest, [in] BYTE cSource, [in] BYTE cCmd, [in] VARIANT vaData, [out, retval] VARIANT_BOOL *bDelete); proxy function: HRESULT Fire_OnNewMessage(SHORT nBytes, BYTE cDest, BYTE cSource, BYTE cCmd, VARIANT vaData, VARIANT_BOOL * bDelete) attempt to fire event: char pcData[3] = { 'A', 'B', 'C' }; _variant_t vaData; VARIANT_BOOL bDelete = VARIANT_FALSE; VariantInit(&vaData); V_VT(&vaData) = VT_ARRAY | VT_UI1; SAFEARRAY* psaData; SAFEARRAYBOUND bounds = { 3, 0 }; psaData = SafeArrayCreate(VT_UI1, 1, &bounds); SafeArrayAccessData(psaData, (void**) &pcData); memcpy(pcData, m_pclsInMsg->m_pcData, 3);//m_pclsInMsg->m_unDataLen); V_ARRAY(&vaData) = psaData; SafeArrayUnaccessData(psaData); pComPtr->Fire_OnNewMessage(6, 1, 100, 1, vaData, &bDelete); resulting errors: S:\ActiveXComponents\ActiveXVer201\ATISerial\ATISerialCP.h(38) : warning C4305: 'argument' : truncation from 'struct tagVARIANT *' to 'bool' S:\ActiveXComponents\ActiveXVer201\ATISerial\ATISerialCP.h(18) : while compiling class-template member function 'long __thiscall CProxy_IMsgSvr422Events<class CMsgSvr422>::Fire_OnNewMessage(short,unsigned char,unsigned char,unsigned char,str uct tagVARIANT,short *)' S:\ActiveXComponents\ActiveXVer201\ATISerial\ATISerialCP.h(38) : warning C4800: 'struct tagVARIANT *' : forcing value to bool 'true' or 'false' (performance warning) S:\ActiveXComponents\ActiveXVer201\ATISerial\ATISerialCP.h(18) : while compiling class-template member function 'long __thiscall CProxy_IMsgSvr422Events<class CMsgSvr422>::Fire_OnNewMessage(short,unsigned char,unsigned char,unsigned char,str uct tagVARIANT,short *)' S:\ActiveXComponents\ActiveXVer201\ATISerial\ATISerialCP.h(39) : warning C4800: 'short *' : forcing value to bool 'true' or 'false' (performance warning) S:\ActiveXComponents\ActiveXVer201\ATISerial\ATISerialCP.h(18) : while compiling class-template member function 'long __thiscall CProxy_IMsgSvr422Events<class CMsgSvr422>::Fire_OnNewMessage(short,unsigned char,unsigned char,unsigned char,str uct tagVARIANT,short *)'
|
Sat, 01 May 2004 03:12:53 GMT |
|
|
Igor Tandetni #2 / 12
|
Safearray as Variant parameter to an Event
ATL event proxy generation wizard incorrectly handles any non-trivial types, e.g. safearrays and parameters that have to be passed by reference. You'll have to correct the generated code manually. Show me the code that the wizard generated, and I'll show you how to correct it. Also, you work with safearray incorrectly. Lose pcData[3], do it as follows: char *pcData; SafeArrayAccessData(psaData, (void**) &pcData); memcpy(pcData, m_pclsInMsg->m_pcData, 3);//m_pclsInMsg->m_unDataLen); SafeArrayUnaccessData(psaData); V_ARRAY(&vaData) = psaData; SafeArrayAccessData sets pcData pointer to point to the buffer allocated for this safearray. You don't need to provide your own buffer. This naturally generalizes to arbitrary number of elements, possibly known at run-time only. -- With best wishes, Igor Tandetnik "For every complex problem, there is a solution that is simple, neat, and wrong." H.L. Mencken
Quote: > Is a safearray a valid event parameter? My event works without the last two > parameters listed below. With those parameters, the compiler fails with the > list of errors below. How can I do this? > thanks > interface definition for event: > [id(1), helpstring("method OnNewMessage")] HRESULT OnNewMessage([in] short > nBytes, [in] BYTE cDest, [in] BYTE cSource, [in] BYTE cCmd, [in] VARIANT > vaData, [out, retval] VARIANT_BOOL *bDelete); > proxy function: > HRESULT Fire_OnNewMessage(SHORT nBytes, BYTE cDest, BYTE cSource, BYTE cCmd, > VARIANT vaData, VARIANT_BOOL * bDelete) > attempt to fire event: > char pcData[3] = { 'A', 'B', 'C' }; > _variant_t vaData; > VARIANT_BOOL bDelete = VARIANT_FALSE; > VariantInit(&vaData); > V_VT(&vaData) = VT_ARRAY | VT_UI1; > SAFEARRAY* psaData; > SAFEARRAYBOUND bounds = { 3, 0 }; > psaData = SafeArrayCreate(VT_UI1, 1, &bounds); > SafeArrayAccessData(psaData, (void**) &pcData); > memcpy(pcData, m_pclsInMsg->m_pcData, 3);//m_pclsInMsg->m_unDataLen); > V_ARRAY(&vaData) = psaData; > SafeArrayUnaccessData(psaData); > pComPtr->Fire_OnNewMessage(6, 1, 100, 1, vaData, &bDelete); > resulting errors: > S:\ActiveXComponents\ActiveXVer201\ATISerial\ATISerialCP.h(38) : warning > C4305: 'argument' : truncation from 'struct tagVARIANT *' to 'bool' > S:\ActiveXComponents\ActiveXVer201\ATISerial\ATISerialCP.h(18) : > while compiling class-template member function 'long __thiscall > CProxy_IMsgSvr422Events<class
CMsgSvr422>::Fire_OnNewMessage(short,unsigned Quote: > char,unsigned char,unsigned char,str > uct tagVARIANT,short *)' > S:\ActiveXComponents\ActiveXVer201\ATISerial\ATISerialCP.h(38) : warning > C4800: 'struct tagVARIANT *' : forcing value to bool 'true' or 'false' > (performance warning) > S:\ActiveXComponents\ActiveXVer201\ATISerial\ATISerialCP.h(18) : > while compiling class-template member function 'long __thiscall > CProxy_IMsgSvr422Events<class
CMsgSvr422>::Fire_OnNewMessage(short,unsigned Quote: > char,unsigned char,unsigned char,str > uct tagVARIANT,short *)' > S:\ActiveXComponents\ActiveXVer201\ATISerial\ATISerialCP.h(39) : warning > C4800: 'short *' : forcing value to bool 'true' or 'false' (performance > warning) > S:\ActiveXComponents\ActiveXVer201\ATISerial\ATISerialCP.h(18) : > while compiling class-template member function 'long __thiscall > CProxy_IMsgSvr422Events<class
CMsgSvr422>::Fire_OnNewMessage(short,unsigned Quote: > char,unsigned char,unsigned char,str > uct tagVARIANT,short *)'
|
Sat, 01 May 2004 03:41:30 GMT |
|
|
Dale Elsto #3 / 12
|
Safearray as Variant parameter to an Event
Thanks for the quick reply, here's the wizard generated code... template <class T> class CProxy_IMsgSvr422Events : public IConnectionPointImpl<T, &DIID__IMsgSvr422Events, CComDynamicUnkArray> { file://Warning this class may be recreated by the wizard. public: HRESULT Fire_OnNewMessage(SHORT nBytes, BYTE cDest, BYTE cSource, BYTE cCmd, VARIANT vaData, VARIANT_BOOL * bDelete) { CComVariant varResult; T* pT = static_cast<T*>(this); int nConnectionIndex; CComVariant* pvars = new CComVariant[6]; 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[5] = nBytes; pvars[4] = cDest; pvars[3] = cSource; pvars[2] = cCmd; pvars[1] = &vaData; pvars[0] = bDelete; DISPPARAMS disp = { pvars, NULL, 6, 0 }; pDispatch->Invoke(0x1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, &varResult, NULL, NULL); } } delete[] pvars; return varResult.scode; } also, the code you noticed errors in was test code that wasn't completed, here is how we really handle safe arrays. _variant_t CTCPMsg::makeSafeArray(const char *pcOut, unsigned long lLength) { _variant_t vaData; char *pcData; VariantInit(&vaData); V_VT(&vaData) = VT_ARRAY | VT_UI1; SAFEARRAY* psaData; SAFEARRAYBOUND bounds = { lLength, 0 }; psaData = SafeArrayCreate(VT_UI1, 1, &bounds); SafeArrayAccessData(psaData, (void**) &pcData); memcpy(pcData, pcOut, lLength); V_ARRAY(&vaData) = psaData; SafeArrayUnaccessData(psaData); return vaData; Quote: }
that's ok, isn't it? thanks
Quote: > ATL event proxy generation wizard incorrectly handles any non-trivial types, > e.g. safearrays and parameters that have to be passed by reference. You'll > have to correct the generated code manually. Show me the code that the > wizard generated, and I'll show you how to correct it. > Also, you work with safearray incorrectly. Lose pcData[3], do it as follows: > char *pcData; > SafeArrayAccessData(psaData, (void**) &pcData); > memcpy(pcData, m_pclsInMsg->m_pcData, 3);//m_pclsInMsg->m_unDataLen); > SafeArrayUnaccessData(psaData); > V_ARRAY(&vaData) = psaData; > SafeArrayAccessData sets pcData pointer to point to the buffer allocated for > this safearray. You don't need to provide your own buffer. This naturally > generalizes to arbitrary number of elements, possibly known at run-time > only. > -- > With best wishes, > Igor Tandetnik > "For every complex problem, there is a solution that is simple, neat, and > wrong." H.L. Mencken
> > Is a safearray a valid event parameter? My event works without the last > two > > parameters listed below. With those parameters, the compiler fails with > the > > list of errors below. How can I do this? > > thanks > > interface definition for event: > > [id(1), helpstring("method OnNewMessage")] HRESULT OnNewMessage([in] short > > nBytes, [in] BYTE cDest, [in] BYTE cSource, [in] BYTE cCmd, [in] VARIANT > > vaData, [out, retval] VARIANT_BOOL *bDelete); > > proxy function: > > HRESULT Fire_OnNewMessage(SHORT nBytes, BYTE cDest, BYTE cSource, BYTE > cCmd, > > VARIANT vaData, VARIANT_BOOL * bDelete) > > attempt to fire event: > > char pcData[3] = { 'A', 'B', 'C' }; > > _variant_t vaData; > > VARIANT_BOOL bDelete = VARIANT_FALSE; > > VariantInit(&vaData); > > V_VT(&vaData) = VT_ARRAY | VT_UI1; > > SAFEARRAY* psaData; > > SAFEARRAYBOUND bounds = { 3, 0 }; > > psaData = SafeArrayCreate(VT_UI1, 1, &bounds); > > SafeArrayAccessData(psaData, (void**) &pcData); > > memcpy(pcData, m_pclsInMsg->m_pcData,
3);//m_pclsInMsg->m_unDataLen); Quote: > > V_ARRAY(&vaData) = psaData; > > SafeArrayUnaccessData(psaData); > > pComPtr->Fire_OnNewMessage(6, 1, 100, 1, vaData, &bDelete); > > resulting errors: > > S:\ActiveXComponents\ActiveXVer201\ATISerial\ATISerialCP.h(38) : warning > > C4305: 'argument' : truncation from 'struct tagVARIANT *' to 'bool' > > S:\ActiveXComponents\ActiveXVer201\ATISerial\ATISerialCP.h(18) : > > while compiling class-template member function 'long __thiscall > > CProxy_IMsgSvr422Events<class > CMsgSvr422>::Fire_OnNewMessage(short,unsigned > > char,unsigned char,unsigned char,str > > uct tagVARIANT,short *)' > > S:\ActiveXComponents\ActiveXVer201\ATISerial\ATISerialCP.h(38) : warning > > C4800: 'struct tagVARIANT *' : forcing value to bool 'true' or 'false' > > (performance warning) > > S:\ActiveXComponents\ActiveXVer201\ATISerial\ATISerialCP.h(18) : > > while compiling class-template member function 'long __thiscall > > CProxy_IMsgSvr422Events<class > CMsgSvr422>::Fire_OnNewMessage(short,unsigned > > char,unsigned char,unsigned char,str > > uct tagVARIANT,short *)' > > S:\ActiveXComponents\ActiveXVer201\ATISerial\ATISerialCP.h(39) : warning > > C4800: 'short *' : forcing value to bool 'true' or 'false' (performance > > warning) > > S:\ActiveXComponents\ActiveXVer201\ATISerial\ATISerialCP.h(18) : > > while compiling class-template member function 'long __thiscall > > CProxy_IMsgSvr422Events<class > CMsgSvr422>::Fire_OnNewMessage(short,unsigned > > char,unsigned char,unsigned char,str > > uct tagVARIANT,short *)'
|
Sat, 01 May 2004 05:50:16 GMT |
|
|
Igor Tandetni #4 / 12
|
Safearray as Variant parameter to an Event
Parameters should be set up like this: VariantClear(&varResult); pvars[5] = nBytes; pvars[4] = cDest; pvars[3] = cSource; pvars[2] = cCmd; pvars[1].Attach(&vaData); V_VT(&pvars[0]) = VT_BOOL | VT_BYREF; V_BOOLREF(&pvars[0]) = bDelete; DISPPARAMS disp = { pvars, NULL, 6, 0 }; pDispatch->Invoke(0x1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, &varResult, NULL, NULL); pvars[1].Detach(&vaData); Attach/Detach games avoid copying SAFEARRAY which can potentially contain large amount of data. Also note how BYREF parameter is handled. It is only good if you have one client at a time. Figure what your semantics are going to be when there are multiple clients sinking this event, potentially setting bDelete to conflicting values. The way it is implemented here, the client that is called last has the final word. Also, the initial value passed to the clients is inconsistent. By the way, I've just noticed that you have declared your bDelete as [out, retval] in IDL, and your function as returning HRESULT. Don't do that. Events should always be void, and have [in] or [in, out] parameters. The client is not required to handle the event, or it can have an empty handler which just returns immediately (e.g. if it is only interested in one event from the set of events). The behavior of your program should be meaningful in the presense of such a client. That usually means that [in, out] parameter should be set to meaningful default value before firing, because you cannot distinsuish between client explicitly setting the variable to this value, or just returning without touching anything. -- With best wishes, Igor Tandetnik "For every complex problem, there is a solution that is simple, neat, and wrong." H.L. Mencken
Quote: > Thanks for the quick reply, here's the wizard generated code... > template <class T> > class CProxy_IMsgSvr422Events : public IConnectionPointImpl<T, > &DIID__IMsgSvr422Events, CComDynamicUnkArray> > { > file://Warning this class may be recreated by the wizard. > public: > HRESULT Fire_OnNewMessage(SHORT nBytes, BYTE cDest, BYTE cSource, BYTE > cCmd, VARIANT vaData, VARIANT_BOOL * bDelete) > { > CComVariant varResult; > T* pT = static_cast<T*>(this); > int nConnectionIndex; > CComVariant* pvars = new CComVariant[6]; > 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[5] = nBytes; > pvars[4] = cDest; > pvars[3] = cSource; > pvars[2] = cCmd; > pvars[1] = &vaData; > pvars[0] = bDelete; > DISPPARAMS disp = { pvars, NULL, 6, 0 }; > pDispatch->Invoke(0x1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, > &disp, &varResult, NULL, NULL); > } > } > delete[] pvars; > return varResult.scode; > } > also, the code you noticed errors in was test code that wasn't completed, > here is how we really handle safe arrays. > _variant_t CTCPMsg::makeSafeArray(const char *pcOut, unsigned long lLength) > { > _variant_t vaData; > char *pcData; > VariantInit(&vaData); > V_VT(&vaData) = VT_ARRAY | VT_UI1; > SAFEARRAY* psaData; > SAFEARRAYBOUND bounds = { lLength, 0 }; > psaData = SafeArrayCreate(VT_UI1, 1, &bounds); > SafeArrayAccessData(psaData, (void**) &pcData); > memcpy(pcData, pcOut, lLength); > V_ARRAY(&vaData) = psaData; > SafeArrayUnaccessData(psaData); > return vaData; > } > that's ok, isn't it? > thanks
> > ATL event proxy generation wizard incorrectly handles any non-trivial > types, > > e.g. safearrays and parameters that have to be passed by reference. You'll > > have to correct the generated code manually. Show me the code that the > > wizard generated, and I'll show you how to correct it. > > Also, you work with safearray incorrectly. Lose pcData[3], do it as > follows: > > char *pcData; > > SafeArrayAccessData(psaData, (void**) &pcData); > > memcpy(pcData, m_pclsInMsg->m_pcData, 3);//m_pclsInMsg->m_unDataLen); > > SafeArrayUnaccessData(psaData); > > V_ARRAY(&vaData) = psaData; > > SafeArrayAccessData sets pcData pointer to point to the buffer allocated > for > > this safearray. You don't need to provide your own buffer. This naturally > > generalizes to arbitrary number of elements, possibly known at run-time > > only. > > -- > > With best wishes, > > Igor Tandetnik > > "For every complex problem, there is a solution that is simple, neat, and > > wrong." H.L. Mencken
> > > Is a safearray a valid event parameter? My event works without the last > > two > > > parameters listed below. With those parameters, the compiler fails with > > the > > > list of errors below. How can I do this? > > > thanks > > > interface definition for event: > > > [id(1), helpstring("method OnNewMessage")] HRESULT OnNewMessage([in] > short > > > nBytes, [in] BYTE cDest, [in] BYTE cSource, [in] BYTE cCmd, [in] VARIANT > > > vaData, [out, retval] VARIANT_BOOL *bDelete); > > > proxy function: > > > HRESULT Fire_OnNewMessage(SHORT nBytes, BYTE cDest, BYTE cSource, BYTE > > cCmd, > > > VARIANT vaData, VARIANT_BOOL * bDelete) > > > attempt to fire event: > > > char pcData[3] = { 'A', 'B', 'C' }; > > > _variant_t vaData; > > > VARIANT_BOOL bDelete = VARIANT_FALSE; > > > VariantInit(&vaData); > > > V_VT(&vaData) = VT_ARRAY | VT_UI1; > > > SAFEARRAY* psaData; > > > SAFEARRAYBOUND bounds = { 3, 0 }; > > > psaData = SafeArrayCreate(VT_UI1, 1, &bounds); > > > SafeArrayAccessData(psaData, (void**) &pcData); > > > memcpy(pcData, m_pclsInMsg->m_pcData, > 3);//m_pclsInMsg->m_unDataLen); > > > V_ARRAY(&vaData) = psaData; > > > SafeArrayUnaccessData(psaData); > > > pComPtr->Fire_OnNewMessage(6, 1, 100, 1, vaData, &bDelete); > > > resulting errors: > > > S:\ActiveXComponents\ActiveXVer201\ATISerial\ATISerialCP.h(38) : warning > > > C4305: 'argument' : truncation from 'struct tagVARIANT *' to 'bool' > > > S:\ActiveXComponents\ActiveXVer201\ATISerial\ATISerialCP.h(18) : > > > while compiling class-template member function 'long __thiscall > > > CProxy_IMsgSvr422Events<class > > CMsgSvr422>::Fire_OnNewMessage(short,unsigned > > > char,unsigned char,unsigned char,str > > > uct tagVARIANT,short *)' > > > S:\ActiveXComponents\ActiveXVer201\ATISerial\ATISerialCP.h(38) : warning > > > C4800: 'struct tagVARIANT *' : forcing value to bool 'true' or 'false' > > > (performance warning) > > > S:\ActiveXComponents\ActiveXVer201\ATISerial\ATISerialCP.h(18) : > > > while compiling class-template member function 'long __thiscall > > > CProxy_IMsgSvr422Events<class > > CMsgSvr422>::Fire_OnNewMessage(short,unsigned > > > char,unsigned char,unsigned char,str > > > uct tagVARIANT,short *)' > > > S:\ActiveXComponents\ActiveXVer201\ATISerial\ATISerialCP.h(39) : warning > > > C4800: 'short *' : forcing value to bool 'true' or 'false' (performance > > > warning) > > > S:\ActiveXComponents\ActiveXVer201\ATISerial\ATISerialCP.h(18) : > > > while compiling class-template member function 'long __thiscall > > > CProxy_IMsgSvr422Events<class > > CMsgSvr422>::Fire_OnNewMessage(short,unsigned > > > char,unsigned char,unsigned char,str > > > uct tagVARIANT,short *)'
|
Sat, 01 May 2004 06:20:48 GMT |
|
|
Ed Elsto #5 / 12
|
Safearray as Variant parameter to an Event
Are you suggesting the IDL be modified as follows? [id(1), helpstring("method OnNewMessage")] void OnNewMessage([in] short nBytes, [in] BYTE cDest, [in] BYTE cSource, [in] BYTE cCmd, [in] VARIANT vaData, [in, out] VARIANT_BOOL *bDelete); Does this change affect the parameter setup shown below to correct the wizard's inability to handle non-trivial types? Ed
Quote: > Parameters should be set up like this: > VariantClear(&varResult); > pvars[5] = nBytes; > pvars[4] = cDest; > pvars[3] = cSource; > pvars[2] = cCmd; > pvars[1].Attach(&vaData); > V_VT(&pvars[0]) = VT_BOOL | VT_BYREF; > V_BOOLREF(&pvars[0]) = bDelete; > DISPPARAMS disp = { pvars, NULL, 6, 0 }; > pDispatch->Invoke(0x1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, > &disp, &varResult, NULL, NULL); > pvars[1].Detach(&vaData); > Attach/Detach games avoid copying SAFEARRAY which can potentially contain > large amount of data. Also note how BYREF parameter is handled. It is only > good if you have one client at a time. Figure what your semantics are going > to be when there are multiple clients sinking this event, potentially > setting bDelete to conflicting values. The way it is implemented here, the > client that is called last has the final word. Also, the initial value > passed to the clients is inconsistent. > By the way, I've just noticed that you have declared your bDelete as [out, > retval] in IDL, and your function as returning HRESULT. Don't do that. > Events should always be void, and have [in] or [in, out] parameters. The > client is not required to handle the event, or it can have an empty handler > which just returns immediately (e.g. if it is only interested in one event > from the set of events). The behavior of your program should be meaningful > in the presense of such a client. That usually means that [in, out] > parameter should be set to meaningful default value before firing, because > you cannot distinsuish between client explicitly setting the variable to > this value, or just returning without touching anything. > -- > With best wishes, > Igor Tandetnik > "For every complex problem, there is a solution that is simple, neat, and > wrong." H.L. Mencken
> > Thanks for the quick reply, here's the wizard generated code... > > template <class T> > > class CProxy_IMsgSvr422Events : public IConnectionPointImpl<T, > > &DIID__IMsgSvr422Events, CComDynamicUnkArray> > > { > > file://Warning this class may be recreated by the wizard. > > public: > > HRESULT Fire_OnNewMessage(SHORT nBytes, BYTE cDest, BYTE cSource, BYTE > > cCmd, VARIANT vaData, VARIANT_BOOL * bDelete) > > { > > CComVariant varResult; > > T* pT = static_cast<T*>(this); > > int nConnectionIndex; > > CComVariant* pvars = new CComVariant[6]; > > 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[5] = nBytes; > > pvars[4] = cDest; > > pvars[3] = cSource; > > pvars[2] = cCmd; > > pvars[1] = &vaData; > > pvars[0] = bDelete; > > DISPPARAMS disp = { pvars, NULL, 6, 0 }; > > pDispatch->Invoke(0x1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, > > &disp, &varResult, NULL, NULL); > > } > > } > > delete[] pvars; > > return varResult.scode; > > } > > also, the code you noticed errors in was test code that wasn't completed, > > here is how we really handle safe arrays. > > _variant_t CTCPMsg::makeSafeArray(const char *pcOut, unsigned long > lLength) > > { > > _variant_t vaData; > > char *pcData; > > VariantInit(&vaData); > > V_VT(&vaData) = VT_ARRAY | VT_UI1; > > SAFEARRAY* psaData; > > SAFEARRAYBOUND bounds = { lLength, 0 }; > > psaData = SafeArrayCreate(VT_UI1, 1, &bounds); > > SafeArrayAccessData(psaData, (void**) &pcData); > > memcpy(pcData, pcOut, lLength); > > V_ARRAY(&vaData) = psaData; > > SafeArrayUnaccessData(psaData); > > return vaData; > > } > > that's ok, isn't it? > > thanks
> > > ATL event proxy generation wizard incorrectly handles any non-trivial > > types, > > > e.g. safearrays and parameters that have to be passed by reference. > You'll > > > have to correct the generated code manually. Show me the code that the > > > wizard generated, and I'll show you how to correct it. > > > Also, you work with safearray incorrectly. Lose pcData[3], do it as > > follows: > > > char *pcData; > > > SafeArrayAccessData(psaData, (void**) &pcData); > > > memcpy(pcData, m_pclsInMsg->m_pcData, 3);//m_pclsInMsg->m_unDataLen); > > > SafeArrayUnaccessData(psaData); > > > V_ARRAY(&vaData) = psaData; > > > SafeArrayAccessData sets pcData pointer to point to the buffer allocated > > for > > > this safearray. You don't need to provide your own buffer. This > naturally > > > generalizes to arbitrary number of elements, possibly known at run-time > > > only. > > > -- > > > With best wishes, > > > Igor Tandetnik > > > "For every complex problem, there is a solution that is simple, neat, > and > > > wrong." H.L. Mencken
> > > > Is a safearray a valid event parameter? My event works without the > last > > > two > > > > parameters listed below. With those parameters, the compiler fails > with > > > the > > > > list of errors below. How can I do this? > > > > thanks > > > > interface definition for event: > > > > [id(1), helpstring("method OnNewMessage")] HRESULT OnNewMessage([in] > > short > > > > nBytes, [in] BYTE cDest, [in] BYTE cSource, [in] BYTE cCmd, [in] > VARIANT > > > > vaData, [out, retval] VARIANT_BOOL *bDelete); > > > > proxy function: > > > > HRESULT Fire_OnNewMessage(SHORT nBytes, BYTE cDest, BYTE cSource, BYTE > > > cCmd, > > > > VARIANT vaData, VARIANT_BOOL * bDelete) > > > > attempt to fire event: > > > > char pcData[3] = { 'A', 'B', 'C' }; > > > > _variant_t vaData; > > > > VARIANT_BOOL bDelete = VARIANT_FALSE; > > > > VariantInit(&vaData); > > > > V_VT(&vaData) = VT_ARRAY | VT_UI1; > > > > SAFEARRAY* psaData; > > > > SAFEARRAYBOUND bounds = { 3, 0 }; > > > > psaData = SafeArrayCreate(VT_UI1, 1, &bounds); > > > > SafeArrayAccessData(psaData, (void**) &pcData); > > > > memcpy(pcData, m_pclsInMsg->m_pcData, > > 3);//m_pclsInMsg->m_unDataLen); > > > > V_ARRAY(&vaData) = psaData; > > > > SafeArrayUnaccessData(psaData); > > > > pComPtr->Fire_OnNewMessage(6, 1, 100, 1, vaData, &bDelete); > > > > resulting errors: > > > > S:\ActiveXComponents\ActiveXVer201\ATISerial\ATISerialCP.h(38) : > warning > > > > C4305: 'argument' : truncation from 'struct tagVARIANT *' to 'bool'
S:\ActiveXComponents\ActiveXVer201\ATISerial\ATISerialCP.h(18) Quote: > : > > > > while compiling class-template member function 'long __thiscall > > > > CProxy_IMsgSvr422Events<class > > > CMsgSvr422>::Fire_OnNewMessage(short,unsigned > > > > char,unsigned char,unsigned char,str > > > > uct tagVARIANT,short *)' > > > > S:\ActiveXComponents\ActiveXVer201\ATISerial\ATISerialCP.h(38) : > warning > > > > C4800: 'struct tagVARIANT *' : forcing value to bool 'true' or 'false' > > > > (performance warning)
S:\ActiveXComponents\ActiveXVer201\ATISerial\ATISerialCP.h(18) Quote: > : > > > > while compiling class-template member function 'long __thiscall > > > > CProxy_IMsgSvr422Events<class > > > CMsgSvr422>::Fire_OnNewMessage(short,unsigned > > > > char,unsigned char,unsigned char,str > > > > uct tagVARIANT,short *)' > > > > S:\ActiveXComponents\ActiveXVer201\ATISerial\ATISerialCP.h(39) : > warning > > > > C4800: 'short *' : forcing value to bool 'true' or 'false' > (performance > > > > warning)
S:\ActiveXComponents\ActiveXVer201\ATISerial\ATISerialCP.h(18) Quote: > : > > > > while compiling class-template member function 'long __thiscall > > > > CProxy_IMsgSvr422Events<class > > > CMsgSvr422>::Fire_OnNewMessage(short,unsigned > > > > char,unsigned char,unsigned char,str > > > > uct tagVARIANT,short *)'
|
Mon, 03 May 2004 00:27:26 GMT |
|
|
Igor Tandetni #6 / 12
|
Safearray as Variant parameter to an Event
Yes, that's what I suggest, and no, it does not change the technical aspect of parameter setup. You still need to address the semantic aspect. E.g. this pseudocode sets bDelete to false by default, and changes it to true if at least one client sets it to true: HRESULT Fire(VARIANT_BOOL *bDelete) { *bDelete = VARIANT_FALSE; for each sink { VARIANT_BOOL bTemp = *bDelete; ActualFire(&bTemp); if (bTemp) *bDelete = VARIANT_TRUE; } Quote: }
-- With best wishes, Igor Tandetnik "For every complex problem, there is a solution that is simple, neat, and wrong." H.L. Mencken
Quote: > Are you suggesting the IDL be modified as follows? > [id(1), helpstring("method OnNewMessage")] void OnNewMessage([in] short > nBytes, [in] BYTE cDest, [in] BYTE cSource, [in] BYTE cCmd, [in] VARIANT > vaData, [in, out] VARIANT_BOOL *bDelete); > Does this change affect the parameter setup shown below to correct the > wizard's inability to handle non-trivial types? > Ed
> > Parameters should be set up like this: > > VariantClear(&varResult); > > pvars[5] = nBytes; > > pvars[4] = cDest; > > pvars[3] = cSource; > > pvars[2] = cCmd; > > pvars[1].Attach(&vaData); > > V_VT(&pvars[0]) = VT_BOOL | VT_BYREF; > > V_BOOLREF(&pvars[0]) = bDelete; > > DISPPARAMS disp = { pvars, NULL, 6, 0 }; > > pDispatch->Invoke(0x1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, > > &disp, &varResult, NULL, NULL); > > pvars[1].Detach(&vaData); > > Attach/Detach games avoid copying SAFEARRAY which can potentially contain > > large amount of data. Also note how BYREF parameter is handled. It is only > > good if you have one client at a time. Figure what your semantics are > going > > to be when there are multiple clients sinking this event, potentially > > setting bDelete to conflicting values. The way it is implemented here, the > > client that is called last has the final word. Also, the initial value > > passed to the clients is inconsistent. > > By the way, I've just noticed that you have declared your bDelete as [out, > > retval] in IDL, and your function as returning HRESULT. Don't do that. > > Events should always be void, and have [in] or [in, out] parameters. The > > client is not required to handle the event, or it can have an empty > handler > > which just returns immediately (e.g. if it is only interested in one event > > from the set of events). The behavior of your program should be meaningful > > in the presense of such a client. That usually means that [in, out] > > parameter should be set to meaningful default value before firing, because > > you cannot distinsuish between client explicitly setting the variable to > > this value, or just returning without touching anything. > > -- > > With best wishes, > > Igor Tandetnik > > "For every complex problem, there is a solution that is simple, neat, and > > wrong." H.L. Mencken
> > > Thanks for the quick reply, here's the wizard generated code... > > > template <class T> > > > class CProxy_IMsgSvr422Events : public IConnectionPointImpl<T, > > > &DIID__IMsgSvr422Events, CComDynamicUnkArray> > > > { > > > file://Warning this class may be recreated by the wizard. > > > public: > > > HRESULT Fire_OnNewMessage(SHORT nBytes, BYTE cDest, BYTE cSource, BYTE > > > cCmd, VARIANT vaData, VARIANT_BOOL * bDelete) > > > { > > > CComVariant varResult; > > > T* pT = static_cast<T*>(this); > > > int nConnectionIndex; > > > CComVariant* pvars = new CComVariant[6]; > > > 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[5] = nBytes; > > > pvars[4] = cDest; > > > pvars[3] = cSource; > > > pvars[2] = cCmd; > > > pvars[1] = &vaData; > > > pvars[0] = bDelete; > > > DISPPARAMS disp = { pvars, NULL, 6, 0 }; > > > pDispatch->Invoke(0x1, IID_NULL, LOCALE_USER_DEFAULT, > DISPATCH_METHOD, > > > &disp, &varResult, NULL, NULL); > > > } > > > } > > > delete[] pvars; > > > return varResult.scode; > > > } > > > also, the code you noticed errors in was test code that wasn't > completed, > > > here is how we really handle safe arrays. > > > _variant_t CTCPMsg::makeSafeArray(const char *pcOut, unsigned long > > lLength) > > > { > > > _variant_t vaData; > > > char *pcData; > > > VariantInit(&vaData); > > > V_VT(&vaData) = VT_ARRAY | VT_UI1; > > > SAFEARRAY* psaData; > > > SAFEARRAYBOUND bounds = { lLength, 0 }; > > > psaData = SafeArrayCreate(VT_UI1, 1, &bounds); > > > SafeArrayAccessData(psaData, (void**) &pcData); > > > memcpy(pcData, pcOut, lLength); > > > V_ARRAY(&vaData) = psaData; > > > SafeArrayUnaccessData(psaData); > > > return vaData; > > > } > > > that's ok, isn't it? > > > thanks
> > > > ATL event proxy generation wizard incorrectly handles any non-trivial > > > types, > > > > e.g. safearrays and parameters that have to be passed by reference. > > You'll > > > > have to correct the generated code manually. Show me the code that the > > > > wizard generated, and I'll show you how to correct it. > > > > Also, you work with safearray incorrectly. Lose pcData[3], do it as > > > follows: > > > > char *pcData; > > > > SafeArrayAccessData(psaData, (void**) &pcData); > > > > memcpy(pcData, m_pclsInMsg->m_pcData,
3);//m_pclsInMsg->m_unDataLen); Quote: > > > > SafeArrayUnaccessData(psaData); > > > > V_ARRAY(&vaData) = psaData; > > > > SafeArrayAccessData sets pcData pointer to point to the buffer > allocated > > > for > > > > this safearray. You don't need to provide your own buffer. This > > naturally > > > > generalizes to arbitrary number of elements, possibly known at > run-time > > > > only. > > > > -- > > > > With best wishes, > > > > Igor Tandetnik > > > > "For every complex problem, there is a solution that is simple, neat, > > and > > > > wrong." H.L. Mencken
> > > > > Is a safearray a valid event parameter? My event works without the > > last > > > > two > > > > > parameters listed below. With those parameters, the compiler fails > > with > > > > the > > > > > list of errors below. How can I do this? > > > > > thanks > > > > > interface definition for event: > > > > > [id(1), helpstring("method OnNewMessage")] HRESULT OnNewMessage([in] > > > short > > > > > nBytes, [in] BYTE cDest, [in] BYTE cSource, [in] BYTE cCmd, [in] > > VARIANT > > > > > vaData, [out, retval] VARIANT_BOOL *bDelete); > > > > > proxy function: > > > > > HRESULT Fire_OnNewMessage(SHORT nBytes, BYTE cDest, BYTE cSource, > BYTE > > > > cCmd, > > > > > VARIANT vaData, VARIANT_BOOL * bDelete) > > > > > attempt to fire event: > > > > > char pcData[3] = { 'A', 'B', 'C' }; > > > > > _variant_t vaData; > > > > > VARIANT_BOOL bDelete = VARIANT_FALSE; > > > > > VariantInit(&vaData); > > > > > V_VT(&vaData) = VT_ARRAY | VT_UI1; > > > > > SAFEARRAY* psaData; > > > > > SAFEARRAYBOUND bounds = { 3, 0 }; > > > > > psaData = SafeArrayCreate(VT_UI1, 1, &bounds); > > > > > SafeArrayAccessData(psaData, (void**) &pcData); > > > > > memcpy(pcData, m_pclsInMsg->m_pcData, > > > 3);//m_pclsInMsg->m_unDataLen); > > > > > V_ARRAY(&vaData) = psaData; > > > > > SafeArrayUnaccessData(psaData); > > > > > pComPtr->Fire_OnNewMessage(6, 1, 100, 1, vaData, &bDelete); > > > > > resulting errors: > > > > > S:\ActiveXComponents\ActiveXVer201\ATISerial\ATISerialCP.h(38) : > > warning > > > > > C4305: 'argument' : truncation from 'struct tagVARIANT *' to 'bool' > S:\ActiveXComponents\ActiveXVer201\ATISerial\ATISerialCP.h(18) > > : > > > > > while compiling class-template member function 'long __thiscall > > > > > CProxy_IMsgSvr422Events<class > > > > CMsgSvr422>::Fire_OnNewMessage(short,unsigned > > > > > char,unsigned char,unsigned char,str > > > > > uct tagVARIANT,short *)' > > > > > S:\ActiveXComponents\ActiveXVer201\ATISerial\ATISerialCP.h(38) : > > warning > > > > > C4800: 'struct tagVARIANT *' : forcing value to bool 'true' or > 'false' > > > > > (performance warning) > S:\ActiveXComponents\ActiveXVer201\ATISerial\ATISerialCP.h(18) > > : > > > > > while compiling class-template member function 'long __thiscall > > > > > CProxy_IMsgSvr422Events<class > > > > CMsgSvr422>::Fire_OnNewMessage(short,unsigned > > > > > char,unsigned char,unsigned char,str > > > > > uct tagVARIANT,short *)' > > > > > S:\ActiveXComponents\ActiveXVer201\ATISerial\ATISerialCP.h(39) : > > warning > > > > > C4800: 'short *' : forcing value to bool 'true' or 'false' > > (performance > > > > > warning) > S:\ActiveXComponents\ActiveXVer201\ATISerial\ATISerialCP.h(18) > > : > > > > > while compiling class-template member function 'long __thiscall > > > > > CProxy_IMsgSvr422Events<class > > > > CMsgSvr422>::Fire_OnNewMessage(short,unsigned > > > > > char,unsigned char,unsigned char,str > > > > > uct tagVARIANT,short *)'
|
Mon, 03 May 2004 01:22:43 GMT |
|
|
Ed Elsto #7 / 12
|
Safearray as Variant parameter to an Event
This is not working properly. If I use only the first 4 params, the values come to the client properly as confirmed with the de{*filter*}. Including the VARIANT and VARIANT_BOOL causes not only some of the first 4 params to have invalid data, but I cannot access the variant data. In fact, the SAFEARRAY *psa always shows Error: expression cannot be evaluated for its members in the variables window. Prototype for the event handler: void __stdcall OnNewMessage(short nBytes, BYTE cDest, BYTE cSource, BYTE cCmd, VARIANT vaData, VARIANT_BOOL *bDelete); // event handler Event from IDL: [id(1), helpstring("method OnNewMessage")] void OnNewMessage([in] short nBytes, [in] BYTE cDest, [in] BYTE cSource, [in] BYTE cCmd, [in] VARIANT vaData, [in, out] VARIANT_BOOL *bDelete); Changes you suggested complete: HRESULT Fire_OnNewMessage(SHORT nBytes, BYTE cDest, BYTE cSource, BYTE cCmd, VARIANT vaData, VARIANT_BOOL * bDelete) { CComVariant varResult; T* pT = static_cast<T*>(this); int nConnectionIndex; CComVariant* pvars = new CComVariant[6]; 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[5] = nBytes; pvars[4] = cDest; pvars[3] = cSource; pvars[2] = cCmd; pvars[1].Attach(&vaData); V_VT(&pvars[0]) = VT_BOOL | VT_BYREF; V_BOOLREF(&pvars[0]) = bDelete; DISPPARAMS disp = { pvars, NULL, 6, 0 }; pDispatch->Invoke(0x1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, &varResult, NULL, NULL); pvars[1].Detach(&vaData); } } delete[] pvars; return varResult.scode; }
Quote: > Yes, that's what I suggest, and no, it does not change the technical aspect > of parameter setup. You still need to address the semantic aspect. E.g. this > pseudocode sets bDelete to false by default, and changes it to true if at > least one client sets it to true: > HRESULT Fire(VARIANT_BOOL *bDelete) > { > *bDelete = VARIANT_FALSE; > for each sink > { > VARIANT_BOOL bTemp = *bDelete; > ActualFire(&bTemp); > if (bTemp) > *bDelete = VARIANT_TRUE; > } > } > -- > With best wishes, > Igor Tandetnik > "For every complex problem, there is a solution that is simple, neat, and > wrong." H.L. Mencken
> > Are you suggesting the IDL be modified as follows? > > [id(1), helpstring("method OnNewMessage")] void OnNewMessage([in] short > > nBytes, [in] BYTE cDest, [in] BYTE cSource, [in] BYTE cCmd, [in] VARIANT > > vaData, [in, out] VARIANT_BOOL *bDelete); > > Does this change affect the parameter setup shown below to correct the > > wizard's inability to handle non-trivial types? > > Ed
> > > Parameters should be set up like this: > > > VariantClear(&varResult); > > > pvars[5] = nBytes; > > > pvars[4] = cDest; > > > pvars[3] = cSource; > > > pvars[2] = cCmd; > > > pvars[1].Attach(&vaData); > > > V_VT(&pvars[0]) = VT_BOOL | VT_BYREF; > > > V_BOOLREF(&pvars[0]) = bDelete; > > > DISPPARAMS disp = { pvars, NULL, 6, 0 }; > > > pDispatch->Invoke(0x1, IID_NULL, LOCALE_USER_DEFAULT, > DISPATCH_METHOD, > > > &disp, &varResult, NULL, NULL); > > > pvars[1].Detach(&vaData); > > > Attach/Detach games avoid copying SAFEARRAY which can potentially > contain > > > large amount of data. Also note how BYREF parameter is handled. It is > only > > > good if you have one client at a time. Figure what your semantics are > > going > > > to be when there are multiple clients sinking this event, potentially > > > setting bDelete to conflicting values. The way it is implemented here, > the > > > client that is called last has the final word. Also, the initial value > > > passed to the clients is inconsistent. > > > By the way, I've just noticed that you have declared your bDelete as > [out, > > > retval] in IDL, and your function as returning HRESULT. Don't do that. > > > Events should always be void, and have [in] or [in, out] parameters. The > > > client is not required to handle the event, or it can have an empty > > handler > > > which just returns immediately (e.g. if it is only interested in one > event > > > from the set of events). The behavior of your program should be > meaningful > > > in the presense of such a client. That usually means that [in, out] > > > parameter should be set to meaningful default value before firing, > because > > > you cannot distinsuish between client explicitly setting the variable to > > > this value, or just returning without touching anything. > > > -- > > > With best wishes, > > > Igor Tandetnik > > > "For every complex problem, there is a solution that is simple, neat, > and > > > wrong." H.L. Mencken
> > > > Thanks for the quick reply, here's the wizard generated code... > > > > template <class T> > > > > class CProxy_IMsgSvr422Events : public IConnectionPointImpl<T, > > > > &DIID__IMsgSvr422Events, CComDynamicUnkArray> > > > > { > > > > file://Warning this class may be recreated by the wizard. > > > > public: > > > > HRESULT Fire_OnNewMessage(SHORT nBytes, BYTE cDest, BYTE cSource, > BYTE > > > > cCmd, VARIANT vaData, VARIANT_BOOL * bDelete) > > > > { > > > > CComVariant varResult; > > > > T* pT = static_cast<T*>(this); > > > > int nConnectionIndex; > > > > CComVariant* pvars = new CComVariant[6]; > > > > 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[5] = nBytes; > > > > pvars[4] = cDest; > > > > pvars[3] = cSource; > > > > pvars[2] = cCmd; > > > > pvars[1] = &vaData; > > > > pvars[0] = bDelete; > > > > DISPPARAMS disp = { pvars, NULL, 6, 0 }; > > > > pDispatch->Invoke(0x1, IID_NULL, LOCALE_USER_DEFAULT, > > DISPATCH_METHOD, > > > > &disp, &varResult, NULL, NULL); > > > > } > > > > } > > > > delete[] pvars; > > > > return varResult.scode; > > > > } > > > > also, the code you noticed errors in was test code that wasn't > > completed, > > > > here is how we really handle safe arrays. > > > > _variant_t CTCPMsg::makeSafeArray(const char *pcOut, unsigned long > > > lLength) > > > > { > > > > _variant_t vaData; > > > > char *pcData; > > > > VariantInit(&vaData); > > > > V_VT(&vaData) = VT_ARRAY | VT_UI1; > > > > SAFEARRAY* psaData; > > > > SAFEARRAYBOUND bounds = { lLength, 0 }; > > > > psaData = SafeArrayCreate(VT_UI1, 1, &bounds); > > > > SafeArrayAccessData(psaData, (void**) &pcData); > > > > memcpy(pcData, pcOut, lLength); > > > > V_ARRAY(&vaData) = psaData; > > > > SafeArrayUnaccessData(psaData); > > > > return vaData; > > > > } > > > > that's ok, isn't it? > > > > thanks
> > > > > ATL event proxy generation wizard incorrectly handles any > non-trivial > > > > types, > > > > > e.g. safearrays and parameters that have to be passed by reference. > > > You'll > > > > > have to correct the generated code manually. Show me the code that > the > > > > > wizard generated, and I'll show you how to correct it. > > > > > Also, you work with safearray incorrectly. Lose pcData[3], do it as > > > > follows: > > > > > char *pcData; > > > > > SafeArrayAccessData(psaData, (void**) &pcData); > > > > > memcpy(pcData, m_pclsInMsg->m_pcData, > 3);//m_pclsInMsg->m_unDataLen); > > > > > SafeArrayUnaccessData(psaData); > > > > > V_ARRAY(&vaData) = psaData; > > > > > SafeArrayAccessData sets pcData pointer to point to the buffer > > allocated > > > > for > > > > > this safearray. You don't need to provide your own buffer. This > > > naturally > > > > > generalizes to arbitrary number of elements, possibly known at > > run-time > > > > > only. > > > > > -- > > > > > With best wishes, > > > > > Igor Tandetnik > > > > > "For every complex problem, there is a solution that is simple, > neat, > > > and > > > > > wrong." H.L. Mencken
> > > > > > Is a safearray a valid event parameter? My event works without the > > > last > > > > > two > > > > > > parameters listed below. With those parameters, the compiler fails > > > with > > > > > the > > > > > > list of errors below. How can I do this? > > > > > > thanks > > > > > > interface definition for event: > > > > > > [id(1), helpstring("method OnNewMessage")] HRESULT > OnNewMessage([in] > > > > short > > > > > > nBytes, [in] BYTE cDest, [in] BYTE cSource, [in] BYTE cCmd, [in] > > > VARIANT > > > > > > vaData, [out, retval] VARIANT_BOOL *bDelete); > > > > > > proxy function: > > > > > > HRESULT Fire_OnNewMessage(SHORT nBytes, BYTE cDest, BYTE cSource, > > BYTE > > > > > cCmd, > > > > > > VARIANT vaData, VARIANT_BOOL * bDelete) > > > > > > attempt to fire event: > > > > > > char pcData[3] = { 'A', 'B', 'C' };
... read more »
|
Mon, 03 May 2004 02:53:15 GMT |
|
|
Igor Tandetni #8 / 12
|
Safearray as Variant parameter to an Event
If you trace into Fire_OnNewMessage, do the parameters look good right before Invoke? Does vaData look good on entry? -- With best wishes, Igor Tandetnik "For every complex problem, there is a solution that is simple, neat, and wrong." H.L. Mencken
Quote: > This is not working properly. If I use only the first 4 params, the values > come to the client properly as confirmed with the de{*filter*}. Including the > VARIANT and VARIANT_BOOL causes not only some of the first 4 params to have > invalid data, but I cannot access the variant data. In fact, the SAFEARRAY > *psa always shows Error: expression cannot be evaluated for its members in > the variables window. > Prototype for the event handler: > void __stdcall OnNewMessage(short nBytes, BYTE cDest, BYTE cSource, BYTE > cCmd, VARIANT vaData, VARIANT_BOOL *bDelete); // event handler > Event from IDL: > [id(1), helpstring("method OnNewMessage")] void OnNewMessage([in] short > nBytes, [in] BYTE cDest, [in] BYTE cSource, [in] BYTE cCmd, [in] VARIANT > vaData, [in, out] VARIANT_BOOL *bDelete); > Changes you suggested complete: > HRESULT Fire_OnNewMessage(SHORT nBytes, BYTE cDest, BYTE cSource, BYTE > cCmd, VARIANT vaData, VARIANT_BOOL * bDelete) > { > CComVariant varResult; > T* pT = static_cast<T*>(this); > int nConnectionIndex; > CComVariant* pvars = new CComVariant[6]; > 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[5] = nBytes; > pvars[4] = cDest; > pvars[3] = cSource; > pvars[2] = cCmd; > pvars[1].Attach(&vaData); > V_VT(&pvars[0]) = VT_BOOL | VT_BYREF; > V_BOOLREF(&pvars[0]) = bDelete; > DISPPARAMS disp = { pvars, NULL, 6, 0 }; > pDispatch->Invoke(0x1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, > &disp, &varResult, NULL, NULL); > pvars[1].Detach(&vaData); > } > } > delete[] pvars; > return varResult.scode; > }
> > Yes, that's what I suggest, and no, it does not change the technical > aspect > > of parameter setup. You still need to address the semantic aspect. E.g. > this > > pseudocode sets bDelete to false by default, and changes it to true if at > > least one client sets it to true: > > HRESULT Fire(VARIANT_BOOL *bDelete) > > { > > *bDelete = VARIANT_FALSE; > > for each sink > > { > > VARIANT_BOOL bTemp = *bDelete; > > ActualFire(&bTemp); > > if (bTemp) > > *bDelete = VARIANT_TRUE; > > } > > } > > -- > > With best wishes, > > Igor Tandetnik > > "For every complex problem, there is a solution that is simple, neat, and > > wrong." H.L. Mencken
> > > Are you suggesting the IDL be modified as follows? > > > [id(1), helpstring("method OnNewMessage")] void OnNewMessage([in] > short > > > nBytes, [in] BYTE cDest, [in] BYTE cSource, [in] BYTE cCmd, [in] VARIANT > > > vaData, [in, out] VARIANT_BOOL *bDelete); > > > Does this change affect the parameter setup shown below to correct the > > > wizard's inability to handle non-trivial types? > > > Ed
> > > > Parameters should be set up like this: > > > > VariantClear(&varResult); > > > > pvars[5] = nBytes; > > > > pvars[4] = cDest; > > > > pvars[3] = cSource; > > > > pvars[2] = cCmd; > > > > pvars[1].Attach(&vaData); > > > > V_VT(&pvars[0]) = VT_BOOL | VT_BYREF; > > > > V_BOOLREF(&pvars[0]) = bDelete; > > > > DISPPARAMS disp = { pvars, NULL, 6, 0 }; > > > > pDispatch->Invoke(0x1, IID_NULL, LOCALE_USER_DEFAULT, > > DISPATCH_METHOD, > > > > &disp, &varResult, NULL, NULL); > > > > pvars[1].Detach(&vaData); > > > > Attach/Detach games avoid copying SAFEARRAY which can potentially > > contain > > > > large amount of data. Also note how BYREF parameter is handled. It is > > only > > > > good if you have one client at a time. Figure what your semantics are > > > going > > > > to be when there are multiple clients sinking this event, potentially > > > > setting bDelete to conflicting values. The way it is implemented here, > > the > > > > client that is called last has the final word. Also, the initial value > > > > passed to the clients is inconsistent. > > > > By the way, I've just noticed that you have declared your bDelete as > > [out, > > > > retval] in IDL, and your function as returning HRESULT. Don't do that. > > > > Events should always be void, and have [in] or [in, out] parameters. > The > > > > client is not required to handle the event, or it can have an empty > > > handler > > > > which just returns immediately (e.g. if it is only interested in one > > event > > > > from the set of events). The behavior of your program should be > > meaningful > > > > in the presense of such a client. That usually means that [in, out] > > > > parameter should be set to meaningful default value before firing, > > because > > > > you cannot distinsuish between client explicitly setting the variable > to > > > > this value, or just returning without touching anything. > > > > -- > > > > With best wishes, > > > > Igor Tandetnik > > > > "For every complex problem, there is a solution that is simple, neat, > > and > > > > wrong." H.L. Mencken
> > > > > Thanks for the quick reply, here's the wizard generated code... > > > > > template <class T> > > > > > class CProxy_IMsgSvr422Events : public IConnectionPointImpl<T, > > > > > &DIID__IMsgSvr422Events, CComDynamicUnkArray> > > > > > { > > > > > file://Warning this class may be recreated by the wizard. > > > > > public: > > > > > HRESULT Fire_OnNewMessage(SHORT nBytes, BYTE cDest, BYTE cSource, > > BYTE > > > > > cCmd, VARIANT vaData, VARIANT_BOOL * bDelete) > > > > > { > > > > > CComVariant varResult; > > > > > T* pT = static_cast<T*>(this); > > > > > int nConnectionIndex; > > > > > CComVariant* pvars = new CComVariant[6]; > > > > > 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[5] = nBytes; > > > > > pvars[4] = cDest; > > > > > pvars[3] = cSource; > > > > > pvars[2] = cCmd; > > > > > pvars[1] = &vaData; > > > > > pvars[0] = bDelete; > > > > > DISPPARAMS disp = { pvars, NULL, 6, 0 }; > > > > > pDispatch->Invoke(0x1, IID_NULL, LOCALE_USER_DEFAULT, > > > DISPATCH_METHOD, > > > > > &disp, &varResult, NULL, NULL); > > > > > } > > > > > } > > > > > delete[] pvars; > > > > > return varResult.scode; > > > > > } > > > > > also, the code you noticed errors in was test code that wasn't > > > completed, > > > > > here is how we really handle safe arrays. > > > > > _variant_t CTCPMsg::makeSafeArray(const char *pcOut, unsigned long > > > > lLength) > > > > > { > > > > > _variant_t vaData; > > > > > char *pcData; > > > > > VariantInit(&vaData); > > > > > V_VT(&vaData) = VT_ARRAY | VT_UI1; > > > > > SAFEARRAY* psaData; > > > > > SAFEARRAYBOUND bounds = { lLength, 0 }; > > > > > psaData = SafeArrayCreate(VT_UI1, 1, &bounds); > > > > > SafeArrayAccessData(psaData, (void**) &pcData); > > > > > memcpy(pcData, pcOut, lLength); > > > > > V_ARRAY(&vaData) = psaData; > > > > > SafeArrayUnaccessData(psaData); > > > > > return vaData; > > > > > } > > > > > that's ok, isn't it? > > > > > thanks
> > > > > > ATL event proxy generation wizard incorrectly handles any > > non-trivial > > > > > types, > > > > > > e.g. safearrays and parameters that have to be passed by > reference. > > > > You'll > > > > > > have to correct the generated code manually. Show me the code that > > the > > > > > > wizard generated, and I'll show you how to correct it. > > > > > > Also, you work with safearray incorrectly. Lose pcData[3], do it > as > > > > > follows: > > > > > > char *pcData; > > > > > > SafeArrayAccessData(psaData, (void**) &pcData); > > > > > > memcpy(pcData, m_pclsInMsg->m_pcData, > > 3);//m_pclsInMsg->m_unDataLen); > > > > > > SafeArrayUnaccessData(psaData); > > > > > > V_ARRAY(&vaData) = psaData; > > > > > > SafeArrayAccessData sets pcData pointer to point to the buffer > > > allocated > > > > > for > > > > > > this safearray. You don't need to provide your own buffer. This > > > > naturally > > > > > > generalizes to arbitrary number of elements, possibly known at > > > run-time > > > > > > only. > > > > > > -- > > > > > > With best wishes, > > > > > > Igor Tandetnik > > > > > > "For every complex problem, there is a solution that is simple, > > neat, > > > > and > > > > > > wrong." H.L. Mencken
... read more »
|
Mon, 03 May 2004 04:02:12 GMT |
|
|
Alexander Nickolo #9 / 12
|
Safearray as Variant parameter to an Event
I wrote a short article in the MVP COM/ATL FAQ a while ago. You may find it useful here. It is item 3 in the COM/ATL FAQ in my signature, or a direct link: http://www.mvps.org/vcfaq/com/3.htm -- ===================================== Alexander Nickolov Microsoft MVP [VC], MCSD
MVP VC FAQ: http://www.mvps.org/vcfaq =====================================
Quote: > Yes, that's what I suggest, and no, it does not change the technical aspect > of parameter setup. You still need to address the semantic aspect. E.g. this > pseudocode sets bDelete to false by default, and changes it to true if at > least one client sets it to true: > HRESULT Fire(VARIANT_BOOL *bDelete) > { > *bDelete = VARIANT_FALSE; > for each sink > { > VARIANT_BOOL bTemp = *bDelete; > ActualFire(&bTemp); > if (bTemp) > *bDelete = VARIANT_TRUE; > } > } > -- > With best wishes, > Igor Tandetnik > "For every complex problem, there is a solution that is simple, neat, and > wrong." H.L. Mencken
> > Are you suggesting the IDL be modified as follows? > > [id(1), helpstring("method OnNewMessage")] void OnNewMessage([in] short > > nBytes, [in] BYTE cDest, [in] BYTE cSource, [in] BYTE cCmd, [in] VARIANT > > vaData, [in, out] VARIANT_BOOL *bDelete); > > Does this change affect the parameter setup shown below to correct the > > wizard's inability to handle non-trivial types? > > Ed
> > > Parameters should be set up like this: > > > VariantClear(&varResult); > > > pvars[5] = nBytes; > > > pvars[4] = cDest; > > > pvars[3] = cSource; > > > pvars[2] = cCmd; > > > pvars[1].Attach(&vaData); > > > V_VT(&pvars[0]) = VT_BOOL | VT_BYREF; > > > V_BOOLREF(&pvars[0]) = bDelete; > > > DISPPARAMS disp = { pvars, NULL, 6, 0 }; > > > pDispatch->Invoke(0x1, IID_NULL, LOCALE_USER_DEFAULT, > DISPATCH_METHOD, > > > &disp, &varResult, NULL, NULL); > > > pvars[1].Detach(&vaData); > > > Attach/Detach games avoid copying SAFEARRAY which can potentially > contain > > > large amount of data. Also note how BYREF parameter is handled. It is > only > > > good if you have one client at a time. Figure what your semantics are > > going > > > to be when there are multiple clients sinking this event, potentially > > > setting bDelete to conflicting values. The way it is implemented here, > the > > > client that is called last has the final word. Also, the initial value > > > passed to the clients is inconsistent. > > > By the way, I've just noticed that you have declared your bDelete as > [out, > > > retval] in IDL, and your function as returning HRESULT. Don't do that. > > > Events should always be void, and have [in] or [in, out] parameters. The > > > client is not required to handle the event, or it can have an empty > > handler > > > which just returns immediately (e.g. if it is only interested in one > event > > > from the set of events). The behavior of your program should be > meaningful > > > in the presense of such a client. That usually means that [in, out] > > > parameter should be set to meaningful default value before firing, > because > > > you cannot distinsuish between client explicitly setting the variable to > > > this value, or just returning without touching anything. > > > -- > > > With best wishes, > > > Igor Tandetnik > > > "For every complex problem, there is a solution that is simple, neat, > and > > > wrong." H.L. Mencken
> > > > Thanks for the quick reply, here's the wizard generated code... > > > > template <class T> > > > > class CProxy_IMsgSvr422Events : public IConnectionPointImpl<T, > > > > &DIID__IMsgSvr422Events, CComDynamicUnkArray> > > > > { > > > > file://Warning this class may be recreated by the wizard. > > > > public: > > > > HRESULT Fire_OnNewMessage(SHORT nBytes, BYTE cDest, BYTE cSource, > BYTE > > > > cCmd, VARIANT vaData, VARIANT_BOOL * bDelete) > > > > { > > > > CComVariant varResult; > > > > T* pT = static_cast<T*>(this); > > > > int nConnectionIndex; > > > > CComVariant* pvars = new CComVariant[6]; > > > > 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[5] = nBytes; > > > > pvars[4] = cDest; > > > > pvars[3] = cSource; > > > > pvars[2] = cCmd; > > > > pvars[1] = &vaData; > > > > pvars[0] = bDelete; > > > > DISPPARAMS disp = { pvars, NULL, 6, 0 }; > > > > pDispatch->Invoke(0x1, IID_NULL, LOCALE_USER_DEFAULT, > > DISPATCH_METHOD, > > > > &disp, &varResult, NULL, NULL); > > > > } > > > > } > > > > delete[] pvars; > > > > return varResult.scode; > > > > } > > > > also, the code you noticed errors in was test code that wasn't > > completed, > > > > here is how we really handle safe arrays. > > > > _variant_t CTCPMsg::makeSafeArray(const char *pcOut, unsigned long > > > lLength) > > > > { > > > > _variant_t vaData; > > > > char *pcData; > > > > VariantInit(&vaData); > > > > V_VT(&vaData) = VT_ARRAY | VT_UI1; > > > > SAFEARRAY* psaData; > > > > SAFEARRAYBOUND bounds = { lLength, 0 }; > > > > psaData = SafeArrayCreate(VT_UI1, 1, &bounds); > > > > SafeArrayAccessData(psaData, (void**) &pcData); > > > > memcpy(pcData, pcOut, lLength); > > > > V_ARRAY(&vaData) = psaData; > > > > SafeArrayUnaccessData(psaData); > > > > return vaData; > > > > } > > > > that's ok, isn't it? > > > > thanks
> > > > > ATL event proxy generation wizard incorrectly handles any > non-trivial > > > > types, > > > > > e.g. safearrays and parameters that have to be passed by reference. > > > You'll > > > > > have to correct the generated code manually. Show me the code that > the > > > > > wizard generated, and I'll show you how to correct it. > > > > > Also, you work with safearray incorrectly. Lose pcData[3], do it as > > > > follows: > > > > > char *pcData; > > > > > SafeArrayAccessData(psaData, (void**) &pcData); > > > > > memcpy(pcData, m_pclsInMsg->m_pcData, > 3);//m_pclsInMsg->m_unDataLen); > > > > > SafeArrayUnaccessData(psaData); > > > > > V_ARRAY(&vaData) = psaData; > > > > > SafeArrayAccessData sets pcData pointer to point to the buffer > > allocated > > > > for > > > > > this safearray. You don't need to provide your own buffer. This > > > naturally > > > > > generalizes to arbitrary number of elements, possibly known at > > run-time > > > > > only. > > > > > -- > > > > > With best wishes, > > > > > Igor Tandetnik > > > > > "For every complex problem, there is a solution that is simple, > neat, > > > and > > > > > wrong." H.L. Mencken
> > > > > > Is a safearray a valid event parameter? My event works without the > > > last > > > > > two > > > > > > parameters listed below. With those parameters, the compiler fails > > > with > > > > > the > > > > > > list of errors below. How can I do this? > > > > > > thanks > > > > > > interface definition for event: > > > > > > [id(1), helpstring("method OnNewMessage")] HRESULT > OnNewMessage([in] > > > > short > > > > > > nBytes, [in] BYTE cDest, [in] BYTE cSource, [in] BYTE cCmd, [in] > > > VARIANT > > > > > > vaData, [out, retval] VARIANT_BOOL *bDelete); > > > > > > proxy function: > > > > > > HRESULT Fire_OnNewMessage(SHORT nBytes, BYTE cDest, BYTE cSource, > > BYTE > > > > > cCmd, > > > > > > VARIANT vaData, VARIANT_BOOL * bDelete) > > > > > > attempt to fire event: > > > > > > char pcData[3] = { 'A', 'B', 'C' }; > > > > > > _variant_t vaData; > > > > > > VARIANT_BOOL bDelete = VARIANT_FALSE; > > > > > > VariantInit(&vaData); > > > > > > V_VT(&vaData) = VT_ARRAY | VT_UI1; > > > > > > SAFEARRAY* psaData; > > > > > > SAFEARRAYBOUND bounds = { 3, 0 }; > > > > > > psaData = SafeArrayCreate(VT_UI1, 1, &bounds); > > > > > > SafeArrayAccessData(psaData, (void**) &pcData); > > > > > > memcpy(pcData, m_pclsInMsg->m_pcData, > > > > 3);//m_pclsInMsg->m_unDataLen); > > > > > > V_ARRAY(&vaData) = psaData; > > > > > > SafeArrayUnaccessData(psaData); > > > > > > pComPtr->Fire_OnNewMessage(6, 1, 100, 1, vaData, &bDelete); > > > > > > resulting errors: > > > > > > S:\ActiveXComponents\ActiveXVer201\ATISerial\ATISerialCP.h(38) : > > > warning > > > > > > C4305: 'argument' : truncation from 'struct tagVARIANT *' to > 'bool' > > S:\ActiveXComponents\ActiveXVer201\ATISerial\ATISerialCP.h(18) > > > : > > > > > > while compiling class-template member function 'long __thiscall > > > > > > CProxy_IMsgSvr422Events<class > > > > > CMsgSvr422>::Fire_OnNewMessage(short,unsigned > > > > > > char,unsigned char,unsigned char,str > > > > > > uct tagVARIANT,short *)' > > > > > > S:\ActiveXComponents\ActiveXVer201\ATISerial\ATISerialCP.h(38) : > > > warning > > > > > > C4800: 'struct tagVARIANT *' : forcing value to bool 'true'
... read more »
|
Mon, 03 May 2004 11:27:39 GMT |
|
|
Igor Tandetni #10 / 12
|
Safearray as Variant parameter to an Event
I checked this article, and I have two questions. First: why is it necessary to drop CComVariant and use plain VARIANT instead? I see nothing wrong with CComVariant. Second: it seems to me that the final code still uses only the flag returned by the last sink. As far as I can see, it does not implement the logic the description promises to implement. -- With best wishes, Igor Tandetnik "For every complex problem, there is a solution that is simple, neat, and wrong." H.L. Mencken
Quote: > I wrote a short article in the MVP COM/ATL FAQ a while ago. You may find > it useful here. It is item 3 in the COM/ATL FAQ in my signature, or a direct > link: > http://www.mvps.org/vcfaq/com/3.htm > -- > ===================================== > Alexander Nickolov > Microsoft MVP [VC], MCSD
> MVP VC FAQ: http://www.mvps.org/vcfaq > =====================================
> > Yes, that's what I suggest, and no, it does not change the technical > aspect > > of parameter setup. You still need to address the semantic aspect. E.g. > this > > pseudocode sets bDelete to false by default, and changes it to true if at > > least one client sets it to true: > > HRESULT Fire(VARIANT_BOOL *bDelete) > > { > > *bDelete = VARIANT_FALSE; > > for each sink > > { > > VARIANT_BOOL bTemp = *bDelete; > > ActualFire(&bTemp); > > if (bTemp) > > *bDelete = VARIANT_TRUE; > > } > > } > > -- > > With best wishes, > > Igor Tandetnik > > "For every complex problem, there is a solution that is simple, neat, and > > wrong." H.L. Mencken
> > > Are you suggesting the IDL be modified as follows? > > > [id(1), helpstring("method OnNewMessage")] void OnNewMessage([in] > short > > > nBytes, [in] BYTE cDest, [in] BYTE cSource, [in] BYTE cCmd, [in] VARIANT > > > vaData, [in, out] VARIANT_BOOL *bDelete); > > > Does this change affect the parameter setup shown below to correct the > > > wizard's inability to handle non-trivial types? > > > Ed
> > > > Parameters should be set up like this: > > > > VariantClear(&varResult); > > > > pvars[5] = nBytes; > > > > pvars[4] = cDest; > > > > pvars[3] = cSource; > > > > pvars[2] = cCmd; > > > > pvars[1].Attach(&vaData); > > > > V_VT(&pvars[0]) = VT_BOOL | VT_BYREF; > > > > V_BOOLREF(&pvars[0]) = bDelete; > > > > DISPPARAMS disp = { pvars, NULL, 6, 0 }; > > > > pDispatch->Invoke(0x1, IID_NULL, LOCALE_USER_DEFAULT, > > DISPATCH_METHOD, > > > > &disp, &varResult, NULL, NULL); > > > > pvars[1].Detach(&vaData); > > > > Attach/Detach games avoid copying SAFEARRAY which can potentially > > contain > > > > large amount of data. Also note how BYREF parameter is handled. It is > > only > > > > good if you have one client at a time. Figure what your semantics are > > > going > > > > to be when there are multiple clients sinking this event, potentially > > > > setting bDelete to conflicting values. The way it is implemented here, > > the > > > > client that is called last has the final word. Also, the initial value > > > > passed to the clients is inconsistent. > > > > By the way, I've just noticed that you have declared your bDelete as > > [out, > > > > retval] in IDL, and your function as returning HRESULT. Don't do that. > > > > Events should always be void, and have [in] or [in, out] parameters. > The > > > > client is not required to handle the event, or it can have an empty > > > handler > > > > which just returns immediately (e.g. if it is only interested in one > > event > > > > from the set of events). The behavior of your program should be > > meaningful > > > > in the presense of such a client. That usually means that [in, out] > > > > parameter should be set to meaningful default value before firing, > > because > > > > you cannot distinsuish between client explicitly setting the variable > to > > > > this value, or just returning without touching anything. > > > > -- > > > > With best wishes, > > > > Igor Tandetnik > > > > "For every complex problem, there is a solution that is simple, neat, > > and > > > > wrong." H.L. Mencken
> > > > > Thanks for the quick reply, here's the wizard generated code... > > > > > template <class T> > > > > > class CProxy_IMsgSvr422Events : public IConnectionPointImpl<T, > > > > > &DIID__IMsgSvr422Events, CComDynamicUnkArray> > > > > > { > > > > > file://Warning this class may be recreated by the wizard. > > > > > public: > > > > > HRESULT Fire_OnNewMessage(SHORT nBytes, BYTE cDest, BYTE cSource, > > BYTE > > > > > cCmd, VARIANT vaData, VARIANT_BOOL * bDelete) > > > > > { > > > > > CComVariant varResult; > > > > > T* pT = static_cast<T*>(this); > > > > > int nConnectionIndex; > > > > > CComVariant* pvars = new CComVariant[6]; > > > > > 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[5] = nBytes; > > > > > pvars[4] = cDest; > > > > > pvars[3] = cSource; > > > > > pvars[2] = cCmd; > > > > > pvars[1] = &vaData; > > > > > pvars[0] = bDelete; > > > > > DISPPARAMS disp = { pvars, NULL, 6, 0 }; > > > > > pDispatch->Invoke(0x1, IID_NULL, LOCALE_USER_DEFAULT, > > > DISPATCH_METHOD, > > > > > &disp, &varResult, NULL, NULL); > > > > > } > > > > > } > > > > > delete[] pvars; > > > > > return varResult.scode; > > > > > } > > > > > also, the code you noticed errors in was test code that wasn't > > > completed, > > > > > here is how we really handle safe arrays. > > > > > _variant_t CTCPMsg::makeSafeArray(const char *pcOut, unsigned long > > > > lLength) > > > > > { > > > > > _variant_t vaData; > > > > > char *pcData; > > > > > VariantInit(&vaData); > > > > > V_VT(&vaData) = VT_ARRAY | VT_UI1; > > > > > SAFEARRAY* psaData; > > > > > SAFEARRAYBOUND bounds = { lLength, 0 }; > > > > > psaData = SafeArrayCreate(VT_UI1, 1, &bounds); > > > > > SafeArrayAccessData(psaData, (void**) &pcData); > > > > > memcpy(pcData, pcOut, lLength); > > > > > V_ARRAY(&vaData) = psaData; > > > > > SafeArrayUnaccessData(psaData); > > > > > return vaData; > > > > > } > > > > > that's ok, isn't it? > > > > > thanks
> > > > > > ATL event proxy generation wizard incorrectly handles any > > non-trivial > > > > > types, > > > > > > e.g. safearrays and parameters that have to be passed by > reference. > > > > You'll > > > > > > have to correct the generated code manually. Show me the code that > > the > > > > > > wizard generated, and I'll show you how to correct it. > > > > > > Also, you work with safearray incorrectly. Lose pcData[3], do it > as > > > > > follows: > > > > > > char *pcData; > > > > > > SafeArrayAccessData(psaData, (void**) &pcData); > > > > > > memcpy(pcData, m_pclsInMsg->m_pcData, > > 3);//m_pclsInMsg->m_unDataLen); > > > > > > SafeArrayUnaccessData(psaData); > > > > > > V_ARRAY(&vaData) = psaData; > > > > > > SafeArrayAccessData sets pcData pointer to point to the buffer > > > allocated > > > > > for > > > > > > this safearray. You don't need to provide your own buffer. This > > > > naturally > > > > > > generalizes to arbitrary number of elements, possibly known at > > > run-time > > > > > > only. > > > > > > -- > > > > > > With best wishes, > > > > > > Igor Tandetnik > > > > > > "For every complex problem, there is a solution that is simple, > > neat, > > > > and > > > > > > wrong." H.L. Mencken
> > > > > > > Is a safearray a valid event parameter? My event works without > the > > > > last > > > > > > two > > > > > > > parameters listed below. With those parameters, the compiler > fails > > > > with > > > > > > the > > > > > > > list of errors below. How can I do this? > > > > > > > thanks > > > > > > > interface definition for event: > > > > > > > [id(1), helpstring("method OnNewMessage")] HRESULT > > OnNewMessage([in] > > > > > short > > > > > > > nBytes, [in] BYTE cDest, [in] BYTE cSource, [in] BYTE cCmd, [in] > > > > VARIANT > > > > > > > vaData, [out, retval] VARIANT_BOOL *bDelete); > > > > > > > proxy function: > > > > > > > HRESULT Fire_OnNewMessage(SHORT nBytes, BYTE cDest, BYTE > cSource, > > > BYTE > > > > > > cCmd, > > > > > > > VARIANT vaData, VARIANT_BOOL * bDelete) > > > > > > > attempt to fire event: > > > > > > > char pcData[3] = { 'A', 'B', 'C' }; > > > > > > > _variant_t vaData; > > > > > > > VARIANT_BOOL bDelete = VARIANT_FALSE; > > > > > > > VariantInit(&vaData); > > > > > > > V_VT(&vaData) = VT_ARRAY | VT_UI1; > > > > > > > SAFEARRAY* psaData; > > > > > > > SAFEARRAYBOUND bounds = { 3, 0 }; > > > > > > > psaData =
... read more »
|
Mon, 03 May 2004 23:44:25 GMT |
|
|
Alexander Nickolo #11 / 12
|
Safearray as Variant parameter to an Event
You could use CComVariant too, just be careful to release the referenced resources as if you are using pure VARIANTs. The reason I chose raw VARIANT is psychological - people tend to be more careless when using classes - tend to think the class will do more than it really does... As for the code - the particular semantics is to loop until the value becomes VARIANT_FALSE. Check the loop again. In other cases the semantics might be different and the loop must be modified accordingly... -- ===================================== Alexander Nickolov Microsoft MVP [VC], MCSD
MVP VC FAQ: http://www.mvps.org/vcfaq =====================================
Quote: > I checked this article, and I have two questions. First: why is it necessary > to drop CComVariant and use plain VARIANT instead? I see nothing wrong with > CComVariant. > Second: it seems to me that the final code still uses only the flag returned > by the last sink. As far as I can see, it does not implement the logic the > description promises to implement. > -- > With best wishes, > Igor Tandetnik > "For every complex problem, there is a solution that is simple, neat, and > wrong." H.L. Mencken
> > I wrote a short article in the MVP COM/ATL FAQ a while ago. You may find > > it useful here. It is item 3 in the COM/ATL FAQ in my signature, or a > direct > > link: > > http://www.mvps.org/vcfaq/com/3.htm > > -- > > ===================================== > > Alexander Nickolov > > Microsoft MVP [VC], MCSD
> > MVP VC FAQ: http://www.mvps.org/vcfaq > > =====================================
> > > Yes, that's what I suggest, and no, it does not change the technical > > aspect > > > of parameter setup. You still need to address the semantic aspect. E.g. > > this > > > pseudocode sets bDelete to false by default, and changes it to true if > at > > > least one client sets it to true: > > > HRESULT Fire(VARIANT_BOOL *bDelete) > > > { > > > *bDelete = VARIANT_FALSE; > > > for each sink > > > { > > > VARIANT_BOOL bTemp = *bDelete; > > > ActualFire(&bTemp); > > > if (bTemp) > > > *bDelete = VARIANT_TRUE; > > > } > > > } > > > -- > > > With best wishes, > > > Igor Tandetnik > > > "For every complex problem, there is a solution that is simple, neat, > and > > > wrong." H.L. Mencken
> > > > Are you suggesting the IDL be modified as follows? > > > > [id(1), helpstring("method OnNewMessage")] void OnNewMessage([in] > > short > > > > nBytes, [in] BYTE cDest, [in] BYTE cSource, [in] BYTE cCmd, [in] > VARIANT > > > > vaData, [in, out] VARIANT_BOOL *bDelete); > > > > Does this change affect the parameter setup shown below to correct the > > > > wizard's inability to handle non-trivial types? > > > > Ed
> > > > > Parameters should be set up like this: > > > > > VariantClear(&varResult); > > > > > pvars[5] = nBytes; > > > > > pvars[4] = cDest; > > > > > pvars[3] = cSource; > > > > > pvars[2] = cCmd; > > > > > pvars[1].Attach(&vaData); > > > > > V_VT(&pvars[0]) = VT_BOOL | VT_BYREF; > > > > > V_BOOLREF(&pvars[0]) = bDelete; > > > > > DISPPARAMS disp = { pvars, NULL, 6, 0 }; > > > > > pDispatch->Invoke(0x1, IID_NULL, LOCALE_USER_DEFAULT, > > > DISPATCH_METHOD, > > > > > &disp, &varResult, NULL, NULL); > > > > > pvars[1].Detach(&vaData); > > > > > Attach/Detach games avoid copying SAFEARRAY which can potentially > > > contain > > > > > large amount of data. Also note how BYREF parameter is handled. It > is > > > only > > > > > good if you have one client at a time. Figure what your semantics > are > > > > going > > > > > to be when there are multiple clients sinking this event, > potentially > > > > > setting bDelete to conflicting values. The way it is implemented > here, > > > the > > > > > client that is called last has the final word. Also, the initial > value > > > > > passed to the clients is inconsistent. > > > > > By the way, I've just noticed that you have declared your bDelete as > > > [out, > > > > > retval] in IDL, and your function as returning HRESULT. Don't do > that. > > > > > Events should always be void, and have [in] or [in, out] parameters. > > The > > > > > client is not required to handle the event, or it can have an empty > > > > handler > > > > > which just returns immediately (e.g. if it is only interested in one > > > event > > > > > from the set of events). The behavior of your program should be > > > meaningful > > > > > in the presense of such a client. That usually means that [in, out] > > > > > parameter should be set to meaningful default value before firing, > > > because > > > > > you cannot distinsuish between client explicitly setting the > variable > > to > > > > > this value, or just returning without touching anything. > > > > > -- > > > > > With best wishes, > > > > > Igor Tandetnik > > > > > "For every complex problem, there is a solution that is simple, > neat, > > > and > > > > > wrong." H.L. Mencken
> > > > > > Thanks for the quick reply, here's the wizard generated code... > > > > > > template <class T> > > > > > > class CProxy_IMsgSvr422Events : public IConnectionPointImpl<T, > > > > > > &DIID__IMsgSvr422Events, CComDynamicUnkArray> > > > > > > { > > > > > > file://Warning this class may be recreated by the wizard. > > > > > > public: > > > > > > HRESULT Fire_OnNewMessage(SHORT nBytes, BYTE cDest, BYTE cSource, > > > BYTE > > > > > > cCmd, VARIANT vaData, VARIANT_BOOL * bDelete) > > > > > > { > > > > > > CComVariant varResult; > > > > > > T* pT = static_cast<T*>(this); > > > > > > int nConnectionIndex; > > > > > > CComVariant* pvars = new CComVariant[6]; > > > > > > 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[5] = nBytes; > > > > > > pvars[4] = cDest; > > > > > > pvars[3] = cSource; > > > > > > pvars[2] = cCmd; > > > > > > pvars[1] = &vaData; > > > > > > pvars[0] = bDelete; > > > > > > DISPPARAMS disp = { pvars, NULL, 6, 0 }; > > > > > > pDispatch->Invoke(0x1, IID_NULL, LOCALE_USER_DEFAULT, > > > > DISPATCH_METHOD, > > > > > > &disp, &varResult, NULL, NULL); > > > > > > } > > > > > > } > > > > > > delete[] pvars; > > > > > > return varResult.scode; > > > > > > } > > > > > > also, the code you noticed errors in was test code that wasn't > > > > completed, > > > > > > here is how we really handle safe arrays. > > > > > > _variant_t CTCPMsg::makeSafeArray(const char *pcOut, unsigned long > > > > > lLength) > > > > > > { > > > > > > _variant_t vaData; > > > > > > char *pcData; > > > > > > VariantInit(&vaData); > > > > > > V_VT(&vaData) = VT_ARRAY | VT_UI1; > > > > > > SAFEARRAY* psaData; > > > > > > SAFEARRAYBOUND bounds = { lLength, 0 }; > > > > > > psaData = SafeArrayCreate(VT_UI1, 1, &bounds); > > > > > > SafeArrayAccessData(psaData, (void**) &pcData); > > > > > > memcpy(pcData, pcOut, lLength); > > > > > > V_ARRAY(&vaData) = psaData; > > > > > > SafeArrayUnaccessData(psaData); > > > > > > return vaData; > > > > > > } > > > > > > that's ok, isn't it? > > > > > > thanks
> > > > > > > ATL event proxy generation wizard incorrectly handles any > > > non-trivial > > > > > > types, > > > > > > > e.g. safearrays and parameters that have to be passed by > > reference. > > > > > You'll > > > > > > > have to correct the generated code manually. Show me the code > that > > > the > > > > > > > wizard generated, and I'll show you how to correct it. > > > > > > > Also, you work with safearray incorrectly. Lose pcData[3], do it > > as > > > > > > follows: > > > > > > > char *pcData; > > > > > > > SafeArrayAccessData(psaData, (void**) &pcData); > > > > > > > memcpy(pcData, m_pclsInMsg->m_pcData, > > > 3);//m_pclsInMsg->m_unDataLen); > > > > > > > SafeArrayUnaccessData(psaData); > > > > > > > V_ARRAY(&vaData) = psaData; > > > > > > > SafeArrayAccessData sets pcData pointer to point to the buffer > > > > allocated > > > > > > for > > > > > > > this safearray. You don't need to provide your own buffer. This > > > > > naturally > > > > > > > generalizes to arbitrary number of elements, possibly known at > > > > run-time > > > > > > > only. > > > > > > > -- > > > > > > > With best wishes, > > > > > > > Igor Tandetnik > > > > > > > "For every complex problem, there is a solution that is simple, > > > neat, > > > > > and > > > > > > > wrong." H.L. Mencken
... read more »
|
Sun, 16 May 2004 02:57:41 GMT |
|
|
Igor Tandetni #12 / 12
|
Safearray as Variant parameter to an Event
Quote: > As for the code - the particular semantics is to loop until the > value becomes VARIANT_FALSE. Check the loop again. In other > cases the semantics might be different and the loop must be > modified accordingly...
Got it now. May not be a good idea - if the first sink returns False, no other sink receives this notification. Of course that may be exactly what the component author wants. -- With best wishes, Igor Tandetnik "For every complex problem, there is a solution that is simple, neat, and wrong." H.L. Mencken
|
Sun, 16 May 2004 03:16:05 GMT |
|
|
|