• 通过CRM API for 3CXPhone与其他应用进行对接


    之前已经完成了一个3CXPhone的集成插件,是基于先前版本的。客户将3CX系统升级到V14以后,之前的插件失效了。因此需要重新研究新版本的CRM API使之重新工作,加上之前没有及时记录具体实施步骤,所以这次一边完成对V14 API的研究一边完成这篇博客,以帮助其他人和未来的自己。

    关于3CXPhone开发的文档并不多,目前唯一能找到的一篇干巴巴的文章就是3cx官方的一个在线文档:http://www.3cx.com/blog/docs/3cxphone-for-windows-api/,并特别强调3CX不对开发者提供API的支持(WTF???!!!)

    首先要强调3CXPhone for windows Plugin开发需要3CX Phone System PRO License.

    3CXPhone for Windows API可以完成以下工作:

    • 从你的应用程序中拨出电话
    • 在你的应用程序中得到电话状态变更的通知(分机登录,分级注销,连接PBX失败等)
    • 在你的应用程序中得到通话状态变更的通知(通话连接,通话断开,拨号,响铃等)

    准备工作

    要使用API,必须先安装3CXPhone for Windows,从而获取即将引用的MyPhoneCRMIntegration.dll。

    要测试插件,必须在测试机上安装3CXPhone for Windows并保证其能够成功运行。

    创建插件

    官方网页建议使用Visual Studio 2010/2012进行开发,我是用的是VS 2013。

    Step 1 - 准备解决方案:

      1.1 创建新的空解决方案,然后创建新的Windows Class Library项目,确定该项目目标框架版本为.net Framework 4.0,由于实际需要,我用到的目标框架为4.5.2。

      1.2 添加对MyPhoneCRMIntegration.dll的引用,该文件通常位于C:ProgramData3CXPhone for WindowsPhoneApp目录下

      1.3 删除VS自动创建的Class1.cs

    Step 2 - 安装并配置log4net:

      调式插件将是一个相对复杂和痛苦的过程,因为没有直观的前台界面可以实时反馈代码的运行状态,也很难通过断点观察程序的运行状况,所以通过Nuget安装log4net并进行必要的设置,一边之后调试过程中观察程序的运行状况

      2.1 在Nuget中找到log4net并安装

      2.2 建立log4net的config文件,如D3CallTriggerPlugin.log4net.config,设置为Always Copy,文件内容参考如下代码

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <configSections>
        <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
      </configSections>
      <log4net>
        <root>
          <level value="ALL" />
          <appender-ref ref="RollingFileAppender" />
        </root>
        <logger name="*">
          <!--<level value="DEBUG"/>-->
          <level value="ALL"/>
          <appender-ref ref="RollingFileAppender"/>
        </logger>
        <appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
          <layout type="log4net.Layout.PatternLayout">
            <conversionPattern value="%date [%thread] %-5level %logger [%ndc] - %message%newline"/>
          </layout>
          <file value="c:D3CallTriggerPlugin" />
          <appendToFile value="true" />
          <rollingStyle value="Date"/>
          <datePattern value="yyyy-MM-dd'.log'" />
          <maxSizeRollBackups value="90" />
          <staticLogFileName value="false" />
        </appender>
      </log4net>
    </configuration>

      2.3 AssemblyInfo.cs中添加如下代码:

    [assembly: log4net.Config.XmlConfigurator(ConfigFile = "D3CallTriggerPlugin.log4net.config", Watch = true)]

    Step 3 - PluginLoader类:

      3.1 在插件项目中创建新的类,命名为PluginLoader.cs

      3.2 用[MyPhonePlugins.CRMPluginLoader]修饰PluginLoader类

    [MyPhonePlugins.CRMPluginLoader]
    public class PluginLoader
    {
    }

      3.3 在类中定义如下局部变量:

    private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
    private static object lockObj = new object();
    private static Plugin pluginInstance = null;

      3.4 向PluginLoader类添加Loader方法,实例化Plugin并记录日志,用[MyPhonePlugins.CRMPluginInitializer]修饰Loader方法

    [MyPhonePlugins.CRMPluginInitializer]
    public static void Loader(MyPhonePlugins.IMyPhoneCallHandler callHandler)
    {
        log4net.Config.XmlConfigurator.Configure(); // Calling log4net's configuration
        log.Info("Loading plugin");
    
        lock (lockObj)
        {
            log.Info("Trying initialize pluginInstance");
            try
            {
                pluginInstance = new Plugin(callHandler);
                log.Info("Initlaized pluginInstance");
            }
            catch (Exception ex)
            {
                log.Error("Error occured while initializing pluginInstance", ex);
            }
        }
    }

    Step 4 - Plugin类:

      4.1 在插件项目中创建新的类,命名为Plugin.cs

      4.2 定义Plugin类,使之继承IDisposable接口

    public class Plugin : IDisposable
    {
    }

      4.3 在类中定义如下局部变量:

    private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
    
    IMyPhoneCallHandler callHandler;
    IExtensionInfo extensionInfo;

      4.4 Plugin的构造函数,将callHandler参数传递给内部变量,绑定各事件的响应函数,追加日志:

    public Plugin(IMyPhoneCallHandler callHandler)
    {
        try
        {
           this.callHandler = callHandler;
           callHandler.OnCallStatusChanged += callHandler_OnCallStatusChanged;
           callHandler.OnMyPhoneStatusChanged += callHandler_OnMyPhoneStatusChanged;
    
           log.Info("Plugin Initialized");
        }
        catch (Exception ex)
        {
        log.Error("Plugin initialization failed", ex);
        Dispose(); } }

      4.5 Dispose方法,解除诸事件响应函数的绑定:

    public void Dispose()
    {
        callHandler.OnCallStatusChanged -= callHandler_OnCallStatusChanged;
        callHandler.OnMyPhoneStatusChanged -= callHandler_OnMyPhoneStatusChanged;
    }

       4.6 实现事件响应函数:

    private void callHandler_OnMyPhoneStatusChanged(object sender, MyPhonePlugins.MyPhoneStatus status)
    {
        if (status == MyPhoneStatus.LoggedIn)
            extensionInfo = sender as IExtensionInfo;
    
        log.Info(String.Format("MyPhoneStatusChanged - Status='{0}' - Extension='{1}'", status, extensionInfo == null ? String.Empty : extensionInfo.Number));
    
        try
        {
            //Something fancy here
        }
        catch (Exception ex)
        {
            log.Error("Error occured while handling OnMyPhoneStatusChanged event", ex);
            Dispose();
        }
    }
    
    private void callHandler_OnCallStatusChanged(object sender, MyPhonePlugins.CallStatus callInfo)
    {
        log.Info(String.Format(
            "CallStatusChanged - CallID='{0}' - Incoming='{1}' - OtherPartyNumber='{2}' - State='{3}'", 
            callInfo.CallID, 
            callInfo.Incoming, 
            callInfo.OtherPartyNumber, 
            callInfo.State));
    
        try
        {
            //Something fancy here
        }
        catch (Exception ex)
        {
            log.Error("Error occured while handling OnCallStatusChanged event", ex);
            Dispose();
        }
    }

    Step 5 - 编译与部署

      5.1 编译整个项目

      5.2 在bin目录中复制除MyPhoneCRMIntegration.dll之外的所有文件至3CX Phone的安装目录,一般情况下为C:ProgramData3CXPhone for WindowsPhoneApp

      5.3 修改config文件,将插件名添加到配置文件中

      5.4 重启Soft Phone,尝试几个inbound/outbound calls,看日志文件和其他需要相应的地方是否及时获知电话状态/通话状态的变化

  • 相关阅读:
    我们的路该如何走?-序言
    [转贴]给想立志入行网络或已经初入行的朋友的建议(三)
    [转贴]给想立志入行网络或已经初入行的朋友的建议(二)
    为应用程序池defaultAppPool提供服务的进程在于world wide web publishing服务通信时遇到致命错误 进程id为1356. 数据字段包含错误号
    this.get_element .style为空或不是对象
    将linq查询转换为DataTable对象——学习笔记
    ASP.NET 未被授权访问所请求的资源。请考虑授予 ASP.NET 请求标识访问此资源的权限。
    常用改变选中行颜色
    DataTable写入Excel中 用Excel标准格式
    导出Excel出错:检索 COM 类工厂中 CLSID 为 {0002450000000000C000000000000046} 的组件失败
  • 原文地址:https://www.cnblogs.com/heuyang/p/4941586.html
Copyright © 2020-2023  润新知