C# 从ActiveX调用时未释放WPF内存
我有一个类,它将1000个组合框填充到UI中,代码是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();
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按钮(链接到我的库关闭窗口)从另一个角度看,我认为它是可以关闭的。如果我还缺少什么,请告诉我。再次感谢。是的,是的