PInvoke

PInvoke

If you want to call one of the many Win32 API functions that are not available directly from the .NET framework, you should be able to call them using PInvoke (Platform Invoke). (PInvoke is not supported with classes. To call an interface method in a COM dll use COM Interop.) There are excellent tutorials on the Microsoft Developers Network on using PInvoke, so I will not belabor this technique. Here is a snippet of code that plays a sound from Eric Gunnerson's tutorial Using Win32 and Other Libraries. The class WrapK32 encapsulates or hides the call to InteropServices. To the user of the Wrap32 class, the Beep function appears as managed code.

using System;
using System.Runtime.InteropServices;
namespace TestPInvoke
{
	/// <summary>
	/// Summary description for Class1.
	/// </summary>
	class WrapK32
	{
		[DllImport("kernel32.dll")]
		public static extern bool Beep(int frequency, int duration);
		/// <summary>
		/// The main entry point for the application.
		/// </summary>
		[STAThread]
		static void Main(string[] args)
		{
			//
			// TODO: Add code to start application here
			//
			Random random = new Random();
			for (int i = 0; i < 10000; i++)
			{
				Beep(random.Next(10000), 100);
			}
		}
	}
}

To invoke the Beep Win32 function, you declare the C# prototype as public static extern as in:

public static extern bool Beep(int frequency, int duration);

The Beep function is in the kernal32 dll so you tell the runtime using the DllImport attribute directive. 

[DllImport("kernel32.dll")]
public static extern bool Beep(int frequency, int duration);

String Stuff

Strings are confusing. Under Win9x, strings are ANSI single byte (8 bit) or double byte (8 _or_ 16 bit aka multi-byte) characters. Under NT, W2K and WXP, strings are 16 bit WIDE characters (Unicode). The LPTSTR type is both! It acts as ANSI on Win9x and as WIDE on WinNT. COM uses BSTR which is just a typedef to a WIDE string:

typedef wchar_t *BSTR;

However, a BSTR must adhere to a very specific set of rules that differs from a LPWSTR:

typedef wchar_t *LPWSTR;

The default marshaling type for strings in PInvoke is LPTSTR. This differs from COM Interop where the default marshaling type for strings is BSTR. If the actual parameter type differs from the PInvoke default then you will need to use the MarshalAs attribute in the function prototype declaration. For instance, it is an error to pass an immutable C# String as an out parameter. Instead, you will need to pass a StringBuffer and use the MarshalAs attribute. Here again is a sample from Eric Gunnerson's tutorial:

[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern int GetShortPathName(
   [MarshalAs(UnmanagedType.LPTStr)]
   string path,
   [MarshalAs(UnmanagedType.LPTStr)]
   StringBuilder shortPath,
   int shortPathLength);

The CharSet= CharSet.Auto attribute is used to tell the CLR to automatically use the type of string supported by the OS, which is either ANSI or WCHAR (Unicode). If you do not declare the CharSet, the default is CharSet.Ansi which could cause performance problems under Win NT. You should declare CharSet as Auto unless the Win32 API call exist only as ANSI or WCHAR.

Calling Convention

The default calling convention is CallingConvention.StdCall which puts the function's last parameter on the stack first and the callee cleans the stack. You may need to set the CallingConvention to CallingConvention.Cdecl if the caller cleans the stack (Cdecl supports a variable number of arguments in a function, but C# does not support VARARG). The enum CallingConvention.Winapi automates the calling convention to StdCall on Windows and Cdecl on Windows CE .NET.

Creating and Calling a C++ Win32 DLL

You can create your own C++ Win32 dll using the Visual Studio Project Wizard. If you tell the wizard to create a dll and export the dll functions, you can see the exported functions using the DUMPBIN tool (You may need to modify the system path to include the dumpbin.exe and mspdb71.dll). Once you have the path set up correctly, run DUMPBIN with the /EXPORTS option. For example, if you use Visual Studio 2003 to create a C++ Win32 dll project named Win32Test and choose to export your functions, the wizard will create a sample exported function

int fnWin32Test(){return 42;}

Change the return type to long and then look at the exported function using dumpbin.

>dumpbin /exports Win32Test.dll

This outputs the exported fnWin32Test function as "?fnWin32Test@@3HA". To look at the undecorated name use the undname tool as in:

>undname ?fnWin32Test@@3HA

This converts the decorated name to "long fnWin32Test". 

It may be easier to use the undecorated name when calling from C#. You can do this by exporting the function using "C" linkage in your Win32 dll using the syntax:

extern "C" long declspect(dllexport)
fnWin32Test() {...}

or

#ifdef WIN32TEST_EXPORTS
#define WIN32TEST_API __declspec(dllexport)
#else
#define WIN32TEST_API __declspec(dllimport)
#endif
extern "C" WIN32TEST_API long fnWin32Test();

You can now call this function from C# as:

	class Class1
	{
		[DllImport("Win32Test.dll")]
		public static extern int fnWin32Test();
		/// <summary>
		/// The main entry point for the application.
		/// </summary>
		[STAThread]
		static void Main(string[] args)
		{
			//
			// TODO: Add code to start application here
			//
			System.Console.WriteLine(fnWin32Test());
			System.Console.ReadLine();
		}
	}

Output: 42. 

Note: The C++ return type long  has been mapped to the C# type int.

Using the EntryPoint DllImportAttribute 

You can rename an unmanaged dll function call from within C# using the EntryPoint attribute. To call a function using its fully decorated name "?fnWin32Test2@@YAJXZ" as "Win32Test2" you can specify the static entry point as "?fnWin32Test2@@YAJXY":

[DllImport("Win32Test.dll", EntryPoint= "?fnWin32Test2@@YAJXZ")]

public static extern int fnWin32Test2();

And call it as:

System.Console.WriteLine(fnWin32Test2());

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
PInvoke Interop Assistant是一款用于帮助开发人员在.NET平台上使用本机代码(Native Code)的工具。它提供了一个简单易用的界面,可以自动生成C#的PInvoke声明(Platform Invoke,平台调用)以及相应的数据类型转换代码,以便在.NET中调用本机函数。 PInvoke是指在.NET平台上调用本机代码的一种技术。由于.NET运行时环境(CLR)与本机代码之间的差异,所以在调用本机函数时会涉及到数据类型转换、内存管理等方面的问题。而PInvoke Interop Assistant就是为了解决这些问题而设计的。 使用PInvoke Interop Assistant,开发人员可以通过输入本机函数的名称、库文件的路径以及返回值和参数的数据类型等信息,自动生成C#的PInvoke声明。生成的声明会包含所需的函数签名、数据类型转换代码以及其他必要的声明。开发人员只需要将生成的代码复制到自己的项目中,然后就可以在.NET平台上调用本机函数了。 PInvoke Interop Assistant的优点在于它能够简化PInvoke的过程,减少了代码的编写工作量。使用这个工具,开发人员不需要手动编写复杂的PInvoke声明和数据类型转换代码,从而提高了开发效率。此外,PInvoke Interop Assistant还提供了一些更高级的功能,例如处理结构体、指针、回调函数等复杂的情况。 总之,PInvoke Interop Assistant是.NET开发人员在调用本机代码时的一个有用工具,它能够帮助开发人员自动生成所需的PInvoke声明和数据类型转换代码,从而简化了与本机代码的集成过程。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值