总结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)]
    /// <summary>
    /// 初始化SDK
    /// </summary>
    /// <returns>第三方API</returns>
    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进行自定义控件绘制

<UserControl x:Class="Land.Survey.VideoCallInterface.common.UserControl1"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:wfi ="clr-namespace:System.Windows.Forms.Integration;assembly=WindowsFormsIntegration"   
             xmlns:wf ="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"  
             mc:Ignorable="d" 
             d:DesignHeight="768" d:DesignWidth="1024" Width="Auto" Height="Auto">
    <wfi:WindowsFormsHost MinHeight ="600" MinWidth="1024" HorizontalAlignment="Center" VerticalAlignment="Top" Margin="0,-5,0,0">
        <wf:PictureBox x:Name="Cv_Main" Margin="0,-100,0,0"></wf:PictureBox>
    </wfi:WindowsFormsHost>
</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);
    }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值