C++ 封裝 DLL 供 C# 調用詳細介紹
1、VLC代碼封裝
1.1 QT(C++)工程
首先需要配置可使用 VLC 正常播放的 QT(C++)工程,獲取VLC每一幀並渲染到Qwidget
Libvlcapi
public static class LIBVLCAPI { #region[libvlc.dll 導出函數] // 創建一個libvlc實例,它是引用計數的 [DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [SuppressUnmanagedCodeSecurity] private static extern IntPtr libvlc_new(int argc, IntPtr argv); // 釋放libvlc實例 [DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [SuppressUnmanagedCodeSecurity] public static extern void libvlc_release(IntPtr libvlc_instance); [DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [SuppressUnmanagedCodeSecurity] public static extern String libvlc_get_version(); // 從視頻來源(例如Url)構建一個libvlc_meida [DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [SuppressUnmanagedCodeSecurity] private static extern IntPtr libvlc_media_new_location(IntPtr libvlc_instance, IntPtr path); // 從視頻來源(例如Url)抓圖 [DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [SuppressUnmanagedCodeSecurity] private static extern int libvlc_video_take_snapshot(IntPtr libvlc_mediaplayer, int num, IntPtr filepath, int i_width, int i_height); // 從本地文件路徑構建一個libvlc_media [DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [SuppressUnmanagedCodeSecurity] private static extern IntPtr libvlc_media_new_path(IntPtr libvlc_instance, IntPtr path); [DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [SuppressUnmanagedCodeSecurity] public static extern void libvlc_media_release(IntPtr libvlc_media_inst); // 創建libvlc_media_player(播放核心) [DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [SuppressUnmanagedCodeSecurity] public static extern IntPtr libvlc_media_player_new(IntPtr libvlc_instance); // 將視頻(libvlc_media)綁定到播放器上 [DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [SuppressUnmanagedCodeSecurity] public static extern void libvlc_media_player_set_media(IntPtr libvlc_media_player, IntPtr libvlc_media); // 設置圖像輸出的窗口 [DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [SuppressUnmanagedCodeSecurity] public static extern void libvlc_media_player_set_hwnd(IntPtr libvlc_mediaplayer, Int32 drawable); [DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [SuppressUnmanagedCodeSecurity] public static extern void libvlc_media_player_play(IntPtr libvlc_mediaplayer); [DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [SuppressUnmanagedCodeSecurity] public static extern void libvlc_media_player_pause(IntPtr libvlc_mediaplayer); [DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [SuppressUnmanagedCodeSecurity] public static extern void libvlc_media_player_stop(IntPtr libvlc_mediaplayer); // 解析視頻資源的媒體信息(如時長等) [DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [SuppressUnmanagedCodeSecurity] public static extern void libvlc_media_parse(IntPtr libvlc_media); // 返回視頻的時長(必須先調用libvlc_media_parse之後,該函數才會生效) [DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [SuppressUnmanagedCodeSecurity] public static extern Int64 libvlc_media_get_duration(IntPtr libvlc_media); // 當前播放的時間 [DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [SuppressUnmanagedCodeSecurity] public static extern Int64 libvlc_media_player_get_time(IntPtr libvlc_mediaplayer); // 設置播放位置(拖動) [DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [SuppressUnmanagedCodeSecurity] public static extern void libvlc_media_player_set_time(IntPtr libvlc_mediaplayer, Int64 time); [DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [SuppressUnmanagedCodeSecurity] public static extern void libvlc_media_player_release(IntPtr libvlc_mediaplayer); // 獲取和設置音量 [DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [SuppressUnmanagedCodeSecurity] public static extern int libvlc_audio_get_volume(IntPtr libvlc_media_player); [DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [SuppressUnmanagedCodeSecurity] public static extern void libvlc_audio_set_volume(IntPtr libvlc_media_player, int volume); // 設置全屏 [DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [SuppressUnmanagedCodeSecurity] public static extern void libvlc_set_fullscreen(IntPtr libvlc_media_player, int isFullScreen); // 設置屏幕因子 [DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [SuppressUnmanagedCodeSecurity] public static extern void libvlc_video_set_scale(IntPtr libvlc_media_player, float f_factor); #endregion #region[VLC方法] public struct PointerToArrayOfPointerHelper { [MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)] public IntPtr[] pointers; } public static IntPtr libvlc_new(string[] arguments) { PointerToArrayOfPointerHelper argv = new PointerToArrayOfPointerHelper(); argv.pointers = new IntPtr[11]; for (int i = 0; i < arguments.Length; i++) { argv.pointers[i] = Marshal.StringToHGlobalAnsi(arguments[i]); } IntPtr argvPtr = IntPtr.Zero; try { int size = Marshal.SizeOf(typeof(PointerToArrayOfPointerHelper)); argvPtr = Marshal.AllocHGlobal(size); Marshal.StructureToPtr(argv, argvPtr, false); return libvlc_new(arguments.Length, argvPtr); } finally { for (int i = 0; i < arguments.Length + 1; i++) { if (argv.pointers[i] != IntPtr.Zero) { Marshal.FreeHGlobal(argv.pointers[i]); } } if (argvPtr != IntPtr.Zero) { Marshal.FreeHGlobal(argvPtr); } } } public static IntPtr libvlc_media_new_path(IntPtr libvlc_instance, string path) { IntPtr pMrl = IntPtr.Zero; try { byte[] bytes = Encoding.UTF8.GetBytes(path); pMrl = Marshal.AllocHGlobal(bytes.Length + 1); Marshal.Copy(bytes, 0, pMrl, bytes.Length); Marshal.WriteByte(pMrl, bytes.Length, 0); return libvlc_media_new_path(libvlc_instance, pMrl); } finally { if (pMrl != IntPtr.Zero) { Marshal.FreeHGlobal(pMrl); } } } public static int libvlc_video_take_snapshot(IntPtr libvlc_mediaplayer, int num, string path, int width, int height) { IntPtr pMrl = IntPtr.Zero; try { byte[] bytes = Encoding.UTF8.GetBytes(path); pMrl = Marshal.AllocHGlobal(bytes.Length + 1); Marshal.Copy(bytes, 0, pMrl, bytes.Length); Marshal.WriteByte(pMrl, bytes.Length, 0); return libvlc_video_take_snapshot(libvlc_mediaplayer, num, pMrl, width, height); } finally { if (pMrl != IntPtr.Zero) { Marshal.FreeHGlobal(pMrl); } } } public static IntPtr libvlc_media_new_location(IntPtr libvlc_instance, string path) { IntPtr pMrl = IntPtr.Zero; try { byte[] bytes = Encoding.UTF8.GetBytes(path); pMrl = Marshal.AllocHGlobal(bytes.Length + 1); Marshal.Copy(bytes, 0, pMrl, bytes.Length); Marshal.WriteByte(pMrl, bytes.Length, 0); return libvlc_media_new_path(libvlc_instance, pMrl); } finally { if (pMrl != IntPtr.Zero) { Marshal.FreeHGlobal(pMrl); } } } #endregion }
VLCPlayer
public class VLCPlayer { private IntPtr libvlc_instance_; private IntPtr libvlc_media_player_; private double duration_; public VLCPlayer(string pluginPath, bool is_record) { if (is_record == true) { string plugin_arg = "--plugin-path=" + pluginPath; string filename = "c:\\" + DateTime.Now.ToString("yyyyMMddhhmmss") + ".mp4"; string record_paramter = "--sout=#duplicate{dst=display,dst=std{accs=file,mux=ts,dst=" + filename; string[] arguments = { "-I", "--fullscreen", "dummy", "--ignore-config", "--no-video-title", "--width=100", "--height=100", plugin_arg, record_paramter };// "--sout=#duplicate{dst=display,dst=std{accs=file,mux=ts,dst=c:\\1.mp4" }; libvlc_instance_ = LIBVLCAPI.libvlc_new(arguments); libvlc_media_player_ = LIBVLCAPI.libvlc_media_player_new(libvlc_instance_); } else { string plugin_arg = "--plugin-path=" + pluginPath; string[] arguments = { "-I", "--fullscreen", "dummy", "--ignore-config", "--no-video-title", plugin_arg }; libvlc_instance_ = LIBVLCAPI.libvlc_new(arguments); libvlc_media_player_ = LIBVLCAPI.libvlc_media_player_new(libvlc_instance_); //float f1=0.1f; //LIBVLCAPI.libvlc_video_set_scale(libvlc_media_player_,f1); } } public void Vlc_release() { if (libvlc_instance_ != IntPtr.Zero) LIBVLCAPI.libvlc_release(libvlc_instance_); // if (libvlc_media_player_ != IntPtr.Zero) // LIBVLCAPI.libvlc_media_release(libvlc_media_player_); } public void SetRenderWindow(int wndHandle) { if (libvlc_instance_ != IntPtr.Zero && wndHandle != 0) { LIBVLCAPI.libvlc_media_player_set_hwnd(libvlc_media_player_, wndHandle); } } public void PlayFile(string filePath) { IntPtr libvlc_media = LIBVLCAPI.libvlc_media_new_path(libvlc_instance_, filePath); if (libvlc_media != IntPtr.Zero) { LIBVLCAPI.libvlc_media_parse(libvlc_media); duration_ = LIBVLCAPI.libvlc_media_get_duration(libvlc_media) / 1000.0; LIBVLCAPI.libvlc_media_player_set_media(libvlc_media_player_, libvlc_media); LIBVLCAPI.libvlc_media_release(libvlc_media); LIBVLCAPI.libvlc_media_player_play(libvlc_media_player_); } } public void PlayFile_rtsp(string filePath)//libvlc_media_new_location { IntPtr libvlc_media = LIBVLCAPI.libvlc_media_new_location(libvlc_instance_, filePath); if (libvlc_media != IntPtr.Zero) { // LIBVLCAPI.libvlc_media_parse(libvlc_media); // duration_ = LIBVLCAPI.libvlc_media_get_duration(libvlc_media) / 1000.0; LIBVLCAPI.libvlc_media_player_set_media(libvlc_media_player_, libvlc_media); LIBVLCAPI.libvlc_media_release(libvlc_media); LIBVLCAPI.libvlc_media_player_play(libvlc_media_player_); } } public void Pause() { if (libvlc_media_player_ != IntPtr.Zero) { LIBVLCAPI.libvlc_media_player_pause(libvlc_media_player_); } } public void take_snapshot() { if (libvlc_media_player_ != IntPtr.Zero) { string filepath = "c:\\"; LIBVLCAPI.libvlc_video_take_snapshot(libvlc_media_player_, 0, filepath, 0, 0); } } public void full_screen() { if (libvlc_media_player_ != IntPtr.Zero) { LIBVLCAPI.libvlc_set_fullscreen(libvlc_media_player_, 0); } } public void Stop() { if (libvlc_media_player_ != IntPtr.Zero) { LIBVLCAPI.libvlc_media_player_stop(libvlc_media_player_); } } public double GetPlayTime() { return LIBVLCAPI.libvlc_media_player_get_time(libvlc_media_player_) / 1000.0; } public void SetPlayTime(double seekTime) { LIBVLCAPI.libvlc_media_player_set_time(libvlc_media_player_, (Int64)(seekTime * 1000)); } public int GetVolume() { return LIBVLCAPI.libvlc_audio_get_volume(libvlc_media_player_); } public void SetVolume(int volume) { LIBVLCAPI.libvlc_audio_set_volume(libvlc_media_player_, volume); } public void SetFullScreen(bool istrue) { LIBVLCAPI.libvlc_set_fullscreen(libvlc_media_player_, istrue ? 1 : 0); } public double Duration() { return duration_; } public string Version() { return LIBVLCAPI.libvlc_get_version(); } }
1.2static 聲明 m_instance 優化效率
如下:
#pragma once #include <memory> #include <basetsd.h> typedef SSIZE_T ssize_t; #include "vlc/vlc.h" #include <mutex> struct libvlc_media_track_info_t; struct libvlc_media_t; struct libvlc_instance_t; struct libvlc_media_player_t; struct libvlc_event_t; class context; enum MediaState { NothingSpecial = 0, Opening = 1, Buffering = 2, Playing = 3, Paused = 4, Stopped = 5, Ended = 6, Error = 7 }; class TestVlcVideo { public: TestVlcVideo(); void init( std::function<void(int)> eventCallback); void setHwnd(const int64_t iHwnd) ; bool loadMedia(const char* &url) ; int play() ; void pause() ; void stop() ; void setRatio(const char* &ratio) ; int getVolume() ; int setVolume(const int volume) ; int getMediaState() ; libvlc_instance_t * getVlcInstance(); libvlc_media_player_t * getVlcMediaPlayer(); private: static void vlcEvents(const libvlc_event_t *ev, void *param); static libvlc_instance_t *m_instance; libvlc_media_player_t *m_mediaPlayer = nullptr; int64_t m_durationMS; std::function<void(int)> m_eventCallback; MediaState m_currentMediaState; };
上面 static 聲明的 m_instance 是為瞭優化效率,不必每次播放視頻的時候都新建。
這是第二步工作。
1.3封裝 DLL
需要封裝真正的 DLL 瞭,向C#暴露的也是這個類裡面的方法。
#pragma once typedef int(*CallBackMediaState)(int); #ifdef DLLVLC_EXPORTS // 用來導出函數 #define DLLVLC_API __declspec(dllexport) #else // 用來標識為導入函數,對於引用該頭文件的外部模塊來說dllimport這個標記對編譯優化有作用 #define DLLVLC_API __declspec(dllimport) #endif #include "Testvlcvideo.h" namespace TestVLCDLL { extern "C" { /* * @brief VLC Instance和Player實例初始化 * @param CallBackMediaState callback 回調函數為媒體播放狀態 * @return 每次vlcInit會返回一個VLC的Player ID,此ID唯一,後面的接口都需要此ID找到對應的Player */ DLLVLC_API int vlcInit(CallBackMediaState callback); /* * @brief VLC 媒體加載接口 * @param int index PlayerID * @param const char *path 媒體路徑 */ DLLVLC_API bool vlcLoad(int index, const char *path); /* * @brief 設置句柄,如不設置則為默認窗口播放 * @param const int64_t iHwnd windows窗口句柄 */ DLLVLC_API bool vlcSetHwnd(int index,const int64_t iHwnd); DLLVLC_API bool play(int index); DLLVLC_API bool pause(int index); DLLVLC_API bool stop(int index); /* * @brief 設置播放窗口比例 * @param 形如 16:9 4:3 等字符串 */ DLLVLC_API bool setRatio(int index,const char* ratio); /* * @brief 設置媒體播放音量 */ DLLVLC_API bool setVolume(int index, int volume); /* * @brief 獲取媒體總時長 */ DLLVLC_API int64_t getMediaLength(int index); /* * @brief 獲取當前播放狀態 */ DLLVLC_API int getMediaState(int index); /* * @brief 銷毀VLC Player */ DLLVLC_API bool vlcDisponse(int index); } }
首先在最開始定義瞭 CallBackMediaState
回調函數,對應C++ 層使用函數指針和std::function
都可以。然後使用 DLLVLC_EXPORTS
指示本類為導出類,然後再使用 DLLVLC_API
宏定義導出函數,這些方法都是 dll
暴露給外部調用的方法。
1.4應用程序的導出函數
// DLLVLC.cpp : 定義 DLL 應用程序的導出函數。 #define DLLVLC_EXPORTS #include "DLLVLC.h" #include "Testvlcvideo.h" #include <iostream> #include <map> #include <mutex> #include <atomic> std::map<int, TestVlcVideo*> g_mapVLC; std::atomic_int g_iIndex = 0; std::mutex g_mt; DLLVLC_API int TestVLCDLL::vlcInit(CallBackMediaState callback) { //如果是初次調用,則初始化instance,否則復用instance std::lock_guard<std::mutex> l(g_mt); ++g_iIndex; TestVlcVideo *vlcVideo = new TestVlcVideo; g_mapVLC.emplace(g_iIndex, vlcVideo); g_mapVLC.at(g_iIndex)->init(callback); return g_iIndex; } DLLVLC_API bool TestVLCDLL::play(int index) { std::lock_guard<std::mutex> l(g_mt); TestVlcVideo *vlcVideo = g_mapVLC.at(index); if (nullptr == vlcVideo) { return false; } vlcVideo->play(); return true; } .......
因為我們采用的是導出接口方法,而不是導出類(導出類比較麻煩,自己測試未能成功),因此在制作 dll 庫時,使用靜態 map 保存相關實例,使用對應的 init方法和 dispose 方法借助 id 參數創建和銷毀對象。
1.5 vlc 簡單封裝的具體實現
下來再看下我們第一段代碼的 cpp 文件,就是 vlc 簡單封裝的具體實現:
#include "Testvlcvideo.h" #include <iostream> libvlc_instance_t *TestVlcVideo::m_instance = nullptr; TestVlcVideo::TestVlcVideo() : m_mediaPlayer(nullptr) , m_durationMS(0) , m_eventCallback(nullptr) { } void TestVlcVideo::init(std::function<void(int)> eventCallback) { getVlcInstance(); { getVlcMediaPlayer(); libvlc_event_manager_t *em = libvlc_media_player_event_manager(m_mediaPlayer); { libvlc_event_attach(em, libvlc_MediaPlayerPlaying, vlcEvents, this); libvlc_event_attach(em, libvlc_MediaPlayerPaused, vlcEvents, this); libvlc_event_attach(em, libvlc_MediaPlayerStopped, vlcEvents, this); libvlc_event_attach(em, libvlc_MediaPlayerNothingSpecial, vlcEvents, this); libvlc_event_attach(em, libvlc_MediaPlayerOpening, vlcEvents, this); libvlc_event_attach(em, libvlc_MediaPlayerBuffering, vlcEvents, this); libvlc_event_attach(em, libvlc_MediaPlayerEndReached, vlcEvents, this); libvlc_event_attach(em, libvlc_MediaPlayerPositionChanged, vlcEvents, this); } m_eventCallback = std::move(eventCallback); } } void TestVlcVideo::setHwnd(const int64_t iHwnd) { libvlc_media_player_set_hwnd(m_mediaPlayer, (void *)iHwnd); } bool TestVlcVideo::loadMedia(const char* &url) { libvlc_media_t *m_media = nullptr; std::string url_ = url; if (url_.find("://") == std::string::npos) { m_media = libvlc_media_new_path(getVlcInstance (), url); } else { m_media = libvlc_media_new_location(getVlcInstance(), url); } if (nullptr == m_media) { m_currentMediaState = MediaState::Error; return false; } libvlc_media_player_set_media(getVlcMediaPlayer (), m_media); libvlc_media_parse(m_media); m_durationMS = libvlc_media_get_duration(m_media); libvlc_media_release(m_media); return true; } libvlc_instance_t * TestVlcVideo::getVlcInstance() { if (nullptr == m_instance) { m_instance = libvlc_new(0, NULL); } return m_instance; } libvlc_media_player_t * TestVlcVideo::getVlcMediaPlayer() { if (nullptr == m_mediaPlayer) { m_mediaPlayer = libvlc_media_player_new(m_instance); } return m_mediaPlayer; } int TestVlcVideo::play() { return libvlc_media_player_play(m_mediaPlayer); } void TestVlcVideo::pause() { if(libvlc_media_player_is_playing(m_mediaPlayer)) { libvlc_media_player_set_pause(m_mediaPlayer, 1); } else { libvlc_media_player_set_pause(m_mediaPlayer, 0); } }
到這兒,一般情況下我們還需要配置 def
文件,以避免導出的函數名被增加額外的信息,而不是簡短的“play
”等。但是可以看到我們在所有的導出函數前增加瞭“extern C
”標識。意思是這些函數按照 C
標準進行編譯,由於C++ 的函數重載,再加上各個編譯器的不同,導致編譯而出的函數名被(mangled name
),且各不相同,但是C不支持重載,因此采用統一的編譯規定,同時也可以保證此函數被 C
正確調用,所以我們就無需寫 def
文件也可以保證函數名不被破壞。
2、C# 調用
上面簡要說完瞭 C++ 端關於 DLL 的封裝,再總結一下大概就是這幾點:
- 至少需要兩個文件,一個是自己對具體實現的封裝類,一個是導出方法文件,本文中我們沒有使用類,而是直接導出函數。
- 回調函數像這樣 typedef int(*CallBackMediaState)(int); 去定義。
- 導出文件添加宏 dllexport
#ifdef DLLVLC_EXPORTS // 用來導出函數 #define DLLVLC_API __declspec(dllexport) #else // 用來標識為導入函數,對於引用該頭文件的外部模塊來說dllimport這個標記對編譯優化有作用 #define DLLVLC_API __declspec(dllimport) #endif
導出函數添加 extern “C” DLLVLC_API 聲明。
2.1C# 回調函數聲明與定義
[DllImport(@"C:\Users\HiWin10\Desktop\DLLVLC\DLLVLC\DLLVLC\x64\Release\DLLVLC.dll", EntryPoint = "vlcInit", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = false, CallingConvention = CallingConvention.Cdecl)] public extern static int vlcInit(DllcallBack pfun); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate int DllcallBack(int MediaState);
C# 的回調函數即為委托,需要提前定義委托 DllcallBack ,然後我們假定它是被 C++ 認可的,作為參數傳入 vlcInit。在下面我們需要寫此委托函數具體的實現:
public static int CsharpCall(int MediaState) { Console.WriteLine(MediaState); return MediaState; }
使用的時候:
static int index; static void Main(string[] args) { DllcallBack mycall; mycall = new DllcallBack(Program.CsharpCall); index = vlcInit(mycall); ...... }
經過驗證,此種方式的回調函數能被 C++ 承認,對應於C++的 std::function。
2.2C# 導出普通函數調用
[DllImport(@"C:\Users\HiWin10\Desktop\DLLVLC\DLLVLC\DLLVLC\x64\Release\DLLVLC.dll", EntryPoint = "vlcLoad", CallingConvention = CallingConvention.Cdecl)] public extern static bool vlcLoad(int index, string path); [DllImport(@"C:\Users\HiWin10\Desktop\DLLVLC\DLLVLC\DLLVLC\x64\Release\DLLVLC.dll", EntryPoint = "vlcSetHwnd", CallingConvention = CallingConvention.Cdecl)] public extern static bool vlcSetHwnd(int index, int iHwnd);
上面是 C# 關於普通導出函數的加載方法,在 main 函數中直接進行調用即可。
static int index; static void Main(string[] args) { DllcallBack mycall; mycall = new DllcallBack(Program.CsharpCall); index = vlcInit(mycall); Console.WriteLine(vlcLoad(index, @"D:\1.mp4")); Console.WriteLine(getMediaLength(index)); play(index); setRatio(index,"16:9");
其實 C#
端的調用還是比較簡單的,上面的方式是采用靜態加載的方式,需要將C++ 的 dll
放到 C# 工程 bin
目錄下,而動態加載的方式筆者未進行嘗試。
整個過程就完成瞭,使用 C++ 封裝的方式可以使用一些隻支持 C++,隻有 C++ API 的強大庫,也可以防止反編譯,還可以使代碼更好的分層等。
下面附上demo鏈接,有需要的小夥伴可下載運行(VS2015)
download.csdn.net/download/u0…
到此這篇關於C++ 封裝 DLL 供 C# 調用詳細介紹的文章就介紹到這瞭,更多相關C++ 封裝 DLL 供 C# 調用內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!