※ 引述《proach (pazroach)》之铭言:
: 问题一:
: VC#需要将上述两个档案加入专案内吗?是的话是加入 reference内?
在C#内, 只需将.DLL加入reference内,并且在要呼叫的CLASS内最前面用
using ThirdPartyDllNamespace之类的。
: 问题二:
: 以前在 VC++内写的程式码要呼叫以下 function
: short openDevice( &Handle );
: 我之前写的程式码是这样的
: HANDLE handle;
┌─────────────────┬──────────────────┐
│MS$ C/C++ │MS$ C# │
├─────────────────┼──────────────────┤
│HANDLE, LPDWORD, LPVOID, void* │IntPtr │
├─────────────────┼──────────────────┤
│LPCTSTR, LPCTSTR, LPSTR, char* │String [in] │
│const char*, Wchar_t*, LPWSTR │StringBuilder [in, out] │
├─────────────────┼──────────────────┤
│DWORD, unsigned long, Ulong │UInt32,[MarshalAs(UnmanagedType.U4)]│
├─────────────────┼──────────────────┤
│bool │bool │
├─────────────────┼──────────────────┤
│LP<struct> │[In] ref <struct> │
├─────────────────┼──────────────────┤
│SIZE_T │uint │
├─────────────────┼──────────────────┤
│LPDWORD │out uint │
├─────────────────┼──────────────────┤
│LPTSTR │[Out] StringBuilder │
├─────────────────┼──────────────────┤
│PULARGE_INTEGER │out ulong │
├─────────────────┼──────────────────┤
│WORD │uInt16 │
├─────────────────┼──────────────────┤
│Byte, unsigned char │byte │
├─────────────────┼──────────────────┤
│Short │Int16 │
├─────────────────┼──────────────────┤
│Long, int │Int32 │
├─────────────────┼──────────────────┤
│float │single │
├─────────────────┼──────────────────┤
│double │double │
├─────────────────┼──────────────────┤
│NULL pointer │IntPtr.Zero │
├─────────────────┼──────────────────┤
│Uint │Uint32 │
└─────────────────┴──────────────────┘
: short shortOpenSuccess = openDevice( &handle );
: 但是在 VC#内找不到 HANDLE 这个关键字,我要怎麽呼叫呢?
在C#内用IntPtr 类别代替C++内的HANDLE。
: 我最後有个奇怪的想法不知是否可行。因为此周边其实都是传回一些byte array而已,
: 如果用 VC++ 写个简单的 Wrapper.dll,以处理 "HANDLE" 这东西的问题。
: VC# 去呼叫 Wrapper.dll,Wrapper.dll 再去呼叫 DriverLib.dll,只要
: 回传一堆 byte就好,HANDLE 留在 Wrapper.dll 内不用回传,这样可行吗?
HANDLE其实只是一个Integer罢了, 给您看一篇文如下:
DllImport介绍
在这篇文章中,我将概述几点关於PInvoke的用法 . PInvoke是.Net程式语言能够呼叫传统
DLL内的函式的机制. 特别有用的地方於它可以呼叫Windows API, 而这些API是未收录在
.Net类别库中的, 以及一些第三方软体业者提供的DLL内的函式也可以用PInvoke呼叫。
PInovke在C#和C++.NET中的用法和VB比较起来大不相同, 因为传统的C++语言之类的内建
有指标或是可以指定unsafe code, 然而VB无法作到。
在VB.NET中使用PInvoke
在VB.Net中主要有两种使用PInoke方式:
1. 使用 Declare 叙述
Declare Auto Function MyMessageBox Lib “user32.dll” Alias _
“MessageBox” (ByVal hWnd as Integer, ByVal msg as String, _
ByVal Caption as String, ByVal Tpe as Integer) As Integer
Sub Main()
MyMessageBox(0, "Hello World !!!", "Project Title", 0)
End Sub
Auto/Ansi/Unicode:字元的编码. 用Auto就好, 让编译器自己去侦测编码的型态。
Lib: DLL函式库名称。一定要用双引号括起来。
Alias 函式的名称和别名: 如果DLL内的函式名称和VB的关键字一样, 那麽一定要使用别
名。
2. 或使用DLLImport
Imports System.Runtime.InteropServices
<DllImport("User32.dll")> Public Shared Function _
MessageBox(ByVal hWnd As Integer, _
ByVal txt As String, ByVal caption As String, _
ByVal typ As Integer) As Integer
End Function
Sub Main()
MessageBox(0, "Imported Hello World !!!", "Project Title", 0)
End Sub
Declare 叙述主要是用来提供和VB6的回溯相容性。其实VB.NET编译器将Declare叙述自动
转成DllImport, 但如果你用到了一些较进阶的选项, 就非得用DllImport不可。
当你使用DllImport时, DLL内的函式是被实作成并带有名称和引数及回传值型态空函式,
它是用DllImport特徵项(Attribute)来指定含有此函式的DLL档的名称, CLR(Common
Language Runtime)会在现行资料夹下搜寻此档, 接着在Windows的System32资料夹下找,
最後才在PATH环境变数内设的目录去找。如果名称和VB.NET的关键字相同, 那麽您必须将
名称用中括号括起来。
下表是DllImport用到的参数列表
Parameter Description
BestFitMapping 包装转换程式(Marshaler)将会为无法在ANSI和Unicode之间找到正确对
映的字元找出一个最佳匹配字元, 如果此参数设为True的话(此参数预设为True) 。
CallingConvention DLL进入点的呼叫惯例。 预设为 stdcall.
CharSet 表示如何包装转换字串资料以及当同时有ANSI和Unicode两种版本函式可用时该
选哪一个, 预设为Charset.Auto。
EntryPoint 该参数指定DLL进入点的名称或值, 若未指定, 将使用函式名称为进入点。
ExactSpelling 该参数控制了包装转换程式是否将会作名称对映。
PreserveSig 该参数用来指定当正在包装转换时是否保留函式签名。
SetLastError 该参数用来表示是否在呼叫完DLL内的函式时自动呼叫Win32的
SetLastError API。可用Marshal.GetLastWin32Error方法取代之。
ThrowOnUnmappableChar 该参数如果设为false, 那麽无法对映的字元将会用问号取代之
, 该参数如果设为true, 那麽当遇到无法对映的字元时, 将会丢出一个例外。
Using PInvoke in C#
Unlike VB, C# does not have the Declare keyword, so we need to use DllImport
in C# and Managed C++.
[DllImport(“user32.dll”]
public static extern int MessageBoxA(
int h, string m, string c, int type);
Here the function is declared as static because function is not instance
dependent, and extern because C# compiler should be notified not to expect
implementation.
C# also provides a way to work with pointers. C# code that uses pointers is
called unsafe code and requires the use of keywords: unsafe and fixed. If
unsafe flag is not used, it will result in compiler error.
Any operation in C# that involves pointers must take place in an unsafe
context. We can use the unsafe keyword at class, method and block levels as
shown below.
public unsafe class myUnsafeClass
{
//This class can freely use unsafe code.
}
public class myUnsafeClass
{
//This class can NOT use unsafe code.
public unsafe void myUnsafeMethod
{
// this method can use unsafe code
}
}
public class myUnsafeClass
{
//This class can NOT use unsafe code.
public void myUnsafeMethod
{
// this method too can NOT use unsafe code
unsafe
{
// Only this block can use unsafe code.
}
}
}
stackalloc
The stackalloc keyword is sometimes used within unsafe blocks to allow
allocating a block of memory on the stack rather than on the heap.
fixed & pinning
GC moves objects in managed heap when it compacts memory during a collection.
If we need to pass a pointer to a managed object to an unmanaged function, we
need to ensure that the GC doesn’t move the object while its address is
being used through the pointer. This process of fixing an object in memory is
called pinning, and it’s accomplished in C# using the fixed keyword.
MarshalAs
MarshalAs attribute can be used to specify how data should be marshaled
between managed and unmanaged code when we need to override the defaults.
When we are passing a string to a COM method, the default conversion is a COM
BSTR; when we are passing a string to a non-COM method, the default
conversion is C- LPSTR. But if you want to pass a C-style null-terminated
string to a COM method, you will need to use MarshalAs to override the
default conversion.
So far we have seen simple data types. But some of the functions need
structures to be passed, which we have to handle differently from simple data
types.
StructLayout
We can define a managed type that is the equivalent of an unmanaged
structure. The problem with marshaling such types is that the common language
runtime controls the layout of managed classes and structures in memory. The
StructLayout Attribute allows a developer to control the layout of managed
types. Possible values for the StructLayout are Auto, Explicit and
Sequential. Charset, Pack and Size are the optional parameters which can be
used with the StructLayout attribute.
Callback functions and passing arrays as parameters involve some more
complications and can be the subject for the next article.
Parameter type mapping
One of the severe problems with using Platform Invoke is deciding which .NET
type to use when declaring the API function. The following table will
summarize the .NET equivalents of the most commonly used Windows data types.
Windows Data Type .NET Data Type
BOOL, BOOLEAN Boolean or Int32
BSTR String
BYTE Byte
CHAR Char
DOUBLE Double
DWORD Int32 or UInt32
FLOAT Single
HANDLE (and all other handle types, such as HFONT and HMENU) IntPtr, UintPtr
or HandleRef
HRESULT Int32 or UInt32
INT Int32
LANGID Int16 or UInt16
LCID Int32 or UInt32
LONG Int32
LPARAM IntPtr, UintPtr or Object
LPCSTR String
LPCTSTR String
LPCWSTR String
LPSTR String or StringBuilder*
LPTSTR String or StringBuilder
LPWSTR String or StringBuilder
LPVOID IntPtr, UintPtr or Object
LRESULT IntPtr
SAFEARRAY .NET array type
SHORT Int16
TCHAR Char
UCHAR SByte
UINT Int32 or UInt32
ULONG Int32 or UInt32
VARIANT Object
VARIANT_BOOL Boolean
WCHAR Char
WORD Int16 or UInt16
WPARAM IntPtr, UintPtr or Object
*As the string is an immutable class in .NET, they aren't suitable for use as
output parameters. So a StringBuilder is used instead.
All the English Text above is quoted from the following web site:
http://www.codeproject.com/KB/dotnet/PInvoke.aspx
--
※ 发信站: 批踢踢实业坊(ptt.cc)
◆ From: 112.105.218.191
1F:推 proach:这些资讯实在是太棒了,非常感谢 <(_,_)> 04/23 10:59
※ 编辑: horngsh 来自: 112.105.218.191 (04/23 11:22)
2F:→ deuter:比较好的做法是用C# SafeHandle class 来存取 C++ HANDLE 04/23 11:57
3F:→ deuter:而不是用IntPtr, MSDN有详细解释 04/23 11:57
4F:→ horngsh:推D大说的 04/23 12:59
5F:推 cogod:很实用的文章,thanks 04/25 23:27
6F:推 TroyLee:推!! 04/25 23:37