From: David P. <dpi...@me...> - 2007-09-24 15:48:18
|
> -----Original Message----- > From: William Fulton [mailto:wi...@fu...] On Behalf Of > William S Fulton > Sent: Friday, September 21, 2007 7:05 PM > To: David Piepgrass > Cc: swi...@li... > Subject: [SPAM] Re: [Swig-user] How are strings returned from C# to C++ > (and back to C#) without leaking memory? >=20 > David Piepgrass wrote: > > I've noticed that strings are returned from C++ using the following C# > > helper methods (two different methods for the two character sizes): > > > > // Helper for wide strings defined in wchar.i. > > // This is converted to a delegate and marshaled to a function pointer > > > > // that is stored in the C global variable > > SWIG_csharp_wstring_callback. > > static string CreateWString([MarshalAs(UnmanagedType.LPWStr)]IntPtr > > cString) { > > return > > System.Runtime.InteropServices.Marshal.PtrToStringUni(cString); > > } > > > > // Helper for ASCII strings defined in csharphead.swg > > // This is converted to a delegate and marshaled to a function pointer > > > > // that is stored in the C global variable > > SWIG_csharp_string_callback. > > static string CreateString(string cString) { > > return cString; > > } > > > > My question is, how does this "reverse marshalling" work when managed > > code is called from unmanaged code? I have not found anything on the > > subject at MSDN. I'm puzzled as to how System.String can be returned to > > unmanaged code without leaking memory and without any risk of premature > > garbage collection, because immediately after CreateString() returns, a > > managed reference to the returned System.String no longer exists. > > > I have also not found much in the way of documentation for this. > However, I have tested quite thoroughly for both memory leaks and > premature garbage collection and there were no problems, eg see > char_strings.i and char_strings_runme.cs under Examples/test-suite. I am > still not 100% happy with the possibility of premature garbage > collection, but so far no reports of it. >=20 > > Another odd thing is that the two callbacks have different return types > > on the C side, even though, in both cases, the return type is "string" > > on the C# side: > > > A lot of the marshalling is bit of a black box and Microsoft don't > publish the source, so the impression I get is that some of the > marshalling behaviour has been reverse engineered. The delegate mappings > make sense (to me anyway). Pointers are mapped onto IntPtr (so no free > is called). IRR if a void* is used, then no memory freeing is done, but > if a char* is used then a memory free is done. The marshaller > automatically converts from wide to ansi strings, that is part of it's > job, rather clever, but mysterious at the same time. >=20 > I just found this: > http://msdn.microsoft.com/library/default.asp?url=3D/library/en- > us/cpguide/html/cpconbufferssample.asp > which contains more info than it did for .Net 1.1 when I last looked at > it, eg I'm sure the StringBuffer information is new. I'm still waiting > for someone to write the StringBuffer typemaps for char *! >=20 > > typedef char * (SWIGSTDCALL* SWIG_CSharpStringHelperCallback)(const char > > *); > > public delegate string SWIGWStringDelegate(IntPtr message); > > typedef void * (SWIGSTDCALL* SWIG_CSharpWStringHelperCallback)(const > > wchar_t *); > > > > How could CreateString return char* when strings are composed of wide > > characters? >=20 > Does the Marshal class and MarshalAs attribute not explain enough?=20 Afraid not. The .NET framework can't see the C side; it doesn't know that one function is declared void* and the other is declared char*. And neither of the delegates that represent the callbacks use Marshal/MarshalAs: public delegate string SWIGStringDelegate(string message); public delegate string SWIGWStringDelegate(IntPtr message); =20 So they must return the same thing. In a debugger I can see that the string returned from SWIG_csharp_wstring_callback is actually an ASCII string, so it should be changed. If change the delegate like so: [return:MarshalAs(UnmanagedType.LPWStr)] public delegate string SWIGWStringDelegate(IntPtr message); It returns a wide string. The return value of all wrappers returning wide strings must be changed too, e.g.: [DllImport("Example", EntryPoint=3D"CSharp_Road_GetStreetName")] [return: MarshalAs(UnmanagedType.LPWStr)] public static extern string Road_GetStreetName(IntPtr jarg1); > http://www.mono- > project.com/Interop_with_Native_Libraries#P.2FInvoke_Specification. Thanks. |