CSharp Tips:调用Win32 API如何处理指针类型的参数

 

0 、前言
从VB到C#,被人诟病比较多的就是交互性比较差,又集中表现在调用Win32 API上。如果说C/C++调用API只是调用函数这类轻松的活,在C#下却成了阻挡入门者的技术活。之所以产生这么大区别在于数据类型的差异,就是因为C#这类采用了“安全”的类型,我们避免了内存释放和内存访问错误的一些困扰,但是不得不面对调用API时的繁琐。有得必有失,关键看你选择了什么。
在调用API时,对于值类型的数据,不存在什么转换问题,只要搞清楚到底是Byte、Int16、Int32 还是Int64就可以了,比较麻烦的地方是指针,因为C#中没有办法显性的使用指针,有时需要借助unsafe code达到这个目的。如果都“unsafe”了,那还用C#干吗,本文的目的就是总结一下,怎样用“safe”的方式解决Win32 API中指针类型参数的问题。
 
1、  基本原则
在我们在调用API时,如果发现参数中有指针类型的时候,不要简单的用IntPtr去替换,或者直接就是用*来定义。虽然C#中能够使用指针,但是这样做就违背了C#设计时的初衷,此外DotNET Framework平台下使用unsafe代码多少会影响应用程序的效率。
当我们拿到一个API,阅读API的说明时,一定要关注以下几点:
l 每一个参数的数据类型是什么?如果是指针,指针指向的是一个什么数据结构,基本数据类型、字符串、结构还就是一块内存。不同的类型在C#下处理的模式是不同的。
l 指针所指向的数据结构是谁创建,该由谁释放?这也非常重要,它两层含义:一个是我们怎么定义接口,并且准备调用参数;另一个就是资源释放的问题,某些调用这申请,被调用这释放的资源,需要约定的方法申请或释放资源,反之亦然。
只要花点时间分析一下,就会发现即便是在复杂的结构,不用“unsafe code”也能够完成调用,只不过有时候过程有点繁琐,不如C/C++调用API那么畅快淋漓。但是我想说的是,如果选择了C#,那么就是C#的思想去解决问题,这样才能够发挥出C#所有的潜力。
 
2 、实例分析
了解了基本原则,下面就逐一分析一下怎样雅致并且“安全”地解决不同类型指针的调用问题。
2.1、           字符串
字符串应该是我们接触到最多的情况,一般在API定义中被描述为“LPSTR/LPTSTR/LPCTSTR/LPWSTR”之类,我们在申明API接口的时候,如果是传入类型的参数,直接用String类型申明即可,例如:
         ///<summary>
        
///原型是:HMODULE LoadLibrary(LPCTSTR lpFileName); 
        
///</summary>
        
///<param name="lpFileName">DLL 文件名</param>
        
///<returns>函数库模块的句柄</returns>

        [DllImport( " kernel32.dll " )]
        
public   static   extern  IntPtr LoadLibrary( string  lpFileName);
但是如果是传出类型的字符串参数,简单的这么写就不行了。我的理解是String变成LPSTR,是DotNET Framework的交互接口帮我们做了一次转换,创建了一个字符数组,将我们提供的String复制了一次,再传递给API,并非简单的指针传递,所以当我们要求在我们设定的一个地址区域去写数据时,就不能够直接申明为String,而应该是Byte或者Char数组,可以参考下面的例子:
函数声明:
         ///<summary>
        
/// int GetClassName(HWND hWnd, LPTSTR lpClassName, int nMaxCount); 
        
///</summary>

        [DllImport( " user32 " ,CharSet = CharSet.Ansi)]
        
public   static   extern  Int32 GetClassName(IntPtr hwnd, Byte[] lpClassName, Int32 nMaxCount);
         调用事例:
         String sClassName  =   null ;
         Byte[] abClassName 
=   null ;
         Int32 dwRet 
=   0 ;
 
         abClassName 
=   new  Byte[ 100 ];
         dwRet 
=  GetClassName( this .Handle, abClassName,  100 );
         sClassName 
=  System.Text.ASCIIEncoding.ASCII.GetString(abClassName, 0 ,dwRet);
         MessageBox.Show(sClassName);
还需要注意一点的就是Ansi还是Unicode的字符集了,申明的是什么就用什么转换。
 
  • 1
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 17
    评论
使用 C# 调用 Win32 API 创建新进程并返回进程句柄的详细方法如下: 1. 首先需要设置一个 StartInfo 对象,用于存储启动进程的相关信息,如进程名称、命令行参数等。 ```csharp ProcessStartInfo startInfo = new ProcessStartInfo(); startInfo.FileName = "notepad.exe"; // 进程名称 startInfo.Arguments = "test.txt"; // 命令行参数 ``` 2. 创建一个 Process 对象,并将 StartInfo 对象赋值给它。 ```csharp Process process = new Process(); process.StartInfo = startInfo; ``` 3. 调用 Win32 API 的 CreateProcess 方法,创建新进程并返回进程句柄。 ```csharp bool success = CreateProcess( startInfo.FileName, startInfo.Arguments, IntPtr.Zero, IntPtr.Zero, false, 0, IntPtr.Zero, null, ref startupInfo, out PROCESS_INFORMATION processInfo); if (success) { process.Handle = processInfo.hProcess; process.Id = processInfo.dwProcessId; } ``` 其中,CreateProcess 方法的参数含义如下: - lpApplicationName:要执行的可执行文件的名称,如果指定了完整路径,则 lpCommandLine 参数可以为 null。 - lpCommandLine:要传递给可执行文件的命令行参数。 - lpProcessAttributes:进程句柄的安全属性,通常设置为 IntPtr.Zero。 - lpThreadAttributes:线程句柄的安全属性,通常设置为 IntPtr.Zero。 - bInheritHandles:指定是否从父进程继承句柄,通常设置为 false。 - dwCreationFlags:指定进程的创建标志,通常设置为 0。 - lpEnvironment:进程的环境变量,通常设置为 IntPtr.Zero。 - lpCurrentDirectory:进程的当前工作目录,通常设置为 null。 - lpStartupInfo:启动信息,包括窗口大小、标题等。 - lpProcessInformation:输出参数,包括进程句柄和进程 ID。 4. 最后可以使用 Process 对象的方法来操作新创建的进程,如 WaitForExit() 等。 ```csharp process.WaitForExit(); ``` 完整的代码示例: ```csharp using System; using System.Diagnostics; using System.Runtime.InteropServices; namespace CreateProcessDemo { class Program { static void Main(string[] args) { // 设置启动信息 ProcessStartInfo startInfo = new ProcessStartInfo(); startInfo.FileName = "notepad.exe"; startInfo.Arguments = "test.txt"; // 创建进程并返回进程句柄 Process process = new Process(); process.StartInfo = startInfo; bool success = CreateProcess( startInfo.FileName, startInfo.Arguments, IntPtr.Zero, IntPtr.Zero, false, 0, IntPtr.Zero, null, ref startupInfo, out PROCESS_INFORMATION processInfo); if (success) { process.Handle = processInfo.hProcess; process.Id = processInfo.dwProcessId; // 等待进程结束 process.WaitForExit(); } } // 声明 CreateProcess 方法 [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] static extern bool CreateProcess( string lpApplicationName, string lpCommandLine, IntPtr lpProcessAttributes, IntPtr lpThreadAttributes, bool bInheritHandles, uint dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation); // 定义 STARTUPINFO 结构体 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] struct STARTUPINFO { public uint cb; public string lpReserved; public string lpDesktop; public string lpTitle; public uint dwX; public uint dwY; public uint dwXSize; public uint dwYSize; public uint dwXCountChars; public uint dwYCountChars; public uint dwFillAttribute; public uint dwFlags; public short wShowWindow; public short cbReserved2; public IntPtr lpReserved2; public IntPtr hStdInput; public IntPtr hStdOutput; public IntPtr hStdError; } // 定义 PROCESS_INFORMATION 结构体 [StructLayout(LayoutKind.Sequential)] struct PROCESS_INFORMATION { public IntPtr hProcess; public IntPtr hThread; public uint dwProcessId; public uint dwThreadId; } } } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值