【问题标题】:OLE Automation - Ghost Excel Instance in Task ManagerOLE 自动化 - 任务管理器中的 Ghost Excel 实例
【发布时间】: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()。

标签: c++ wxwidgets ole


【解决方案1】:

您可能已经这样做了,但只是为了确定。

在调试此类事情时,请确保应用程序实例始终可见,以便您可以看到它何时卡住等待用户输入,例如,要求保存修改后的工作簿,防止其退出。

我已经编写了基于 wxWidgets 的 MS Excel 自动化包装器 wxAutoExcel(它使用共享指针而不是 Excel 对象的唯一指针)并且我从未遇到过应用程序幽灵,除非我在调试时终止了我的应用程序,但没有处理 Excel 对象.

顺便说一句,我不明白为什么您的 ExcelApp 都源自 wxAutomationObject 并且将 wxAutomationObject 作为成员变量 (m_App)。 ExcelApp ctor 中使用的 IDispatch 是什么?

【讨论】:

  • 我添加了更多信息,请查看我对问题的编辑。
  • 关于你的问题:如果我不使用m_App,那么程序就会崩溃。但是,您是对的,如果我使用m_App,我不必从wxAutomationObject 派生它。坦率地说,IDispatch 目前除了在继承链中传递给wxAutomationObject 的构造函数外,没有任何用途。
  • 抱歉,即使使用了十年前的 C++11,我也不是最新的,所有这些移动语义让我感到恶心。例如,wxAutomationObject 似乎不支持直接移动,因为 wxWidgets 不使用现代 C++。但是,当你移动它时,它的 dtor 被调用了吗?这会在其IDispatch 上调用Release(),并且ctor 从不调用AddRef()...但这实际上会导致对象过早释放而不是剩余?
  • 我也很惊讶您不仅可以在属性上调用wxAutomation::GetObject(),还可以使用方法(“Workbooks.Add”)?!抱歉,关于事件对象的问题我没有答案。而且这是我第一次使用 StackOverflow 的 cmets,我发现它非常有限,所以我不想在这里再写了。
【解决方案2】:

最后,我似乎通过以下步骤解决了这个问题:

  1. 现在使用 对话框,而不是 菜单;因此,菜单不再拥有指针。
  2. 所有wxAutomationObject 继承对象都通过std::unique_ptr 传递。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-07-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-19
    相关资源
    最近更新 更多