Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I pass a variable-length string from an RPC server to the client?

Tags:

c++

rpc

winapi

idl

I am implementing a Windows system service, which acts as RPC server, and a corresponding client, both in C++. I am using plain Windows RPC functionality.

Passing strings from the RPC client to the server is easy. Just declare the function parameter in the IDL file like this:

[in, string] wchar_t* myString

MIDL will take care of the memory-allocation magic. Works like a treat.

Returning a modified client string is also easy:

[in, out, string] wchar_t* myString

That requires me to properly size the string on the client side, though.

Problem:

I need to return strings from the server to the client. I do not know on the client how large they are going to be, so memory allocation on the client is not an option.

I could allocate a very large amount of memory, say 10K, an amount large enough for every string the server could possibly return. But that is a huge waste of resources (memory, network), and I still cannot know for certain that the server never needs to return a larger string.

What I tried:

Amonst many other things I tried the technique used in Microsoft's strout sample. It worked when calling the RPC function for the first time, but crashed the server when called for the second time.

like image 541
Helge Klein Avatar asked Apr 22 '12 10:04

Helge Klein


1 Answers

The MSDN page Multiple Levels of Pointers brought me on the right track. With the example and explanation given there I managed to make it work. The essentail parts are as follows:

IDL file:

error_status_t ReturnsString
(
   [out]                   long*     size,
   [out, size_is(, *size)] wchar_t** outString
);

Server function:

error_status_t ReturnsString (long* size, wchar_t** outString)
{
   wstring outStringWString = L"Return this to caller";

   int stringSize = sizeof(wchar_t) * (outStringWString.size() + 1);
   *outString = (wchar_t*) midl_user_allocate (stringSize * 2);
   wcscpy_s (*outString, stringSize, outStringWString.c_str());

   *size = outStringWString.size() + 1;

   return ERROR_SUCCESS;
}

I do not know why the multiplication by two (stringSize * 2) is necessary, but it is. If omitted, you get instant heap corruption.

Client code:

wchar_t** versionRPC = (wchar_t**) midl_user_allocate (sizeof(wchar_t*));
*versionRPC = NULL;     // Required to create a unique pointer
long stringSize = 0;
DWORD retVal = ReturnsString (&stringSize, versionRPC);

// Copy the returned string
wstring stringFromServer (*rpcString);

MIDL_user_free (*rpcString);
MIDL_user_free (rpcString);
like image 161
Helge Klein Avatar answered Nov 02 '22 09:11

Helge Klein