C#調用C/C++ DLL方式

一、編寫一個簡單的DLLc#

設置爲導出函數,並採用C風格。函數前加extern "C" __declspec(dllexport)。定義函數在退出前本身清空堆棧,在函數前加__stdcall。api

新建一個頭文件,在頭文件中:函數

/* 加入任意你想加入的函數定義*/工具

extern "C" _declspec(dllexport) int _stdcall add(int *x,int *y); // 聲明爲C編譯、連接方式的外部函數
extern "C" _declspec(dllexport) int _stdcall sub(int x,int y); // 聲明爲C編譯、連接方式的外部函數學習

新建一個.cpp文件ui

#include "mydll.h"//貌似這兩個頭文件的順序不能顛倒。我試了不少次,可是不能肯定。spa

int add(int *x,int *y)//是否能夠理解爲,VS2010已經默認是 _stdcall,因此函數不用添加該修飾符
{
return *x+*y;
}指針

int sub(int x,int y)
{
return x-y;
}接口

把導出函數名稱變爲標準名稱,需加模塊定義文件,就是.def文件。字符串

內容以下:(須要註釋,前面加分號就能夠了,註釋須要單獨行)

LIBRARY "TEST"

     EXPORTS

          ;add函數

          add

   ;sub函數

         sub

LIBRARY 庫名稱

EXPORTS 須要導出的各個函數名稱

     從新編譯以後,再用Depends工具看一下,函數已經變成標準add。這個在動態加載時頗有用,特別是在GetProcAddress函數尋找入庫函數的時候。

二、靜態調用

新建一個C#的控制檯項目,

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;//添加

namespace mytest
{
  class Program
  {
    //----------------------------------------------------------------------------------------------
    [DllImport("mydll.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
    extern static int add(ref int a, ref int b);

    [DllImport("mydll.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
    extern static int sub(int a, int b);
    //--------------------------------------------------------------------------------------------------

    static void Main(string[] args)
    {

      int a, b,c,d;
      a = 1;
      b = 2;
      c=d= 0;

      Console.WriteLine(add(ref a,ref b).ToString());
      Console.WriteLine(sub(b,a).ToString());
      Console.Read();
    }
  }
}

 三、動態調用

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;//添加

namespace mytest
{
  class Program
  {

[DllImport("kernel32.dll")]
private extern static IntPtr LoadLibrary(String path);//path 就是dll路徑 返回結果爲0表示失敗。
[DllImport("kernel32.dll")]
private extern static IntPtr GetProcAddress(IntPtr lib, String funcName);//lib是LoadLibrary返回的句柄,funcName 是函數名稱 返回結果爲0標識失敗。
[DllImport("kernel32.dll")]
private extern static bool FreeLibrary(IntPtr lib);

//聲明委託
[UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
delegate int ADD(ref int x, ref int y);
[UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
delegate int SUB(int x, int y);

static void Main(string[] args)
{

string dllPath = "G:\\VS2010軟件學習\\c#調用C_dll\\mytest\\mytest\\mydll.dll";
string apiName1 = "add";
string apiName2 = "sub";
//使用動態加載

IntPtr hLib = LoadLibrary(dllPath);//加載函數

IntPtr apiFunction1 = GetProcAddress(hLib, apiName1);//獲取函數地址
IntPtr apiFunction2 = GetProcAddress(hLib, apiName2);//獲取函數地址

int i = Marshal.GetLastWin32Error();
if (apiFunction1.ToInt32() == 0)//0表示函數沒找到
return;
if (apiFunction2.ToInt32() == 0)//0表示函數沒找到
return;

//獲取函數接口,至關於函數指針
ADD add1 = (Delegate)Marshal.GetDelegateForFunctionPointer(apiFunction1, typeof(ADD)) as ADD;
SUB sub1 = (SUB)Marshal.GetDelegateForFunctionPointer(apiFunction2, typeof(SUB));

// //調用函數
int a, b,c;
a = 1;
b = 2;
c= 0;
//add1(ref a,ref b);
c=sub1(b,a);
// //釋放句柄

FreeLibrary(hLib );

Console.WriteLine(c.ToString());
//Console.WriteLine(add(ref a,ref b).ToString());
//Console.WriteLine(sub(10,2).ToString());
Console.Read();

}

  }
}

 

注意:

C#時常須要調用C/C++DLL,當傳遞參數時時常遇到問題,尤爲是傳遞和返回字符串時。VC++中主要字符串類型爲:LPSTR,LPCSTR, LPCTSTR, string, CString, LPCWSTR, LPWSTR等,但轉爲C#類型卻不徹底相同。

類型對照:

C/C++----------C#

BSTR ---------  StringBuilder

LPCTSTR --------- StringBuilder

LPCWSTR ---------  IntPtr

handle---------IntPtr

hwnd-----------IntPtr

char *----------string

int * -----------ref int

int &-----------ref int

void *----------IntPtr

unsigned char *-----ref byte

Struct須要在C#裏從新定義一個Struct

CallBack回調函數須要封裝在一個委託裏,delegate static extern int FunCallBack(string str);

注意在每一個函數的前面加上public static extern +返回的數據類型,若是不加public ,函數默認爲私有函數,調用就會出錯。

 

在C#調用C++ DLL封裝庫時會出現兩個問題:

1. 數據類型轉換問題 2. 指針或地址參數傳送問題

相關文章
相關標籤/搜索