代码改变世界

.net 互操作之p/invoke- 数据封送(字符串版本-ANSI和UniCode,返回错误码,回调函数,常量)(6)

2010-08-26 23:52  Clingingboy  阅读(932)  评论(0编辑  收藏  举报

处理字符串的函数(ANSI和UniCode版本)



非托管函数后缀名A表示ANSI,W表示宽字符(Unicode),如GetUserName有GetUserNameA和GetUserNameW

p/inlvoke设置方式

1.设置CharSet
DllImport默认为Ansi

[DllImport("advapi32.dll", CharSet = CharSet.Ansi)]

2.设置EntryPoint

[DllImport("advapi32.dll", EntryPoint = "GetUserNameA")]

3.自动行为

当设置CharSet 会自动去寻找后缀名为W和A的函数,设置ExactSpelling为true时,需要保证生命函数与非托管函数名是一致的

[DllImport("advapi32.dll", EntryPoint = "GetUserNameA", CharSet = CharSet.Auto,ExactSpelling=true)]

4.自动系统平台设置
当CharSet 设置为Auto时,会根据系统来寻找函数封送

[DllImport("advapi32.dll", EntryPoint = "GetUserNameW", CharSet = CharSet.Auto)]

错误码

首先要设置SetLastError=true

[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]

两种处理方式

1.使用Win32 API FormatMessage

public class FormatErrorCode
 {
     const uint FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100;
     const uint FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200;
     const uint FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000;
 
     //Win32 API
     //DWORD WINAPI FormatMessage(
     //  __in      DWORD dwFlags,
     //  __in_opt  LPCVOID lpSource,
     //  __in      DWORD dwMessageId,
     //  __in      DWORD dwLanguageId,
     //  __out     LPTSTR lpBuffer,
     //  __in      DWORD nSize,
     //  __in_opt  va_list* Arguments
     //);
 
     [DllImport("kernel32.dll")]
     public static extern uint FormatMessage(uint dwFlags, IntPtr lpSource,
         uint dwMessageId, uint dwLanguageId, ref IntPtr lpMsgBuf,
         uint nSize, IntPtr Arguments);
 
     //Win32 API
     //HLOCAL WINAPI LocalFree(
     //  __in  HLOCAL hMem
     //);
 
     [DllImport("kernel32.dll", SetLastError = true)]
     public static extern IntPtr LocalFree(IntPtr hMem);
 
     public static string GetLastErrorMsg()
     {
         
         int lastError = Marshal.GetLastWin32Error();
 
         IntPtr lpMsgBuf = IntPtr.Zero;
 
         uint dwChars = FormatMessage(
             FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM
             | FORMAT_MESSAGE_IGNORE_INSERTS,
             IntPtr.Zero, (uint)lastError, 0, ref lpMsgBuf, 0, IntPtr.Zero);
 
         if (dwChars == 0)
         {
             return "";
         }
 
         string errorMsg = Marshal.PtrToStringAnsi(lpMsgBuf);
         // 释放内存
         LocalFree(lpMsgBuf);
 
         return errorMsg;
     }
 }

测试

public static void TestErrorMsgByWin32Exception()
 {
 
         GetFileAttributes("FileNotFoundDemo.txt");
 
     // error code获得最后一次获得的错误
int lastErrorCode = Marshal.GetLastWin32Error(); // 将Win32的错误码转化成一个用户友好的托管异常 Win32Exception win32Exception = new Win32Exception(lastErrorCode); Console.WriteLine("GetFileAttributes last win32 error message: {0}", win32Exception.Message); }


2.使用托管的Win32Exception

public static void TestErrorMsgByWin32ExceptionDefault()
 {
     // 试图获得一个不存在的文件的属性
     GetFileAttributes("FileNotFoundDemo.txt");
 
     // 使用Win32的缺省构造函数,使其自动调用Marshal.GetLastWin32Error()来
     // 将Win32的错误码转化成一个用户友好的托管异常
     Win32Exception win32Exception = new Win32Exception();
     Console.WriteLine("GetFileAttributes last win32 error message: {0}", win32Exception.Message);
 }

明显的第2种简单很多,会自动去调用Marshal.GetLastWin32Error()方法

处理回调函数

定义常量

这个比较麻烦,烦是Win32 API 定义的常量,想使用的话,必须重新定义一遍,所以找到常量的值是关键.可以讲一组功能相近的常量定义成枚举

[Flags]
 public enum FileAttributeFlags : uint
 {
     //文件属性标志
     FILE_ATTRIBUTE_READONLY = 0x00000001,
     FILE_ATTRIBUTE_HIDDEN = 0x00000002,
     FILE_ATTRIBUTE_SYSTEM = 0x00000004,
     FILE_ATTRIBUTE_DIRECTORY = 0x00000010,
     FILE_ATTRIBUTE_ARCHIVE = 0x00000020,
     FILE_ATTRIBUTE_DEVICE = 0x00000040,
     FILE_ATTRIBUTE_NORMAL = 0x00000080,
     FILE_ATTRIBUTE_TEMPORARY = 0x00000100,
     FILE_ATTRIBUTE_SPARSE_FILE = 0x00000200,
     FILE_ATTRIBUTE_REPARSE_POINT = 0x00000400,
     FILE_ATTRIBUTE_COMPRESSED = 0x00000800,
     FILE_ATTRIBUTE_OFFLINE = 0x00001000,
     FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x00002000,
     FILE_ATTRIBUTE_ENCRYPTED = 0x00004000,
     FILE_ATTRIBUTE_VALID_FLAGS = 0x00007fb7,
     FILE_ATTRIBUTE_VALID_SET_FLAGS = 0x000031a7,
 }