【发布时间】:2021-04-14 12:08:53
【问题描述】:
我正在使用wxAutomationObject 将数据导出到 MS Excel。我创建了一些帮助类 ExcelApp、ExcelWorkbook 等...所有这些都继承自 wxAutomationObject。
简单来说,ExcelApp如下:
class ExcelApp :public wxAutomationObject
{
public:
ExcelApp(WXIDISPATCH* dispatchPtr = NULL);
~ExcelApp() = default;
void Quit();
std::unique_ptr<ExcelWorkbook> CreateWorkbook(bool Visible = true);
std::vector<std::unique_ptr<ExcelWorkbook>> GetOpenedWorkbooks();
long GetNumberofOpenedWorkbooks() const;
private:
wxAutomationObject m_App;
};
构造函数实现如下:
ExcelApp::ExcelApp(WXIDISPATCH* dispatchPtr):wxAutomationObject(dispatchPtr)
{
if (!m_App.GetInstance("Excel.Application"))
throw std::exception("An instance of Excel object could not be created.");
}
为了创建一个工作簿,我习惯了以下代码:
std::unique_ptr<ExcelWorkbook> ExcelApp::CreateWorkbook(bool Visible)
{
auto wb = std::make_unique<ExcelWorkbook>();
bool suc = m_App.GetObject(*wb, "Workbooks.Add");
if (!suc)
return nullptr;
if (Visible)
m_App.PutProperty("Visible", true);
return std::move(wb);
}
整个 OLE 实现是动态菜单的一部分。菜单事件处理程序的部分代码是:
void MyFrame::OnExportToExcelButtonHandler(wxCommandEvent& event)
{
auto item = static_cast<wxMenu*>(event.GetEventObject());
std::unique_ptr<ExcelWorkbook> xlsWB ((ExcelWorkbook*)event.GetEventUserData());
bool CreateNewWB = false;
try
{
//Export either the entire workbook or the active worksheet to a new Workbook
if (xlsWB == nullptr)
{
auto xlsApp = ExcelApp();
xlsWB = std::move(xlsApp.CreateWorkbook());
CreateNewWB = true;
}
在导出数据和格式化方面,一切正常。但是,关闭创建的工作簿后Excel.exe 仍保留在任务栏列表中。我想知道我可能会错过什么?
顺便说一句,我尝试了 wxWidgets 附带的非常基本的示例,退出后似乎没有 Excel 的幽灵实例。我
我在使用 VS 2019 的 Windows 10 上使用 wxWidgets 3.1.4。
编辑 1:
生成动态菜单的功能区按钮:
void MyFrame::OnExportToExcelButtonClicked(wxRibbonButtonBarEvent& event)
{
auto excel = sys::win32::ole::ExcelApp();
auto PrepareMenu = [&](wxMenu* Menu)
{
try
{
wxMenuItem* item1 = Menu->Append(wxID_ANY, "New Excel Workbook");
item1->SetBitmap(wxArtProvider::GetBitmap(wxART_NEW));
Bind(wxEVT_COMMAND_MENU_SELECTED, &MyFrame::OnExportToExcelButtonHandler, this, item1->GetId());
auto wbs = excel.GetOpenedWorkbooks();
if (wbs.size() > 0)
{
for (int i = 0; i < wbs.size(); ++i)
{
auto ExcelWB = std::move(wbs[i]);
wxMenuItem* item1 = Menu->Append(wxID_ANY, ExcelWB->GetFullName());
Bind(wxEVT_COMMAND_MENU_SELECTED, &MyFrame::OnExportToExcelButtonHandler, this, item1->GetId(), wxID_ANY, ExcelWB.release());
}
}
}
}
问题似乎来自以下行:
Bind(wxEVT_COMMAND_MENU_SELECTED, &MyFrame::OnExportToExcelButtonHandler, this, item1->GetId(), wxID_ANY, ExcelWB.release());
ExcelWB 指针现在归 wxWidgets 系统所有,因此保留了一个引用,因此保留了幽灵 Excel.exe。
为了解决这个问题,我在下面的过程中添加了:
void MyFrame::OnExportToExcelButtonHandler(wxCommandEvent& event)
{
Unbind(wxEVT_COMMAND_MENU_SELECTED, &MyFrame::OnExportToExcelButtonHandler, this, event.GetId(), wxID_ANY, xlsWB.get());
但问题仍然存在。我不确定如何正确摆脱菜单现在拥有的指针。我用 shared_ptr 重新设计了一些部分,但没有帮助。
【问题讨论】:
-
没有 Quit() 调用。当 xlsApp 是局部变量时很难做到。
-
我用“Application.Quit”手动尝试过,鬼实例仍然存在。问题是当 xlsApp 被创建和销毁时,一切都很好;但是,只要我调用
xlsApp.CreateWorkbook,ghost 实例就会保留。 -
@macroland,那么示例如何关闭所有内容?你有什么不同的做法?你能让样本以最小的变化离开那个幽灵实例吗?另一个想法 - 尝试摆脱智能指针并使用普通的旧指针。
-
@Igor,该示例非常基础,只是创建了一个 wxAutomationObject 实例,并且仅适用于单个对象并且只执行几个命令。最初我使用原始指针,这种方法也存在问题。我在一篇文章中读到,如果某处仍有引用,则 Excel 实例不会终止。为了摆脱那个未知的“引用”,我改用智能指针。
-
您不能混合 ATL 和 wx 是否有技术原因?我会使用 ATL 智能指针来管理 COM 对象。我猜你可以为 UI 做 wx。当然,我会做一个测试,你所做的只是创建工作簿,而不是通过添加内容或东西来操纵它。在应用程序上调用 Quit() 之前,我会尝试在工作簿上调用 Close()。