FIX: Call to managed class method with StringBuilder as in or out parameter may fail (317577)



The information in this article applies to:

  • Microsoft Windows .NET Framework 1.1
  • Microsoft .NET Framework 1.0
  • Microsoft .NET Framework Class Libraries 1.0

This article was previously published under Q317577

SYMPTOMS

When you call a managed method that takes System.Text.StringBuilder as a parameter from an unmanaged client, you may receive an ArgumentOutOfRangeException exception error message.

CAUSE

The unmanaged client calls the Test method of the class and passes a string that is larger than 16 characters.

RESOLUTION

To work around this problem, follow these steps:
  1. Use IntPtr instead of System.Text.StringBuilder to build a modified managed server, as follows:
    using System;
    using System.Text;
    using System.Runtime.InteropServices;
    namespace Q317577 {
          public interface ITest{
                void Test(ref IntPtr sbString, ref int length);
          }
          public class Class1 : ITest{
                public Class1(){
                }
                public void Test(ref IntPtr sbString, ref int length){
                     Marshal.FreeCoTaskMem(sbString);
                     string str = "changed changed";
                     length = str.Length;
                     sbString = Marshal.StringToCoTaskMemUni(str);
                }
          }
    }
    					
  2. Use Regasm.exe to register the server, as follows:
    #include <stdio.h>
    #include <tchar.h>
    #import "Q317577.tlb" no_namespace named_guids
    
    int _tmain(int argc, _TCHAR* argv[])
    {
          CoInitialize(NULL);
          try{
                ITestPtr spTest;
                //wchar_t wszTestString[] = L"This is a test.";
                wchar_t wszTestString[] = L"A long long string for test.";
                long length = (wcslen(wszTestString) + 1) * sizeof(WCHAR);
                LPWSTR lpszTestString = (LPWSTR)::CoTaskMemAlloc(length);
                spTest.CreateInstance(CLSID_Class1);
                wcscpy((LPWSTR)lpszTestString, wszTestString);
                spTest->Test((long*)&lpszTestString,&length);
                MessageBoxW(NULL, (LPWSTR)lpszTestString, L"Results", 0); 
                ::CoTaskMemFree((LPWSTR)lpszTestString);
          }
          catch(_com_error cerr){
                MessageBox(NULL, cerr.Description(), "err", 0); 
          }
          CoUninitialize(); 
          return 0;
    }
    					
If you are using Visual Studio .NET to build this application, follow these steps:
  1. In the IntOpTst Property Pages dialog box, click Configuration Properties, and then click Build. Change the Register for COM Interop value to True.
  2. Modify the unmanaged client code to make a call to the managed server.

STATUS

This bug was corrected in .NET Framework 2003|1.1.

MORE INFORMATION

During interoperation, the runtime ignores the size of the input string that is passed in. When an unmanaged client calls the Test method of the class and passes a string that is larger than 16 characters, the method fails with the COR_E_ARGUMENTOUTOFRANGE error code.

Steps to reproduce the behavior

Define a managed class with a method that resembles the following:
using System;
using System.Text;
using System.Runtime.InteropServices;
namespace Q317577
{	
	public interface ITest 
	{
		void Test(StringBuilder sbString);
	}
	public class Class1 : ITest
	{
		public Class1()
		{
			
		}
		public void Test(StringBuilder sbString)
		{
                        string str = "changed changed";
                        sbString.Replace(sbString.ToString(), str);
		}
	}
}
				
Client code:
#include <stdio.h>
#include <tchar.h>
#import "Q317577.tlb" no_namespace named_guids
int _tmain(int argc, _TCHAR* argv[])
{
	CoInitialize(NULL);
        try
	{
		ITestPtr spTest;
		//wchar_t wszTestString[] = L"This is a test."; //<- This works
		wchar_t wszTestString[] = L"A long long string for test.";
		long length = (wcslen(wszTestString) + 1) * sizeof(WCHAR);
		LPWSTR lpszTestString = (LPWSTR)::CoTaskMemAlloc(length);
		spTest.CreateInstance(CLSID_Class1);
		wcscpy((LPWSTR)lpszTestString, wszTestString);
		spTest->Test((LPWSTR)lpszTestString);		
		MessageBoxW(NULL, (LPWSTR)lpszTestString, L"Results", 0); 
		::CoTaskMemFree((LPWSTR)lpszTestString);
	}
	catch(_com_error cerr)
	{
		MessageBox(NULL, cerr.Description(), "err", 0); 
	}
	CoUninitialize(); 
	
	return 0;
}
				
Note The sample code assumes that the Q317577.tlb type library is in the same location as the source file. If this is not true, be sure that you add the path to the type library in the #import statement. When you compile the client from the command line, be sure that you specify the /EHsc compiler option.

Modification Type:MajorLast Reviewed:9/26/2005
Keywords:kbfix kbbug kbCOMInterop kbnofix KB317577