Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / .NET Framework / Interop / August 2003

Tip: Looking for answers? Try searching our database.

P/Invoke: Use StringBuilder for LPVOID type

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
John Ting - 27 Aug 2003 19:37 GMT
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.

Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.