三種C++轉C#的方法(帶指針轉換、Demo源碼)

第一種是直接添加c++項目引用

第二種是使用DllImport的方式

前兩種可以參考:

https://blog.csdn.net/zxy13826134783/article/details/84190800

第三種是本文需要介紹的,而且使用C#封裝ffmpeg視頻庫的開源庫FFmpeg.AutoGen也是使用下面的方法

 

本文測試環境:

win7

vistual studio 2012

 

步驟如下:

一   C++項目端操作

1.1 新建C++類庫項目,名爲Operation,選擇Win32項目,如下圖:

點擊確定,彈出對話框,如下圖:

點擊下一步,接着彈出對話框,選擇DLL,勾選空項目,如下圖:

1.2  新建測試類FunctionOutPut,鼠標右鍵項目,選擇     添加 /類,如下圖:

在FunctionOutPut.cpp中輸入如下代碼,代碼過於簡單,就兩個減法操作的函數,一個帶指針而已,不做解釋:

#include "FunctionOutPut.h"


FunctionOutPut::FunctionOutPut(void)
{
}


FunctionOutPut::~FunctionOutPut(void)
{
}

int sub(int a,int b){
	return a-b;
}

int pointer(int &a,int &b){
	return a-b;
}

1.3  添加一個名爲FunctionOutput.def模塊定義文件,添加方式如下,鼠標右鍵項目,選擇   添加   / 新建項     後,彈出對話框,如下圖:

並編輯如下:

 

1.4  生成項目,然後找到Debug(要找仔細咯,與C#的不同,它居然與項目文件同一個目錄)目錄下生成的Operation.dll,需要拷貝到C#的bin/Debug目錄下

 

二    在同一個解決方案下新建C#的控制檯項目,名爲callDLL

2.1   由於在C#項目中使用指針,需要勾選項目中的  ‘運許不安全代碼’ 選項,鼠標右鍵C#項目,選擇屬性,然後再彈出的對話框中勾選如下:

2.2  在主程序中編輯代碼如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace callDLL
{
    class Program
    {
        //加載dll庫,參數爲dll庫的名稱,返回句柄
        [DllImport("kernel32")]
        public static extern IntPtr LoadLibrary(string lpFileName);
        //通過句柄釋放dll庫
        [DllImport("Kernel32")]
        public static extern bool FreeLibrary(IntPtr handle);
        //根據函數名輸出庫函數,返回函數的指針
        [DllImport("Kernel32")]
        public static extern IntPtr GetProcAddress(IntPtr handle, String funcname);
 
 
        //定義c++中sub方法對應的委託
        [UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
        public delegate int CppSub(int a, int b);
 
        //定義c++中pointer方法對應的委託,注意如果帶指針,需要在委託前面加上unsafe關機字
        [UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
        unsafe public delegate int pointer(int *a,int *b);
       

        //因爲Main方法中使用到指針,所以需要在Main方法前加上unsafe關鍵字
        unsafe static void Main(string[] args)
        {
            //加載c++對應的dll庫
            IntPtr dll = LoadLibrary("Operation.dll");
            //獲取c++對應的庫函數sub
            IntPtr func = GetProcAddress(dll, "sub");
            //根據庫函數sub獲取委託實例
            CppSub sub = (CppSub)Marshal.GetDelegateForFunctionPointer(func, typeof(CppSub));
            //委託間接調用sub函數
            int res=sub(6, 3);
            Console.WriteLine(res);

            //獲取c++對應的庫函數pointer
            IntPtr func1 = GetProcAddress(dll, "pointer");
            //根據庫函數pointer獲取委託pointer實例
            pointer p = (pointer)Marshal.GetDelegateForFunctionPointer(func1, typeof(pointer));
            int a = 8;
            int b=3;
            int res1 = p(&a, &b);
            Console.WriteLine(res1);

            //釋放dll庫
            FreeLibrary(dll);
            Console.Read();

        }
    }
}

核心是通過委託來調用c++中對應的函數,同時需要注意的是,凡是涉及到使用指針相關的方法名或者定義委託的前面都要加上unsafe關鍵字,對應這方面的基礎知識參考:https://www.cnblogs.com/lgx5/p/7353346.html

2.3  生成項目,把剛纔生成的Operation.dll拷貝的bin/Debug目錄下,並把項目設置爲啓動項目,然後運行效果圖如下圖:

 

如果發生錯誤如下:

1  託管調試助手 "PInvokeStackImbalance":的調用導致堆棧不對稱。原因可能是託管的 PInvoke 簽名與非託管的目標籤名不匹配。請檢查 PInvoke 簽名的調用約定和參數與非託管

原因可能是:

你的委託定義的上方沒有加:

[UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]

 

2  不能使用非固定表達式中包含的固定大小緩衝區。請嘗試使用 fixed 語句

原因是使用了指針進行賦值,卻沒有使用fixed語句,如下圖分析:

3  嘗試讀取或寫入受保護的內存。這通常指示其他內存已損壞。

這樣的錯誤我折騰了好久也沒解決

 

Demo源碼下載:http://zxy15914507674.gitee.io/shared_resource_name/Charp和C++間指針操作.rar

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章