Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何正确释放暴露于COM的.NET对象?_C#_.net_Com_Vb6_Com Interop - Fatal编程技术网

C# 如何正确释放暴露于COM的.NET对象?

C# 如何正确释放暴露于COM的.NET对象?,c#,.net,com,vb6,com-interop,C#,.net,Com,Vb6,Com Interop,因此,我试图向COM公开一个.NET对象,以便在我们的一个旧版VB6应用程序中使用。我相信我已经正确设置了.NET类,因为我现在可以在VB6中使用它,但是我不认为当我的VB6应用程序关闭/崩溃时我会释放对COM对象的所有引用。这是一个非常受事件驱动的.NET对象,我们已经看到了一些奇怪的问题,在这些问题中,.NET对象的前一个实例化接收到一个用于该对象的新实例化的事件 为了测试这一点,我在.NET对象中添加了跟踪编写器。这些跟踪打印出本地秒表的Stopwatch.elapsedmillesons

因此,我试图向COM公开一个.NET对象,以便在我们的一个旧版VB6应用程序中使用。我相信我已经正确设置了.NET类,因为我现在可以在VB6中使用它,但是我不认为当我的VB6应用程序关闭/崩溃时我会释放对COM对象的所有引用。这是一个非常受事件驱动的.NET对象,我们已经看到了一些奇怪的问题,在这些问题中,.NET对象的前一个实例化接收到一个用于该对象的新实例化的事件

为了测试这一点,我在.NET对象中添加了跟踪编写器。这些跟踪打印出本地秒表的
Stopwatch.elapsedmillesons
属性,我使用该属性跟踪.NET对象的生存期,以及在.NET对象的每个实例化上创建的新GUID

此对象的第一个实例化(通过VB6)如下所示:


0>>>实例化手机>进入代理登录>创建CTISoftphone>订阅PhoneEvent>订阅OnCreateCitconf>等待任务结果。>接收到的事件:onCreateTicConf:无消息>onCreateTicConf不为空,正在触发事件。>调用LoginAgent>Unsubscribing from onCreateTicConf>Entered AgentLogin>Creating CTISoftphone>Subscribing to PhoneEvent>Subscribing to onCreateTicConf>等待任务结果。>接收到的事件:OnCreateTicConf:无消息>OnCreateTicConf为空,无法触发事件。如果我理解,您正在.NET对象中接收事件。在某些时候,您必须订阅活动才能接收它们。你退订了吗?订阅添加了一个引用——无论是COM类型还是.NET类型。您仍在接收活动的事实向我表明,您仍然订阅了这些活动。我也建议取消订阅活动。我怀疑@JoeWillcoxson关于活动订阅的说法是正确的。您是否尝试过通过Visual Studio运行VB6应用程序来调试.Net类?i、 e.在项目属性->调试选项卡->启动操作->启动外部程序(VB6可执行文件)下。您提到了GC,您是否实现了强制收集?作为调试测试,您可以在类中实现析构函数,并验证它是否在强制GC集合上运行;这将允许您确定假定未引用的实例是否已发布。VB6早在.NET之前就已“管理”。将VB6对象的引用设置为nothing是无用的,除非底层COM对象引用系统有很多缺陷。因为它是一个.NET对象,所以这是无用的。事实上,由于这两个世界都是可以管理的,所以在正常的操作过程中,您不必做任何事情。问题(来自另一个实例的调用)可能是由您的代码造成的。有什么不受管理的吗?一切都是VB6和.NET吗?有自定义线程启动吗?COM对象线程模型?没有一些复制代码很难帮助更多人。好吧,所以我在
Dispose
中实现了
IDisposable
并取消订阅所有事件。这似乎是可行的,但我认为这更多的是一个副产品,而不是因为GC实际上正在拾取这些对象(例如,如果没有任何孤立对象具有实时事件,它们就不能错误地捕获任何内容)。这感觉更像是一种黑客行为,我仍然很想知道“正确”的方法来做到这一点。@SimonMourier,唯一使用的非托管资源是一个日志文件,我在记录时只掌握了它的句柄。关于线程,是的,.NET对象中还有其他线程-我们有一个线程正在使用来自电话系统的事件。我自己还没有设置线程模型,所以.NET对象仍然是MTA,VB6是STA(我不完全理解这些,所以可能我没有正确设置?)。我同意你的观点,这个问题可能在我的代码中,这是我第一次使用COM和.NET,而且我肯定我不是第一个遇到这个问题的人。
0:003> !dumpheap -stat -type MyApp.ClickToDial.Phone
Statistics:
      MT    Count    TotalSize Class Name
038492c4        1           24 MyApp.ClickToDial.Phone+<>c__DisplayClass77_0
03848b8c        1           32 MyApp.ClickToDial.Phone+OnCallReleasedHandler
03848aa8        1           32 MyApp.ClickToDial.Phone+OnCallTransferredHandler
038488e0        1           32 MyApp.ClickToDial.Phone+OnLoginAgentConfHandler
038487fc        1           32 MyApp.ClickToDial.Phone+OnLogoutAgentConfHandler
03848718        1           32 MyApp.ClickToDial.Phone+OnSendDtmfConfHandler
03848634        1           32 MyApp.ClickToDial.Phone+OnGetCtiDataConfHandler
03848550        1           32 MyApp.ClickToDial.Phone+OnInitiateConferenceConfHandler
0384846c        1           32 MyApp.ClickToDial.Phone+OnCancelConferenceConfHandler
03848388        1           32 MyApp.ClickToDial.Phone+OnMakeCallConfHandler
038482a4        1           32 MyApp.ClickToDial.Phone+OnCompleteConferenceConfHandler
038481c0        1           32 MyApp.ClickToDial.Phone+OnCallConferencedHandler
038480dc        1           32 MyApp.ClickToDial.Phone+OnErrorHandler
03847ff8        1           32 MyApp.ClickToDial.Phone+OnCallDroppedHandler
03847f14        1           32 MyApp.ClickToDial.Phone+OnCallEstablishedHandler
038489c4        3           96 MyApp.ClickToDial.Phone+OnCreateCtiConfHandler
03844ac4        1          116 MyApp.ClickToDial.Phone
Total 19 objects
public bool AgentLogin(string extension, string agentId, string password)
{
    writeTrace($"Entered {nameof(AgentLogin)}");
    _extension = extension;
    _agentId = agentId;
    try
    {
        writeTrace($"Creating {nameof(CTISoftphone)}");
        _phone = new CTISoftphone($"ext={extension},logFile={Settings.LogLocation}");
    }
    catch (Exception ex)
    {
        writeTrace($"Caught exception: {ex.GetType()} | {ex.Message}");
        //this one might get lost since we haven't subscribed to CTISoftphone.PhoneEvent yet.
        OnError?.Invoke($"{_wrapperLogPrefix} Unable to create CTISoftPhone. The error was: {ex.Message}");
        return false;
    }

    writeTrace($"Subscribing to {nameof(CTISoftphone.PhoneEvent)}");
    _phoneEventHandler = new CTISoftphone.PhoneEventHandler(cti_phone_event);
    _phone.PhoneEvent += _phoneEventHandler;

    var are = new AutoResetEvent(false);
    var task = new Task(() =>
    {
        try
        {
            writeTrace("Calling LoginAgent");
            _phone.LoginAgent(agentId, password);
        }
        catch (Exception)
        {
        }
    });
    task.ContinueWith((t) => are.Set());

    var task_handler = new OnCreateCtiConfHandler(task.Start);

    writeTrace("Subscribing to OnCreateCtiConf");
    OnCreateCtiConf += task_handler;
    writeTrace("Waiting for task result");
    are.WaitOne();
    writeTrace("Unsubscribing from OnCreateCtiConf");
    OnCreateCtiConf -= task_handler;

    return true;
}
private void cti_phone_event(CTISoftphone.CTIEvent type, string info)
{
    writeTrace($"Received event: {type} : {(string.IsNullOrEmpty(info) ? "No message" : info)}");
    switch (type)
    {
        case CTISoftphone.CTIEvent.OnMakeCallConf:
            OnMakeCallConf?.Invoke();
            break;
        case CTISoftphone.CTIEvent.OnCompleteConferenceConf:
            OnCompleteConferenceConf?.Invoke();
            break;
        case CTISoftphone.CTIEvent.OnCancelConferenceConf:
            OnCancelConferenceConf?.Invoke();
            break;
        case CTISoftphone.CTIEvent.OnCallEstablished:
            OnCallEstablished?.Invoke();
            break;
        case CTISoftphone.CTIEvent.OnCallDropped:
            OnCallDropped?.Invoke();
            break;
        case CTISoftphone.CTIEvent.OnCallConferenced:
            OnCallConferenced?.Invoke();
            break;
        case CTISoftphone.CTIEvent.OnError:
            OnError?.Invoke(info);
            break;
        case CTISoftphone.CTIEvent.OnInitiateConferenceConf:
            OnInitiateConferenceConf?.Invoke();
            break;
        case CTISoftphone.CTIEvent.OnGetCTIDataConf:
            OnGetCtiDataConf?.Invoke();
            break;
        case CTISoftphone.CTIEvent.OnSendDTMFConf:
            OnSendDtmfConf?.Invoke();
            break;
        case CTISoftphone.CTIEvent.OnLogoutAgentConf:
            OnLogoutAgentConf?.Invoke();
            break;
        case CTISoftphone.CTIEvent.OnLoginAgentConf:
            OnLoginAgentConf?.Invoke();
            break;
        case CTISoftphone.CTIEvent.OnCreateCTIConf:
            if (OnCreateCtiConf != null)
            {
                writeTrace("OnCreateCtiConf was not null, firing event");
                OnCreateCtiConf();
            }
            else
            {
                writeTrace("OnCreateCtiConf was null, CANNOT fire event");
            }
            break;
        case CTISoftphone.CTIEvent.OnReleaseCallConf:
            OnCallReleased?.Invoke();
            break;
        case CTISoftphone.CTIEvent.OnCallTransferred:
            OnCallTransferred?.Invoke();
            break;
        default:
            OnError?.Invoke($"{_wrapperLogPrefix} Received unknown CTI event type: {type}");
            break;
    }
}
public void Dispose()
{
    writeTrace("Dispose called");
    if (_phone != null)
        _phone.PhoneEvent -= _phoneEventHandler;
    _phone = null;
    _ctiData = null;

    OnCallEstablished = null;
    OnCallDropped = null;
    OnError = null;
    OnCallConferenced = null;
    OnCompleteConferenceConf = null;
    OnMakeCallConf = null;
    OnCancelConferenceConf = null;
    OnInitiateConferenceConf = null;
    OnGetCtiDataConf = null;
    OnSendDtmfConf = null;
    OnLogoutAgentConf = null;
    OnLoginAgentConf = null;
    OnCreateCtiConf = null;
    OnCallTransferred = null;
    OnCallReleased = null;
}

~Phone()
{
    writeTrace("Destructor called");
    Dispose();
}
0:003> !dumpheap -stat -type Avaya.APS.CTD.
Statistics:
      MT    Count    TotalSize Class Name
03846d64        1           32 Avaya.APS.CTD.CTISoftphone+PhoneEventHandler
03846bf0        1          108 Avaya.APS.CTD.CTISoftphone
Total 2 objects