Hi,
I wrote a C# wrapper for the ReadConsole Win32API as following:
(Original unmanaged signature)
BOOL ReadConsole(
HANDLE hConsoleInput,
LPVOID lpBuffer,
DWORD nNumberOfCharsToRead,
LPDWORD lpNumberOfCharsRead,
LPVOID lpReserved
);
(Managed signature)
[DllImport(KERNEL32_DLL, CharSet=CharSet.Auto, SetLastError=true)]
private static extern bool ReadConsole(
IntPtr hConsoleInput,
System.Text.StringBuilder lpBuffer,
uint nNumberOfCharsToRead,
out uint lpNumberOfCharsRead,
IntPtr lpReserved
);
The weird thing of the above is that, if the function returns successfully
and "lpNumberOfCharsRead" is non-zero, I got strange stuff stored in
"lpBuffer" (which is a StringBuilder). For example:
StringBuilder buffer = new StringBuilder();
uint len;
if (ReadConsole(hConsoleInput, buffer, (uint)buffer.Capacity, out len,
IntPtr.Zero)) {
System.Console.WriteLine(buffer.Length);
// ...
}
If I type the char, "A", followed by <ENTER>, the above code yields the
following result:
"len" is set to 3.
"buffer.Length" is set to 4.
buffer[0] = 'A' (hex: 0x41)
buffer[1] = '\r' (hex: 0x0d)
buffer[2] = '\n' (hex: 0x0a)
buffer[3] = ?? (hex: 0x0600)
Depending on how many characters read by the ReadConsole API, the
StringBuilder, buffer, sometime contains tons of garbage. So I ended up with
manually setting the buffer.Length after the ReadConsole call returns
(successfully):
if (ReadConsole(hConsoleInput, buffer, (uint)buffer.Capacity, out len, ...))
{
buffer.Length = len;
// ...
}
Now the code starts to work.
My question is, is it necessary for one to manually set StringBuffer.Length
after a P/invoke returns or there is something wrong with the marshalling in
my C# wrapper?
Thanks in advance.
--John
David Stucki [MS] - 27 Aug 2003 22:21 GMT
How can .NET know the number of characters that ReadConsole put into the
buffer? It appears the ReadConsole does not insert a '\0' character to
terminate the string. If ReadConsole did null-terminate the string things
would work as you expect. You can either put a null char at the end of the
StringBuffer or do as you're doing already.
sb[len] = '\0';
--or--
sb.Length = len;
Hope this helps,
David Stucki
Microsoft Developer Support
This posting is provided "AS IS" with no warranties, and confers no rights.
John Ting - 27 Aug 2003 23:13 GMT
Thanks for the quick response.
Your explanation make perfect senses from C/C++ and native application point
of view :-)
The primary reason I got confused is that all the online help I can get do
not mention this behavior.
Take the "HandleRef.cs" in the
...\Samples\Technologies\Interop\PlatformInvoke\WinAPIs\CS directory as an
example. It uses a StringBuilder instance to interop an unmanaged LPVOID.
The problem is, it never appends a null or sets the length before calling
Console.WriteLine:
LibWrap.ReadFile( hr, buffer, 5, out read, 0 );
Console.WriteLine( "Read with struct parameter: {0}", buffer );
Even though I was suspecting the same things as you suggested, it's always
nice to get a confirmation from MS :-)
BTW, I just found two weird things which could be bugs (or "features" I was
not aware of).
Try the following executable code, which yields a System.TypeLoadException:
using System;
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Explicit)]
public struct CHAR_UNION {
[FieldOffset(0)] public char UnicodeChar;
[FieldOffset(0)] public byte AsciiChar;
}
sealed class Test {
public static void Main() {
try {
Console.WriteLine(Marshal.SizeOf(typeof(CHAR_UNION)));
}
catch (Exception x) {
Console.WriteLine(x.ToString());
}
}
}
It seems that the MSIL code (or more likely, metadata) has some problems
with the definition
of CHAR_UNION.
In addition, when the exception is thrown, the exception can never be caught
by the "catch" block.
Once again, thanks for your help.
--John
----- Original Message -----
From: "David Stucki [MS]" <a-davids@online.microsoft.com>
Newsgroups: microsoft.public.dotnet.framework.interop
Sent: Wednesday, August 27, 2003 2:21 PM
Subject: RE: P/Invoke: Use StringBuilder for LPVOID type
> How can .NET know the number of characters that ReadConsole put into the
> buffer? It appears the ReadConsole does not insert a '\0' character to
[quoted text clipped - 13 lines]
>
> This posting is provided "AS IS" with no warranties, and confers no rights.