/src/libreoffice/oox/source/ole/vbaproject.cxx
Line | Count | Source |
1 | | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | /* |
3 | | * This file is part of the LibreOffice project. |
4 | | * |
5 | | * This Source Code Form is subject to the terms of the Mozilla Public |
6 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
7 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
8 | | * |
9 | | * This file incorporates work covered by the following license notice: |
10 | | * |
11 | | * Licensed to the Apache Software Foundation (ASF) under one or more |
12 | | * contributor license agreements. See the NOTICE file distributed |
13 | | * with this work for additional information regarding copyright |
14 | | * ownership. The ASF licenses this file to you under the Apache |
15 | | * License, Version 2.0 (the "License"); you may not use this file |
16 | | * except in compliance with the License. You may obtain a copy of |
17 | | * the License at http://www.apache.org/licenses/LICENSE-2.0 . |
18 | | */ |
19 | | |
20 | | #include <oox/ole/vbaproject.hxx> |
21 | | |
22 | | #include <com/sun/star/beans/XPropertySet.hpp> |
23 | | #include <com/sun/star/document/XStorageBasedDocument.hpp> |
24 | | #include <com/sun/star/embed/ElementModes.hpp> |
25 | | #include <com/sun/star/embed/XTransactedObject.hpp> |
26 | | #include <com/sun/star/frame/XModel.hpp> |
27 | | #include <com/sun/star/lang/XMultiServiceFactory.hpp> |
28 | | #include <com/sun/star/script/ModuleType.hpp> |
29 | | #include <com/sun/star/script/XLibraryContainer.hpp> |
30 | | #include <com/sun/star/script/vba/XVBACompatibility.hpp> |
31 | | #include <com/sun/star/script/vba/XVBAMacroResolver.hpp> |
32 | | #include <com/sun/star/uno/XComponentContext.hpp> |
33 | | #include <comphelper/configurationhelper.hxx> |
34 | | #include <comphelper/documentinfo.hxx> |
35 | | #include <comphelper/storagehelper.hxx> |
36 | | #include <osl/diagnose.h> |
37 | | #include <rtl/tencinfo.h> |
38 | | #include <sal/log.hxx> |
39 | | #include <comphelper/diagnose_ex.hxx> |
40 | | #include <oox/helper/binaryinputstream.hxx> |
41 | | #include <oox/helper/propertyset.hxx> |
42 | | #include <oox/helper/textinputstream.hxx> |
43 | | #include <oox/ole/olestorage.hxx> |
44 | | #include <oox/ole/vbacontrol.hxx> |
45 | | #include <oox/ole/vbahelper.hxx> |
46 | | #include <oox/ole/vbainputstream.hxx> |
47 | | #include <oox/ole/vbamodule.hxx> |
48 | | #include <oox/token/properties.hxx> |
49 | | #include <utility> |
50 | | |
51 | | namespace oox::ole { |
52 | | |
53 | | using namespace ::com::sun::star; |
54 | | using namespace ::com::sun::star::container; |
55 | | using namespace ::com::sun::star::document; |
56 | | using namespace ::com::sun::star::embed; |
57 | | using namespace ::com::sun::star::frame; |
58 | | using namespace ::com::sun::star::io; |
59 | | using namespace ::com::sun::star::lang; |
60 | | using namespace ::com::sun::star::script; |
61 | | using namespace ::com::sun::star::script::vba; |
62 | | using namespace ::com::sun::star::uno; |
63 | | |
64 | | using ::comphelper::ConfigurationHelper; |
65 | | |
66 | | namespace { |
67 | | |
68 | | bool lclReadConfigItem( const Reference< XInterface >& rxConfigAccess, const OUString& rItemName ) |
69 | 29 | { |
70 | | // some applications do not support all configuration items, assume 'false' in this case |
71 | 29 | try |
72 | 29 | { |
73 | 29 | Any aItem = ConfigurationHelper::readRelativeKey( rxConfigAccess, u"Filter/Import/VBA"_ustr, rItemName ); |
74 | 29 | return aItem.has< bool >() && aItem.get< bool >(); |
75 | 29 | } |
76 | 29 | catch(const Exception& ) |
77 | 29 | { |
78 | 29 | } |
79 | 29 | return false; |
80 | 29 | } |
81 | | |
82 | | } // namespace |
83 | | |
84 | | VbaFilterConfig::VbaFilterConfig( const Reference< XComponentContext >& rxContext, std::u16string_view rConfigCompName ) |
85 | 4.21k | { |
86 | 4.21k | OSL_ENSURE( rxContext.is(), "VbaFilterConfig::VbaFilterConfig - missing component context" ); |
87 | 4.21k | if( rxContext.is() ) try |
88 | 4.21k | { |
89 | 4.21k | OSL_ENSURE( !rConfigCompName.empty(), "VbaFilterConfig::VbaFilterConfig - invalid configuration component name" ); |
90 | 4.21k | OUString aConfigPackage = OUString::Concat("org.openoffice.Office.") + rConfigCompName; |
91 | 4.21k | mxConfigAccess = ConfigurationHelper::openConfig( rxContext, aConfigPackage, comphelper::EConfigurationModes::ReadOnly ); |
92 | 4.21k | } |
93 | 4.21k | catch(const Exception& ) |
94 | 4.21k | { |
95 | 4.21k | TOOLS_WARN_EXCEPTION("oox", ""); |
96 | 4.21k | } |
97 | 4.21k | OSL_ENSURE( mxConfigAccess.is(), "VbaFilterConfig::VbaFilterConfig - cannot open configuration" ); |
98 | 4.21k | } |
99 | | |
100 | | VbaFilterConfig::~VbaFilterConfig() |
101 | 4.21k | { |
102 | 4.21k | } |
103 | | |
104 | | bool VbaFilterConfig::isImportVba() const |
105 | 1 | { |
106 | 1 | return lclReadConfigItem( mxConfigAccess, u"Load"_ustr ); |
107 | 1 | } |
108 | | |
109 | | bool VbaFilterConfig::isImportVbaExecutable() const |
110 | 27 | { |
111 | 27 | return lclReadConfigItem( mxConfigAccess, u"Executable"_ustr ); |
112 | 27 | } |
113 | | |
114 | | bool VbaFilterConfig::isExportVba() const |
115 | 1 | { |
116 | 1 | return lclReadConfigItem( mxConfigAccess, u"Save"_ustr ); |
117 | 1 | } |
118 | | |
119 | | VbaMacroAttacherBase::VbaMacroAttacherBase( OUString aMacroName ) : |
120 | 0 | maMacroName(std::move( aMacroName )) |
121 | 0 | { |
122 | 0 | OSL_ENSURE( !maMacroName.isEmpty(), "VbaMacroAttacherBase::VbaMacroAttacherBase - empty macro name" ); |
123 | 0 | } |
124 | | |
125 | | VbaMacroAttacherBase::~VbaMacroAttacherBase() |
126 | 0 | { |
127 | 0 | } |
128 | | |
129 | | void VbaMacroAttacherBase::resolveAndAttachMacro( const Reference< XVBAMacroResolver >& rxResolver ) |
130 | 0 | { |
131 | 0 | try |
132 | 0 | { |
133 | 0 | attachMacro( rxResolver->resolveVBAMacroToScriptURL( maMacroName ) ); |
134 | 0 | } |
135 | 0 | catch(const Exception& ) |
136 | 0 | { |
137 | 0 | TOOLS_WARN_EXCEPTION("oox", ""); |
138 | 0 | } |
139 | 0 | } |
140 | | |
141 | | VbaProject::VbaProject( const Reference< XComponentContext >& rxContext, |
142 | | const Reference< XModel >& rxDocModel, std::u16string_view rConfigCompName ) : |
143 | 4.21k | VbaFilterConfig( rxContext, rConfigCompName ), |
144 | 4.21k | mxContext( rxContext ), |
145 | 4.21k | mxDocModel( rxDocModel ), |
146 | 4.21k | maPrjName( u"Standard"_ustr ) |
147 | 4.21k | { |
148 | 4.21k | OSL_ENSURE( mxContext.is(), "VbaProject::VbaProject - missing component context" ); |
149 | 4.21k | OSL_ENSURE( mxDocModel.is(), "VbaProject::VbaProject - missing document model" ); |
150 | 4.21k | } |
151 | | |
152 | | VbaProject::~VbaProject() |
153 | 4.21k | { |
154 | 4.21k | } |
155 | | |
156 | | bool VbaProject::importVbaProject( StorageBase& rVbaPrjStrg ) |
157 | 0 | { |
158 | | // create GraphicHelper |
159 | 0 | Reference< css::frame::XFrame > xFrame; |
160 | 0 | if ( mxDocModel.is() ) |
161 | 0 | { |
162 | 0 | Reference< css::frame::XController > xController = mxDocModel->getCurrentController(); |
163 | 0 | xFrame = xController.is() ? xController->getFrame() : nullptr; |
164 | 0 | } |
165 | | |
166 | | // if the GraphicHelper tries to use noStorage it will of course crash |
167 | | // but... this shouldn't happen as there is no reason for GraphicHelper |
168 | | // to do that when importing VBA projects |
169 | 0 | GraphicHelper grfHlp(mxContext, xFrame, StorageRef()); |
170 | 0 | importVbaProject( rVbaPrjStrg, grfHlp ); |
171 | | // return true if something has been imported |
172 | 0 | return (mxBasicLib.is() && mxBasicLib->hasElements()) || |
173 | 0 | (mxDialogLib.is() && mxDialogLib->hasElements()); |
174 | 0 | } |
175 | | |
176 | | void VbaProject::importVbaProject( StorageBase& rVbaPrjStrg, const GraphicHelper& rGraphicHelper ) |
177 | 1 | { |
178 | 1 | if( rVbaPrjStrg.isStorage() ) |
179 | 1 | { |
180 | | // load the code modules and forms |
181 | 1 | if( isImportVba() ) |
182 | 0 | importVba( rVbaPrjStrg, rGraphicHelper ); |
183 | | // copy entire storage into model |
184 | 1 | if( isExportVba() ) |
185 | 0 | copyStorage( rVbaPrjStrg ); |
186 | 1 | } |
187 | 1 | } |
188 | | |
189 | | void VbaProject::importVbaData(const uno::Reference<io::XInputStream>& xInputStream) |
190 | 0 | { |
191 | 0 | uno::Reference<document::XStorageBasedDocument> xStorageBasedDoc(mxDocModel, uno::UNO_QUERY); |
192 | 0 | uno::Reference<embed::XStorage> xDocStorage = xStorageBasedDoc->getDocumentStorage(); |
193 | 0 | { |
194 | 0 | const sal_Int32 nOpenMode = ElementModes::SEEKABLE | ElementModes::WRITE | ElementModes::TRUNCATE; |
195 | 0 | uno::Reference<io::XOutputStream> xDocStream(xDocStorage->openStreamElement(u"_MS_VBA_Macros_XML"_ustr, nOpenMode), uno::UNO_QUERY); |
196 | 0 | comphelper::OStorageHelper::CopyInputToOutput(xInputStream, xDocStream); |
197 | 0 | } |
198 | 0 | uno::Reference<embed::XTransactedObject>(xDocStorage, uno::UNO_QUERY_THROW)->commit(); |
199 | 0 | } |
200 | | |
201 | | void VbaProject::registerMacroAttacher( const VbaMacroAttacherRef& rxAttacher ) |
202 | 0 | { |
203 | 0 | OSL_ENSURE( rxAttacher, "VbaProject::registerMacroAttacher - unexpected empty reference" ); |
204 | 0 | maMacroAttachers.push_back( rxAttacher ); |
205 | 0 | } |
206 | | |
207 | | // protected ------------------------------------------------------------------ |
208 | | |
209 | | void VbaProject::addDummyModule( const OUString& rName, sal_Int32 nType ) |
210 | 0 | { |
211 | 0 | OSL_ENSURE( !rName.isEmpty(), "VbaProject::addDummyModule - missing module name" ); |
212 | 0 | maDummyModules[ rName ] = nType; |
213 | 0 | } |
214 | | |
215 | | void VbaProject::prepareImport() |
216 | 0 | { |
217 | 0 | } |
218 | | |
219 | | // private -------------------------------------------------------------------- |
220 | | |
221 | | Reference< XLibraryContainer > VbaProject::getLibraryContainer( sal_Int32 nPropId ) |
222 | 8 | { |
223 | 8 | PropertySet aDocProp( mxDocModel ); |
224 | 8 | Reference< XLibraryContainer > xLibContainer( aDocProp.getAnyProperty( nPropId ), UNO_QUERY ); |
225 | 8 | return xLibContainer; |
226 | 8 | } |
227 | | |
228 | | Reference< XNameContainer > VbaProject::openLibrary( sal_Int32 nPropId ) |
229 | 4 | { |
230 | 4 | Reference< XNameContainer > xLibrary; |
231 | 4 | try |
232 | 4 | { |
233 | 4 | Reference< XLibraryContainer > xLibContainer( getLibraryContainer( nPropId ), UNO_SET_THROW ); |
234 | 4 | if( !xLibContainer->hasByName( maPrjName ) ) |
235 | 0 | xLibContainer->createLibrary( maPrjName ); |
236 | 4 | xLibrary.set( xLibContainer->getByName( maPrjName ), UNO_QUERY_THROW ); |
237 | 4 | } |
238 | 4 | catch(const Exception& ) |
239 | 4 | { |
240 | 4 | TOOLS_WARN_EXCEPTION("oox", ""); |
241 | 4 | } |
242 | 4 | OSL_ENSURE( xLibrary.is(), "VbaProject::openLibrary - cannot create library" ); |
243 | 4 | return xLibrary; |
244 | 4 | } |
245 | | |
246 | | Reference< XNameContainer > const & VbaProject::createBasicLibrary() |
247 | 4 | { |
248 | 4 | if( !mxBasicLib.is() ) |
249 | 4 | mxBasicLib = openLibrary( PROP_BasicLibraries ); |
250 | 4 | return mxBasicLib; |
251 | 4 | } |
252 | | |
253 | | Reference< XNameContainer > const & VbaProject::createDialogLibrary() |
254 | 0 | { |
255 | 0 | if( !mxDialogLib.is() ) |
256 | 0 | mxDialogLib = openLibrary( PROP_DialogLibraries ); |
257 | 0 | return mxDialogLib; |
258 | 0 | } |
259 | | |
260 | | void VbaProject::importVba( StorageBase& rVbaPrjStrg, const GraphicHelper& rGraphicHelper ) |
261 | 0 | { |
262 | 0 | readVbaModules( rVbaPrjStrg ); |
263 | 0 | importModulesAndForms(rVbaPrjStrg, rGraphicHelper ); |
264 | | // attach macros to registered objects |
265 | 0 | attachMacros(); |
266 | 0 | } |
267 | | |
268 | | void VbaProject::readVbaModules( StorageBase& rVbaPrjStrg ) |
269 | 18 | { |
270 | 18 | StorageRef xVbaStrg = rVbaPrjStrg.openSubStorage( u"VBA"_ustr, false ); |
271 | 18 | OSL_ENSURE( xVbaStrg, "VbaProject::readVbaModules - cannot open 'VBA' substorage" ); |
272 | 18 | if( !xVbaStrg ) |
273 | 4 | return; |
274 | | |
275 | | /* Read the 'VBA/dir' stream which contains general settings of the VBA |
276 | | project such as the text encoding used throughout several streams, and |
277 | | a list of all code modules. |
278 | | */ |
279 | 14 | BinaryXInputStream aInStrm( xVbaStrg->openInputStream( u"dir"_ustr ), true ); |
280 | | // VbaInputStream implements decompression |
281 | 14 | VbaInputStream aDirStrm( aInStrm ); |
282 | 14 | OSL_ENSURE( !aDirStrm.isEof(), "VbaProject::importVba - cannot open 'dir' stream" ); |
283 | 14 | if( aDirStrm.isEof() ) |
284 | 1 | return; |
285 | | |
286 | | // virtual call, derived classes may do some preparations |
287 | 13 | prepareImport(); |
288 | | |
289 | | // read all records of the directory |
290 | 13 | rtl_TextEncoding eTextEnc = RTL_TEXTENCODING_MS_1252; |
291 | 13 | sal_uInt16 nModuleCount = 0; |
292 | 13 | bool bExecutable = isImportVbaExecutable(); |
293 | | |
294 | 13 | sal_uInt16 nRecId = 0; |
295 | 13 | StreamDataSequence aRecData; |
296 | 149 | while( VbaHelper::readDirRecord( nRecId, aRecData, aDirStrm ) && (nRecId != VBA_ID_PROJECTEND) ) |
297 | 136 | { |
298 | | // create record stream object from imported record data |
299 | 136 | SequenceInputStream aRecStrm( aRecData ); |
300 | 136 | sal_Int32 nRecSize = aRecData.getLength(); |
301 | 136 | switch( nRecId ) |
302 | 136 | { |
303 | 4 | case VBA_ID_PROJECTCODEPAGE: |
304 | 4 | { |
305 | 4 | OSL_ENSURE( nRecSize == 2, "VbaProject::importVba - invalid record size" ); |
306 | 4 | OSL_ENSURE( maModules.empty(), "VbaProject::importVba - unexpected PROJECTCODEPAGE record" ); |
307 | 4 | rtl_TextEncoding eNewTextEnc = rtl_getTextEncodingFromWindowsCodePage( aRecStrm.readuInt16() ); |
308 | 4 | OSL_ENSURE( eNewTextEnc != RTL_TEXTENCODING_DONTKNOW, "VbaProject::importVba - unknown text encoding" ); |
309 | 4 | if( eNewTextEnc != RTL_TEXTENCODING_DONTKNOW ) |
310 | 4 | eTextEnc = eNewTextEnc; |
311 | 4 | } |
312 | 4 | break; |
313 | 4 | case VBA_ID_PROJECTNAME: |
314 | 4 | { |
315 | 4 | OUString aPrjName = aRecStrm.readCharArrayUC( nRecSize, eTextEnc ); |
316 | 4 | OSL_ENSURE( !aPrjName.isEmpty(), "VbaProject::importVba - invalid project name" ); |
317 | 4 | if( !aPrjName.isEmpty() ) |
318 | 4 | maPrjName = aPrjName; |
319 | 4 | } |
320 | 4 | break; |
321 | 4 | case VBA_ID_PROJECTMODULES: |
322 | 4 | OSL_ENSURE( nRecSize == 2, "VbaProject::importVba - invalid record size" ); |
323 | 4 | OSL_ENSURE( maModules.empty(), "VbaProject::importVba - unexpected PROJECTMODULES record" ); |
324 | 4 | nModuleCount = aRecStrm.readuInt16(); |
325 | 4 | break; |
326 | 20 | case VBA_ID_MODULENAME: |
327 | 20 | { |
328 | 20 | OUString aName = aRecStrm.readCharArrayUC( nRecSize, eTextEnc ); |
329 | 20 | OSL_ENSURE( !aName.isEmpty(), "VbaProject::importVba - invalid module name" ); |
330 | 20 | OSL_ENSURE( !maModules.has( aName ), "VbaProject::importVba - multiple modules with the same name" ); |
331 | 20 | VbaModuleMap::mapped_type& rxModule = maModules[ aName ]; |
332 | 20 | rxModule = std::make_shared<VbaModule>( mxContext, mxDocModel, aName, eTextEnc, bExecutable ); |
333 | | // read all remaining records until the MODULEEND record |
334 | 20 | rxModule->importDirRecords( aDirStrm ); |
335 | 20 | OSL_ENSURE( !maModulesByStrm.has( rxModule->getStreamName() ), "VbaProject::importVba - multiple modules with the same stream name" ); |
336 | 20 | maModulesByStrm[ rxModule->getStreamName() ] = rxModule; |
337 | 20 | } |
338 | 20 | break; |
339 | 136 | } |
340 | 136 | } |
341 | 13 | SAL_WARN_IF( nModuleCount != maModules.size(), "oox", "VbaProject::importVba - invalid module count" ); |
342 | | |
343 | | /* The directory does not contain the real type of the modules, it |
344 | | distinguishes only between 'procedural' and 'document' (the latter |
345 | | includes class and form modules). Now, the exact type of all modules |
346 | | will be read from the 'PROJECT' stream. It consists of text lines in |
347 | | 'key=value' format which list the code modules by type. |
348 | | |
349 | | - The line 'document=<modulename>/&HXXXXXXXX' declares document |
350 | | modules. These are attached to the Word document (usually called |
351 | | 'ThisDocument'), the Excel workbook (usually called |
352 | | 'ThisWorkbook'), or single Excel worksheets or chartsheets (usually |
353 | | called 'SheetX' or 'ChartX', X being a decimal number). Of course, |
354 | | users may rename all these modules. The slash character separates |
355 | | an automation server version number (hexadecimal 'XXXXXXXX') from |
356 | | the module name. |
357 | | - The line 'Module=<modulename>' declares common procedural code |
358 | | modules. |
359 | | - The line 'Class=<modulename>' declares a class module. |
360 | | - The line 'BaseClass=<modulename>' declares a code module attached |
361 | | to a user form with the same name. |
362 | | */ |
363 | 13 | BinaryXInputStream aPrjStrm( rVbaPrjStrg.openInputStream( u"PROJECT"_ustr ), true ); |
364 | 13 | OSL_ENSURE( !aPrjStrm.isEof(), "VbaProject::importVba - cannot open 'PROJECT' stream" ); |
365 | | // do not exit if this stream does not exist, but proceed to load the modules below |
366 | 13 | if( !aPrjStrm.isEof() ) |
367 | 13 | { |
368 | 13 | TextInputStream aPrjTextStrm( mxContext, aPrjStrm, eTextEnc ); |
369 | 13 | OUString aKey, aValue; |
370 | 13 | bool bExitLoop = false; |
371 | 161 | while( !bExitLoop && !aPrjTextStrm.isEof() ) |
372 | 148 | { |
373 | | // read a text line from the stream |
374 | 148 | OUString aLine = aPrjTextStrm.readLine().trim(); |
375 | 148 | sal_Int32 nLineLen = aLine.getLength(); |
376 | | // exit if a subsection starts (section name is given in brackets) |
377 | 148 | bExitLoop = (nLineLen >= 2) && (aLine[ 0 ] == '[') && (aLine[ nLineLen - 1 ] == ']'); |
378 | 148 | if( !bExitLoop && VbaHelper::extractKeyValue( aKey, aValue, aLine ) ) |
379 | 113 | { |
380 | 113 | sal_Int32 nType = ModuleType::UNKNOWN; |
381 | 113 | if( aKey.equalsIgnoreAsciiCase( "Document" ) ) |
382 | 24 | { |
383 | 24 | nType = ModuleType::DOCUMENT; |
384 | | // strip automation server version from module names |
385 | 24 | sal_Int32 nSlashPos = aValue.indexOf( '/' ); |
386 | 24 | if( nSlashPos >= 0 ) |
387 | 24 | aValue = aValue.copy( 0, nSlashPos ); |
388 | 24 | } |
389 | 89 | else if( aKey.equalsIgnoreAsciiCase( "Module" ) ) |
390 | 6 | nType = ModuleType::NORMAL; |
391 | 83 | else if( aKey.equalsIgnoreAsciiCase( "Class" ) ) |
392 | 0 | nType = ModuleType::CLASS; |
393 | 83 | else if( aKey.equalsIgnoreAsciiCase( "BaseClass" ) ) |
394 | 0 | nType = ModuleType::FORM; |
395 | | |
396 | 113 | if( (nType != ModuleType::UNKNOWN) && !aValue.isEmpty() ) |
397 | 30 | { |
398 | 30 | OSL_ENSURE( maModules.has( aValue ), "VbaProject::importVba - module not found" ); |
399 | 30 | if( VbaModule* pModule = maModules.get( aValue ).get() ) |
400 | 19 | pModule->setType( nType ); |
401 | 30 | } |
402 | 113 | } |
403 | 148 | } |
404 | 13 | } |
405 | | |
406 | 13 | if( maModules.empty() ) |
407 | 9 | return; |
408 | | |
409 | 4 | try |
410 | 4 | { |
411 | | /* Set library container to VBA compatibility mode. This will create |
412 | | the VBA Globals object and store it in the Basic manager of the |
413 | | document. */ |
414 | 4 | try |
415 | 4 | { |
416 | 4 | Reference< XVBACompatibility > xVBACompat( getLibraryContainer( PROP_BasicLibraries ), UNO_QUERY_THROW ); |
417 | 4 | xVBACompat->setVBACompatibilityMode( true ); |
418 | 4 | xVBACompat->setProjectName( maPrjName ); |
419 | | |
420 | 4 | uno::Reference<beans::XPropertySet> xProps(xVBACompat, uno::UNO_QUERY_THROW); |
421 | 4 | xProps->setPropertyValue(u"VBATextEncoding"_ustr, uno::Any(eTextEnc)); |
422 | 4 | } |
423 | 4 | catch(const Exception& ) |
424 | 4 | { |
425 | 4 | } |
426 | 4 | } |
427 | 4 | catch(const Exception& ) |
428 | 4 | { |
429 | 0 | TOOLS_WARN_EXCEPTION("oox", ""); |
430 | 0 | } |
431 | 4 | } |
432 | | |
433 | | void VbaProject::importModulesAndForms( StorageBase& rVbaPrjStrg, const GraphicHelper& rGraphicHelper ) |
434 | 15 | { |
435 | 15 | StorageRef xVbaStrg = rVbaPrjStrg.openSubStorage( u"VBA"_ustr, false ); |
436 | 15 | OSL_ENSURE( xVbaStrg, "VbaProject::importModulesAndForms - cannot open 'VBA' substorage" ); |
437 | 15 | if( !xVbaStrg ) |
438 | 1 | return; |
439 | 14 | rtl_TextEncoding eTextEnc = RTL_TEXTENCODING_MS_1252; |
440 | 14 | bool bExecutable = isImportVbaExecutable(); |
441 | | |
442 | | // create empty dummy modules |
443 | 14 | VbaModuleMap aDummyModules; |
444 | 14 | for (auto const& dummyModule : maDummyModules) |
445 | 0 | { |
446 | 0 | OSL_ENSURE( !maModules.has( dummyModule.first ) && !aDummyModules.has( dummyModule.first ), "VbaProject::importVba - multiple modules with the same name" ); |
447 | 0 | VbaModuleMap::mapped_type& rxModule = aDummyModules[ dummyModule.first ]; |
448 | 0 | rxModule = std::make_shared<VbaModule>( mxContext, mxDocModel, dummyModule.first, eTextEnc, bExecutable ); |
449 | 0 | rxModule->setType( dummyModule.second ); |
450 | 0 | } |
451 | | |
452 | | /* Now it is time to load the source code. All modules will be inserted |
453 | | into the Basic library of the document specified by the 'maPrjName' |
454 | | member. Do not create the Basic library, if there are no modules |
455 | | specified. */ |
456 | 14 | if( !maModules.empty() || !aDummyModules.empty() ) try |
457 | 4 | { |
458 | | // get the model factory and the basic library |
459 | 4 | Reference< XMultiServiceFactory > xModelFactory( mxDocModel, UNO_QUERY_THROW ); |
460 | 4 | Reference< XNameContainer > xBasicLib( createBasicLibrary(), UNO_SET_THROW ); |
461 | | |
462 | | // try to get access to document objects related to code modules |
463 | 4 | Reference< XNameAccess > xDocObjectNA; |
464 | 4 | try |
465 | 4 | { |
466 | 4 | xDocObjectNA.set( xModelFactory->createInstance( u"ooo.vba.VBAObjectModuleObjectProvider"_ustr ), UNO_QUERY ); |
467 | 4 | } |
468 | 4 | catch(const Exception& ) |
469 | 4 | { |
470 | | // not all documents support this |
471 | 0 | } |
472 | | |
473 | 4 | if( xBasicLib.is() ) |
474 | 0 | { |
475 | | // #TODO cater for mxOleOverridesSink, like I used to before |
476 | | // call Basic source code import for each module, std::[c]ref enforces pass-by-ref |
477 | 0 | maModules.forEachMem( &VbaModule::createAndImportModule, |
478 | 0 | ::std::ref( *xVbaStrg ), ::std::cref( xBasicLib ), |
479 | 0 | ::std::cref( xDocObjectNA ) ); |
480 | | |
481 | | // create empty dummy modules |
482 | 0 | aDummyModules.forEachMem( &VbaModule::createEmptyModule, |
483 | 0 | ::std::cref( xBasicLib ), ::std::cref( xDocObjectNA ) ); |
484 | 0 | } |
485 | 0 | } |
486 | 4 | catch(const Exception& ) |
487 | 4 | { |
488 | 4 | } |
489 | | |
490 | | /* Load the forms. The file format specification requires that a module |
491 | | must exist for every form. We are a bit more tolerant and scan the |
492 | | project storage for all form substorages. This may 'repair' broken VBA |
493 | | storages that misses to mention a module for an existing form. */ |
494 | 14 | ::std::vector< OUString > aElements; |
495 | 14 | rVbaPrjStrg.getElementNames( aElements ); |
496 | 14 | for (auto const& elem : aElements) |
497 | 42 | { |
498 | | // try to open the element as storage |
499 | 42 | if( elem != "VBA" ) |
500 | 28 | { |
501 | 28 | StorageRef xSubStrg = rVbaPrjStrg.openSubStorage( elem, false ); |
502 | 28 | if( xSubStrg ) try |
503 | 0 | { |
504 | | // resolve module name from storage name (which equals the module stream name) |
505 | 0 | VbaModule* pModule = maModulesByStrm.get( elem ).get(); |
506 | 0 | OSL_ENSURE( pModule && (pModule->getType() == ModuleType::FORM), |
507 | 0 | "VbaProject::importVba - form substorage without form module" ); |
508 | 0 | OUString aModuleName; |
509 | 0 | if( pModule ) |
510 | 0 | aModuleName = pModule->getName(); |
511 | | |
512 | | // create and import the form |
513 | 0 | Reference< XNameContainer > xDialogLib( createDialogLibrary(), UNO_SET_THROW ); |
514 | 0 | VbaUserForm aForm( mxContext, mxDocModel, rGraphicHelper, true/*bDefaultColorBgr*/ ); |
515 | 0 | aForm.importForm( xDialogLib, *xSubStrg, aModuleName, eTextEnc ); |
516 | 0 | } |
517 | 0 | catch(const Exception& ) |
518 | 0 | { |
519 | 0 | TOOLS_WARN_EXCEPTION("oox", ""); |
520 | 0 | } |
521 | 28 | } |
522 | 42 | } |
523 | 14 | } |
524 | | |
525 | | void VbaProject::attachMacros() |
526 | 4.21k | { |
527 | 4.21k | if( maMacroAttachers.empty() || !mxContext.is() ) |
528 | 4.21k | return; |
529 | | |
530 | 0 | try |
531 | 0 | { |
532 | 0 | comphelper::DocumentInfo::notifyMacroEventRead(mxDocModel); |
533 | |
|
534 | 0 | Reference< XMultiComponentFactory > xFactory( mxContext->getServiceManager(), UNO_SET_THROW ); |
535 | 0 | Sequence< Any > aArgs{ Any(mxDocModel), Any(maPrjName) }; |
536 | 0 | Reference< XVBAMacroResolver > xResolver( xFactory->createInstanceWithArgumentsAndContext( |
537 | 0 | u"com.sun.star.script.vba.VBAMacroResolver"_ustr, aArgs, mxContext ), UNO_QUERY_THROW ); |
538 | 0 | maMacroAttachers.forEachMem( &VbaMacroAttacherBase::resolveAndAttachMacro, ::std::cref( xResolver ) ); |
539 | |
|
540 | 0 | } |
541 | 0 | catch(const Exception& ) |
542 | 0 | { |
543 | 0 | TOOLS_WARN_EXCEPTION("oox", ""); |
544 | 0 | } |
545 | 0 | } |
546 | | |
547 | | void VbaProject::copyStorage( StorageBase& rVbaPrjStrg ) |
548 | 0 | { |
549 | 0 | if( !mxContext.is() ) |
550 | 0 | return; |
551 | | |
552 | 0 | try |
553 | 0 | { |
554 | 0 | Reference< XStorageBasedDocument > xStorageBasedDoc( mxDocModel, UNO_QUERY_THROW ); |
555 | 0 | Reference< XStorage > xDocStorage( xStorageBasedDoc->getDocumentStorage(), UNO_SET_THROW ); |
556 | 0 | { |
557 | 0 | const sal_Int32 nOpenMode = ElementModes::SEEKABLE | ElementModes::WRITE | ElementModes::TRUNCATE; |
558 | 0 | Reference< XStream > xDocStream( xDocStorage->openStreamElement( u"_MS_VBA_Macros"_ustr, nOpenMode ), UNO_SET_THROW ); |
559 | 0 | OleStorage aDestStorage( mxContext, xDocStream, false ); |
560 | 0 | rVbaPrjStrg.copyStorageToStorage( aDestStorage ); |
561 | 0 | aDestStorage.commit(); |
562 | 0 | } |
563 | 0 | Reference< XTransactedObject >( xDocStorage, UNO_QUERY_THROW )->commit(); |
564 | 0 | } |
565 | 0 | catch(const Exception& ) |
566 | 0 | { |
567 | 0 | } |
568 | 0 | } |
569 | | |
570 | | } // namespace oox |
571 | | |
572 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |