最近,项目中经常需要Calling Win32 DLLs in C# 。
1、use of P/Invoke for calling Windows API functions
2、COM Interop
这里讨论1.
A、Enter P/Invoke
sample:
the Win32 MessageBeep function whose unmanaged declaration is shown in the following code:
BOOL MessageBeep(
UINT uType // beep type
);
You'll need the following code to add to a class or struct definition in C# in order to call MessageBeep:
[DllImport("User32.dll")]
static extern Boolean MessageBeep(UInt32 beepType);
A possible call from managed code might look like this:
MessageBeep(0);
the extern method must be defined using CLR types, as you saw in the preceding code snippet. This requirement to use CLR types that are different from, but compatible with, the underlying API function types is one of the more difficult aspects of using P/Invoke. Therefore, I'll devote a whole section to data marshaling a little later in this column.
B、Style
In general, if the class library offers a way to achieve your goals, it is preferable to use that API rather than making direct calls to unmanaged code because of the significant difference in style between the CLR types and Win32. I can sum up my advice on this matter in a single sentence. When you P/Invoke, don't subject your application logic directly to any extern methods or artifacts thereof.
Sample:
namespace Wintellect.Interop.Sound{
using System;
using System.Runtime.InteropServices;
using System.ComponentModel;
sealed class Sound{
public static void MessageBeep(BeepTypes type){
if(!MessageBeep((UInt32) type)){
Int32 err = Marshal.GetLastWin32Error();
throw new Win32Exception(err);
}
}
[DllImport("User32.dll", SetLastError=true)]
static extern Boolean MessageBeep(UInt32 beepType);
private Sound(){}
}
enum BeepTypes{
Simple = -1,
Ok = 0x00000000,
IconHand = 0x00000010,
IconQuestion = 0x00000020,
IconExclamation = 0x00000030,
IconAsterisk = 0x00000040
}
}
C、The DLL Import Attribute
Toward the bottom of the topic, notice that it says that the library file is User32.lib; this indicates that MessageBeep is exported from User32.dll.
根据.lib名确定对应的.dll名。
D、Optional DllImportAttribute Properties
EntryPoint You can set this property to indicate the entry point name of the exported DLL function in cases where you do not want your extern managed method to have the same name as the DLL export. This is particularly useful when you are defining two extern methods that call into the same unmanaged function.
Win32 Types | Specification | CLR Type |
---|---|---|
char, INT8, SBYTE, CHAR | 8-bit signed integer | System.SByte |
short, short int, INT16, SHORT | 16-bit signed integer | System.Int16 |
int, long, long int, INT32, LONG32, BOOL , INT | 32-bit signed integer | System.Int32 |
__int64, INT64, LONGLONG | 64-bit signed integer | System.Int64 |
unsigned char, UINT8, UCHAR, BYTE | 8-bit unsigned integer | System.Byte |
unsigned short, UINT16, USHORT, WORD, ATOM, WCHAR , __wchar_t | 16-bit unsigned integer | System.UInt16 |
unsigned, unsigned int, UINT32, ULONG32, DWORD32, ULONG, DWORD, UINT | 32-bit unsigned integer | System.UInt32 |
unsigned __int64, UINT64, DWORDLONG, ULONGLONG | 64-bit unsigned integer | System.UInt64 |
float, FLOAT | Single-precision floating point | System.Single |
double, long double, DOUBLE | Double-precision floating point | System.Double |
In Win32 this type is an integer with a specially assigned meaning; in contrast, the CLR provides a specific type devoted to this meaning. |
Sample:
BOOL Beep(
DWORD dwFreq, // Frequency
DWORD dwDuration // Duration in milliseconds
);
==>
[DllImport("Kernel32.dll", SetLastError=true)]
static extern Boolean Beep(
UInt32 frequency, UInt32 duration);
Parameters that are Pointers:
BOOL FileEncryptionStatus(
LPCTSTR lpFileName, // file name
LPDWORD lpStatus // encryption status
);
Notice that the function doesn't return the status using its return value, but instead returns a Boolean value indicating whether the call succeeded. In the success case, the actual status value is returned through the second parameter.
==>
[DllImport("Advapi32.dll", CharSet=CharSet.Auto)]
static extern Boolean FileEncryptionStatus(String filename,
out UInt32 status);
Marshaling Opaque Pointers: a Special Case
Remember, any API function that returns or accepts a handle is really working with an opaque pointer. Your code should marshal handles in Windows as System.IntPtr values.
Marshaling Text:
// ** Documentation for Win32 GetShortPathName() API Function
// DWORD GetShortPathName(
// LPCTSTR lpszLongPath, // file for which to get short path
// LPTSTR lpszShortPath, // short path name (output)
// DWORD cchBuffer // size of output buffer
// );
[DllImport("Kernel32", CharSet = CharSet.Auto)]
static extern Int32 GetShortPathName(
String path, // input string (常量、不变)
StringBuilder shortPath, // output string (可变)
Int32 shortPathLength); // StringBuilder.Capacity