[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

microsoft.public.dotnet.framework.interop

C# 2 fixed sized buffers and interop

Jon Skeet

7/10/2007 11:47:00 AM

I've just started looking into fixed sized buffers in C# 2, and I
think I must be missing something. I have no problems when not dealing
with interop, but as soon as I start using interop things go wrong.

I thought I'd try GetTimeZoneInformation as a starting point, partly
as I know I can get at the character data using
UnmanagedType.ByValTStr for a consistency check. Unfortunately, I'm
only getting the first character of the daylight name and the standard
name. A complete program follows - could someone point out the stupid
mistake I've doubtless made? Uncommenting the version using
"Marshal.As UnmanagedType.ByValTStr" gives the correct values (GMT
etc) but as posted it just prints out "G" twice.

Jon


using System;
using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential, Pack=1)]
struct SystemTime
{
public ushort Year;
public ushort Month;
public ushort DayOfWeek;
public ushort Day;
public ushort Hour;
public ushort Minute;
public ushort Second;
public ushort Milliseconds;
}

[StructLayout(LayoutKind.Sequential, Pack=1, CharSet=CharSet.Unicode)]
unsafe struct TimeZoneInformation
{
public int Bias;
//[MarshalAs(UnmanagedType.ByValTStr, SizeConst=32)]
//public string StandardName;
public fixed char StandardName[32];
public SystemTime StandardDate;
public int StandardBias;
//[MarshalAs(UnmanagedType.ByValTStr, SizeConst=32)]
//public string DaylightName;
public fixed char DaylightName[32];
public SystemTime DaylightDate;
public int DaylightBias;
}

public class Test
{
[DllImport("kernel32.dll", CharSet=CharSet.Unicode)]
static extern uint GetTimeZoneInformation
(out TimeZoneInformation timeZoneInformation);

static void Main()
{
unsafe
{
TimeZoneInformation info;
uint result = GetTimeZoneInformation (out info);
//Console.WriteLine (info.StandardName);
//Console.WriteLine (info.DaylightName);
Console.WriteLine (new string(info.DaylightName));
Console.WriteLine (new string(info.StandardName));
}
}
}

6 Answers

Willy Denoyette [MVP]

7/11/2007 12:31:00 AM

0

<skeet@pobox.com> wrote in message
news:1184068046.501479.300280@57g2000hsv.googlegroups.com...
> I've just started looking into fixed sized buffers in C# 2, and I
> think I must be missing something. I have no problems when not dealing
> with interop, but as soon as I start using interop things go wrong.
>
> I thought I'd try GetTimeZoneInformation as a starting point, partly
> as I know I can get at the character data using
> UnmanagedType.ByValTStr for a consistency check. Unfortunately, I'm
> only getting the first character of the daylight name and the standard
> name. A complete program follows - could someone point out the stupid
> mistake I've doubtless made? Uncommenting the version using
> "Marshal.As UnmanagedType.ByValTStr" gives the correct values (GMT
> etc) but as posted it just prints out "G" twice.
>
> Jon
>
>
> using System;
> using System.Runtime.InteropServices;
>
> [StructLayout(LayoutKind.Sequential, Pack=1)]
> struct SystemTime
> {
> public ushort Year;
> public ushort Month;
> public ushort DayOfWeek;
> public ushort Day;
> public ushort Hour;
> public ushort Minute;
> public ushort Second;
> public ushort Milliseconds;
> }
>
> [StructLayout(LayoutKind.Sequential, Pack=1, CharSet=CharSet.Unicode)]
> unsafe struct TimeZoneInformation
> {
> public int Bias;
> //[MarshalAs(UnmanagedType.ByValTStr, SizeConst=32)]
> //public string StandardName;
> public fixed char StandardName[32];
> public SystemTime StandardDate;
> public int StandardBias;
> //[MarshalAs(UnmanagedType.ByValTStr, SizeConst=32)]
> //public string DaylightName;
> public fixed char DaylightName[32];
> public SystemTime DaylightDate;
> public int DaylightBias;
> }
>
> public class Test
> {
> [DllImport("kernel32.dll", CharSet=CharSet.Unicode)]
> static extern uint GetTimeZoneInformation
> (out TimeZoneInformation timeZoneInformation);
>
> static void Main()
> {
> unsafe
> {
> TimeZoneInformation info;
> uint result = GetTimeZoneInformation (out info);
> //Console.WriteLine (info.StandardName);
> //Console.WriteLine (info.DaylightName);
> Console.WriteLine (new string(info.DaylightName));
> Console.WriteLine (new string(info.StandardName));
> }
> }
> }
>


I have encountered the same issue some time ago, I guess the marshaler
ignores the CharSet attribute for fixed char buffers and considers the
unmanaged char buffer to be Ansi encoded.
Here is how I would fix it.

public fixed byte StandardName[64]; // declare a byte type to prevent the
marshaler to mess with the buffer (length in bytes now!)
....
public fixed byte DaylightName[64];
....
}

public class Test
{

....
Console.WriteLine (new string((char*)info.DaylightName)); // cast
the byte* to char*
....

Willy.

Jon Skeet

7/11/2007 6:50:00 PM

0

Willy Denoyette [MVP] <willy.denoyette@telenet.be> wrote:

<snip>

> I have encountered the same issue some time ago, I guess the marshaler
> ignores the CharSet attribute for fixed char buffers and considers the
> unmanaged char buffer to be Ansi encoded.

How bizarre. In this case, I really just want the marshaller to get out
of the way - all it needs to do is pass in a pointer, without doing any
extra copying. Is there any way of doing this?

> Here is how I would fix it.
>
> public fixed byte StandardName[64]; // declare a byte type to prevent the
> marshaler to mess with the buffer (length in bytes now!)
> ...
> public fixed byte DaylightName[64];
> ...
> }
>
> public class Test
> {
>
> ...
> Console.WriteLine (new string((char*)info.DaylightName)); // cast
> the byte* to char*
> ...

Yup, that appears to fix it. (I thought I'd tried something like that
before, but apparently not.)

Out of interest, have you used the new-to-C#2 fixed buffers? Have you
found them handy? I can't say I've ever used them before, as I very
rarely touch unsafe code in the first place. Have you encountered them
outside interop?

--
Jon Skeet - <skeet@pobox.com>
http://www.pobox.... Blog: http://www.msmvps.com...
If replying to the group, please do not mail me too

Willy Denoyette [MVP]

7/12/2007 9:32:00 AM

0

"Jon Skeet [C# MVP]" <skeet@pobox.com> wrote in message
news:MPG.20ff36c0a1713e872d0@msnews.microsoft.com...
> Willy Denoyette [MVP] <willy.denoyette@telenet.be> wrote:
>
> <snip>
>
>> I have encountered the same issue some time ago, I guess the marshaler
>> ignores the CharSet attribute for fixed char buffers and considers the
>> unmanaged char buffer to be Ansi encoded.
>
> How bizarre. In this case, I really just want the marshaller to get out
> of the way - all it needs to do is pass in a pointer, without doing any
> extra copying. Is there any way of doing this?
>

The callee (kernel32 function) stores the value of a TimeZoneInformation
structure into the location pointed to by the caller (here to "info" on the
callers stack) .
The marshaller stays out of the picture as long as the structure contains
only blittable types, however, a *char* is non-blittable , so the marshaller
must convert the source (Unicode or MBCS) buffer to the dest *char* buffer.
This conversion occurs after the actual store of the value type, this
requires some *extra copying* of the fixed char array's (which is what you
meant I guess).
This is the point where the marshaller goes wrong (a bug!), he treats the
return buffer as a MBCS, irrespective the CharSet attribute, that means that
the conversion stops after the first character in case of a Unicode buffer.
One way to prevent this *extra copying* is to make the structure blittable
(as I illustrated).

>> Here is how I would fix it.
>>
>> public fixed byte StandardName[64]; // declare a byte type to prevent
>> the
>> marshaler to mess with the buffer (length in bytes now!)
>> ...
>> public fixed byte DaylightName[64];
>> ...
>> }
>>
>> public class Test
>> {
>>
>> ...
>> Console.WriteLine (new string((char*)info.DaylightName)); // cast
>> the byte* to char*
>> ...
>
> Yup, that appears to fix it. (I thought I'd tried something like that
> before, but apparently not.)
>
> Out of interest, have you used the new-to-C#2 fixed buffers?

We maintain company wide "Programming Guidelines" that define the usage
context of API's an features of a product (MS and others). For C# and the
FCL, we have three categories of API guidelines (the red, orange and green
books), the green book lists all API's that can safely be used in production
code, the orange book lists (annotated) all API's that need special care,
they may be used for prototyping and in "trusted" libraries, while the red
book lists the "prohibited" API's and features.
"unsafe" constructs and "fixed" are in the orange book for V2 they need some
care as they may cause unexpected behavior when used (as illustrated).

>Have you
> found them handy? I can't say I've ever used them before, as I very
> rarely touch unsafe code in the first place. Have you encountered them
> outside interop?
>
Honestly, I don't write that much code these days, but IMO they have not
much value outside interop.

Willy.

Jon Skeet

7/12/2007 6:14:00 PM

0

Willy Denoyette [MVP] <willy.denoyette@telenet.be> wrote:
> >> I have encountered the same issue some time ago, I guess the marshaler
> >> ignores the CharSet attribute for fixed char buffers and considers the
> >> unmanaged char buffer to be Ansi encoded.
> >
> > How bizarre. In this case, I really just want the marshaller to get out
> > of the way - all it needs to do is pass in a pointer, without doing any
> > extra copying. Is there any way of doing this?
>
> The callee (kernel32 function) stores the value of a TimeZoneInformation
> structure into the location pointed to by the caller (here to "info" on the
> callers stack) .
> The marshaller stays out of the picture as long as the structure contains
> only blittable types, however, a *char* is non-blittable , so the marshaller
> must convert the source (Unicode or MBCS) buffer to the dest *char* buffer.
> This conversion occurs after the actual store of the value type, this
> requires some *extra copying* of the fixed char array's (which is what you
> meant I guess).

Right - in this case I want to basically tell the marshaller that it
really *is* blittable, just get on with it.

> This is the point where the marshaller goes wrong (a bug!), he treats the
> return buffer as a MBCS, irrespective the CharSet attribute, that means that
> the conversion stops after the first character in case of a Unicode buffer.
> One way to prevent this *extra copying* is to make the structure blittable
> (as I illustrated).

Right - and there's no way of doing that while still using a char
buffer?

> > Yup, that appears to fix it. (I thought I'd tried something like that
> > before, but apparently not.)
> >
> > Out of interest, have you used the new-to-C#2 fixed buffers?
>
> We maintain company wide "Programming Guidelines" that define the usage
> context of API's an features of a product (MS and others). For C# and the
> FCL, we have three categories of API guidelines (the red, orange and green
> books), the green book lists all API's that can safely be used in production
> code, the orange book lists (annotated) all API's that need special care,
> they may be used for prototyping and in "trusted" libraries, while the red
> book lists the "prohibited" API's and features.
> "unsafe" constructs and "fixed" are in the orange book for V2 they need some
> care as they may cause unexpected behavior when used (as illustrated).

Indeed!

> >Have you
> > found them handy? I can't say I've ever used them before, as I very
> > rarely touch unsafe code in the first place. Have you encountered them
> > outside interop?
> >
> Honestly, I don't write that much code these days, but IMO they have not
> much value outside interop.

Okay - thanks for that.

--
Jon Skeet - <skeet@pobox.com>
http://www.pobox.... Blog: http://www.msmvps.com...
If replying to the group, please do not mail me too

Willy Denoyette [MVP]

7/12/2007 7:11:00 PM

0

"Jon Skeet [C# MVP]" <skeet@pobox.com> wrote in message
news:MPG.21007fd7cebd3f7c2d4@msnews.microsoft.com...
> Willy Denoyette [MVP] <willy.denoyette@telenet.be> wrote:
>> >> I have encountered the same issue some time ago, I guess the marshaler
>> >> ignores the CharSet attribute for fixed char buffers and considers the
>> >> unmanaged char buffer to be Ansi encoded.
>> >
>> > How bizarre. In this case, I really just want the marshaller to get out
>> > of the way - all it needs to do is pass in a pointer, without doing any
>> > extra copying. Is there any way of doing this?
>>
>> The callee (kernel32 function) stores the value of a TimeZoneInformation
>> structure into the location pointed to by the caller (here to "info" on
>> the
>> callers stack) .
>> The marshaller stays out of the picture as long as the structure contains
>> only blittable types, however, a *char* is non-blittable , so the
>> marshaller
>> must convert the source (Unicode or MBCS) buffer to the dest *char*
>> buffer.
>> This conversion occurs after the actual store of the value type, this
>> requires some *extra copying* of the fixed char array's (which is what
>> you
>> meant I guess).
>
> Right - in this case I want to basically tell the marshaller that it
> really *is* blittable, just get on with it.
>
>> This is the point where the marshaller goes wrong (a bug!), he treats the
>> return buffer as a MBCS, irrespective the CharSet attribute, that means
>> that
>> the conversion stops after the first character in case of a Unicode
>> buffer.
>> One way to prevent this *extra copying* is to make the structure
>> blittable
>> (as I illustrated).
>
> Right - and there's no way of doing that while still using a char
> buffer?
>
It should have done it in your case (a Unicode encode struct), but it can't
do it when the native structure holds Ansi character buffers. The Ansi
buffer need to be *marshaled* to a fixed char buffer (Unicode only) .

Consider this sample:
a "native" Ansi encoded structure:
stuct MyNativeStruct {
fixed char buff[16];
int val;
}

and it's C# counterpart:
stuct MyManagedStruct {
fixed char buff[16];
int val;
}

here MyNativeStruct.buff is 16 bytes, while the MyManagedStruct.buff is 32
bytes. You can't treat the structure as blittable and just pass the
structure as is, both the char encoding and the lay-out don't match, so you
need to marshal.

Willy.



Jon Skeet

7/12/2007 8:33:00 PM

0

Willy Denoyette [MVP] <willy.denoyette@telenet.be> wrote:
> > Right - and there's no way of doing that while still using a char
> > buffer?
>
> It should have done it in your case (a Unicode encode struct), but it can't
> do it when the native structure holds Ansi character buffers. The Ansi
> buffer need to be *marshaled* to a fixed char buffer (Unicode only) .

Yup - that makes perfect sense. I was only considering the cases where
the structure really was going to be filled with Unicode characters in
the first place.

Hmm. I'm still looking for a really good example of using this...

--
Jon Skeet - <skeet@pobox.com>
http://www.pobox.... Blog: http://www.msmvps.com...
If replying to the group, please do not mail me too