博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[C#/C++]C#调用非托管DLL的APIs
阅读量:6350 次
发布时间:2019-06-22

本文共 3850 字,大约阅读时间需要 12 分钟。

  
上网baidu一下或google一下这个东东就有很多人在问这个问题,最近我也用到了这个,所以就留下来以备往后需要是可以查找。我想通过这个来作为C#调用windows APIs的出发点,在以后的随笔当中介绍一下我现阶段用到的一些APIs或非托管类库。在调用非托管DLL的APIs前,我们应该好好掌握一下DllImportAttribute,MSDN给出的定义为:可将该属性应用于方法。DllImportAttribute 属性提供对从非托管 DLL 导出的函数进行调用所必需的信息。作为最低要求,必须提供包含入口点的 DLL 的名称。
1 
[DllImport(
"
KERNEL32.DLL
"
, EntryPoint
=
"
MoveFileW
"
,     SetLastError
=
true
,
2 
3 
CharSet
=
CharSet.Unicode, ExactSpelling
=
true
,
4 
5 
CallingConvention
=
CallingConvention.StdCall)]
6 
7 
public
 
static
 
extern
 
bool
 MoveFile(String src, String dst);
8 
9 
    从上面的例子中我们可以看出,从Kernel32.dll中引入这个API,其中EntryPoint一看就知道是入口点,也就是DLL中的函数名称。其实只要用过VC++的人都知道,Windows APIs中都提供两个版本,一个是W,一个是A也就是Ansi和Unicode之分,现在一般都采用W,Unicode编程,但是.Net和win32交互的时候,默认是使用CharSet.Ansi来传送。 在 DllImportAttribute.ExactSpelling 字段为 true 时(它是 Visual Basic .NET 中的默认值),平台调用将只搜索您指定的名称。例如,如果指定 MessageBox,则平台调用将搜索 MessageBox,如果它找不到完全相同的拼写则失败。 当 ExactSpelling 字段为 false(它是 C++ 托管扩展和 C# 中的默认值),平台调用将首先搜索未处理的别名 (MessageBox),如果没有找到未处理的别名,则将搜索已处理的名称 (MessageBoxA)。
    由于很多参数和C#中有所区别,下面让我们看看参数是怎么个情况:
    extern “C” __declspec(dllexport)  int WINAPI sumAB(int a,int b)这个和C#中一样是值类型,a及b的变化不会对C#中的参数造成影响。通过DllImport表示为:
1 
[DllImport(“CppDll.dll
"
)]
2 
//
返回个int 类型
3 
public
 
static
 
extern
 
int
 sumAB (
int
 a1,
int
 b1); 
    下面的是引用类型:也就是说a和b的变化直接影响到a1和b1的值。
public
 
static
 
extern
 
int
 sum (
ref
 
int
 a1,
ref
 
int
 b1);
//
DLL中申明
extern
 “C” __declspec(dllexport)  
int
 WINAPI sum(
int
 
*
a,
int
 
*
b) 
    同理我们可以得到一下几种传值方法:第一种参数的改变不影响C#中参数的变化,需要传入char*类型。
[DllImport(“CppDll.dll
"
)]
//
传入值
public
 
static
 
extern
 
int
 together (
string
  astr,
string
 bstr);
//
DLL中申明
extern
 “C” __declspec(dllexport)  
int
 WINAPI together(
char
 
*
 stra,
char
 
*
 strb) 
    当我们需要从参数中传出char*类型是,那我们就要用到StringBuilder了。
1 
//
 传出值
2 
public
 
static
 
extern
 
int
 getParameter (StringBuilder sb1, StringBuilder sb2);
3 
//
DLL中申明
4 
extern
 “C” __declspec(dllexport)  
int
 WINAPI getParameter(
char
 
*
 stra,
char
 
*
 strb) 
    
    接着我们先来看看结构的封送,这里我们要看看StructLayoutAttribute这个标签,通过它可以定义自己的格式化类型,在托管代码中,格式化类型是一个用StructLayoutAttribute说明的结构或类成员,通过它能够保证其内部成员预期的布局信息,他的成员说明如下:
     LayoutKind.Automatic 为了提高效率允许运行态对类型成员重新排序。 注意:永远不要使用这个选项来调用非托管的动态链接库函数。
     LayoutKind.Explicit 对每个域按照FieldOffset属性对类型成员排序
     LayoutKind.Sequential 对出现在托管类型定义地方的非托管内存中的类型成员进行排序。
    我们可以通过GetSystemInfo来获取系统的信息,代码如下:
 1 
using
 System.Runtime.InteropServices;
 2 
[StructLayout(LayoutKind.Sequential)]
 3 
    
public
 
struct
 SYSTEM_INFO {
 4 
    
public
 
uint
 dwOemId;
 5 
    
public
 
uint
 dwPageSize;
 6 
    
public
 
uint
 lpMinimumApplicationAddress;
 7 
    
public
 
uint
 lpMaximumApplicationAddress;
 8 
    
public
 
uint
 dwActiveProcessorMask;
 9 
    
public
 
uint
 dwNumberOfProcessors;
10 
    
public
 
uint
 dwProcessorType;
11 
    
public
 
uint
 dwAllocationGranularity;
12 
    
public
 
uint
 dwProcessorLevel;
13 
    
public
 
uint
 dwProcessorRevision;
14 
}
15 
//
调用
16 
[DllImport(
"
kernel32
"
)]
17 
static
 
extern
 
void
 GetSystemInfo(
ref
 SYSTEM_INFO pSI);
18 
SYSTEM_INFO pSI 
=
 
new
 SYSTEM_INFO();
19 
GetSystemInfo(
ref
 pSI);
    当我们遇到函数指针参数是我们就需要考虑是否要用到回调函数,例如枚举所有窗口。
BOOL EnumWindows(WNDENUMPROC lpEnumFunc, LPARMAM IParam)
    我们可以通过创建一个代理,它带有两个参数hwnd和lparam,第一个参数是一个窗口句柄,第二个参数由应用程序定义,两个参数均为整形。当这个回调函数返回一个非零值时,标示执行成功,零则暗示失败,这个例子总是返回True值,以便持续枚举。 最后创建以代理对象(delegate),并把它作为一个参数传递给EnumWindows 函数,平台会自动地 把代理对象转化成函数能够识别的回调格式。 
 1 
using
 System;
 2 
using
 System.Runtime.InteropServices;
 3 
public
 
delegate
 
bool
 CallBack(
int
 hwnd, 
int
 lParam);
 4 
public
 
class
 EnumReportApp {
 5 
[DllImport(
"
user32
"
)]
 6 
public
 
static
 
extern
 
int
 EnumWindows(CallBack x, 
int
 y);
 7 
public
 
static
 
void
 Main()
 8 
{
 9 
CallBack myCallBack 
=
 
new
 CallBack(EnumReportApp.Report);
10 
EnumWindows(myCallBack, 
0
);
11 
}
12 
public
 
static
 
bool
 Report(
int
 hwnd, 
int
 lParam) {
13 
Console.Write(
"
窗口句柄为
"
);
14 
Console.WriteLine(hwnd);
15 
return
 
true
;
16 
}
17 
}

    OK,如果熟悉了以上方方面面,基本上也能够调用APIs了别忘了P/Invoke能够帮上很大的忙,我们可以去wiki网站查询我们所要的API:http://pinvoke.net。还需要说明的是很多例子等都来自MSDN和网上检索得到的!!!

本文转自网魂小兵博客园博客,原文链接:http://www.cnblogs.com/xdotnet/archive/2007/08/10/csharp_cpp_dll_pinvoke.html,如需转载请自行联系原作者

你可能感兴趣的文章
界面设计模式(第2版)(全彩)
查看>>
解决VMware Workstation错误:未能锁定文件
查看>>
CentOS6 手动编译升级 gcc
查看>>
memcached的安装与开启脚本
查看>>
zabbix 邮件报警 -- sendmail
查看>>
JavaScript异步编程
查看>>
tcpdump用法小记
查看>>
MySQL基础安全注意细节
查看>>
Oracle随机函数—dbms_random
查看>>
pvr 批量转换
查看>>
linux命令basename使用方法
查看>>
windows下开发库路径解决方案
查看>>
linux迁移mysql数据目录
查看>>
脚本源码安装LNMP
查看>>
Percona Server安装
查看>>
函数为左边表达式
查看>>
2015.06.04 工作任务与心得
查看>>
icinga2使用587端口发邮件
查看>>
hpasmcli查看HP服务器内存状态
查看>>
极客工具
查看>>