C# 从ActiveX调用时未释放WPF内存

C# 从ActiveX调用时未释放WPF内存,c#,wpf,memory-leaks,activex,C#,Wpf,Memory Leaks,Activex,我有一个类,它将1000个组合框填充到UI中,代码是 namespace ActiveXMemory { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent();

我有一个类,它将1000个组合框填充到UI中,代码是

namespace ActiveXMemory
{
 /// <summary>
 /// Interaction logic for MainWindow.xaml
 /// </summary>
 public partial class MainWindow : Window
 {
    public MainWindow()
    {
        InitializeComponent();
        PopulateControls();
    }
    public void PopulateControls() {
        var newGrid = new Grid();
        for (var i = 0; i < 1000; i++)
        {
            ComboBox newcombo = new ComboBox();
            newGrid.Children.Add(newcombo);
        }
        this.Content = newGrid;
    }

    public bool OpenWindow(bool isRunning)
    {
        Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() =>
        {
            try
            {
                this.ShowDialog();
            }
            catch (Exception exp)
            {
                Console.WriteLine(exp.Message);
            }
        }));
        return true;
    }

    public bool CloseWindow()
    {
        Dispatcher.Invoke(DispatcherPriority.Render, new Action(() =>
        {
            try
            {
                this.Close();
            }
            catch (Exception exp)
            {
                //Console.WriteLine(exp.Message);
            }
        }));

        return true;
    }

    private void Window_Closed(object sender, EventArgs e)
    {
        var grid = Content as Grid;

        var children = grid.Children;

        while (children.Count > 0)
        {
            var child = children[0];
            grid.Children.Remove(child);
            child = null;
        }
        grid = null;
    }

 }
}
}

然后我创建了一个演示项目

  ActiveXLib activeXRef;
    public MainWindow()
    {
        InitializeComponent();
    }

    private void startButton_Click(object sender, RoutedEventArgs e)
    {
        activeXRef = new ActiveXLib();
        activeXRef.Initialize();

    }

    private void stopButton_Click(object sender, RoutedEventArgs e)
    {
        activeXRef.CloseActiveX();
        GC.Collect();
    }
这里的问题是,每当我单击开始和停止按钮时,内存就会不断增加,应用程序第一次启动时创建的内存(17MB)没有释放,即使我删除了组合框并将网格内容设置为空。甚至GC.Collect()也没有效果,因为它只将垃圾添加到队列中。这有什么办法吗

我还尝试像中一样刷新内存,但即使这样,内存仍然保持不变(因为我不确定当前进程的句柄是否来自demo project/ActiveXMemory)

编辑

我包括了这一行

 this.Dispatcher.Invoke(DispatcherPriority.Render, GCDelegate);
到“窗口关闭”事件

现在,内存被清除

但我现在面临的一个问题是,如果我试图立即关闭(调用CloseActiveX()),那么这种情况就不会发生


上面的代码仍然锁定了内存,我不理解其中的区别,这只是另一个事件,有人对此有任何想法吗?

目前我对ActiveX部分没有任何评论(将再次检查并让您知道),但我可以建议与WPF代码相关的内容

这将使组合项目成为一次性的

public class DispCombo : ComboBox, IDisposable
{
    public void Dispose()
    {
        GC.SuppressFinalize(this);
    }
}
public class DispGrid : Grid, IDisposable
{
    public void Dispose()
    {
        GC.SuppressFinalize(this);
    }
}
您将填充一次性控件

public void PopulateControls()
{
    var newGrid = new DispGrid();
    for (var i = 0; i < 1000; i++)
    {
        DispCombo newcombo = new DispCombo();
        newGrid.Children.Add(newcombo);
    }
    this.Content = newGrid;
}
也就是说,现在您可以将WPF视图包装到using子句中

public class MainLibrary
    {
        public bool OpenWindow() //found no ref to bool isRunning
        {

            using (var mw = new MainWindow())
            {
                mw.ShowDialog();
                mw.Close();
            }

            return true;
        }

        public bool CloseWindow()
        {
            GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
            GC.WaitForPendingFinalizers();
            GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
            return true;
        }
    }
垃圾收集即将结束。 这些是我在这些更改后的内存快照,(1)在开始时拍摄,然后(2)在
OpenWindow
中的主
ShowDialog
之后拍摄,最后(3)在
CloseWindow

编辑

最后,如果您需要启动一个任务,调用Dispatcher,这里就是函数

public bool CloseWindow()
        {
            winClose = () =>
            {
                mw.Dispatcher.Invoke(() =>
                   {
                       mw.Close();
                       mw.Dispose();
                   });
            };
            cleanProc = () =>
            {
                mw.Dispatcher.Invoke(() =>
                {
                    GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
                    GC.WaitForPendingFinalizers();
                    GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
                });
            };
            Task.Run(() =>
           {
               winClose.Invoke();
           }).ContinueWith(x =>

               {
                   cleanProc.Invoke();
               });

            return true;
        }


目前,我对ActiveX部分没有任何评论(将再次检查并让您知道),但我可以建议一些与WPF代码相关的内容

这将使组合项目成为一次性的

public class DispCombo : ComboBox, IDisposable
{
    public void Dispose()
    {
        GC.SuppressFinalize(this);
    }
}
public class DispGrid : Grid, IDisposable
{
    public void Dispose()
    {
        GC.SuppressFinalize(this);
    }
}
您将填充一次性控件

public void PopulateControls()
{
    var newGrid = new DispGrid();
    for (var i = 0; i < 1000; i++)
    {
        DispCombo newcombo = new DispCombo();
        newGrid.Children.Add(newcombo);
    }
    this.Content = newGrid;
}
也就是说,现在您可以将WPF视图包装到using子句中

public class MainLibrary
    {
        public bool OpenWindow() //found no ref to bool isRunning
        {

            using (var mw = new MainWindow())
            {
                mw.ShowDialog();
                mw.Close();
            }

            return true;
        }

        public bool CloseWindow()
        {
            GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
            GC.WaitForPendingFinalizers();
            GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
            return true;
        }
    }
垃圾收集即将结束。 这些是我在这些更改后的内存快照,(1)在开始时拍摄,然后(2)在
OpenWindow
中的主
ShowDialog
之后拍摄,最后(3)在
CloseWindow

编辑

最后,如果您需要启动一个任务,调用Dispatcher,这里就是函数

public bool CloseWindow()
        {
            winClose = () =>
            {
                mw.Dispatcher.Invoke(() =>
                   {
                       mw.Close();
                       mw.Dispose();
                   });
            };
            cleanProc = () =>
            {
                mw.Dispatcher.Invoke(() =>
                {
                    GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
                    GC.WaitForPendingFinalizers();
                    GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
                });
            };
            Task.Run(() =>
           {
               winClose.Invoke();
           }).ContinueWith(x =>

               {
                   cleanProc.Invoke();
               });

            return true;
        }


我想我已经得到了解决这个问题的建议,尽管我不知道你的MainWindow.Open/CloseWindow()方法是什么样子。使用线程当然是问题的一部分,为了防止泄漏具有线程亲缘关系的内部WPF管道对象,您必须做一件非直观的事情

必须关闭线程的调度程序。当UI线程终止时,这通常只在WPF应用程序中发生一次。同样非常重要的是,调度器执行调度,WPF严重依赖它来确保“弱事件”实际上是弱的

不泄漏的MainWindow的示例实现:

public class MainWindow : Window {
    public void OpenWindow(bool noidea) {
        this.ShowDialog();

    }
    public bool CloseWindow() {
        this.Dispatcher.Invoke(() => {
            this.Dispatcher.InvokeShutdown();   // <== Important!
        });
        return true;
    }
}
公共类主窗口:窗口{
公共空间开放窗口(布尔诺伊达){
this.ShowDialog();
}
公共窗口关闭窗口(){
this.Dispatcher.Invoke(()=>{

this.Dispatcher.InvokeShutdown();//我想我已经得到了解决这个问题的方法,尽管我不知道MainWindow.Open/CloseWindow()方法是什么样子的。使用线程当然是问题的一部分,为了防止泄漏具有线程关联的内部WPF管道对象,必须做一件非直观的事情

必须关闭线程的调度程序。当UI线程终止时,这通常只在WPF应用程序中发生一次。调度程序执行调度也是非常重要的,WPF严重依赖它来确保“弱事件”实际上是弱的

不泄漏的MainWindow的示例实现:

public class MainWindow : Window {
    public void OpenWindow(bool noidea) {
        this.ShowDialog();

    }
    public bool CloseWindow() {
        this.Dispatcher.Invoke(() => {
            this.Dispatcher.InvokeShutdown();   // <== Important!
        });
        return true;
    }
}
公共类主窗口:窗口{
公共空间开放窗口(布尔诺伊达){
this.ShowDialog();
}
公共窗口关闭窗口(){
this.Dispatcher.Invoke(()=>{

this.Dispatcher.InvokeShutdown();//在创建新对象之前(如果activeXRef当然不为null),您是否尝试过封送处理.ReleaseComObject(activeXRef)。我尝试过,获取ArgumentException“{”对象的类型必须是uuuComObject或从uuuComObject派生。\r\n参数名称:o“}”哦,是的,当然,对不起,我忽略了你的代码,认为IActiveXLib不是.net。你可以用WinDbg分析你的程序,这很酷,可以理解发生了什么:或者我尝试了内存分析器,看到了物理内存中仍然存在的内容,但我不知道如何修复它。你是否尝试过封送。ReleaseComObject(activeXRef)在创建一个新对象之前(如果activeXRef当然不是null)。我尝试了获取ArgumentException“{”对象的类型必须是_ComObject或派生自_ComObject。\r\n参数名称:o“}”哦,是的,当然,对不起,我忽略了你的代码,认为IActiveXLib不是.net。你可以用WinDbg分析你的程序,这很酷,可以理解发生了什么:或者我尝试了内存分析器,看到了物理内存中仍然存在的东西,但我不知道如何修复它感谢你的回答。实际上,我需要主窗口e可以从演示项目中关闭,但是如果我在没有调度程序的情况下显示对话框,那么关闭必须由用户事件显式完成ActiveX库中的对象必须作为实例,才能在主窗口中进行任何其他更改,你是对的。这就是我添加编辑的原因。很抱歉不清楚。我已尝试使用wpf按钮(链接到我的库关闭窗口)从另一个角度看,我认为它是可以关闭的。如果我还缺少什么,请告诉我。再次感谢。是的,是的