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
- Download the WtlOOo 1.1 application- 88.83 KB
- Download the SDK preparation script- 1.85 KB
- Download the WtlOOo 1.1 project - 34.73 KB
- Download the (updated) atlOOo.h classes and functions - 4.9 KB
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.
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.rdb; cppumaker 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.
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioPropertySheet
ProjectType="Visual C++"
Version="8.00"
Name="OOo"
>
<Tool
Name="VCCLCompilerTool"
AdditionalIncludeDirectories=
""$(OOoSdkDir)\include";"$(OOoSdkDir)\includecpp""
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 theOOo
namespace code, and is based both onOOo:DocWindow
andATL::CWindow
.
OOo Functions
- uno helper functions:
OOo::GetBootContext()
, bootstrap context accessor: gets acom::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 callsFindInstallAndSetPath()
, and if successful, callsGetBootContext()
, 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. IfOOo::IsAvailable()
returnsfalse
, 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 genericcom::sun::star::util::URL
from aLPCWSTR
(for instance, a command string as".uno:ExportDirectToPDF"
).URL OOo::GetPathURL(LPCWSTR sPath)
returns a correctly filledcom::sun::star::util::URL
from aLPCWSTR
Win32 path name.
com::sun::star::beans::PropertyValue
andcom::sun::star::uno::Sequence<com::sun::star::beans::PropertyValue>
helpers:template <typename T> PropertyValue OOo::PropVal(LPCWSTR sName, T t_Val)
returns acom::sun::star::beans::PropertyValue
from aLPCWSTR
and a value of any type.template <typename T> Sequence<PropertyValue> OOo::SeqPropVal(LPCWSTR sName, T t_Val)
returns an elementcom::sun::star::uno::Sequence
containing acom::sun::star::beans::PropertyValue<T>
namedsName
.
com::sun::star::uno::Any
/HWND
converters:- Any
OOo::HWNDToAny(HWND hWnd)
and HWND OOo::AnyToHWND(Any& any)
- Any
-
File loadability test:
-
bool OOo::CanLoad(LPCWSTR sPath)
checks that the Win32sPath
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.
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 ofhWndParent,
and inserts it in the uno architecture. On success, returnstrue
, otherwisefalse
.HWND OOo::DocWindow::GetHWND()
returns the Win32HWND
of theDocWindow
,NULL
if not created.void OOo::DocWindow::SetWindowRect(RECT& rect)
sets the position of theDocWindow
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"
fromsName
and aSequence<PropertyValue>
fromPropVal(L"ReadOnly", bReadOnly)
before callingOpenDocument(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()
.
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.
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.
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.
// 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.
// 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.
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.
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 Print, Print Preview, and Save commands are handled by CMainFrame
which calls COOoCtrl::ExecuteId()
with the respective command string ID.
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()
, addedOOO::TerminationMonitor
, small code improvements, changed function names toOoo::DocWindow::OpenFileDocument
andOoo::DocWindow::OpenNewDocument
- WtlOOo application: added New Document functionalities and
Ooo::TerminationMonitor
in_tWinMain
. - Article updated accordingly
- atlOOo.h: Fixed bug in
License
This article, along with any associated source code and files, is licensed under The Common Public License Version 1.0 (CPL)