//
you're reading...
BSTR

Using BSTR in Managed Code Part 2

1. Introduction.

1.1 In part 1 of this series of blogs, I demonstrated how to use BSTRs in managed code complete with example codes that focuses on passing BSTRs from managed code to unmanaged code.

1.2 In this part 2, I shall provide example codes that demonstrate the receiving of BSTRs from unmanaged code.

1.3 I shall also endeavour to demonstrate inter-usability of BSTRs regardless of whether an API or a Marshal Class method was used to create them.

2. Basic Returning of BSTRs.

2.1 Listed below is an API that takes a pointer to a BSTR. This BSTR pointer parameter is an “out” parameter :

void __stdcall ReceiveBSTR(/*[out]*/ BSTR* ppBstrReceiver)
{
  *ppBstrReceiver = ::SysAllocString(L"Hello Managed World");
}

This API creates a BSTR and assigns the pointer to this BSTR to the “out” parameter.

2.2 The C# declaration for this API is listed below :

[DllImport("TestDLL.dll", CallingConvention = CallingConvention.StdCall)]
public static extern void ReceiveBSTR(out IntPtr ppBstrReceiver);

2.3 And a sample calling C# code :

private static void ReceiveBSTRFromUnmanagedCode()
{
  IntPtr pbstr = IntPtr.Zero;
  // Call the API that will allocate
  // a BSTR and make "pbstr" point
  // to it.
  ReceiveBSTR(out pbstr);
  // Display the BSTR.
  DisplayBSTR(pbstr);
  // Free the BSTR.
  Marshal.FreeBSTR(pbstr);
}

The DisplayBSTR() API (seen previously in part 1) is used to display the BSTR pointed to by “pbstr”.

2.4 Notice that SysAllocString() was used inside the API to allocate the BSTR and Marshal.FreeBSTR() was used to free the BSTR. These functions can be freely used together.

3. Returning a BSTR Using a VARIANT.

3.1 Listed below is an API that returns a VARIANT pointer as an “out” parameter :

void __stdcall ReceiveBSTRByVARIANT(/*[out]*/ VARIANT** ppvarReceiver)
{
  // Allocate a VARIANT structure in memory.
  *ppvarReceiver = (VARIANT*)::CoTaskMemAlloc(sizeof(VARIANT));
  // Initialize the VARIANT.
  VariantInit(*ppvarReceiver);
  // Assign BSTR value to the VARIANT.
  V_VT(*ppvarReceiver) = VT_BSTR;
  V_BSTR(*ppvarReceiver) = ::SysAllocString(L"Hello Managed World"); 
}

This API will allocate in memory a VARIANT struct and then fill the VARIANT with a newly allocated BSTR value. The VARIANT will be returned to the caller via an “out” parameter.

3.2 The C# declaration :

[DllImport("TestDLL.dll", CallingConvention = CallingConvention.StdCall)]
public static extern void ReceiveBSTRByVARIANT(out IntPtr ppvarReceiver);

3.3 And a sample C# calling code :

private static void ReceiveBSTRVariantFromUnmanagedCode()
{
  IntPtr pVariant = IntPtr.Zero;
  // Call API that will allocate a VARIANT,
  // fill it with a BSTR and then return
  // a pointer to the VARIANT.
  ReceiveBSTRByVARIANT(out pVariant);
  // Display the BSTR contained inside the VARIANT.
  DisplayBSTRByVARIANT(pVariant);
  // Clear the VARIANT. This will free the BSTR.
  VariantClear(pVariant);
  // Finally free the memory used by the VARIANT.
  Marshal.FreeCoTaskMem(pVariant);
  pVariant = IntPtr.Zero;
}

After calling ReceiveBSTRByVARIANT(), we display the BSTR contents of the returned VARIANT and then proceed to perform cleanup. Since the pointer to the VARIANT was returned to the calling code, the calling code owns the memory of the VARIANT and therefore it is responsible for freeing this memory (done using Marshal.FreeCoTaskMem()).

4. Ultimate Demonstration of Inter-Usability.

4.1 In this section, I shall endeavour to show complete inter-use of various APIs and methods for working with BSTRs. The following is a summary of the concepts that will be demonstrated in this section :

  • Allocation of a BSTR from managed code.
  • Creation of a VARIANT from unmanaged code using the allocated BSTR.
  • Creation of a managed string from the newly created VARIANT.
  • Clearing of the VARIANT (and the contained BSTR).
  • Display of the managed string to show that it is independent of the earlier VARIANT-contained BSTR.

4.2 The unmanaged C++ API is listed below :

void __stdcall ReceiveBSTRByVARIANT_WithInitValue(/*[out]*/ VARIANT** ppvarReceiver, /*[in]*/ BSTR bstrStr)
{
  // Allocate a VARIANT structure in memory.
  *ppvarReceiver = (VARIANT*)::CoTaskMemAlloc(sizeof(VARIANT));
  // Initialize the VARIANT.
  VariantInit(*ppvarReceiver);
  // Assign the passed-in BSTR value to the VARIANT.
  V_VT(*ppvarReceiver) = VT_BSTR;
  V_BSTR(*ppvarReceiver) = bstrStr;
}

The API will allocate a VARIANT and initialize it with a BSTR that was passed in from the caller.

4.3 The C# declaration :

[DllImport("TestDLL.dll", CallingConvention = CallingConvention.StdCall)]
public static extern void ReceiveBSTRByVARIANT_WithInitValue(out IntPtr ppvarReceiver, IntPtr bstrStr);

4.4 And a sample C# calling code :

private static void ReceiveBSTRVariantWithInitValue()
{
  IntPtr pVariant = IntPtr.Zero;
  // Call API that will allocate a VARIANT,
  // fill it with a BSTR created from managed
  // code and then return a pointer to the VARIANT.
  ReceiveBSTRByVARIANT_WithInitValue(out pVariant, Marshal.StringToBSTR("From A Managed String."));
  // Display the BSTR contained inside the VARIANT.
  DisplayBSTRByVARIANT(pVariant);
  // Convert the BSTR inside the VARIANT
  // into a managed string.
  string str = (string)Marshal.GetObjectForNativeVariant(pVariant);
  // Clear the VARIANT. This will free the BSTR.
  VariantClear(pVariant);
  // Finally free the memory used by the VARIANT.
  Marshal.FreeCoTaskMem(pVariant);
  pVariant = IntPtr.Zero;
  // The above freeing actions will not
  // after "str" which has its own copy
  // of the string which originated
  // from the VARIANT.
  //
  // We prove it by printing it out
  // on the console even after the
  // VARIANT has been cleared.
  Console.WriteLine("{0:S}", str);
}

 Please refer to the self-explanatory comments embedded in the code above. I hope this example will boost the reader’s confidence in the use of BSTRs from managed code.

5. In Summary.

5.1 I hope that the reader has benefitted from the 2 parts of this blog series exploring interoping with BSTRs.

5.2 I hope to have lifted any veil of mystery surrounding the separation of managed from unmanaged memory, which is much hyped and illuminated the fact that whereas managed memory is usually offi-limits to unmanaged code, unmanaged memory can be accessed freely by managed code (e.g. MarshalFreeBSTR() will free a BSTR which undoubtedly lives in unmanaged memory).

Advertisements

About Lim Bio Liong

I've been in software development for nearly 20 years specializing in C , COM and C#. It's truly an exicting time we live in, with so much resources at our disposal to gain and share knowledge. I hope my blog will serve a small part in this global knowledge sharing network. For many years now I've been deeply involved with C development work. However since circa 2010, my current work has required me to use more and more on C# with a particular focus on COM interop. I've also written several articles for CodeProject. However, in recent years I've concentrated my time more on helping others in the MSDN forums. Please feel free to leave a comment whenever you have any constructive criticism over any of my blog posts.

Discussion

No comments yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: