Safearray as Variant parameter to an Event 
Author Message
 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  
 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

- Show quoted text -

Quote:
> char,unsigned char,unsigned char,str
> uct tagVARIANT,short *)'



Sat, 01 May 2004 03:41:30 GMT  
 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);

- Show quoted text -

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  
 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  
 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)

- Show quoted text -

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  
 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);

- Show quoted text -

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  
 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  
 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  
 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  
 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  
 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  
 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  
 
 [ 12 post ] 

 Relevant Pages 

1. variant of safearray passed as [in, out] parameter

2. Event with variant parameter

3. SAFEARRAY(VARIANT) Marshalling

4. How to COM's VARIANT* or SAFEARRAY data type in C#

5. SafeArray of Variants - argh!

6. Returning a VARIANT* holding a SAFEARRAY from one Exe server to another

7. safearray , variant* and others.

8. wrapping variants/safearrays..how?

9. Set up a VARIANT to hold a SAFEARRAY

10. how to convert a SAFEARRAY to VARIANT?

11. VARIANTS , Safearrays

12. ConnectionPoints and VARIANTs or SAFEARRAYs

 

 
Powered by phpBB® Forum Software