总结C# 调用 C相应的库和WPF 控件嵌入Winform空间问题

前段时间做了一个通过C# 封装中间件调用C风格dll 提供给winform 窗口使用的任务,一下先把问题描述下,将解决问题的思路记录下
问题1:C# 能否调用C++动态库?
答案是肯定的,调用方式的话我在下面写上我前段时间解决的例子:

(1)带有回调函数的C风格方式的方法在C#中的声明

	//外部方法
    [DllImport(@"ButelAgentAdapter.dll", EntryPoint = "ButelTryInitVedio", CharSet = CharSet.Ansi,
    CallingConvention = CallingConvention.StdCall)]
   [DllImport(@"ButelAgentAdapter.dll", EntryPoint = "ButelTryInitVedio", CharSet = CharSet.Ansi,
    CallingConvention = CallingConvention.StdCall)]
    /// 
    /// 初始化SDK
    /// 
    /// 第三方API
    public static extern int ButelTryInitVedio(BUTELCONECTEVENTCALLBACK handleL, int nAgentType, int nTypeParam);
    public delegate void BUTELCONECTEVENTCALLBACK(int type, IntPtr data, string msg, string szExtendSignalInfo);
    public static BUTELCONECTEVENTCALLBACK callback;
    /// 使用
     public bool InitSDK()
    {
        callback = butelconectevent_callback; //为方法名
        if (0 != ButelTryInitVedio(callback, 3, 1))
        {
            return false;
        }
        return true;
    }

(2)C++ 结构体在C#中使用

       C#定义外部结构体
           [StructLayout(LayoutKind.Sequential)]
		    public struct AgentInfo
	    {
	        public Int32 m_callEvent;

    public Int32 m_agentStatus;

    public Int32 agentCallType;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
    public char[] dstNube; //被叫号码
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
    public char[] dstNickName;//被叫昵称
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
    public char[] localNube; //主叫
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
    public char[] localNickName;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
    public char[] sid;   //会话id
    };
    //指针转化成结构体的方式
     static public Object BytesToStruct(IntPtr buffer, AgentInfo obj)
    {
        var result = Marshal.PtrToStructure(buffer, obj.GetType());
        return result;
    }

(3)C# winform 窗体传给MFC窗体进行窗体重绘

    //带有窗口处理的C风格函数声明
       [DllImport(@"ButelAgentAdapter.dll", EntryPoint = "ButelSetVideoWindow", CharSet=CharSet.Ansi,
    CallingConvention = CallingConvention.StdCall)]
    public static extern int ButelSetVideoWindow(Int32 localWindow, Int32 remoteWindow);
    //将C# winform窗口转化成窗口句柄
    static public IntPtr PictureBoxtoIntPtr(ref PictureBox obj)
    {
        return obj.Handle;
    }

(4) C# WPF窗体转化成MFC窗体进行窗口重绘

     //带有窗口处理的C风格函数声明
       [DllImport(@"ButelAgentAdapter.dll", EntryPoint = "ButelSetVideoWindow", CharSet=CharSet.Ansi,
    CallingConvention = CallingConvention.StdCall)]
    public static extern int ButelSetVideoWindow(Int32 localWindow, Int32 remoteWindow);
     //将C# WPF窗口转化成窗口句柄
    public IntPtr FormtoIntPtr()
    {
        return ((HwndSource)PresentationSource.FromVisual(romateTextBox)).Handle;
    }
   在这里要说明下WPF 和 winform窗口的区别 wpf 窗口是依赖
   WPF和winform最大的区别在于WPF底层使用的DirectX,winform底层使用的是GDI+

(5)WPF 给 MFC窗口绘制无法绘制在活动区问题

首先上面已经说名了WPF 和winforms不是一种底层库,所以我们在使用中尽量给MFC的接口尽量传入winforms的句柄赋值
所以需用WPF 里面嵌入 winform控件在通过wpf将子控件赋值给mfc接口,办法如下使用usercontrol进行自定义控件绘制


    
        
    

private UserControl1 romateTextBox = new UserControl1(); 声明自定义控件
ButelSetVideoWindow((Int32)m_localpictureBox.Handle, (Int32)romateTextBox.Cv_Main.Handle);

将自定义控件里面的子控件赋值给C++接口进行图像绘制

(6)通过线程解决C#事件的异步调用

首先第一个问题说明了C++里面有回调函数,说明整个代码执行是在多线程情况下进行的,所以我们通过回调函数里面的状态来通知C#窗口接收数据(事件通知)
C#定义的三个事件

public event VideoCallStateChangedEventHandler VideoCallStateChanged;
  public event RecordCallbackEventHanlder RecordCallback;
  public event LocationChangedEventHanlder LocationChanged;
  //委托
    public delegate void SendToParent();
   private void sendVideoServer()
        {
            //线程的相关操作
            SendToParent send1 = new SendToParent(sendVideoConnect);
            this.BeginInvoke(send1);
        }
        private void sendVideoConnect()
        {
            VideoCallStateChanged(VideoCallState.Connected, "呼叫成功");
        }
        //通过C++执行回调函数后发送事件处理来驱动主程序数据改变
         private void butelconectevent_callback(int type, IntPtr data, string msg, string szExtendSignalInfo)
        {
            m_agentinfo = (AgentInfo)BytesToStruct(data, m_agentinfo);
            if (m_agentinfo.m_callEvent == (int)CallEvent.ON_INIT_SUCCESS)
            {
                linkthread = new Thread(new ThreadStart(ConnServer));
                linkthread.IsBackground = true;
                linkthread.Start();
            }
            if(m_agentinfo.m_callEvent == (int)CallEvent.ON_CONNECT)
            {
                Thread linkthreadobj = new Thread(new ThreadStart(sendVideoServer));
                linkthreadobj.IsBackground = true;
                linkthreadobj.Start();
            }
            if (m_agentinfo.m_callEvent == (int)CallEvent.ON_DISCONNECT)
            {
                Thread linkthreadobj = new Thread(new ThreadStart(sendVideoEndServer));
                linkthreadobj.IsBackground = true;
                linkthreadobj.Start();
            }
        }

(7)通过委托实现子线程处理主线程对象

         //委托
    public delegate void SendToParent();

    private void ConnServer()
    {
        //线程的相关操作
        SendToParent send1 = new SendToParent(ConnServerRes);
        this.BeginInvoke(send1);
    }
    private void ConnServerRes()
    {
        ButelTryEnableCamera(true);
        ButelSetVideoWindow((Int32)m_localpictureBox.Handle, (Int32)romateTextBox.Cv_Main.Handle); // 属于主线程,传入后相当于C++方面的线程需要处理
        //初始化音频和视频窗口
        var keystr = common.Common.GetValue("userconfig", "key");
        var username = common.Common.GetValue("userconfig", "username");
        var userpw = common.Common.GetValue("userconfig", "userpw");
        var alias = common.Common.GetValue("userconfig", "alias");
        //初始化坐席
        ButelTryLogin(keystr, username, userpw, alias);
    }

你可能感兴趣的:(C#)