发新话题
打印

如何在C#中使用Win32和其他库(四)

如何在C#中使用Win32和其他库(四)

如何在C#中使用 Win32和其他库(四)
字符串缓冲区
  .NET  中的字符串类型是不可改变的类型,这意味着它的值将永远保持不变。对于要将字符串值复制到字符串缓冲区的函数,字符串将无效。这样做至少会破坏由封送拆收器在转换字符串时创建的临时缓冲区;严重时会破坏**堆,而这通常会导致错误的发生。无论哪种情况都不可能获得正确的返回值。  
  要解决此问题,我们需要使用其他类型。StringBuilder  类型就是被设计为用作缓冲区的,我们将使用它来代替字符串。下面是一个示例:  
[dllimport("kernel32.dll",  charset  =  charset.auto)]
public  static  extern  int  GetShortPathName(
   [MarshalAs(UnmanagedType.LPTStr)]
   string  path,
   [MarshalAs(UnmanagedType.LPTStr)]
   StringBuilder  shortPath,
   int  shortPathLength);   
  使用此函数很简单:  
stringbuilder  shortpath  =  new  stringbuilder(80);
int  result  =  GetShortPathName(
@"d:\test.jpg",  shortPath,  shortPath.Capacity);
string  s  =  shortPath.ToString();   
  请注意,StringBuilder  的  Capacity  传递的是缓冲区大小。  
  具有内嵌字符数组的结构
  某些函数接受具有内嵌字符数组的结构。例如,GetTimeZoneInformation()  函数接受指向以下结构的指针:  
typedef  struct  _time_zone_information  {  
  LONG     Bias;  
  WCHAR   StandardName[  32  ];  
  SYSTEMTIME  StandardDate;  
  LONG     StandardBias;  
  WCHAR   DaylightName[  32  ];  
  SYSTEMTIME  DaylightDate;  
  LONG     DaylightBias;  
}  TIME_ZONE_INFORMATION,  *PTIME_ZONE_INFORMATION;   
  在  C#  中使用它需要有两种结构。一种是  SYSTEMTIME,它的设置很简单:  
   struct  SystemTime
   {
   public  short  wYear;
   public  short  wMonth;
   public  short  wDayOfWeek;
   public  short  wDay;
   public  short  wHour;
   public  short  wMinute;
   public  short  wSecond;
   public  short  wMilliseconds;
   }   
  这里没有什么特别之处;另一种是  TimeZoneInformation,它的定义要复杂一些:  
[structlayout(layoutkind.sequential,  charset  =  charset.unicode)]
struct  TimeZoneInformation
{  
   public  int  bias;
   [MarshalAs(UnmanagedType.ByValTStr,  SizeConst  =  32)]
   public  string  standardName;
   SystemTime  standardDate;
   public  int  standardBias;
   [MarshalAs(UnmanagedType.ByValTStr,  SizeConst  =  32)]
   public  string  daylightName;
   SystemTime  daylightDate;
   public  int  daylightBias;
}   
  此定义有两个重要的细节。第一个是  MarshalAs  属性:  
   [MarshalAs(UnmanagedType.ByValTStr,  SizeConst  =  32)]  
  查看  ByValTStr  的文档,我们发现该属性用于内嵌的字符数组;另一个是  SizeConst,它用于设置数组的大小。  
  我在第一次编写这段代码时,遇到了执行引擎错误。通常这意味着部分互操作覆盖了某些内存,表明结构的大小存在错误。我使用  Marshal.SizeOf()  来获取所使用的封送拆收器的大小,结果是  108  字节。我进一步进行了调查,很快回忆起用于互操作的默认字符类型是  Ansi  或单字节。而函数定义中的字符类型为  WCHAR,是双字节,因此导致了这一问题。  
  我通过添加  StructLayout  属性进行了更正。结构在默认情况下按顺序布局,这意味着所有字段都将以它们列出的顺序排列。CharSet  的值被设置为  Unicode,以便始终使用正确的字符类型。  
  经过这样处理后,该函数一切正常。您可能想知道我为什么不在此函数中使用  CharSet.Auto。这是因为,它也没有  A  和  W  变体,而始终使用  Unicode  字符串,因此我采用了上述方法编码。

TOP

发新话题