Integrate the OpenOffice.org Power in a WTL (or Other Native Win32) Application

Integrate the OpenOffice.org Power in a WTL (or Other Native Win32) Application

OOo

Presentation

OpenOffice.org – OOo – is a free Open Software Office suite, competing with Microsoft-Office for many business tasks. Using it to work with Microsoft-Office documents may be an alternative to the acquisition of the Microsoft-Office suite. OOo is free. OOo 3.0 was released in October 2008, and is a mature and impressive piece of software.

This article should enable you to start integrating the OpenOffice.org power in a WTL (or other native Win32) application.

Preview

First, you need to download and install OpenOffice.org 3.0 from OpenOffice.org, if not already done.

Next, extract the WtlOOo.exe application from WtlOOo_exe.zip to any suitable location, and run it. Try opening any Microsoft-Office file you have at hand, and check the Print and Print Preview abilities as well as the Save to PDF function.

These OOo built-in functionalities are called through a simple WTL command handler using the <a href="#COOoCtrl">WTL::COOoCtrl</a> class.

C++
class CMainFrame : public CFrameWindowImpl<CMainFrame>, public CUpdateUI<CMainFrame>,
                   public CMessageFilter, public CIdleHandler
{
//...
        COOoCtrl m_view;

//...
        BEGIN_MSG_MAP(CMainFrame)
//...
                COMMAND_ID_HANDLER(ID_FILE_SAVE, OnSaveToPDF)

//...
        LRESULT OnSaveToPDF(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/,
                BOOL& /*bHandled*/)
        {
                m_view.ExecuteId(ID_OOo_PDF);
                return 0;
        }
//...

Prepare the OOo SDK

To use the Uno API and access the OOo capabilities in your applications, you should go through the following steps:

  • Download and install the OOo 3.0 SDK from OpenOffice.org to a <OOoSdkDir> on your development station. On Vista systems, avoid installing in C:\Program Files or any other UAC protected folder.
  • Generate the OOo SDK C++ code matching your OOo installation:
    • Extract OOoCpp.js and OOo.vsprops from OOo.zip to <OOoSdkDir>\sdk.
    • Run OOoCpp.js. This script will create a <OOoSdkDir>\includecpp folder, run the SDK cppumaker tool with the correct parameters to populate this folder with the C++ headers matching the uno type libraries types.rdb and offapi.rdbcppumaker is located in <OOoSdkDir>\bin and needs the OOo DLLs located in <OOoInstallDir>\URE\bin. The source type libraries are in different folders under <OOoInstallDir>, so this script is the way to avoid many frustrations.

      OOoCpp.js will also update in OOo.vsprops, the OOoSdkDir variable to your <OOoSdkDir> installation folder.

Adding this updated OOo.vsprops property set to any Win32 project in Visual Studio/VCExpress 2005/2008 is enough to use the OOo SDK in your C++ projects.

OOo.vsprops defines the preprocessor macro WNT, sets the correct search paths and names for headers and libraries, and sets delayed loading of sal3.dll, cppu3.dll, and cppuhelper3MSC.dll. Thus, the application can add to its runtime path the OOo DLLs located in <OOoInstallDir>\URE\bin and found in the Registry, before actually calling them.

C++
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioPropertySheet
        ProjectType="Visual C++"
        Version="8.00"
        Name="OOo"
        >
        <Tool
                Name="VCCLCompilerTool"
                AdditionalIncludeDirectories=
                 "&quot;$(OOoSdkDir)\include&quot;;&quot;$(OOoSdkDir)\includecpp&quot;"
                PreprocessorDefinitions="WNT"
        />
        <Tool
                Name="VCLinkerTool"
                PerUserRedirection="true"
                AdditionalDependencies=
                 "isal.lib icppu.lib icppuhelper.lib isal.lib
                  isalhelper.lib ireg.lib irmcxt.lib stlport_vc71.lib"
                AdditionalLibraryDirectories="$(OOoSdkDir)\lib"
                DelayLoadDLLs="sal3.dll;cppu3.dll;cppuhelper3MSC.dll"
                DataExecutionPrevention="1"
        />
        <UserMacro
                Name="OOoSdkDir"
                Value="<OOoSdkDir>\sdk"
                PerformEnvironmentSet="true"
        />
</VisualStudioPropertySheet>

Compile WtlOOo.exe

WTLOOo.sln is a VC2005 Express solution which will upgrade if opened with VS2008 or VC2008 Express. You should be ready now to compile it.

As this article is in the WTL section, I assume that your Visual Studio/VCExpress 2005/2008 compiler is correctly set to access WTL 8.0.

  • Download WtlOOo.zip and extract the project files to your choice of <WtlOOoProj> folder.
  • Copy your updated OOo.vsprops from <OOoSdkDir>\sdk to <WtlOOoProj>.
  • Compile, run, and enjoy.

Working in WindowsTM with the uno API

This article is not a tutorial about the uno API. Consult the OpenOffice.org Developer's Guide and associated links for documentation on the namespaces and classes used here.

The code in atlOOo.h is split in two namespaces:

  • The OOo namespace is pure Win32 code, and does not require ATL or WTL. It is composed of helper functions and two classes:
    • Ooo::DocWindow.
    • Ooo::TerminationMonitor.
  • The WTL::COOoCtrlT<> class requires the OOo namespace code, and is based both on OOo:DocWindow and ATL::CWindow.

OOo Functions

  • uno helper functions:
    • OOo::GetBootContext(), bootstrap context accessor: gets a com::sun::star::uno::XComponentContext through the ::cppu::bootstrap() call.
    • OOo::GetComponentFactory(), main factory accessor.
    • OOo::Instance(), uno service accessor helper.
  • Runtime helper functions:
    • LRESULT OOo::FindInstallAndSetPath() adds the OOo DLLs location, if found in the registry, to the application PATH environment variable.
    • const bool OOo::IsAvailable() performs a runtime connection test: it calls FindInstallAndSetPath(), and if successful, calls GetBootContext(), thus linking to the OOo DLLs after setting their access path.

      Calling OOo::IsAvailable() at the beginning of your application code is the best way to trigger the delayed loading of the OOo DLLs. If OOo::IsAvailable() returns false, you cannot use OOo in this runtime environment.

  • com::sun::star::util::URL parsers: OOo uses its URL class for many descriptors, not only file specifiers:
    • URL OOo::GetURL(LPCWSTR sUrl) returns a generic com::sun::star::util::URL from a LPCWSTR (for instance, a command string as ".uno:ExportDirectToPDF").
    • URL OOo::GetPathURL(LPCWSTR sPath) returns a correctly filled com::sun::star::util::URL from a LPCWSTR Win32 path name.
  • com::sun::star::beans::PropertyValue and com::sun::star::uno::Sequence<com::sun::star::beans::PropertyValue> helpers:
    • template <typename T> PropertyValue OOo::PropVal(LPCWSTR sName, T t_Val) returns a com::sun::star::beans::PropertyValue from a LPCWSTR and a value of any type.
    • template <typename T> Sequence<PropertyValue> OOo::SeqPropVal(LPCWSTR sName, T t_Val) returns an element com::sun::star::uno::Sequence containing a com::sun::star::beans::PropertyValue<T> named sName.
  • com::sun::star::uno::Any/HWND converters:
    • Any OOo::HWNDToAny(HWND hWnd) and
    • HWND OOo::AnyToHWND(Any& any)
  • File loadability test:

    • bool OOo::CanLoad(LPCWSTR sPath) checks that the Win32 sPath named file can be loaded as a uno document.

OOo::TerminationMonitor

A single instance of OOo sOffice.exe is running on a system, and the user may end it with the Quit menu command. As long as a OOo::TerminationMonitor object is alive, it can prevent sOffice.exe termination by user or program action (which would unload the OOo DLLs and crash our application).

The default constructor: OOo::TerminationMonitor(bool bAllow = false, bool bRegister = true) is designed to prevent OOo termination during the object's lifetime. A simple default instantiation of a TerminationMonitor will register it (bRegister = true) with sOffice.exe and prevent (bAllow = false) user termination.

C++
if (OOo::IsAvailable())
{
    OOo::TerminationMonitor tm; // prevent user OOo termination ...
     
} // ...until here

In most cases this is enough, if you need smaller grained operation construct without registration and later use the bool Register(bool bRegister) and bool AllowTermination(bool bAllow) members.

OOo::DocWindow: A uno Document Window Wrapper Class

This standalone class wraps a OOo window containing a OOo document. It has no explicit constructor and, as the only (private) data members are smart pointers, no explicit destructor. The Window related members are:

  • bool OOo::DocWindow::Create(HWND hWndParent, LPCWSTR sFrameName = NULL, bool bVisible = true) creates a uno window as a child of hWndParent, and inserts it in the uno architecture. On success, returns true, otherwise false.
  • HWND OOo::DocWindow::GetHWND() returns the Win32 HWND of the DocWindowNULL if not created.
  • void OOo::DocWindow::SetWindowRect(RECT& rect) sets the position of the DocWindow through the OOo procedures.
  • bool OOo::DocWindow::UIShow(LPCWSTR sURL, bool bShow) sets the UI elements visibility: sURL is a visual element identifier like "private:resource/menubar/menubar".

    The Document related members are:

  • C++
    bool OOo::DocWindow::OpenDocument(URL& url, Sequence<PropertyValue>& sProperties)

    opens a document from the requested URL with the sProperties PropertyValue: for instance, PropVal(L"ReadOnly", true).

  • C++
    bool OOo::DocWindow::OpenNewDocument(LPCWSTR sName, bool bReadOnly = true)

    prepares an empty document URL like "private:factory/scalc" from sName and a Sequence<PropertyValue> from PropVal(L"ReadOnly", bReadOnly) before calling OpenDocument(URL&, Sequence<PropertyValue>&).

  • C++
    bool OOo::DocWindow::OpenFileDocument(LPCWSTR sPath, bool bReadOnly = true)

    does the same with a file path name.

  • C++
    bool Ooo::DocWindow::CloseDocument()
  • C++
    bool OOo::DocWindow::HasDocument()
  • C++
    LPCWSTR Ooo::DocWindow::GetDocTitle()
  • C++
    bool OOo::DocWindow::ExecuteURL(URL& url, Sequence<PropertyValue>& sProperties, 
    	Reference<XStatusListener> xListener = NULL)

    executes a command URL like ".uno:ExportDirectToPDF". Valid command names may be found in this article.

WTL::COOoCtrl: A WTL Wrapper for OOo::DocWindow

This very simple class derives from ATL::CWindow and OOo::DocWindow.

Some CWindow members are overridden to avoid misuse: you cannot Attach() or Detach() a COOoCtrl.

The operating Create() member calls OOo::DocWindow::Create(), and on success sets CWindow::m_hWnd to the result of OOo::DocWindow::GetHWND().

C++
HWND Create(HWND hWndParent, LPCWSTR szWindowName = NULL, bool bVisible = true)
{
    ATLASSERT(m_hWnd == NULL);
    ATLASSERT(::IsWindow(hWndParent));

    if (DocWindow::Create(hWndParent, szWindowName, bVisible))
        m_hWnd = GetHWND();

    ATLASSERT(IsWindow());

    return m_hWnd;
}

The other Create() members call this one and ignore most of their parameters.

WTL::COOoCtrl adds two members to its parents, allowing the usage of resource strings for UI visibility and command execution.

C++
bool ExecuteId(UINT uIdOOoCmd)
{
    CTempBuffer<WCHAR> sCmd(64);
    AtlLoadString(uIdOOoCmd, sCmd, 64);
    return ExecuteURL(GetURL(sCmd), Sequence<PropertyValue>());
}

bool UIShowId(UINT uIdOOoUIPart, bool bShow)
{
    CTempBuffer<WCHAR> sUIPart(64);
    AtlLoadString(uIdOOoUIPart, sUIPart, 64);
    return UIShow(sUIPart, bShow);
}

WtlOOo Usage of COOoCtrl

WtlOOo starts with a simple (WTL AppWizard generated) multi threaded SDI application, using WTL::COOoCtrl as view.

The command and UI descriptor string resources are added to WtlOOo.rc.

C++
STRINGTABLE
BEGIN
    IDR_MAINFRAME           "WtlOOo"
    ID_OOo_PDF              ".uno:ExportDirectToPDF"
    ID_OOo_PRINT            ".uno:PrintDefault"
    ID_OOo_PRINT_PREVIEW    ".uno:PrintPreview"
    ID_OOo_UI_MENUBAR       "private:resource/menubar/menubar"
    ID_OOo_UI_STANDARDBAR   "private:resource/toolbar/standardbar"
END

Before running the message loop, we check OOo::IsAvailable(); if false, we abort with a message box, otherwise we instantiate a Ooo::TerminationMonitor and run the application.

C++
// WtlOOo.cpp : main source file for WtlOOo.exe
//...
int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/,
      LPTSTR lpstrCmdLine, int nCmdShow)
{
//...
    // BLOCK: Run application
    if (OOo::IsAvailable())
    {
        OOo::TerminationMonitor tm;
        CWtlOOoThreadManager mgr;
        nRet = mgr.Run(lpstrCmdLine, nCmdShow);
    }
    else
        AtlMessageBox(NULL, L"OpenOffice.org 3 not found!", IDR_MAINFRAME,
                MB_OK | MB_ICONERROR);
//...

In CMainFrame, we declare a COOoCtrl m_view member.

C++
// MainFrm.h : interface of the CMainFrame class
//
/////////////////////////////////////////////////////////////////////////////

#pragma once

class CMainFrame : public CFrameWindowImpl<CMainFrame>, public CUpdateUI<CMainFrame>,
        public CMessageFilter, public CIdleHandler
{
public:
    DECLARE_FRAME_WND_CLASS(NULL, IDR_MAINFRAME)

    COOoCtrl m_view;

We complete the Message Map.

C++
BEGIN_MSG_MAP(CMainFrame)
    MESSAGE_HANDLER(WM_CREATE, OnCreate)
    MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
    COMMAND_ID_HANDLER(ID_APP_EXIT, OnFileExit)
    COMMAND_ID_HANDLER(ID_FILE_OPEN, OnFileOpen)
    COMMAND_ID_HANDLER(ID_FILE_NEW, OnNewWindow)
    COMMAND_RANGE_HANDLER(ID_NEW_WRITER, ID_NEW_WEBPAGE, OnNewDoc)
    COMMAND_ID_HANDLER(ID_FILE_SAVE, OnSaveToPDF)
    COMMAND_ID_HANDLER(ID_FILE_PRINT_PREVIEW, OnPrintPreview)
    COMMAND_ID_HANDLER(ID_FILE_PRINT, OnPrint)
    COMMAND_ID_HANDLER(ID_VIEW_TOOLBAR, OnViewToolBar)
    COMMAND_ID_HANDLER(ID_APP_ABOUT, OnAppAbout)
    NOTIFY_CODE_HANDLER(TBN_DROPDOWN, OnMenuNew)
END_MSG_MAP()

File Open and New Document Handling

OnFileOpen() executes a File Open dialog and checks that OOo::CanLoad() the selected file, OnNewDoc() builds the name of the selected new document.

Both open it in m_view, read only for existing documents, hide the menu and main toolbar (they are attached to the document, not to the window), and update the window title.

Loading a new document will close the current one, so both restore the previously existing document menu and toolbar visibility.

C++
void ShowDocBars(bool bShow)
{
   if (m_view.HasDocument())
   {
      m_view.UIShowId(ID_OOo_UI_MENUBAR, bShow);
      m_view.UIShowId(ID_OOo_UI_STANDARDBAR, bShow);
   }
}

void UpdateTitle()
{
   static CString sApp(MAKEINTRESOURCE(IDR_MAINFRAME));
   CString sTitle;
   if (m_view.HasDocument())
      sTitle.Format(L"%s - %s", m_view.GetDocTitle(), sApp);
   else
      sTitle = sApp;
   SetWindowText(sTitle);
}

LRESULT OnNewDoc(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, 
    BOOL& /*bHandled*/)
{
   static CString sTitle = L"private:factory/";
   LPCWSTR names[] =
   {
      L"swriter", L"scalc", L"sdraw", L"simpress", L"swriter/web"
   };

   ShowDocBars(true);
   m_view.OpenNewDocument(sTitle + names[wID - ID_NEW_WRITER], false);
   ShowDocBars(false);
   UpdateTitle();
   return 0;
}

LRESULT OnFileOpen(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, 
    BOOL& /*bHandled*/)
{
   CFileDialog dlg(TRUE);
   if (dlg.DoModal() == IDOK && OOo::CanLoad(dlg.m_szFileName))
   {
      CWaitCursor wc;
      ShowDocBars(true);
      m_view.OpenFileDocument(dlg.m_szFileName);
      ShowDocBars(false);
      UpdateTitle();
   }
   return 0;
}

The PrintPrint Preview, and Save commands are handled by CMainFrame which calls COOoCtrl::ExecuteId() with the respective command string ID.

C++
LRESULT OnSaveToPDF(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/,
     BOOL& /*bHandled*/)
{
    m_view.ExecuteId(ID_OOo_PDF);
    return 0;
}

LRESULT OnPrintPreview(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/,
     BOOL& /*bHandled*/)
{
    m_view.ExecuteId(ID_OOo_PRINT_PREVIEW);
    return 0;
}

LRESULT OnPrint(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
    m_view.ExecuteId(ID_OOo_PRINT);
    return 0;
}

Conclusion

With OpenOffice.org 3.0, integration of OOo document windows in native Win32 applications is possible with some work. OOo associated to WTL can be an alternative to scripts or macro programming for business documents handling.

History

  • 1.10.2009 - Initial release
  • 2.16.2009 – Update:
    • atlOOo.h: Fixed bug in Ooo::IsAvailable(), added OOO::TerminationMonitor, small code improvements, changed function names to Ooo::DocWindow::OpenFileDocument and Ooo::DocWindow::OpenNewDocument
    • WtlOOo application: added New Document functionalities and Ooo::TerminationMonitor in _tWinMain.
    • Article updated accordingly

License

This article, along with any associated source code and files, is licensed under The Common Public License Version 1.0 (CPL)

posted @ 2022-12-13 14:21  小风风的博客  阅读(23)  评论(0编辑  收藏  举报