现在的位置: 首页 > 综合 > 正文

C#创建和调用托管的DLL

2018年04月05日 ⁄ 综合 ⁄ 共 6893字 ⁄ 字号 评论关闭

本文介绍利用
c#
创建和调用托管的
DLL
的方法和给出一个示例,采用的环境是
vs2005.

托管
DLL
的创建和调用

利用
vs2005
,菜单,文件,
c#
语言,
window,
类库,代码如下:

using
System;

using
System.Collections.Generic;

using
System.Text;

namespace
Libary_Test

{

   
public
partial
class
Form1
: Form

 
  
{

       
public
Form1()

       
{

           
InitializeComponent();

       
}

       
public
void
 

Function()

       
{

           
MessageBox
.Show(Class1
.Calc(10).ToString());

       
}

 

       
private
void
button1_Click(object
sender, EventArgs
e)

 
      
{

           
Function();

       
}

   
}

}

c#
当中,我们是以命名空间来来组织的,所以在在调用的时候也是一样,编译生成dll
文件。

在写一个示例的程序来调用它。

using
Libary_Sample;

namespace
Libary_Test

{

   
public
partial
class
Form1
: Form

   
{

       
public
Form1()

       
{

           
InitializeComponent();

       
}

       
public
void
 

Function()

       
{

           
MessageBox
.Show(Class1
.Calc(10).ToString());

       
}

 

       
private
void
button1_Click(object
sender, EventArgs
e)

       
{

           
Function();

       
}

}

 

 

上面是代码:我们有几步需要搞清楚。

1
把之前生成的dll
文件存放在我们的调用端的文件夹中,避免丢失。

2
添加引用,在解决方案里面添加对dll
的引用。

3
添加命名空间的引用using Libary_Sample.

 

      

这样我们就完成了托管的
DLL
创建和调用!实际上可以理解相当于一个类的声明,只不过这里是以
dll
的形式,而非文件的形式!

 

 

非托管的调用:

      

这个我们就是称之为调用外部的
DLL,
例如用
c++
语言编写的。

首先我们需要声明一个本地的
DLL
实现方法。具体的是用参数
static

extern
来声明方法,接着在声明前加上
DllImport
特性。

首先我们来详细的讲解下
DllImport
的特性和使用方法(
MSDN

,
在这里我们就是叫做平台调用(
P/Invoke

DllImport()
:
该属性化方法由非托管动态链接库
(DLL)
作为静态入口点公开。

 

      

下面给出一个例子来调用
Wind32 api
函数的例子。

using System.Runtime.InteropServices;

 

public class Win32 {

    

[DllImport("user32.dll", CharSet=CharSet.Auto)]

    

public static extern int MessageBox(int hWnd, String text,

                    
String caption, uint
type);

}

 

public class HelloWorld {

   

public static void Main() {

      

Win32.MessageBox(0, "Hello World", "Platform Invoke
Sample", 0);

   

}

}

这一个也就是一个简单的平台调用的例子。但是这里我们还需要注意的几点就是在平台调用之间用的是标准封装处理服务。会碰见许多如数据传递的问题,我们下面就这些问题来深入一下:

例如,如何传递一个数组?字符串?

这里面就设计到平台调用数据类型,是如何封送数据的?

 

 

参考一下文档:

平台调用服务 (PInvoke)
允许托管代码调用在 DLL
中实现的非托管函数




本教程说明使用什么方法才能从 C#


调用非托管 DLL 函数



。该教程所讨论的属性



允许您调用这些函数



并使数据类型得到正确封送。

教程

C#


代码有以下两种可以直接调用非托管代码的方法:

对于这两种技术,都必须向 C#


编译器提供非托管函数



的声明,并且还可能需要向
C#


编译器提供如何封送与非托管代码之间传递的参数和返回值的说明。

该教程由下列主题组成:

该教程包括下列示例:

直接从 C#


调用 DLL
导出

若要声明一个方法使其具有来自 DLL
导出的实现,请执行下列操作:

  • 使用 C#


    关键字 static
    extern
    声明方法。

  • DllImport 属性



    附加到该方法。DllImport 属性



    允许您指定包含该方法的 DLL
    的名称。通常的做法是用与导出的方法相同的名称命名 C#


    方法,但也可以对 C#


    方法使用不同的名称。
  • 还可以为方法的参数和返回值指定自定义封送处理信息,这将重写 .NET Framework
    的默认封送处理。

示例 1

本示例显示如何使用 DllImport 属性



通过调用 msvcrt.dll
中的 puts
输出消息。

// PInvokeTest.cs

using System;

using System.Runtime.InteropServices;

class PlatformInvokeTest

{

  

 
[DllImport("msvcrt.dll")]

   

public static extern int puts(string c);

   

[DllImport("msvcrt.dll")]

   

internal static extern int _flushall();

   

public static void Main()

   

{

       

puts("Test");

       

_flushall();

   

}

}

输出

Test

代码讨论

前面的示例显示了声明在非托管 DLL
中实现的 C#


方法的最低要求。PlatformInvokeTest.puts
方法用 static
extern
修饰符声明并且具有 DllImport 属性



,该属性



使用默认名称 puts
通知编译器此实现来自 msvcrt.dll
。若要对 C#


方法使用不同的名称(如
putstring

),则必须在 DllImport 属性



中使用 EntryPoint
选项,如下所示:

[DllImport("msvcrt.dll",
EntryPoint="puts")]

有关 DllImport 属性



的语法的更多信息,请参见
DllImportAttribute





默认封送处理和为非托管方法的参数指定自定义封送处理

当从 C#


代码中调用非托管函数



时,公共语言运行库必须封送参数和返回值。

对于每个 .NET Framework
类型均有一个默认非托管类型,公共语言运行库将使用此非托管类型在托管到非托管的函数



调用中封送数据。例如,C#


字符串值的默认封送处理是封送为
LPTSTR

(指向 TCHAR
字符缓冲区的指针



)类型。可以在非托管函数



C#


声明中使用 MarshalAs 属性



重写默认封送处理。

示例 2

本示例使用 DllImport 属性



输出一个字符串。它还显示如何通过使用 MarshalAs 属性



重写函数



参数的默认封送处理。

// Marshal.cs

using System;

using System.Runtime.InteropServices;

class PlatformInvokeTest

{

   

[DllImport("msvcrt.dll")]

   

public static extern int puts(

       

[MarshalAs(UnmanagedType.LPStr)]

       

string m);

   

[DllImport("msvcrt.dll")]

   

internal static extern int _flushall();

   

public static void Main()

   

{

       

puts("Hello World!");

       

_flushall();

   

}

}

输出

运行此示例时,字符串

Hello World!

将显示在控制台上。

代码讨论

在前面的示例中,puts 函数



的参数的默认封送处理已从默认值 LPTSTR
重写为 LPSTR

MarshalAs 属性



可以放置在方法参数、方法返回值以及结构和类的字段上。若要设置方法返回值的封送处理,请将 MarshalAs 属性



与返回属性



位置重写一起放置在方法上的属性



块中。例如,若要显式设置 puts
方法返回值的封送处理:

...

[DllImport("msvcrt.dll")]

[return : MarshalAs(UnmanagedType.I4)]

public static extern int puts(

...

有关 MarshalAs 属性



的语法的更多信息,请参见
MarshalAsAttribute





注意   In
Out 属性



可用于批注非托管方法的参数。它们与 MIDL
源文件中的 in
out

修饰符的工作方式类似。请注意,Out 属性



C#


参数修饰符 out


不同。有关 In
Out 属性



的更多信息,请参见
InAttribute




OutAttribute





为用户定义的结构指定自定义封送处理

可以为传递到非托管函数



或从非托管函数



返回的结构和类的字段指定自定义封送处理属性



。通过向结构或类的字段中添加 MarshalAs 属性



可以做到这一点。还必须使用 StructLayout 属性



设置结构的布局,还可以控制字符串成员的默认封送处理,并设置默认封装大小。

示例 3

本示例说明如何为结构指定自定义封送处理属性




请考虑下面的 C
结构:

typedef struct tagLOGFONT

{

  

LONG lfHeight;

  

LONG lfWidth;

  

LONG lfEscapement;

  

LONG lfOrientation;

  

LONG lfWeight;

  

BYTE lfItalic;

  

BYTE lfUnderline;

  

BYTE lfStrikeOut;

  

BYTE lfCharSet;

  

BYTE lfOutPrecision;

  

BYTE lfClipPrecision;

  

BYTE lfQuality;

  

BYTE lfPitchAndFamily;

  

TCHAR lfFaceName[LF_FACESIZE];

} LOGFONT;

C#


中,可以使用 StructLayout

MarshalAs 属性



描述前面的结构,如下所示:

// logfont.cs

// compile with: /target:module

using System;

using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential)]

public class LOGFONT

{

   

public const int LF_FACESIZE = 32;

   

public int lfHeight;

   

public int lfWidth;

   

public int lfEscapement;

   

public int lfOrientation;

   

public int lfWeight;

   

public byte lfItalic;

   

public byte lfUnderline;

   

public byte lfStrikeOut;

   

public byte lfCharSet;

   

public byte lfOutPrecision;

   

public byte lfClipPrecision;

 
  
public byte lfQuality;

   

public byte lfPitchAndFamily;

   

[MarshalAs(UnmanagedType.ByValTStr, SizeConst=LF_FACESIZE)]

   

public string lfFaceName;

}

有关 StructLayout 属性



的语法的更多信息,请参见
StructLayoutAttribute





然后即可将该结构用在 C#


代码中,如下所示:

// pinvoke.cs

// compile with:
/addmodule:logfont.netmodule

using System;

using System.Runtime.InteropServices;

 

class PlatformInvokeTest

{  

     

[DllImport("gdi32.dll", CharSet=CharSet.Auto)]

     

public static extern IntPtr CreateFontIndirect(

           
[In,
MarshalAs(UnmanagedType.LPStruct)]

           
LOGFONT lplf  
// characteristics

           
);

 

     

[DllImport("gdi32.dll")]

     

public static extern bool DeleteObject(

           
IntPtr handle

           
);

 

     

public static void Main()

     

{

           
LOGFONT lf = new LOGFONT();

           
lf.lfHeight = 9;

           
lf.lfFaceName = "Arial";

           
IntPtr handle =
CreateFontIndirect(lf);

 

           
if (IntPtr.Zero == handle)

           
{

          
       
Console.WriteLine("Can't creates a
logical font.");

           
}

           
else

           
{

                 

                 
if (IntPtr.Size == 4)

                       

Console.WriteLine("{0:X}", handle.ToInt32());

                 
else

                       

Console.WriteLine("{0:X}", handle.ToInt64());        

                 
// Delete the logical font
created.

                 
if (!DeleteObject(handle))

                      

Console.WriteLine("Can't delete the logical font");

  

         
}

     

}

}

运行示例

C30A0AE5

代码讨论

在前面的示例中,CreateFontIndirect
方法使用了一个 LOGFONT
类型的参数。MarshalAs
In 属性



用于限定此参数。程序将由此方法返回的数值显示为十六进制大写字符串。

注册回调方法

若要注册调用非托管函数



的托管回调,请用相同的参数列表声明一个委托并通过 PInvoke
传递它的一个实例。在非托管端,它将显示为一个函数


指针



。有关 PInvoke
和回调的更多信息,请参见平台调用详解




例如,考虑以下非托管函数


MyFunction

,此函数



要求 callback
作为其参数之一:

typedef void (__stdcall
*PFN_MYCALLBACK)();

int __stdcall MyFunction(PFN_ MYCALLBACK
callback);

若要从托管代码调用 MyFunction
,请声明该委托,将
DllImport

附加到函数



声明,并根据需要封送任何参数或返回值:

public delegate void MyCallback();

[DllImport("MYDLL.DLL")]

public static extern void
MyFunction(MyCallback callback);

同时,请确保委托实例的生存期覆盖非托管代码的生存期;否则,委托在经过垃圾回收后将不再可用。

 

抱歉!评论已关闭.