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 "#COOoCtrl">WTL::COOoCtrl
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 , WORD , HWND ,
BOOL& )
{
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:
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:
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:
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;
}
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 DocWindow
, NULL
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:
-
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)
.
-
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>&)
.
-
bool OOo::DocWindow::OpenFileDocument(LPCWSTR sPath, bool bReadOnly = true)
does the same with a file path name.
-
bool Ooo::DocWindow::CloseDocument()
-
bool OOo::DocWindow::HasDocument()
-
LPCWSTR Ooo::DocWindow::GetDocTitle()
-
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.
int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE ,
LPTSTR lpstrCmdLine, int nCmdShow)
{
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.
#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 , WORD wID, HWND ,
BOOL& )
{
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 , WORD , HWND ,
BOOL& )
{
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 , WORD , HWND ,
BOOL& )
{
m_view.ExecuteId(ID_OOo_PDF);
return 0;
}
LRESULT OnPrintPreview(WORD , WORD , HWND ,
BOOL& )
{
m_view.ExecuteId(ID_OOo_PRINT_PREVIEW);
return 0;
}
LRESULT OnPrint(WORD , WORD , HWND , BOOL& )
{
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