2

注意:更新了问题底部的解决方案

我在使用 Lync 2013 SDK 的应用程序时遇到了一些问题。这是我看到的行为:

  • 如果在我启动应用程序时 Lync 已经在运行,那么调用LyncClient.GetClient()将返回一个有效的客户端对象。
  • 如果在我启动应用程序时 Lync 未运行,则调用LyncClient.GetClient()将抛出ClientNotFoundException. 我可以处理异常并启动一个计时器来 ping 客户端出现。后来,当我启动 Lync 时,LyncClient.GetClient()会返回一个有效的客户端对象。
  • 如果在我的应用程序运行时 Lync 退出,那么我可以通过多种方式检测这种情况并启动计时器以 ping 客户端返回。

到目前为止一切顺利,但问题出在:

  • 如果在我的应用程序运行时 Lync 消失了,那么后续调用LyncClient.GetClient()似乎会返回一个有效的客户端对象(即使 Lync 没有运行),但会尝试调用这个对象 throw InvalidCastException
  • 即使在重新启动 Lync 后,后续调用仍会返回一个在我尝试访问它时LyncClient.GetClient()抛出的对象。InvalidCastException

异常详情如下:

无法将“System.__ComObject”类型的 COM 对象转换为接口类型“Microsoft.Office.Uc.IClient”。此操作失败,因为 IID 为“{EE9F3E74-AC61-469E-80D6-E22BF4EEFF5C}”的接口的 COM 组件上的 QueryInterface 调用因以下错误而失败:RPC 服务器不可用。(来自 HRESULT 的异常:0x800706BA)。

我在这里尝试了建议:Lync 2013 SDK 开发问题疑难解答。它似乎没有任何区别。在 Lync 运行并再次登录后的几分钟内,我继续获得无效的客户端对象。

这不会每次都发生。如果在我的应用程序启动时 Lync 已经在运行(即第一次调用成功),该问题似乎一直在发生LyncClient.GetClient()。另一方面,如果我在我的应用程序已经启动 Lync 之后,在多次重新启动 Lync 时一切正常运行(即第一次尝试GetClient()失败。)

有没有其他人见过这个?有什么建议么?


更新并尝试卸载 AppDomain

我尝试使用此代码获取客户端对象,但行为完全相同:

public class LyncClientProvider
{
    private AppDomain _domain = CreateDomain();

    public LyncClient GetLyncClient()
    {
        if (_domain == null) CreateDomain();
        var client = GetClient();
        if (client != null && client.Capabilities != LyncClientCapabilityTypes.Invalid) return client;

        // Reload AppDomain
        if (_domain != null) AppDomain.Unload(_domain);
        _domain = CreateDomain();
        return GetClient();
    }

    private LyncClient GetClient()
    {
        if (_domain == null) return null;
        return ((InternalProvider)
            _domain.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName,
                typeof (InternalProvider).FullName)).GetLyncClient();
    }

    private static AppDomain CreateDomain()
    {
        return AppDomain.CreateDomain("LyncClientCreator", null, new AppDomainSetup
        {
            ApplicationBase = AppDomain.CurrentDomain.BaseDirectory,
            ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile
        });
    }

    [Serializable]
    private class InternalProvider
    {
        public LyncClient GetLyncClient()
        {
            try
            {
                return LyncClient.GetClient();
            }
            catch (Exception)
            {
                return null;
            }
        }
    }
}

使用专用线程的解决方案示例

感谢来自djp的输入,我实现了这个简单的类来提供 a LyncClient,并且重新连接现在可以正常工作。

public class LyncClientProvider
{
    private Thread _thread;
    private volatile bool _running;
    private volatile bool _clientRequested;
    private volatile LyncClient _lyncClient;

    private void MakeClientLoop()
    {
        while (_running)
        {
            if (_clientRequested) MakeClient();
            Thread.Sleep(100);
        }
    }

    private void MakeClient()
    {
        try
        {
            _lyncClient = LyncClient.GetClient();
        }
        catch (Exception)
        {
            _lyncClient = null;
        }
        _clientRequested = false;
    }

    public void Start()
    {
        if (_running) return;
        _running = true;
        _thread = new Thread(MakeClientLoop);
        _thread.Start();
    }

    public void Stop()
    {
        if (!_running) return;
        _running = false;
        _thread.Join();
        _clientRequested = false;
    }

    public LyncClient GetLyncClient()
    {
        if (!_running) return null;
        _clientRequested = true;
        while (_clientRequested) Thread.Sleep(100);
        return _lyncClient;
    }
}
4

3 回答 3

1

我有同样的问题,对我来说解决方法是确保每次调用都LyncClient.GetClient()发生在同一个线程上

于 2015-07-10T01:44:21.687 回答
0

“•如果在我的应用程序运行时 Lync 消失了,那么对 LyncClient.GetClient() 的后续调用似乎会返回一个有效的客户端对象(即使 Lync 没有运行),但尝试调用此对象会引发 InvalidCastException。”

您能否详细说明“如果 Lync 离开”是什么意思。您的意思是您已退出 Lync 或您已退出 Lync(即没有运行 Lync 进程)?如果您刚刚退出 Lync,LyncClient则不会抛出异常。您可以收听LyncClient.StateChanged事件并检查clientStateChangedEventArgs.NewState事件。

private void LyncClient_StateChanged(object sender, ClientStateChangedEventArgs e) {
        switch (e.NewState) {
                case ClientState.Invalid:
                case ClientState.ShuttingDown:                  
                    this.IsLyncStarted = false;
                    this.IsLyncSignedIn = false;
                    break;
                case ClientState.SignedOut:
                case ClientState.SigningIn:
                case ClientState.SigningOut  
                    this.IsLyncStarted = true;
                    this.IsLyncSignedIn = false;
                    break;
                case ClientState.SignedIn:
                    this.IsLyncStarted = true;
                    this.IsLyncSignedIn = true;
                    break;
            }
            if (!this.IsLyncStarted || !this.IsLyncSignedIn) {                    
                // Do relevant operation
                // Show error message to user - Lync is not present
            }
    }

当用户退出 Lync - 我启动类似于您的方法的计时器并尝试初始化 Lync,如下所示:

private void InitializeLync() {
        try {
            if (this.LyncClient == null || this.LyncClient.State == ClientState.Invalid) {
                this.LyncClient = LyncClient.GetClient();

                if (this.LyncClient != null) {
                    this.LyncClient.StateChanged += this.LyncClient_StateChanged;       
                }
            }

        } catch (Exception ex) {
            // Show message to user that Lync is not started
        }
}

这对我有用。可能是机器特定的问题。

于 2015-06-06T06:11:52.853 回答
-2

我假设您正在与 Lync 服务器断开连接。该LyncClient.GetClient()方法可能仍然有效,因为您有一些挥之不去的变量,但您将无法使用它执行任何 Lync 操作。

我认为查看 UI 抑制内容会有所帮助。 https://msdn.microsoft.com/en-us/library/office/hh345230%28v=office.14%29.aspx

于 2015-06-05T13:30:30.380 回答