From: JPB on
Hi,

I've also been experimenting with connecting to my server in a
separate thread (using the dhRichClient3 thread classes) so that the
client app isn't locked while it connects (and auto-reconnects on
disconnection) to an RPC listener. The issue that I've run into is
that I can't return a CRpcConnection class over the thread handler. To
overcome this, I have added an RPC function to the thread object class
like so:

Public Function Rpc(Dllname As String, ClassName As String, ProcName
As String, ParamArray P() As Variant) As Variant
Select Case UBound(P)
Case -1
Rpc = mo_RpcCnn.Rpc(Dllname, ClassName, ProcName, 5)
Case 0
Rpc = mo_RpcCnn.Rpc(Dllname, ClassName, ProcName, 5, P(0))
Case 1
Rpc = mo_RpcCnn.Rpc(Dllname, ClassName, ProcName, 5, P(0), P(1))
'Case ETC.... ************
End Select
End Function

And then I call the Rpc method client-side as follows:

Debug.Print TH.CallSynchronous("RPC", "ThreadTest.dll", "CTest",
"Test", "param1", "param2")

This works OK, except that since there is no nice way to pass a
ParamArray received from one function to another function (i.e. from
the Thread class RPC function to the CRPCConnection RPC function), I
will have to have a big SELECT CASE statement - not the end of the
world, just ugly. I was just wondering if any of you have tried this
previously, and if so, do you have a cleaner solution?

Thanks in advance,
Jason
From: Schmidt on

"JPB" <jasonpeterbrown(a)gmail.com> schrieb im Newsbeitrag
news:3c31c0ba-8940-40b1-9d8c-be718522d269(a)h10g2000vbm.googlegroups.com...

> I've also been experimenting with connecting to my server in a
> separate thread (using the dhRichClient3 thread classes) so that the
> client app isn't locked while it connects (and auto-reconnects on
> disconnection) to an RPC listener.
The very first connect on an encrypted connection takes about 0.5-
1 sec (due to the Diffie-Hellman key-exchange and the required
math on large primes) - but subsequent connects on such an
encrypted connection should work fast IMO.
The normal (unencrypted) connects are working much faster,
depending only on the normal socket-performance and the
type of the LAN.

So, I would carefully decide, if you really want to introduce
such an additional thread-layer, since you seem to perform
all your RPC-Calls over the thread synchronously in either case.

But putting the clientside RPCObject into its own thread
has also benefits of course, since all the compression/
decompression + encryption/decryption on the "socket-
ByteArrays" is done on a different CPU-Core then.
And you get the opportunity, to call certain "long-runner"
RPCs in an asynchronous fashion later on as well.

[Wrapping the RPC-Call in a similar function-construct]
> Public Function Rpc(Dllname As String, ClassName As String, ProcName
> As String, ParamArray P() As Variant) As Variant
> Select Case UBound(P)
> Case -1
> Rpc = mo_RpcCnn.Rpc(Dllname, ClassName, ProcName, 5)
> Case 0
> Rpc = mo_RpcCnn.Rpc(Dllname, ClassName, ProcName, 5, P(0))
> Case 1
> Rpc = mo_RpcCnn.Rpc(Dllname, ClassName, ProcName, 5, P(0), P(1))
> 'Case ETC.... ************
> End Select
> End Function
>
> And then I call the Rpc method client-side as follows:
>
> Debug.Print TH.CallSynchronous("RPC", "ThreadTest.dll", "CTest",
> "Test", "param1", "param2")
>
> This works OK, except that since there is no nice way to pass a
> ParamArray received from one function to another function (i.e. from
> the Thread class RPC function to the CRPCConnection RPC function),
> I will have to have a big SELECT CASE statement - not the end of the
> world, just ugly. I was just wondering if any of you have tried this
> previously, and if so, do you have a cleaner solution?

Such a cascade looks "ugly" (or not, depending on ones "appreciation
for symmetries" <g>), but it's the usual approach - and not that costly
in terms of performance.
I write it usually this way, to have a better look at potential
copy/paste-errors regarding the Index-numerations:
Select case Ubound(P)+1
case 0: Result = SomeFunc(A, B, C)
case 1: Result = SomeFunc(A, B, C, P(0))
case 2: Result = SomeFunc(A, B, C, P(0), P(1))
case 3: Result = SomeFunc(A, B, C, P(0), P(1), P(2))
etc...

In case of a LateBound-Call to a method of a given (As Object)
ClassInstance one could spare the calling-cascade, using Eduardo
Morcillos OleLib.tlb and performing the IDispatch-Call directly (using
Pointers) - that's what I'm doing internally in the serverside RPC-
WorkerthreadClasses, if these have to call the serverside Dll-
Functions.
But you have an early-bound call-delegation here - and the cascade
is IMO the easiest and fastest way.

Olaf


From: JPB on
On Dec 2, 1:11 pm, "Schmidt" <s...(a)online.de> wrote:
> The very first connect on an encrypted connection takes about 0.5-
> 1 sec (due to the Diffie-Hellman key-exchange and the required
> math on large primes) - but subsequent connects on such an
> encrypted connection should work fast IMO.
> The normal (unencrypted) connects are working much faster,
> depending only on the normal socket-performance and the
> type of the LAN.
>
> So, I would carefully decide, if you really want to introduce
> such an additional thread-layer, since you seem to perform
> all your RPC-Calls over the thread synchronously in either case.

I'm not so much concerned about the time for successful connections as
you are right that they do happen quite fast. Although, my connection
process takes longer because of my unusual Credentials/Business server
connection process mentioned in previous posts. Essentially, a
"successful" connection can take up to 3 connections (including 1
failed connection which can take some time to timeout) before the UI
is handed back to the users. I've also put in place an auto-reconnect
feature that will continually try to reconnect the client to the
server (say, every 30 seconds or so) when the connection has gone down
for whatever reason (Network problems, admin killed the service
temporarily, etc...). Because I do cache some things client-side,
users can still do some productive work without the RPC server being
available, so it would be a nice feature not to have to lock them out
every time the auto-connection feature kicks in.

I also just like the strides I've been able to make in UI
responsiveness thanks to your threading classes, and I'm getting very
picky about UI freezes :) For example, I have a network printer that
is sometimes not available (for example, if I am out of office). Some
other applications that I use will freeze for up to thirty or so
seconds before they timeout if that printer happens to be my default
printer (and this is just to open the program in some cases!). Thanks
to the dhRichclient3 thread classes, I am able to run the scan for
online printers in a separate thread, and update the UI accordingly -
so the UI stays continuously responsive no matter what is going on
with the printers.

> But putting the clientside RPCObject into its own thread
> has also benefits of course, since all the compression/
> decompression + encryption/decryption on the "socket-
> ByteArrays" is done on a different CPU-Core then.
> And you get the opportunity, to call certain "long-runner"
> RPCs in an asynchronous fashion later on as well.

That is a bonus that I did not consider.

> Such a cascade looks "ugly" (or not, depending on ones "appreciation
> for symmetries" <g>)

True! I guess it feels ugly more than it looks ugly ;)

> In case of a LateBound-Call to a method of a given (As Object)
> ClassInstance one could spare the calling-cascade, using Eduardo
> Morcillos OleLib.tlb and performing the IDispatch-Call directly (using
> Pointers) - that's what I'm doing internally in the serverside RPC-
> WorkerthreadClasses, if these have to call the serverside Dll-
> Functions.
> But you have an early-bound call-delegation here - and the cascade
> is IMO the easiest and fastest way.

Thanks for the advice again. Since I'm already going through a couple
of layers, I think that I will go with the fastest way.
Jason