FIX: BSTR Passed to Method is Truncated at Double NULL When Marshalled in COM+ (277660)



The information in this article applies to:

  • Microsoft COM+ 1.0, when used with:
    • the operating system: Microsoft Windows 2000

This article was previously published under Q277660

SYMPTOMS

When using a BSTR to hold binary data, if the BSTR is passed into or out of a configured COM+ component, the string is truncated at the first occurrence of a double NULL byte sequence (that is, a single Unicode NULL character).

This problem only occurs when the method is called across contexts (that is, when marshalling the BSTR), and only in components that are configured for COM+ and Microsoft Transaction Server (MTS). The same code works in a non-configured Component Object Model (COM) and Distributed Component Object Model (DCOM) application.

RESOLUTION

To resolve this problem, obtain the latest service pack for Windows 2000. For additional information, click the following article number to view the article in the Microsoft Knowledge Base:

260910 How to Obtain the Latest Windows 2000 Service Pack


If you are a developer who is building components that take BSTR arguments that contain binary data, you might consider one of the following alternatives to work around this issue:
  • Arguments passed by reference (that is, BSTR* pbstr, or sVal as String) are not affected and can be used to pass the data instead of by value (BSTR bstr, or ByVal sVal As String).
  • Use a binary array (SAFEARRAY of type VT_UI1) or an IStream* to pass the binary data.
  • Encode the binary data so that it does not have two NULL bytes in mid-stream (for example, use Base64 encoding). Then decode the string to convert it back to the native binary. This solution requires extra work by both sender and receiver.

STATUS

Microsoft has confirmed that this is a problem in the Microsoft products that are listed at the beginning of this article. This problem was first corrected in Windows 2000 Service Pack 2.

MORE INFORMATION

Steps to Reproduce Behavior

  1. With Microsoft Visual C++ 6.0 on a Windows 2000 computer, create a new Active Template Library (ATL) Project (DLL) and name the project PassBinaryBSTR.
  2. On the Insert ATL Object menu, add a Simple Object to the DLL. Name the object PassBSTR, and accept all default settings.
  3. Add the following methods to the IPassBSTR interface:
    [id(1)] HRESULT SaveData([in] BSTR bstrBinaryData);
    [id(2)] HRESULT DoTest();
    					
  4. In PassBSTR.cpp, add the following code to implement the methods:
    /////////////////////////////////////////////////////////// 
    // Change these values to point to the location to output
    // the test results...
    // 
    LPCSTR pszNoMarshalResults = "C:\\ResultsInContext.txt";
    LPCSTR pszMarshaledResults = "C:\\ResultsOutOfContext.txt";
    
    STDMETHODIMP CPassBSTR::SaveData(BSTR bstrBinaryData)
    {
       HANDLE hFile;
       DWORD dwLen, dwWritten;
    
       hFile = CreateFile(pszMarshaledResults, 
                          GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
       if (hFile == INVALID_HANDLE_VALUE)
          return HRESULT_FROM_WIN32(GetLastError());
    
       dwLen = SysStringByteLen(bstrBinaryData);
    
       WriteFile(hFile, (void*)bstrBinaryData, dwLen, &dwWritten, NULL);
       CloseHandle(hFile);
       return (dwLen == dwWritten) ? S_OK : E_FAIL;
    }
    
    STDMETHODIMP CPassBSTR::DoTest()
    {
       HRESULT hr;
       HANDLE hFile;
       DWORD dwLen, dwWritten;
    
       IPassBSTR* ptest = NULL;
    
     // Create the binary data in a BSTR...
       BSTR bstrData = SysAllocStringByteLen("Test\0\0Binary Data!", 18);
       if (bstrData == NULL) return E_OUTOFMEMORY;
    
     // Do same code as the SaveData but runs inside context and works...
       hFile = CreateFile(pszNoMarshalResults,
                         GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
       if (hFile == INVALID_HANDLE_VALUE)
       {
          SysFreeString(bstrData);
          return HRESULT_FROM_WIN32(GetLastError());
       }
    
       dwLen = SysStringByteLen(bstrData);
       WriteFile(hFile, (void*)bstrData, dwLen, &dwWritten, NULL);
       CloseHandle(hFile);
    
     // Now create a new instance of this object in a different
     // context (that's why it's called CoCreateInstance). If this object is
     // configured in COM+, it loads in a new context and the problem
     // occurs. If it is loaded under normal COM/DCOM for cross-apartment 
     // calls, the problem does not occur...
     // 
       hr = CoCreateInstance(CLSID_PassBSTR, NULL, CLSCTX_SERVER,
                            IID_IPassBSTR, (void**)&ptest);
       if (FAILED(hr) || (ptest == NULL))
       {
          SysFreeString(bstrData);
          return (hr) ? hr : E_UNEXPECTED;
       }
    
     // Have the out-of-context object save the binary data...
       hr = ptest->SaveData(bstrData);
    
       ptest->Release();
       SysFreeString(bstrData);
       return hr;
    }
    					
  5. Compile the ATL DLL. ATL registers the DLL as a COM component.
  6. On the Start menu, select Administrative Tools, and then select the Component Services tool to create a new COM+ package.
  7. Add the PassBinaryBSTR.PassBSTR object to the package.
  8. From Visual Basic, Visual Basic for Applications, or VBScript, run the following code to run the DLL:
    Sub RunIt()
       Dim x
       Set x = CreateObject("PassBinaryBSTR.PassBSTR")
       x.DoTest
       MsgBox "Done. Check the files for results."
    End Sub
    					
  9. Note that the ResultsInContext.txt file contains the full binary string in the BSTR. The ResultsOutOfContext.txt file shows that the string was truncated at the double NULL bytes when the BSTR was passed, leaving just the word "Test" in the file.

Modification Type:MajorLast Reviewed:5/28/2003
Keywords:kbAutomation kbbug kbfix kbWin2000PreSP2Fix KB277660