使用 DllImport 属性
本主题说明 DllImport 属性的常见用法。第一节讨论使用 DllImport 从托管应用程序调用本机代码的优点。第二节集中讨论封送处理和 DllImport 属性的各个方面。
从托管应用程序调用非托管代码
当在托管应用程序中重用现有的非托管代码时,DllImport 属性非常有用。例如,托管应用程序可能需要调用非托管 WIN32 API。
下面的代码示例说明此通用方案,此示例将调用 MessageBox(位于 User32.lib 中):
#using <mscorlib.dll>
using namespace System::Runtime::InteropServices;
// for DllImportAttribute namespace SysWin32
{
[DllImport("user32.dll", EntryPoint = "MessageBox", CharSet = Unicode)]
int MessageBox(void* hWnd, wchar_t* lpText, wchar_t* lpCaption,
unsigned int uType);
} int main( )
{
SysWin32::MessageBox( 0, L"Hello world!", L"Greetings", 0 );
}
主要注意包含 DllImport 的代码行。此代码行根据参数值通知编译器,使之声明位于 User32.dll 中的函数并将签名中出现的所有字符串(如参数或返回值)视为 Unicode 字符串。如果缺少 EntryPoint参数,则默认值为函数名。另外,由于 CharSet 参数指定 Unicode,因此公共语言运行库将首先查找称为 MessageBoxW(有 W 是因为 Unicode 规范)的函数。如果运行库未找到此函数,它将根据调用约定查找 MessageBox 以及相应的修饰名。受支持的调用约定只有 __cdecl 和 __stdcall。
当调用用户定义的 DLL 中所包含的函数时,有必要将 extern "C" 添加在 DLL 函数声明之前,如下所示:
// The function declaration in SampleDLL.h file
extern "C" SAMPLEDLL_API int fnSampleDLL(void);
有关受支持的其他参数值的更多信息,请参见 DllImport。
将非结构化参数由托管封送处理为非托管
除使用上述方法外,还可以使用另一种方法将托管参数(来自托管应用程序)封送处理为非托管参数(在非托管 DLL 中)。
以下代码示例说明封送处理技术:
#using <mscorlib.dll>
using namespace System; // To bring System::String in
using namespace System::Runtime::InteropServices;
// for DllImportAttribute
namespace SysWin32
{
[DllImport("user32.dll", EntryPoint = "MessageBox", CharSet = Unicode)]
Int32 MessageBox( Int32 hWnd, String* lpText, String* lpCaption,
UInt32 uType );
} int main( )
{
SysWin32::MessageBox(0, S"Hello world!", S"Greetings", 0);
}
完成实际的调用后,由于 CharSet 参数值的作用,所有参数字符串都自动转换为 wchar_t*。同样,所有 Int32 参数类型都转换为非托管 int,而 UInt32 参数类型转换为非托管 unsigned int。
下表提供关于转换非托管和托管上下文的指导:
| 非托管代码 | C++ 的托管扩展 |
|---|---|
| int | Int32 |
| unsigned int | UInt32 |
| short | Int16 |
| char* | 用于 [in] 参数的 String* (CharSet = Ansi),用于 [out] 参数或返回值的 Text::StringBuilder*。 |
| wchar_t* | 用于 [in] 参数的 String* (CharSet = Unicode),用于 [out] 参数或返回值的 Text::StringBuilder*。 |
| 函数指针(回调) 限制:函数指针必须具有 __stdcall 调用约定,因为这是 DllImport 支持的唯一类型。 |
委托类型 |
| 数组(如 wchar_t*[]) 限制:CharSet 参数仅应用于函数参数的根类型。因此,无论 CharSet 的值是什么,String* __gc[] 都将被封送处理为 wchar_t* []。 |
相应类型的托管数组(如 String*__gc[]) |
将结构化类型由非托管封送处理为托管
除简单类型外,运行库还提供了一种机制,可以将简单结构由托管上下文封送处理为非托管上下文。简单结构不包含任何内部数据成员指针、结构化类型的成员或其他元素。
例如,本主题显示如何调用本机 DLL 中具有以下签名的函数:
#include <stdio.h>
struct S
{
char* str;
int n;
}; int __cdecl func( struct S* p )
{
printf( "%s\n", p->str );
return p->n;
}
若要创建此函数的托管包装,请将 StructLayout 属性应用到调用类。此属性确定封送处理结构时结构的组织方式。若要确保以传统的 C 格式组织结构,请指定顺序布局 (LayoutKind::Sequential)。结果代码如下:
#using <mscorlib.dll>
using namespace System;
using namespace System::Runtime::InteropServices; // CharSet = Ansi(Unicode) means that everything that is a string
// in this structure should be marshaled as Ansi(Unicode)
// strings
[StructLayout( LayoutKind::Sequential, CharSet=Ansi )]
__gc class MS // To be compatible with the type in the native
// code, this structure should have the members laid out in
// the same order as those in the native struct
{
public:
String* m_str;
Int32 m_n;
}; [DllImport("some.dll")]
Int32 func( MS* ptr );
int main( )
{
MS* p = new MS;
p->m_str = S"Hello native!";
p->m_n = 7;
Console::WriteLine(func(p)); // Should print 7
}
也可以在托管应用程序中使用 __nogc 关键字,以确保不发生封送处理:
#include <stdlib.h>
#include <string.h>
#using <mscorlib.dll>
using namespace System;
using namespace System::Runtime::InteropServices;
__nogc class UMS
{
public:
char* m_str;
int m_n;
};
[DllImport("some.dll")]
Int32 func( UMS* ptr );
int main( )
{
UMS* p = new UMS;
p->m_str = strdup( "Hello native!" );
p->m_n = 7;
Console::WriteLine(func(p)); // Should print 7
free( p->m_str );
delete p;
}
第二个方案是:
#include <stdio.h>
struct S
{
wchar_t* str;
int n;
};
int __cdecl func( struct S p )
{
printf( "%S\n", p.str );
return p.n;
}
注意参数是通过值传递的。若要在托管应用程序中包装此调用,请使用值而不是 __gc 类型。结果代码如下:
#using <mscorlib.dll>
using namespace System;
using namespace System::Runtime::InteropServices;
[StructLayout( LayoutKind::Sequential, CharSet=Unicode )]
__value class VS
{
public:
String* m_str;
Int32 m_n;
};
[DllImport( "some.dll" )]
Int32 func( VS ptr );
int main( )
{
VS v;
v.m_str = S"Hello native!";
v.m_n = 7;
Console::WriteLine(func(v)); // should print 7 also
}
使用 DllImport 属性的更多相关文章
- DllImport属性详解
API函数是构筑Windows的基石, 是Windows编程的必备利器.每一种Windows应用程序开发工具都提供间接或者直接的方式调用Win32API,C#也不例外.使用Win32API的一个好处就 ...
- DllImport attribute的总结
C#有没有方法可以直接都用已经存在的功能(比如Windows中的一些功能,C++中已经编写好的一些方法),而不需要重新编写代码? 答案是肯定,就是通过接下来要说的 DllImport . DllImp ...
- C# DllImport用法和路径问题
DllImport是System.Runtime.InteropServices命名空间下的一个属性类,其功能是提供从非托管DLL导出的函数的必要调用信息. DllImport属性应用于方法,要 ...
- C#中的DllImport
大家在实际工作学习C#的时候,可能会问:为什么我们要为一些已经存在的功能(比如 Windows中的一些功能,C++中已经编写好的一些方法)要重新编写代码,C#有没有方法可以直接都用这些原本已经存在的功 ...
- C#中DllImport用法和路径问题
DllImport是System.Runtime.InteropServices命名空间下的一个属性类,其功能是提供从非托管DLL导出的函数的必要调用信息. DllImport属性应用于方法,要 ...
- C# DllImport的用法
大家在实际工作学习C#的时候,可能会问:为什么我们要为一些已经存在的功能(比如Windows中的一些功能,C++中已经编写好的一些方法)要重新编写代码,C#有没有方法可以直接都用这些原本已经存在的功能 ...
- .Net中C#的DllImport的用法
大家在实际工作学习C#的时候,可能会问:为什么我们要为一些已经存在的功能(比如 Windows中的一些功能,C++中已经编写好的一些方法)要重新编写代码,C#有没有方法可以直接都用这些原本已经存在的功 ...
- 【转帖】.Net中C#的DllImport的用法
在 C# 中通过 P/Invoke 调用Win32 DLL http://msdn.microsoft.com/zh-cn/library/aa686045.aspx 大家在实际工作学习C#的时候 ...
- .net 中的DllImport
只有做成COM的C++ dll才能直接引用.没有做成COM的就只能用P/Invoke(DllImport)或者C++/CLI那种.不过P/Invoke容易类型对不上,所以要是函数多,最好用C++/CL ...
随机推荐
- java 基础学习
a+b: import java.util.Scanner; public class Main { public static void main(String args[]){ Scanner c ...
- Asp.Net读写XML简单方法
xml文件 <?xml version="1.0" encoding="utf-8"?> <book> <title>web ...
- RecyclerView 结合 CardView 使用(二)
上一篇的基础上,修改了,CardView的布局和点击效果 总结: CardView的奇葩属性 :app:cardPreventCornerOverlap="false" 和园角边框 ...
- codevs 1135 选择客栈
这题没什么话说. #include<iostream> #include<cstdio> #include<cstring> #include<algorit ...
- HDU 1213 How Many Tables (并查集,常规)
并查集基本知识看:http://blog.csdn.net/dellaserss/article/details/7724401 题意:假设一张桌子可坐无限多人,小明准备邀请一些朋友来,所有有关系的朋 ...
- Java [Leetcode 263]Ugly Number
题目描述: Write a program to check whether a given number is an ugly number. Ugly numbers are positive n ...
- 转载RabbitMQ入门(5)--主题
主题(topic) (使用Java客户端) 在先前的指南中我们改进了我们的日志系统.取代使用fanout类型的交易所,那个仅仅有能力实现哑的广播,我们使用一个direct类型的交易所,获得一个可以有选 ...
- 【转】IOS NSTimer 定时器用法总结
原文网址:http://my.oschina.net/u/2340880/blog/398598 NSTimer在IOS开发中会经常用到,尤其是小型游戏,然而对于初学者时常会注意不到其中的内存释放问题 ...
- Spring4整合Hibernate4
首先,要明确Spring整合Hibernate可以做什么? 答案是: 1.由IOC容器来管理Hibernate的SessionFactory 2.让Hibernate使用上Spring的声明式事务 整 ...
- [Irving] SQL 2005/SQL 2008 备份数据库并自动删除N天前备份的脚本
以下为SQL脚本,本人以执行计划来调用,所以改成了执行命令,大家可根据自己需要改为存储过程使用 )='E:\MsBackUp\SqlAutoBackup\' --备份路径; --备份类型为全备,1为差 ...