当前位置 博文首页 > 文章内容

    C++ 封装 DLL 供 C# 调用详细介绍

    作者:shunshunshun18 栏目:未分类 时间:2021-09-10 14:43:27

    本站于2023年9月4日。收到“大连君*****咨询有限公司”通知
    说我们IIS7站长博客,有一篇博文用了他们的图片。
    要求我们给他们一张图片6000元。要不然法院告我们

    为避免不必要的麻烦,IIS7站长博客,全站内容图片下架、并积极应诉
    博文内容全部不再显示,请需要相关资讯的站长朋友到必应搜索。谢谢!

    另祝:版权碰瓷诈骗团伙,早日弃暗投明。

    相关新闻:借版权之名、行诈骗之实,周某因犯诈骗罪被判处有期徒刑十一年六个月

    叹!百花齐放的时代,渐行渐远!



    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…