公共语言运行库(CLR)开发系列课程(2):Pinvoke 进阶 学习笔记
上一章地址
API版本
具有字符串参数的API通常有两种版本
- GetWindowText
- GetWindowTextA
- GetWindowTextW
- GetWindowText
缺省情况下CLR会自动寻找合适的匹配
- CharSet=Ansi时寻找A版本
- CharSet=Unicode寻找W版本
- CharSet=Auto和平台相关
DLLImportAttribute(ExactSpelling=true)会关掉这种行为
Ref/Out参数 (C#关键字)
out=[Out]&
- C#:out int a
- IL:[Out] int &a
- C++:int *pa;
ref=[In,Out]&
- C#:ref int a
- IL:[In,Out] int &a
- C++:int *pa;
个人感觉 使用Ref/Out 参数P/Invoke CRL会对根据对应关键字添加对应IL特性 System.Runtime.InteropServices下面的InAttribute、OutAttribute
引用类型和值类型
非托管C/C++ 代码:struct A{int a;}
托管代码:struct A{int a;} class A{int a;}
非托管C/C++代码 A A* A** A为值类型 A ref A IntPtr a A为引用类型 N/A A ref A
自定义参数转换
托管类型和非托管类型之间是一对多关系
- String
- char */wchar_t *
- char[N]/wchar_t[N] 固定长度的字符串
- BSTR, Ansi BSTR
- String
- MarshalAsAttribute(UnmanagedType)
- UnmanagedType 枚举指定对应非托管类型
- [MarshalAsAttribute(UnmanagedType.LPWSTR)] string A
- 非托管函数:void Func(wchar_t *)
- 托管函数:Public static extern void Func([MarshalAs(UnmanagdType.LPWSTR)] string);
内存管理
在数据转换中需要进行内存分配
举例:
- C#调用Func(string) Marshal.StringToHGlobalAnsi("balabala")
- C#调用Func(ref string)
- C#调用string Func()
CoTaskMemAlloc / CoTaskMemFree (分配/释放 内存) 内存交换才使用
实例分析CreateProcess
API原型
BOOL WINAPI CreateProcess(
_In_opt_ LPCTSTR lpApplicationName,
_Inout_opt_ LPTSTR lpCommandLine,
_In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
_In_ BOOL bInheritHandles,
_In_ DWORD dwCreationFlags,
_In_opt_ LPVOID lpEnvironment,
_In_opt_ LPCTSTR lpCurrentDirectory,
_In_ LPSTARTUPINFO lpStartupInfo,
_Out_ LPPROCESS_INFORMATION lpProcessInformation
);
(opt 可选的输入参数)
C#
Public static extern bool CreateProcess(
[In] string applicationName,
[In,Out] StringBuilder commandName,
[In] ref SECURITY_ATTRIBUTES processAttributes,
[In] ref SECURITY_ATTRIBUTES threadAttributes,
[In] bool inheritHandles,
[In] uint creationFlags,
[In] IntPtr lpEnvironment,
[In] string currentDirectory,
[In] ref STARTUP_INFO startupInfo,
[Out] out PROCESS_INFORMATION processInformation
);
DLL生命周期
Dll在第一次调用的时候自动加载,不会自动被释放,加载不到或者找不到函数就会抛出异常
Dll不会自动被卸载,建议做法生命一个外部调用类 DLL会伴随外部调用类一起被清理卸载
如果需要手段管理DLL生命周期,只能手动调用LoadLibrary/GetProcAddress
逆向P/Invoke
非托管代码到托管代码这种叫逆向P/Invoke
部分API接收函数指针作为参数
EnumWindows(WNDENUMPROC IpEnumFunc,LPARAM lPararm)
.NET 程序可以通过P/Invoke调用 EnumWindows API
CLR能够将Delegate 转换为函数指针,EnumWindows 来回调这个函数指针
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
delegate bool EnumWindowProc(IntPtr hwnd, IntPtr lParam);
EnumWindow(new EnumWindowProc(MyEnumWindow), IntPtr.Zero);
DEMO
class Program
{
/// Return Type: BOOL->int
///hwnd: HWND->HWND__*
///lParam: LPARAM->LONG_PTR->int
[UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)]
public delegate bool WNDENUMPROC(IntPtr hwnd, IntPtr lParam); class NativeMethods
{
[DllImport("User32.dll",CallingConvention=CallingConvention.Winapi)]
public static extern int GetWindowText(IntPtr hwnd, StringBuilder text, int count); /// Return Type: BOOL->int
///lpEnumFunc: WNDENUMPROC
///lParam: LPARAM->LONG_PTR->int
[DllImportAttribute("user32.dll", EntryPoint = "EnumWindows")]
[return: MarshalAsAttribute(UnmanagedType.Bool)]
public static extern bool EnumWindows(WNDENUMPROC lpEnumFunc, int lParam);
} private static bool WNDENUMPROC_Callback(IntPtr hwnd, IntPtr lParam)
{
StringBuilder text = new StringBuilder();
NativeMethods.GetWindowText(hwnd, text, );
Console.WriteLine("窗口名:" + text + ",id=" + hwnd.ToInt32() + ",lParam=" + lParam);
return true;
}
static void Main(string[] args)
{
WNDENUMPROC myWNDENUMPROC = new WNDENUMPROC(WNDENUMPROC_Callback);
NativeMethods.EnumWindows(myWNDENUMPROC,);
Console.Read();
} }常见问题
抛出DllNotFoundException
找不到DLL一般他会到当前目录去找 或者系统目录去找 DLL如果实在无法差错可以使用全路径+dll名不推荐最好是放相对路径
抛出AccessViolationException
访问违规,一般是内存不可以读不可以写或者一起读一起写造成的,通常原因参数转换错误,类型不对应,长度问题。
抛出EntryPointNotFoundException
API方法入口找不到一般都是方法名写错或者调用协定不对
MDA报错StackImbalanceMDA
VS提示报错,参数错位调用协定不对
返回数据错误
参数是否正确,MarshalAs转换是否成功,开启非托管调试查看。
进阶DEMO
- c++
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <Windows.h>
/*自定义*/
typedef struct Mytype
{
int i;
char *s;
double d;
struct Mytype *p;
}Mytype; int TestInt(int a,int b)
{
return a + b;
}
void TestIntPtr(int *i)
{
*i = *i * -;
}
char * TestCharPtr(char *a,char *b)
{
return strcat(a, b);
}
void TestStructPtr(Mytype *p)
{
p->i++;
p->d++;
}
void TestPtrPtr(int **a,int length)
{
*a = (int*)malloc(sizeof(int)*length);
memset(*a, , sizeof(int)* length);
}
void TestArray(int *a, int length)
{
for (int i = ; i < length; i++)
{
a[i]++;
}
} void TestCallback(void(*pf)())
{
pf();
}
int TestCharWidth(char *s)
{
return strlen(s);
} 文件名 Source.cppLIBRARY "PInvoke"
EXPORTS
TestInt
TestIntPtr
TestCharPtr
TestStructPtr
TestPtrPtr
TestArray
TestCallback
TestCharWidth
文件名 Source.def - C#
class Program
{ [StructLayoutAttribute(LayoutKind.Sequential)]
public struct Mytype
{ /// int
public int i; /// char*
[MarshalAsAttribute(UnmanagedType.LPStr)]
public string s; /// double
public double d; /// Mytype*
public System.IntPtr p;
} /// Return Type: void
public delegate void Callback(); static void CallbackFunction()
{
Console.WriteLine("callback invoked");
}
/// Return Type: int
///a: int
///b: int
[DllImportAttribute("Pinvoke.dll", EntryPoint = "TestInt", CallingConvention = CallingConvention.Cdecl)]
public static extern int TestInt(int a, int b); /// Return Type: void
///i: int*
[DllImportAttribute("Pinvoke.dll", EntryPoint = "TestIntPtr", CallingConvention = CallingConvention.Cdecl)]
public static extern void TestIntPtr(ref int i); /// Return Type: char*
///a: char*
///b: char*
[DllImportAttribute("Pinvoke.dll", EntryPoint = "TestCharPtr", CallingConvention = CallingConvention.Cdecl)]
public static extern System.IntPtr TestCharPtr(System.IntPtr a, System.IntPtr b); /// Return Type: void
///p: Mytype*
[DllImportAttribute("Pinvoke.dll", EntryPoint = "TestStructPtr", CallingConvention = CallingConvention.Cdecl)]
public static extern void TestStructPtr(ref Mytype p); /// Return Type: void
///a: int**
///length: int
[DllImportAttribute("Pinvoke.dll", EntryPoint = "TestPtrPtr", CallingConvention = CallingConvention.Cdecl)]
public static extern void TestPtrPtr(ref System.IntPtr a, int length); /// Return Type: void
///a: int*
///length: int
[DllImportAttribute("Pinvoke.dll", EntryPoint = "TestArray", CallingConvention = CallingConvention.Cdecl)]
public static extern void TestArray(int[] a, int length); /// Return Type: void
///pf: Anonymous_5afb5371_1680_4be9_99a9_ab5bd7ded029
[DllImportAttribute("Pinvoke.dll", EntryPoint = "TestCallback", CallingConvention = CallingConvention.Cdecl )]
public static extern void TestCallback(Callback pf); /// Return Type: int
///s: char*
[DllImportAttribute("Pinvoke.dll", EntryPoint = "TestCharWidth", CallingConvention = CallingConvention.Cdecl)]
public static extern int TestCharWidth( IntPtr s); static void Main(string[] args)
{ Console.WriteLine("TestInt:" + TestInt(, )); // TestIntPtr
int i = ;
TestIntPtr(ref i);
Console.WriteLine("TestIntPtr:" + i); // TestCharPtr
IntPtr helloPtr = Marshal.StringToHGlobalAnsi("啊阿斯达斯的阿萨德+");
IntPtr worldPtr = Marshal.StringToHGlobalAnsi("+阿萨德阿萨德");
IntPtr helloWorldPtr = TestCharPtr(helloPtr, worldPtr);
string helloWorld = Marshal.PtrToStringAnsi(helloWorldPtr);
Console.WriteLine("TestCharPtr:" + helloWorld); //helloPtr = Marshal.StringToHGlobalUni("啊阿斯蒂芬a");
//worldPtr = Marshal.StringToHGlobalUni("阿萨德阿萨德");
//helloWorldPtr = TestCharPtr(helloPtr, worldPtr);
//helloWorld = Marshal.PtrToStringUni(helloWorldPtr);
//Console.WriteLine("TestCharPtr01:" + helloWorld);
// Marshal.FreeCoTaskMem()
Marshal.FreeHGlobal(helloPtr);
Marshal.FreeHGlobal(worldPtr); // Marshal.FreeHGlobal(helloWorldPtr); // 因为helloWorldPtr和helloPtr指向的是同一地址,所以再次释放会报错 // TestCharWidth
string a = "a的";
IntPtr aPtr = Marshal.StringToHGlobalAnsi(a); // Ansi
int len = TestCharWidth(aPtr);
Console.WriteLine("TestCharWidth:" + len);
a = Marshal.PtrToStringAnsi(aPtr);
Marshal.FreeHGlobal(aPtr); aPtr = Marshal.StringToHGlobalUni(a); // Unicode
len = TestCharWidth(aPtr); // 值是1,strlen没有正确处理unicode,所以不要使用strlen测量unicode字符串的长度
Console.WriteLine("TestCharWidth:" + len);
a = Marshal.PtrToStringUni(aPtr);
Marshal.FreeHGlobal(aPtr); // TestStructPtr
Mytype myType = new Mytype { i = , d = 1.1, s = a, p = IntPtr.Zero };
TestStructPtr(ref myType);
Console.WriteLine("Mytype->i:" + myType.i);
Console.WriteLine("Mytype->d:" + myType.d); // TestArray
int[] array = new int[] { , , };
TestArray(array, array.Length); for (int z = ; z < array.Length; z++)
{
Console.WriteLine("array[{0}]" + array[z], z);
} // TestCallback
TestCallback(CallbackFunction); Console.Read();
} }
- c++
公共语言运行库(CLR)开发系列课程(2):Pinvoke 进阶 学习笔记的更多相关文章
- 公共语言运行库(CLR)开发系列课程(1):Pinvoke 简介 学习笔记
前言 让拖管代码对象和非托管对象协同工作的过程称为互用性(Interoperability),通常简称为 Interop. P/Invoke在托管代码与非托管代码交互式时产生一个事务(Transiti ...
- 公共语言运行库(CLR)开发系列课程(3):COM Interop基础 学习笔记
上章地址 什么是COM Component Object Model 组建对象模型 基于接口(Interface) 接口=协议 IID 标识接口 V-table 虚表 方式调用 单继承 对象(Obje ...
- CLR 公共语言运行库
1..支持多语言..只是语言是面向CLR的..均可以在此基础上运行. 2..程序集加载..程序打包之后的Dll文件由CLR(公共语言运行库)来编译并加载到可以执行状态..由CLR(公共语言运行库)加载 ...
- 公共语言运行库(CLR)和中间语言(IL)(一)
公共语言运行库(.net运行库)即CLR 1.C#先编译为IL,IL为ms的中间语言,IL是平台无关性的. 2.CLR再将IL编译为平台专用语言. 3.CLR在编译IL时为即时编译(JIT) VB.V ...
- Silverlight for Windows Phone开发系列课程
Silverlight for Windows Phone开发系列课程(1):Windows Phone平台概况 课程简介:本节开始介绍系列课程的概况,包括课程内容,先决条件,学习目的 ...
- ASP.NET MVC框架开发系列课程 (webcast视频下载)
课程讲师: 赵劼 MSDN特邀讲师 赵劼(网名“老赵”.英文名“Jeffrey Zhao”,技术博客为http://jeffreyzhao.cnblogs.com),微软最有价值专家(ASP.NET ...
- CLR(Common Language Runtime) 公共语言运行库
.NET Core 使用 CoreCLR .NET Framework 使用CLR. 1. 将代码编译为IL (Intermediate Language) 2. CLR 把IL 编译为平台专用的本地 ...
- iOS开发系列课程预告
近期在Mac和iOS上做开发,认为应该写一点东西分享给感兴趣的童鞋们.在此之前.以前有非常多同行们都在埋怨苹果Objective-C的复杂和难以上手,为此也有非常多人对今年(2014年)刚推出的Swi ...
- 【VS开发】Windows平台下Makefile学习笔记
作者:朱金灿 来源:http://blog.csdn.net/clever101 决心学习Makefile,一方面是为了解决编译开源代码时需要跨编译平台的问题(发现一些开源代码已经在使用VS2010开 ...
随机推荐
- alpha阶段的 postmortem 报告
1. 每个成员到了第二次alpha 阶段与第一次相比,取得什么进步? 成员 黄杰 学会了app环境的搭建和代码的基本理解 李炫宗 更加明白安卓代码的编写和理解 康取 对安卓界面的设计有一些了解 ...
- ElasticSearch 2 (33) - 信息聚合系列之聚合过滤
ElasticSearch 2 (33) - 信息聚合系列之聚合过滤 摘要 聚合范围限定还有一个自然的扩展就是过滤.因为聚合是在查询结果范围内操作的,任何可以适用于查询的过滤器也可以应用在聚合上. 版 ...
- Apache修改了配置文件中的路径后,登录该地址网页出现问题 (其中介绍了selinux的域和安全上下文)
默认的网站数据存放在: /var/www/html 首页名称: index.html Apache服务程序的主配置文件: /etc/httpd/conf/httpd.conf (若是将 119 行 ...
- Mybatis返回HashMap时,某个字段值为null时,不会保存key
转载: http://blog.csdn.net/little2z/article/details/38525327 mybatis 的 callSettersOnNulls 问题项目用到mybati ...
- Day12-navicat for sqlite 11.1.12 patch 永久使用版
参考来源:http://www.cnblogs.com/yueyue184/p/6407963.html 深深感谢!!! 因为最近需要用这个但是网上都是注册机没有成功注册,所以就自己动手使用ollyd ...
- 认清Android框架 MVC,MVP和MVVM
编者按:现在很多时候,我们都是面向搜索(或 Google 或百度).GitHub 编程,那么,在早期没有互联网的情况下,该如何学习编程,成为一名真正的开发者?亦或是作为一名小白,如何进入互联网编程时代 ...
- aws上部署zabbix3.4
三台机器 10.0.0.149 AmazonLinux2.0 zabbix-server zabbix-agent 10.0.1.61 CentOS6.9 zabbix-agent 10.0.1.11 ...
- java web 验证码-字符变形(推荐)
该文章转载自:http://www.cnblogs.com/jianlun/articles/5553452.html 因为在我做的这个系统中发现验证码有点偏上,整体效果看起来不太好,就做了一些修改. ...
- 异步IO的并发能力:backlog的配置很重要
今天中午正准备完工的时候,发现一个让人抓狂的问题. 一个精简版的AIO应用理论上应该比一个完整版的AIO应用并发能力高一些(因为完整版的事务处理复杂一些),在同一台机器上测试. 但测试结果显示,精简版 ...
- 纯干货!一款APP从设计稿到切图过程全方位揭秘(转)
@BAT_LCK :我本身是一名GUI设计师,所以我只站在GUI设计师的角度去把APP从项目启动到切片输出的过程写一写,相当于工作流程的介绍吧.公司不同,流程不尽相同,但是终究还是能有些帮助. 依旧声 ...