Board index » cppbuilder » Client Side Events in BCB4

Client Side Events in BCB4

Having followed the pilgramage of "Events on the Client Side" ,I am at this
point:

    I have a server firing an event that passes an out param of
LPSAFEARRAY(short)* to a BCB4 client
The server works fine.It deallocates the memory used in the out param OK.
    The Client receives the event, copies out the data ( array of short)
into a local array in a Form property,calls SafeArrayDestroy() on the
descriptor and nothing happpens. If you watch the virtual memory allocation
in the NT task manager for that process it ratchets up 4K periodically
 seems to match the time when I have sent 4K of data) and a page fault
occurs.There are no open files and no other events firing. I deleted the
copy process in case I was creating the problem, no change. Any thoughts?

Joe Bingham

 

Re:Client Side Events in BCB4


Joe,

I'm curious about the following:

Quote
>> The server works fine.It deallocates the memory used in the out param OK.

I think we're in sync. but I just want to confirm since things get a little
fuzzy when talking about events. The server's on the sending/calling end.

An [out] parameter implies that the implementation will receive the address
of something that's pointer to NULL. Good implementations will fail any
calls where an [out] parameter was not pointing to NULL. This is just to
make it clear to the caller that the receiver will *NOT* free the parameter
but will overwrite it with something new. So pointing to NULL ensures
there's no leakage.

So I expect your server to be doing something along the following:

   LPSAFEARRAY p = 0;
   Fire_Event(&p);
   // Here the server uses the array and then frees it

The client, on the other hand, will do something along the lines of:

   HRESULT HandleEventWithArray(LPSAFEARRAY* ppArray)
   {
       if (!ppArray)
         return E_POINTER;
       if (*ppArray != 0)
         return E_INVALIDARG;
       *ppArray = GetSafeArrayForServer();
       return S_OK;
   }

Does the above match your scenario? If yes, I'm curious on the jump from the
IDispatch.Invoke to HandleEventWithArray. Since you'll be dealing with
VARIANTs, they could be leakage when manipulating the VARIANTs. Let me know.

Regards,

Bruneau.

Quote
Joe Bingham wrote in message <7juca6$e...@forums.borland.com>...
>Having followed the pilgramage of "Events on the Client Side" ,I am at this
>point:

>    I have a server firing an event that passes an out param of
>LPSAFEARRAY(short)* to a BCB4 client
>The server works fine.It deallocates the memory used in the out param OK.
>    The Client receives the event, copies out the data ( array of short)
>into a local array in a Form property,calls SafeArrayDestroy() on the
>descriptor and nothing happpens. If you watch the virtual memory allocation
>in the NT task manager for that process it ratchets up 4K periodically
> seems to match the time when I have sent 4K of data) and a page fault
>occurs.There are no open files and no other events firing. I deleted the
>copy process in case I was creating the problem, no change. Any thoughts?

>Joe Bingham

Re:Client Side Events in BCB4


Sorry I explained it backwards to you the server creates the safearray and
sends it to the client. You are correct it is an "in" param not an "out"
param.  At the client the  the event handler is as follows:

void __fastcall TServerEventsMgr::AsynchReadEvent(TVariant * params)
{
    try
    {
    static bool state=true;
    short Node=(short)(*(short *)params[0]);
    short Start=(short)(*(short *)params[1]);
    short Count=(short)(*(short *)params[2]);

    LPSAFEARRAY q=*reinterpret_cast<LPSAFEARRAY*>(params[3].pparray);
    HRESULT t=SafeArrayLock(q);
    if(t!=S_OK)
    {
        MessageBox(Form1->Handle,"Can't Lock Array from Server","",IDOK);
    }
    short *p=reinterpret_cast<short *>(q->pvData);
    memcpy(reinterpret_cast<BYTE *>(&Form1->NodeData[Node][Start]),
                reinterpret_cast<BYTE *>(p),sizeof(short)*Count);
    delete[] p; // This is probably frustration code on this line
    HRESULT t1=SafeArrayUnlock(q);
    if(t1!=S_OK)
    {
        MessageBox(Form1->Handle,"Can't Un-Lock Array from Server","",IDOK);
    }
    HRESULT t2=SafeArrayDestroy(q);
    if(t2!=S_OK)
    {
        MessageBox(Form1->Handle,"Can't Destroy Array from Server","",IDOK);
    }
    state^=true;
    if(state)
    Form1->Label1->Color=clRed; // flash a dot on the form to signal that
events are being fired
    else
    Form1->Label1->Color=clLime;
    }
    catch(...)
    {
        // Something happened
        // probably the server terminated
        return;
    }

Quote
}
Jean-Marie Babet <brune...@msn.com> wrote in message

news:7k14qc$gdh2@forums.borland.com...
Quote
> Joe,

> I'm curious about the following: ...

Re:Client Side Events in BCB4


PS The leak is still there. It appears that I am receiving a copy of the
safearray and that the original is still burried in the TVariant somewhere.
I have tried destroying the *SomeTvariant.pparray same results a relentless
leak.

Joe IBngham

Quote
Jean-Marie Babet <brune...@msn.com> wrote in message

news:7k14qc$gdh2@forums.borland.com...
Quote
> Joe,

> I'm curious about the following:

> >> The server works fine.It deallocates the memory used in the out param
OK.

> I think we're in sync. but I just want to confirm since things get a
little
> fuzzy when talking about events. The server's on the sending/calling end.

> An [out] parameter implies that the implementation will receive the
address
> of something that's pointer to NULL. Good implementations will fail any
> calls where an [out] parameter was not pointing to NULL. This is just to
> make it clear to the caller that the receiver will *NOT* free the
parameter
> but will overwrite it with something new. So pointing to NULL ensures
> there's no leakage.

> So I expect your server to be doing something along the following:

>    LPSAFEARRAY p = 0;
>    Fire_Event(&p);
>    // Here the server uses the array and then frees it

> The client, on the other hand, will do something along the lines of:

>    HRESULT HandleEventWithArray(LPSAFEARRAY* ppArray)
>    {
>        if (!ppArray)
>          return E_POINTER;
>        if (*ppArray != 0)
>          return E_INVALIDARG;
>        *ppArray = GetSafeArrayForServer();
>        return S_OK;
>    }

> Does the above match your scenario? If yes, I'm curious on the jump from
the
> IDispatch.Invoke to HandleEventWithArray. Since you'll be dealing with
> VARIANTs, they could be leakage when manipulating the VARIANTs. Let me
know.

> Regards,

> Bruneau.

> Joe Bingham wrote in message <7juca6$e...@forums.borland.com>...
> >Having followed the pilgramage of "Events on the Client Side" ,I am at
this
> >point:

> >    I have a server firing an event that passes an out param of
> >LPSAFEARRAY(short)* to a BCB4 client
> >The server works fine.It deallocates the memory used in the out param OK.
> >    The Client receives the event, copies out the data ( array of short)
> >into a local array in a Form property,calls SafeArrayDestroy() on the
> >descriptor and nothing happpens. If you watch the virtual memory
allocation
> >in the NT task manager for that process it ratchets up 4K periodically
> > seems to match the time when I have sent 4K of data) and a page fault
> >occurs.There are no open files and no other events firing. I deleted the
> >copy process in case I was creating the problem, no change. Any thoughts?

> >Joe Bingham

Re:Client Side Events in BCB4


Joe,

Thanks for the additional explanation. I would suggest the following:

(a) The array will be destroyed twice:

The following lines accesses the underlying array in the TVariant:

Quote
>    LPSAFEARRAY q=*reinterpret_cast<LPSAFEARRAY*>(params[3].pparray);

However, params[3] will still hold a SAFEARRAY with VT_ARRAY|VT_xxxx. When
its destructor is called, it will attempt to destroy the SAFEARRAY.

(b) The following code is very dangerous:

Quote
>  short *p=reinterpret_cast<short *>(q->pvData);        [1] Get pointer to
data in safearray
>    memcpy(reinterpret_cast<BYTE *>(&Form1->NodeData[Node][Start]),
>                reinterpret_cast<BYTE *>(p),sizeof(short)*Count);
>    delete[] p; // This is probably frustration code on this line  [2]

Delete data in safearray

Calling delete[] on a pointer that's pointing to data in a SAFEARRAY is not
OK: The RTL's memory manager is not the same one used by the OS when
allocating SAFEARRAY. Furthermore, the SafeArrayDestroy takes care of
cleaning up the array's data.

I general I strongly discourage accessing the raw member of VARIANT in a
TVariant because of resource ownership. At the very least, you should check
the VariantType. If you control both ends, you can do it, but then I would
still (a) put an assert to check the vt member (b) decide whether I was
TVariant to own the data or take full ownership.

(c) This is not related to SAFEARRAYs but I find the following lines
intriguing too:

Quote
>    short Node=(short)(*(short *)params[0]);
>    short Start=(short)(*(short *)params[1]);

I'm assuming either (i) These are really Variants that contain
VT_BYREF|VT_I2 or (ii) this call was not done via OLE and somehow you're
passing a array of 3 pointers, the first two pointing to short, the third
pointing to a TVariant. Right? In case if (i) you should just use the
short() operator. They'll work if the variant contains a short by ref. More
importantly, they'll work if the variant contains other types (int, char,
string, etc) that can be converted to a short.

Regarding the SAFEARRAY I suggest:

(a) Using the LPSAFEARRAY() operator to access the data. This operator will
return a copy of the SAFEARRAY (NOTE: The data is not copied). You can then
safely destroy your copy and the TVariant can destroy it's copy. Something
along the lines of:

Handler(TVariant* params)
{
   // Extract copy of SAFEARRAY
   _ASSERTE(params[3].vt & VT_ARRAY);
   LPSAFEARRAY arrayOfShort = LPSAFEARRAY(params[3]);

   // Here you Lock Copy, Unlock the data.

   // Destroy my copy of array data
   ::SafeArrayDestroy(arrayOfShort);

Quote
}

(b) You can also use a wrapper to make sure the SAFEARRAY gets destroyed
safely (in case of exceptions):

Handler(TVariant* params)
{
      TSafeArrayShort1 arrayofShort(LPSAFEARRAY(params[3]);

      // In this scenario you don't have to call ::SafeArrayDestroy

Quote
}

I hope the above helps,

Bruneau.

Quote
Joe Bingham wrote in message <7k19cp$g...@forums.borland.com>...
>Sorry I explained it backwards to you the server creates the safearray and
>sends it to the client. You are correct it is an "in" param not an "out"
>param.  At the client the  the event handler is as follows:

>void __fastcall TServerEventsMgr::AsynchReadEvent(TVariant * params)
>{
>    try
>    {
>    static bool state=true;
>    short Node=(short)(*(short *)params[0]);
>    short Start=(short)(*(short *)params[1]);
>    short Count=(short)(*(short *)params[2]);

>    LPSAFEARRAY q=*reinterpret_cast<LPSAFEARRAY*>(params[3].pparray);
>    HRESULT t=SafeArrayLock(q);
>    if(t!=S_OK)
>    {
>        MessageBox(Form1->Handle,"Can't Lock Array from Server","",IDOK);
>    }
>    short *p=reinterpret_cast<short *>(q->pvData);
>    memcpy(reinterpret_cast<BYTE *>(&Form1->NodeData[Node][Start]),
>                reinterpret_cast<BYTE *>(p),sizeof(short)*Count);
>    delete[] p; // This is probably frustration code on this line
>    HRESULT t1=SafeArrayUnlock(q);
>    if(t1!=S_OK)
>    {
>        MessageBox(Form1->Handle,"Can't Un-Lock Array from
Server","",IDOK);
>    }
>    HRESULT t2=SafeArrayDestroy(q);
>    if(t2!=S_OK)
>    {
>        MessageBox(Form1->Handle,"Can't Destroy Array from
Server","",IDOK);
>    }
>    state^=true;
>    if(state)
>    Form1->Label1->Color=clRed; // flash a dot on the form to signal that
>events are being fired
>    else
>    Form1->Label1->Color=clLime;
>    }
>    catch(...)
>    {
>        // Something happened
>        // probably the server terminated
>        return;
>    }
>}

>Jean-Marie Babet <brune...@msn.com> wrote in message
>news:7k14qc$gdh2@forums.borland.com...
>> Joe,

>> I'm curious about the following: ...

Re:Client Side Events in BCB4


Joe,

See my previous post. Indeed the TVariant is still holding on to the
SAFEARRAY. You cannot simply destroy the underlying data member, you need to
take ownership of the SAFEARRAY. Otherwise, TVariant's destructor will
attempt to destroy since as far as it is concerned, it holds a
VT_ARRAY_VTxxxx type.

Hope that helps,

Bruneau.

Quote
Joe Bingham wrote in message <7k1h71$gc...@forums.borland.com>...
>PS The leak is still there. It appears that I am receiving a copy of the
>safearray and that the original is still burried in the TVariant somewhere.
>I have tried destroying the *SomeTvariant.pparray same results a relentless
>leak.

>Joe IBngham

>Jean-Marie Babet <brune...@msn.com> wrote in message
>news:7k14qc$gdh2@forums.borland.com...
>> Joe,

>> I'm curious about the following:

>> >> The server works fine.It deallocates the memory used in the out param
>OK.

>> I think we're in sync. but I just want to confirm since things get a
>little
>> fuzzy when talking about events. The server's on the sending/calling end.

>> An [out] parameter implies that the implementation will receive the
>address
>> of something that's pointer to NULL. Good implementations will fail any
>> calls where an [out] parameter was not pointing to NULL. This is just to
>> make it clear to the caller that the receiver will *NOT* free the
>parameter
>> but will overwrite it with something new. So pointing to NULL ensures
>> there's no leakage.

>> So I expect your server to be doing something along the following:

>>    LPSAFEARRAY p = 0;
>>    Fire_Event(&p);
>>    // Here the server uses the array and then frees it

>> The client, on the other hand, will do something along the lines of:

>>    HRESULT HandleEventWithArray(LPSAFEARRAY* ppArray)
>>    {
>>        if (!ppArray)
>>          return E_POINTER;
>>        if (*ppArray != 0)
>>          return E_INVALIDARG;
>>        *ppArray = GetSafeArrayForServer();
>>        return S_OK;
>>    }

>> Does the above match your scenario? If yes, I'm curious on the jump from
>the
>> IDispatch.Invoke to HandleEventWithArray. Since you'll be dealing with
>> VARIANTs, they could be leakage when manipulating the VARIANTs. Let me
>know.

>> Regards,

>> Bruneau.

>> Joe Bingham wrote in message <7juca6$e...@forums.borland.com>...
>> >Having followed the pilgramage of "Events on the Client Side" ,I am at
>this
>> >point:

>> >    I have a server firing an event that passes an out param of
>> >LPSAFEARRAY(short)* to a BCB4 client
>> >The server works fine.It deallocates the memory used in the out param
OK.
>> >    The Client receives the event, copies out the data ( array of short)
>> >into a local array in a Form property,calls SafeArrayDestroy() on the
>> >descriptor and nothing happpens. If you watch the virtual memory
>allocation
>> >in the NT task manager for that process it ratchets up 4K periodically
>> > seems to match the time when I have sent 4K of data) and a page fault
>> >occurs.There are no open files and no other events firing. I deleted the
>> >copy process in case I was creating the problem, no change. Any
thoughts?

>> >Joe Bingham

Re:Client Side Events in BCB4


Hello Bruneau:

    Everything works perfectly. Thanks for the advise and prodding. THE
LPSAFEARRAY() operator made all the difference. The three mystery lines are
values sent from the server as VT_I2|VT_BYREF. I used the short operator as
you suggested.... works fine. I guess my experience with VB tends to make me
use pointers everywhere.

thanks

Joe Bingham

Re:Client Side Events in BCB4


Joe,

Glad to hear you're up and running!

Bruneau.

Quote
Joe Bingham wrote in message <7k1rql$h...@forums.borland.com>...
>Hello Bruneau:

>    Everything works perfectly. Thanks for the advise and prodding. THE
>LPSAFEARRAY() operator made all the difference. The three mystery lines are
>values sent from the server as VT_I2|VT_BYREF. I used the short operator as
>you suggested.... works fine. I guess my experience with VB tends to make
me
>use pointers everywhere.

>thanks

>Joe Bingham

Other Threads