Modifying an array passed from a .NET Client to a COM Server

I'm encountering a situation where it would be nice to pass an array from a managed client (in c#) to a COM object (in-proc server) and have the COM object change the values in the array.  In my situation it's an array of strings but the principles apply to an array of base types as well.

 

In this case the COM object is an MFC dll.  There are several elements involved in setting up the communication.  These elements are:

 

IDL

MFC COM, at least in visual studio 2003, uses a combination of wizard generated IDL, macros and dynamic registration.  Since the focus of this post is COM Interop, I'll leave a discussion of MFC COM to another post (see technote 38 for helpful pointers).

 

The dispatch interface in the IDL needs to specify that the array is a SAFEARRAY of BSTRs.  e.g:


[id(4)] HRESULT MyArrayFunc([in, out] SAFEARRAY(BSTR) *saArray);

Managed strings are, by default, marshaled as BSTRs by the interop marshaler (not necessarily the same as the platform invoke default marshaling behavior).

SAFEARRAYs are arrays that know their size and number of dimensions.  They're an improvement over old C style arrays, which don't know their length.  The downside is that you have to use a bunch of API calls to access elements of the array (most importantly SafeArrayGetElement() and SafeArrayGetUBound()).

 

NATIVE CODE/C++/C

MFC uses dispatch maps to associate a native method with the declared methods of an interface.  Each interface method needs to be included in the dispatch map (BEGIN_DISPATCH_MAP/END_DISPATCH_MAP in your C++ source) via a DISP_FUNCTION (or DISP_FUNCTION_ID) macro.

 

COM Interop is extremely sensitive to the values set by DISP_FUNCTION_ID.  If the return type or parameter types are incorrectly specified then there is a good chance that a type exception will be thrown when the method is invoked!

 

To pass an array of strings, the parameter type must be set to VTS_VARIANT, e.g.:


DISP_FUNCTION_ID(CMyClass, "MyArrayFunc", dispidStrArrayFunc, MyArrayFunc, VT_I4, VTS_VARIANT)


NOTE: setting a return type of VT_HRESULT does not work, use VT_I4 (long) instead.

 

Inside the header file, declare the prototype (make sure it's public, vs2003 wizards seem to put it in a protected: section):

HRESULT MyArrayFunc(VARIANT &vArray)

 

Then in the source file, implement ala:


HRESULT CMyClass::MyArrayFunc(VARIANT &vArray)

{

AFX_MANAGE_STATE(AfxGetStaticModuleState());

// make sure it's a string array

if (V_VT(&vArray) != (VT_BYREF | VT_ARRAY | VT_BSTR))

AfxThrowOleDispatchException(1001, "Type Mismatch in Parameter. Pass a string array by reference");

// get the safearray from the variant

SAFEARRAY **ppsa = V_ARRAYREF(&vArray);

cout << "In StrArrayFunc()" << endl;


cout << "dimensions=" << SafeArrayGetDim(*ppsa) << endl;

// get lower and upper bounds

long lLBound, lUBound;

SafeArrayGetLBound(*ppsa, 1, &lLBound);

SafeArrayGetUBound(*ppsa, 1, &lUBound);

cout << "lower bound=" << lLBound << ", upper bound=" << lUBound << endl;

// access each element

BSTR bstrCurrent;

BSTR bstrNew;

CString curStr;

for (long i=lLBound; i <= lUBound; i++)

{

SafeArrayGetElement(*ppsa, &i, &bstrCurrent);

cout << "vArray[" << i << "]=" << CW2A(bstrCurrent) << endl;

SysFreeString(bstrCurrent);

bstrNew = SysAllocString(L"replaced");

HRESULT hr = SafeArrayPutElement(*ppsa, &i, bstrNew);

if (FAILED(hr))

goto error;

}

 

return S_OK;

error:

AfxThrowOleDispatchException(1003, "Unexpected Failure in StrArrayFunc method");

return 0;

}


MANAGED CODE/C#

Once a reference to the COMServer is added, an interop class will be created with methods defined in the dispatch interface.  According to the documentation the type library importer is supposed convert [in, out] SAFEARRAY(BSTR) *param to a ref string[].  Unfortunately that doesn't seem to be what happens; it gets imported as a System.Array.  This means there are a few extra steps to calling the unmanaged function and then reading the modified array.  e.g.,


MyCOMServer.MyClassClass mc = new MyClassClass();

String[] ar = new string[30];

for (int i=0; i < ar.Length; i++)

ar[i] = "str1";

Array a = (Array) ar;

mc.MyArrayFunc(ref a);

// NOTE: to get return value, have to cast back from the object passed in by reference...

string[] nAr = (string[]) a;

 


 

 

 

More on the Token Bucket flow limiting algorithm

So far, the Token Bucket algorithm is doing an excellent job of keeping the bandwidth rate near the desired rate (the r parameter in the algorithm).



However, it has taken a bit to get here. I choose a burst rate that was way too conservative. I ran across a suggested burst rate formula on Cisco's site; burst rate = r * 8 * 1.5 where r is the desired bit rate, 8 is selected because we're using bits (instead of bytes) and 1.5 represents seconds. They empirically determined this value.



My own measurements agree with their indication that Token Bucket will tend to underflow if the burst rate is too small.



At least so far it looks like we get the best results with a bucket that is initially 1/3rd full and an incrementer that adds tokens every 3 milliseconds.

Using multiple threads to process a list of items.

Another multithreading issue I've come across frequently is where I have a fixed size list of items that need to be processed in some way.  Maybe it's a list of IP addresses to check or a list of files to parse.  Either way, the list size is fixed and each item can be processed independently.

 

One way to deal with these situations is to create a small number of worker threads and have them execute the same loop on the list.  Each thread should be numbered (e.g., from 0 to n-1 where n is the number of threads).  Each thread needs to know its number and the total number of workers.  The loop for each thread then becomes:

 

for (int i=myNumber; i < size of the list; i += numberOfThreads)

   process list[i]

 

As far as I can tell this is an example of the isolation approach to concurrency.  The data is carved up into independently processable segments and each segment is handled by a separate thread.  It reminds of the approach that an MPI based program might take.

 

Since the list is fixed and the elements are independent there's no need for locking.

 

Of course, the master thread usually needs some way to figure out when everyone is done.  I tend to have the worker threads signal an event then have the master thread wait on all of the thread's events.  An alternative approach might be to use an InterlockedIncrement/Decrement then have the master thread use an InterlockedCompareAndExchange to continually poll until a counter falls back to zero.

 

 

A Concurrency Sub-Pattern

I often find that I need to have some work done periodically in the background for some length of time.  The frequency varies but basically a background thread needs to run in a loop, indefinitely, and wake up every so often to do the work.

 

Since I don't know in advance when I'm going to need to terminate the background thread, I've found that I can use a single Event to kill 2 birds with 1 stone.

 

Declare a stop event - make sure it's unsignaled by default.  In the Win32 world this is done via CreateEvent().

 

In the thread loop, instead of sleeping for a set period of time then doing the work, use a timed wait on the StopEvent.  If it times out, do the work, otherwise exit the loop.  Something like

 

while ( true )

{

   WaitForSingleObject(stopEvent, SleepTime);

   if ( timed out )

     do work

   else

      break out of loop

}

 

When it's time to stop the background thread all you have to do is signal the stop event.  If it's signaled while the thread is doing its work then as soon as it's done the thread will stop.

 

If the application needs to know when the background thread has stopped, at least in the Windows world, all you need to do is poll GetExitCodeForThread() (have your background thread func return 0 when it's done).  This has the advantage of not introducing any additional event objects (e.g., an "I'm done" event) which plays into my preference for using the existing framework as much as possible.  On the downside it does require some busy waiting (in between checks to GetExitCodeForThread()).  And if your background thread gets "stuck" your foreground thread may get stuck waiting on it forever.

 

Another rule of thumb I've found useful is "Never Lock and Block()".  As long as you make sure that your background thread doesn't block then there's no need to worry about locking up the foreground thread waiting on the background thread to terminate.

Implementing the Token Bucket algorithm


Implementing this algorithm is relatively straightforward.  I run a thread in the background that periodically fills the token bucket.  This required a minor transformation of the algorithm; instead of adding a single token every 1/r seconds I added T tokens every S seconds where T = r * S.

 

So the background thread just loops ala:

while ( true )

{

   sleep(S);

   EnterCriticalSection(...);

   if tokens < maxTokens

      tokens += T;

   LeaveCriticalSection(...);

}

 

I've left off a few details (e.g., try/catch blocks) but that's the heart of the implementation.

 

In my case the bucket starts off full (since no data has been sent yet) but that may not be the case in other situations.

Limiting outgoing bandwidth

I needed a way to limit the amount of bandwidth sent by an application.  Ideally the bandwidth limiting would be done in a way that didn't introduce large gaps in transmission (also known as jitter).  After looking around Wikipedia I came across an algorithm that looked like the perfect fit; the token bucket algorithm.

 

The simplicity of the Token Bucket algorithm for limiting bandwidth is exceeded only by its elegance.  Instead of attacking the problem of sending too much or too little data head on, e.g., by sending more or less data based on what has already been sent, it uses a generative approach. 

 

Controlling rate of flow based on what has already been sent requires keeping track of what has been sent and determining how much history to keep.  Those are 2 parameters that may vary depending on the characteristics of the flow (bursty vs non-bursty).  In other words, determining how much data to send based solely on how much data has been sent isn't an easy problem.

 

On the other hand, generating data in proportion to the desired rate of flow is relatively straightforward.  As indicated in the token bucket algorithm, you basically want to generate a token every 1/r seconds where r is the rate of flow.  If an application is only allowed to send data equivalent to the generated amount in the bucket then the application will tend to send data at the desired rate of flow.

 

The other parameter in this algorithm is b, the maximum bucket size.  Again this was a parameter largely dictated by the data; we have a pretty good idea of the largest burst we'll ever need to send.  In the absence of any other information I suppose you could always use the Maximum Transmission Unit for whatever network you're application is using.

Yahoo Music subscription + XBox 360

I've had an XBox 360 for a little over a year and have totally converted to Console gaming.  With the exception of a brief stint in Second Life, I haven't used my PC for gaming since I got the XBox 360.

 

Beyond being a great gaming system the 360 is bundled with loads of media streaming functionality.  This is perfect for displaying pictures, listening to music, watching movies, etc...,  since it's already near the TV (which is downstairs).

 

While playing around with Yahoo Music Jukebox (YMJ) I discovered that the 360 can play subscription tracks!!  I had been considering canceling the subscription because it only saves you 20cents per song purchase AND it's inconvenient having to connect a laptop to the home stereo just to listen to music.  Lo and Behold, with network music enabled in the Yahoo Music Jukebox the XBox360 recognizes it as a music source.

 

Under the covers I suspect that YMJ is using the Windows Universal Plug-and-Play Device Host API.  If so, it's a perfect example of providing extra functionality via standard frameworks (not necessarily standard in the "committee" sense, but standard in the de-facto sense) leading to greater compatability.