1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 24 #include "precompiled_basic.hxx" 25 #include "sal/config.h" 26 27 #include <algorithm> 28 #include <cstddef> 29 #include <list> 30 #include <map> 31 #include <vector> 32 33 #include "basic/sbx.hxx" 34 #include "basic/sbxvar.hxx" 35 #include "runtime.hxx" 36 #include "osl/thread.h" 37 #include "rtl/ref.hxx" 38 #include "rtl/string.hxx" 39 #include "rtl/ustring.hxx" 40 #include "salhelper/simplereferenceobject.hxx" 41 #include "tools/svwin.h" 42 43 #undef max 44 45 #include "dllmgr.hxx" 46 47 /* Open issues: 48 49 Only 32-bit Windows for now. 50 51 Missing support for functions returning structs (see TODO in call()). 52 53 Missing support for additional data types (64 bit integers, Any, ...; would 54 trigger OSL_ASSERT(false) in various switches). 55 56 It is assumed that the variables passed into SbiDllMgr::Call to represent 57 the arguments and return value have types that exactly match the Declare 58 statement; it would be better if this code had access to the function 59 signature from the Declare statement, so that it could convert the passed 60 variables accordingly. 61 */ 62 63 #if defined WNT // only 32-bit Windows, actually 64 65 extern "C" { 66 67 int __stdcall DllMgr_call32(FARPROC, void const * stack, std::size_t size); 68 double __stdcall DllMgr_callFp(FARPROC, void const * stack, std::size_t size); 69 70 } 71 72 namespace { 73 74 char * address(std::vector< char > & blob) { 75 return blob.empty() ? 0 : &blob[0]; 76 } 77 78 SbError convert(rtl::OUString const & source, rtl::OString * target) { 79 return 80 source.convertToString( 81 target, osl_getThreadTextEncoding(), 82 (RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR | 83 RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR)) 84 ? ERRCODE_NONE : ERRCODE_BASIC_BAD_ARGUMENT; 85 //TODO: more specific errcode? 86 } 87 88 SbError convert(char const * source, sal_Int32 length, rtl::OUString * target) { 89 return 90 rtl_convertStringToUString( 91 &target->pData, source, length, osl_getThreadTextEncoding(), 92 (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR | 93 RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR | 94 RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR)) 95 ? ERRCODE_NONE : ERRCODE_BASIC_BAD_ARGUMENT; 96 //TODO: more specific errcode? 97 } 98 99 struct UnmarshalData { 100 UnmarshalData(SbxVariable * theVariable, void * theBuffer): 101 variable(theVariable), buffer(theBuffer) {} 102 103 SbxVariable * variable; 104 void * buffer; 105 }; 106 107 struct StringData: public UnmarshalData { 108 StringData(SbxVariable * theVariable, void * theBuffer, bool theSpecial): 109 UnmarshalData(theVariable, theBuffer), special(theSpecial) {} 110 111 bool special; 112 }; 113 114 class MarshalData: private boost::noncopyable { 115 public: 116 std::vector< char > * newBlob() { 117 blobs_.push_front(std::vector< char >()); 118 return &blobs_.front(); 119 } 120 121 std::vector< UnmarshalData > unmarshal; 122 123 std::vector< StringData > unmarshalStrings; 124 125 private: 126 std::list< std::vector< char > > blobs_; 127 }; 128 129 std::size_t align(std::size_t address, std::size_t alignment) { 130 // alignment = 2^k for some k >= 0 131 return (address + (alignment - 1)) & ~(alignment - 1); 132 } 133 134 char * align( 135 std::vector< char > & blob, std::size_t alignment, std::size_t offset, 136 std::size_t add) 137 { 138 std::vector< char >::size_type n = blob.size(); 139 n = align(n - offset, alignment) + offset; //TODO: overflow in align() 140 blob.resize(n + add); //TODO: overflow 141 return address(blob) + n; 142 } 143 144 template< typename T > void add( 145 std::vector< char > & blob, T const & data, std::size_t alignment, 146 std::size_t offset) 147 { 148 *reinterpret_cast< T * >(align(blob, alignment, offset, sizeof (T))) = data; 149 } 150 151 std::size_t alignment(SbxVariable * variable) { 152 OSL_ASSERT(variable != 0); 153 if ((variable->GetType() & SbxARRAY) == 0) { 154 switch (variable->GetType()) { 155 case SbxINTEGER: 156 return 2; 157 case SbxLONG: 158 case SbxSINGLE: 159 case SbxSTRING: 160 return 4; 161 case SbxDOUBLE: 162 return 8; 163 case SbxOBJECT: 164 { 165 std::size_t n = 1; 166 SbxArray * props = PTR_CAST(SbxObject, variable->GetObject())-> 167 GetProperties(); 168 for (sal_uInt16 i = 0; i < props->Count(); ++i) { 169 n = std::max(n, alignment(props->Get(i))); 170 } 171 return n; 172 } 173 case SbxBOOL: 174 case SbxBYTE: 175 return 1; 176 default: 177 OSL_ASSERT(false); 178 return 1; 179 } 180 } else { 181 SbxDimArray * arr = PTR_CAST(SbxDimArray, variable->GetObject()); 182 int dims = arr->GetDims(); 183 std::vector< sal_Int32 > low(dims); 184 for (int i = 0; i < dims; ++i) { 185 sal_Int32 up; 186 arr->GetDim32(i + 1, low[i], up); 187 } 188 return alignment(arr->Get32(&low[0])); 189 } 190 } 191 192 SbError marshal( 193 bool outer, SbxVariable * variable, bool special, 194 std::vector< char > & blob, std::size_t offset, MarshalData & data); 195 196 SbError marshalString( 197 SbxVariable * variable, bool special, MarshalData & data, void ** buffer) 198 { 199 OSL_ASSERT(variable != 0 && buffer != 0); 200 rtl::OString str; 201 SbError e = convert(variable->GetString(), &str); 202 if (e != ERRCODE_NONE) { 203 return e; 204 } 205 std::vector< char > * blob = data.newBlob(); 206 blob->insert( 207 blob->begin(), str.getStr(), str.getStr() + str.getLength() + 1); 208 *buffer = address(*blob); 209 data.unmarshalStrings.push_back(StringData(variable, *buffer, special)); 210 return ERRCODE_NONE; 211 } 212 213 SbError marshalStruct( 214 SbxVariable * variable, std::vector< char > & blob, std::size_t offset, 215 MarshalData & data) 216 { 217 OSL_ASSERT(variable != 0); 218 SbxArray * props = PTR_CAST(SbxObject, variable->GetObject())-> 219 GetProperties(); 220 for (sal_uInt16 i = 0; i < props->Count(); ++i) { 221 SbError e = marshal(false, props->Get(i), false, blob, offset, data); 222 if (e != ERRCODE_NONE) { 223 return e; 224 } 225 } 226 return ERRCODE_NONE; 227 } 228 229 SbError marshalArray( 230 SbxVariable * variable, std::vector< char > & blob, std::size_t offset, 231 MarshalData & data) 232 { 233 OSL_ASSERT(variable != 0); 234 SbxDimArray * arr = PTR_CAST(SbxDimArray, variable->GetObject()); 235 int dims = arr->GetDims(); 236 std::vector< sal_Int32 > low(dims); 237 std::vector< sal_Int32 > up(dims); 238 for (int i = 0; i < dims; ++i) { 239 arr->GetDim32(i + 1, low[i], up[i]); 240 } 241 for (std::vector< sal_Int32 > idx = low;;) { 242 SbError e = marshal( 243 false, arr->Get32(&idx[0]), false, blob, offset, data); 244 if (e != ERRCODE_NONE) { 245 return e; 246 } 247 int i = dims - 1; 248 while (idx[i] == up[i]) { 249 idx[i] = low[i]; 250 if (i == 0) { 251 return ERRCODE_NONE; 252 } 253 --i; 254 } 255 ++idx[i]; 256 } 257 } 258 259 // 8-aligned structs are only 4-aligned on stack, so alignment of members in 260 // such structs must take that into account via "offset" 261 SbError marshal( 262 bool outer, SbxVariable * variable, bool special, 263 std::vector< char > & blob, std::size_t offset, MarshalData & data) 264 { 265 OSL_ASSERT(variable != 0); 266 267 SbxDataType eVarType = variable->GetType(); 268 bool bByVal = (variable->GetFlags() & SBX_REFERENCE) == 0; 269 if( !bByVal && !SbiRuntime::isVBAEnabled() && eVarType == SbxSTRING ) 270 bByVal = true; 271 272 if (bByVal) { 273 if ((eVarType & SbxARRAY) == 0) { 274 switch (eVarType) { 275 case SbxINTEGER: 276 add(blob, variable->GetInteger(), outer ? 4 : 2, offset); 277 break; 278 case SbxLONG: 279 add(blob, variable->GetLong(), 4, offset); 280 break; 281 case SbxSINGLE: 282 add(blob, variable->GetSingle(), 4, offset); 283 break; 284 case SbxDOUBLE: 285 add(blob, variable->GetDouble(), outer ? 4 : 8, offset); 286 break; 287 case SbxSTRING: 288 { 289 void * p; 290 SbError e = marshalString(variable, special, data, &p); 291 if (e != ERRCODE_NONE) { 292 return e; 293 } 294 add(blob, p, 4, offset); 295 break; 296 } 297 case SbxOBJECT: 298 { 299 align(blob, outer ? 4 : alignment(variable), offset, 0); 300 SbError e = marshalStruct(variable, blob, offset, data); 301 if (e != ERRCODE_NONE) { 302 return e; 303 } 304 break; 305 } 306 case SbxBOOL: 307 add(blob, variable->GetBool(), outer ? 4 : 1, offset); 308 break; 309 case SbxBYTE: 310 add(blob, variable->GetByte(), outer ? 4 : 1, offset); 311 break; 312 default: 313 OSL_ASSERT(false); 314 break; 315 } 316 } else { 317 SbError e = marshalArray(variable, blob, offset, data); 318 if (e != ERRCODE_NONE) { 319 return e; 320 } 321 } 322 } else { 323 if ((eVarType & SbxARRAY) == 0) { 324 switch (eVarType) { 325 case SbxINTEGER: 326 case SbxLONG: 327 case SbxSINGLE: 328 case SbxDOUBLE: 329 case SbxBOOL: 330 case SbxBYTE: 331 add(blob, variable->data(), 4, offset); 332 break; 333 case SbxSTRING: 334 { 335 std::vector< char > * blob2 = data.newBlob(); 336 void * p; 337 SbError e = marshalString(variable, special, data, &p); 338 if (e != ERRCODE_NONE) { 339 return e; 340 } 341 add(*blob2, p, 4, 0); 342 add(blob, address(*blob2), 4, offset); 343 break; 344 } 345 case SbxOBJECT: 346 { 347 std::vector< char > * blob2 = data.newBlob(); 348 SbError e = marshalStruct(variable, *blob2, 0, data); 349 if (e != ERRCODE_NONE) { 350 return e; 351 } 352 void * p = address(*blob2); 353 if (outer) { 354 data.unmarshal.push_back(UnmarshalData(variable, p)); 355 } 356 add(blob, p, 4, offset); 357 break; 358 } 359 default: 360 OSL_ASSERT(false); 361 break; 362 } 363 } else { 364 std::vector< char > * blob2 = data.newBlob(); 365 SbError e = marshalArray(variable, *blob2, 0, data); 366 if (e != ERRCODE_NONE) { 367 return e; 368 } 369 void * p = address(*blob2); 370 if (outer) { 371 data.unmarshal.push_back(UnmarshalData(variable, p)); 372 } 373 add(blob, p, 4, offset); 374 } 375 } 376 return ERRCODE_NONE; 377 } 378 379 template< typename T > T read(void const ** pointer) { 380 T const * p = static_cast< T const * >(*pointer); 381 *pointer = static_cast< void const * >(p + 1); 382 return *p; 383 } 384 385 void const * unmarshal(SbxVariable * variable, void const * data) { 386 OSL_ASSERT(variable != 0); 387 if ((variable->GetType() & SbxARRAY) == 0) { 388 switch (variable->GetType()) { 389 case SbxINTEGER: 390 variable->PutInteger(read< sal_Int16 >(&data)); 391 break; 392 case SbxLONG: 393 variable->PutLong(read< sal_Int32 >(&data)); 394 break; 395 case SbxSINGLE: 396 variable->PutSingle(read< float >(&data)); 397 break; 398 case SbxDOUBLE: 399 variable->PutDouble(read< double >(&data)); 400 break; 401 case SbxSTRING: 402 read< char * >(&data); // handled by unmarshalString 403 break; 404 case SbxOBJECT: 405 { 406 data = reinterpret_cast< void const * >( 407 align( 408 reinterpret_cast< sal_uIntPtr >(data), 409 alignment(variable))); 410 SbxArray * props = PTR_CAST(SbxObject, variable->GetObject())-> 411 GetProperties(); 412 for (sal_uInt16 i = 0; i < props->Count(); ++i) { 413 data = unmarshal(props->Get(i), data); 414 } 415 break; 416 } 417 case SbxBOOL: 418 variable->PutBool(read< sal_Bool >(&data)); 419 break; 420 case SbxBYTE: 421 variable->PutByte(read< sal_uInt8 >(&data)); 422 break; 423 default: 424 OSL_ASSERT(false); 425 break; 426 } 427 } else { 428 SbxDimArray * arr = PTR_CAST(SbxDimArray, variable->GetObject()); 429 int dims = arr->GetDims(); 430 std::vector< sal_Int32 > low(dims); 431 std::vector< sal_Int32 > up(dims); 432 for (int i = 0; i < dims; ++i) { 433 arr->GetDim32(i + 1, low[i], up[i]); 434 } 435 for (std::vector< sal_Int32 > idx = low;;) { 436 data = unmarshal(arr->Get32(&idx[0]), data); 437 int i = dims - 1; 438 while (idx[i] == up[i]) { 439 idx[i] = low[i]; 440 if (i == 0) { 441 goto done; 442 } 443 --i; 444 } 445 ++idx[i]; 446 } 447 done:; 448 } 449 return data; 450 } 451 452 SbError unmarshalString(StringData const & data, SbxVariable & result) { 453 rtl::OUString str; 454 if (data.buffer != 0) { 455 char const * p = static_cast< char const * >(data.buffer); 456 sal_Int32 len; 457 if (data.special) { 458 len = static_cast< sal_Int32 >(result.GetULong()); 459 if (len < 0) { // i.e., DWORD result >= 2^31 460 return ERRCODE_BASIC_BAD_ARGUMENT; 461 //TODO: more specific errcode? 462 } 463 } else { 464 len = rtl_str_getLength(p); 465 } 466 SbError e = convert(p, len, &str); 467 if (e != ERRCODE_NONE) { 468 return e; 469 } 470 } 471 data.variable->PutString(String(str)); 472 return ERRCODE_NONE; 473 } 474 475 struct ProcData { 476 rtl::OString name; 477 FARPROC proc; 478 }; 479 480 SbError call( 481 rtl::OUString const & dll, ProcData const & proc, SbxArray * arguments, 482 SbxVariable & result) 483 { 484 std::vector< char > stack; 485 MarshalData data; 486 // For DWORD GetLogicalDriveStringsA(DWORD nBufferLength, LPSTR lpBuffer) 487 // from kernel32, upon return, filled lpBuffer length is result DWORD, which 488 // requires special handling in unmarshalString; other functions might 489 // require similar treatment, too: 490 bool special = 491 dll.equalsIgnoreAsciiCaseAsciiL( 492 RTL_CONSTASCII_STRINGPARAM("KERNEL32.DLL")) && 493 (proc.name == 494 rtl::OString(RTL_CONSTASCII_STRINGPARAM("GetLogicalDriveStringsA"))); 495 for (sal_uInt16 i = 1; i < (arguments == 0 ? 0 : arguments->Count()); ++i) { 496 SbError e = marshal( 497 true, arguments->Get(i), special && i == 2, stack, stack.size(), 498 data); 499 if (e != ERRCODE_NONE) { 500 return e; 501 } 502 align(stack, 4, 0, 0); 503 } 504 switch (result.GetType()) { 505 case SbxEMPTY: 506 DllMgr_call32(proc.proc, address(stack), stack.size()); 507 break; 508 case SbxINTEGER: 509 result.PutInteger( 510 static_cast< sal_Int16 >( 511 DllMgr_call32(proc.proc, address(stack), stack.size()))); 512 break; 513 case SbxLONG: 514 result.PutLong( 515 static_cast< sal_Int32 >( 516 DllMgr_call32(proc.proc, address(stack), stack.size()))); 517 break; 518 case SbxSINGLE: 519 result.PutSingle( 520 static_cast< float >( 521 DllMgr_callFp(proc.proc, address(stack), stack.size()))); 522 break; 523 case SbxDOUBLE: 524 result.PutDouble( 525 DllMgr_callFp(proc.proc, address(stack), stack.size())); 526 break; 527 case SbxSTRING: 528 { 529 char const * s1 = reinterpret_cast< char const * >( 530 DllMgr_call32(proc.proc, address(stack), stack.size())); 531 rtl::OUString s2; 532 SbError e = convert(s1, rtl_str_getLength(s1), &s2); 533 if (e != ERRCODE_NONE) { 534 return e; 535 } 536 result.PutString(String(s2)); 537 break; 538 } 539 case SbxOBJECT: 540 //TODO 541 DllMgr_call32(proc.proc, address(stack), stack.size()); 542 break; 543 case SbxBOOL: 544 result.PutBool( 545 static_cast< sal_Bool >( 546 DllMgr_call32(proc.proc, address(stack), stack.size()))); 547 break; 548 case SbxBYTE: 549 result.PutByte( 550 static_cast< sal_uInt8 >( 551 DllMgr_call32(proc.proc, address(stack), stack.size()))); 552 break; 553 default: 554 OSL_ASSERT(false); 555 break; 556 } 557 for (sal_uInt16 i = 1; i < (arguments == 0 ? 0 : arguments->Count()); ++i) { 558 arguments->Get(i)->ResetFlag(SBX_REFERENCE); 559 //TODO: skipped for errors?!? 560 } 561 for (std::vector< UnmarshalData >::iterator i(data.unmarshal.begin()); 562 i != data.unmarshal.end(); ++i) 563 { 564 unmarshal(i->variable, i->buffer); 565 } 566 for (std::vector< StringData >::iterator i(data.unmarshalStrings.begin()); 567 i != data.unmarshalStrings.end(); ++i) 568 { 569 SbError e = unmarshalString(*i, result); 570 if (e != ERRCODE_NONE) { 571 return e; 572 } 573 } 574 return ERRCODE_NONE; 575 } 576 577 SbError getProcData(HMODULE handle, rtl::OUString const & name, ProcData * proc) 578 { 579 OSL_ASSERT(proc != 0); 580 if (name.getLength() != 0 && name[0] == '@') { //TODO: "@" vs. "#"??? 581 sal_Int32 n = name.copy(1).toInt32(); //TODO: handle bad input 582 if (n <= 0 || n > 0xFFFF) { 583 return ERRCODE_BASIC_BAD_ARGUMENT; //TODO: more specific errcode? 584 } 585 FARPROC p = GetProcAddress(handle, reinterpret_cast< LPCSTR >(n)); 586 if (p != 0) { 587 proc->name = rtl::OString(RTL_CONSTASCII_STRINGPARAM("#")) + 588 rtl::OString::valueOf(n); 589 proc->proc = p; 590 return ERRCODE_NONE; 591 } 592 } else { 593 rtl::OString name8; 594 SbError e = convert(name, &name8); 595 if (e != ERRCODE_NONE) { 596 return e; 597 } 598 FARPROC p = GetProcAddress(handle, name8.getStr()); 599 if (p != 0) { 600 proc->name = name8; 601 proc->proc = p; 602 return ERRCODE_NONE; 603 } 604 sal_Int32 i = name8.indexOf('#'); 605 if (i != -1) { 606 name8 = name8.copy(0, i); 607 p = GetProcAddress(handle, name8.getStr()); 608 if (p != 0) { 609 proc->name = name8; 610 proc->proc = p; 611 return ERRCODE_NONE; 612 } 613 } 614 rtl::OString real( 615 rtl::OString(RTL_CONSTASCII_STRINGPARAM("_")) + name8); 616 p = GetProcAddress(handle, real.getStr()); 617 if (p != 0) { 618 proc->name = real; 619 proc->proc = p; 620 return ERRCODE_NONE; 621 } 622 real = name8 + rtl::OString(RTL_CONSTASCII_STRINGPARAM("A")); 623 p = GetProcAddress(handle, real.getStr()); 624 if (p != 0) { 625 proc->name = real; 626 proc->proc = p; 627 return ERRCODE_NONE; 628 } 629 } 630 return ERRCODE_BASIC_PROC_UNDEFINED; 631 } 632 633 struct Dll: public salhelper::SimpleReferenceObject { 634 private: 635 typedef std::map< rtl::OUString, ProcData > Procs; 636 637 virtual ~Dll(); 638 639 public: 640 Dll(): handle(0) {} 641 642 SbError getProc(rtl::OUString const & name, ProcData * proc); 643 644 HMODULE handle; 645 Procs procs; 646 }; 647 648 Dll::~Dll() { 649 if (handle != 0 && !FreeLibrary(handle)) { 650 OSL_TRACE("FreeLibrary(%p) failed with %u", handle, GetLastError()); 651 } 652 } 653 654 SbError Dll::getProc(rtl::OUString const & name, ProcData * proc) { 655 Procs::iterator i(procs.find(name)); 656 if (i != procs.end()) { 657 *proc = i->second; 658 return ERRCODE_NONE; 659 } 660 SbError e = getProcData(handle, name, proc); 661 if (e == ERRCODE_NONE) { 662 procs.insert(Procs::value_type(name, *proc)); 663 } 664 return e; 665 } 666 667 rtl::OUString fullDllName(rtl::OUString const & name) { 668 rtl::OUString full(name); 669 if (full.indexOf('.') == -1) { 670 full += rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(".DLL")); 671 } 672 return full; 673 } 674 675 } 676 677 struct SbiDllMgr::Impl: private boost::noncopyable { 678 private: 679 typedef std::map< rtl::OUString, rtl::Reference< Dll > > Dlls; 680 681 public: 682 Dll * getDll(rtl::OUString const & name); 683 684 Dlls dlls; 685 }; 686 687 Dll * SbiDllMgr::Impl::getDll(rtl::OUString const & name) { 688 Dlls::iterator i(dlls.find(name)); 689 if (i == dlls.end()) { 690 i = dlls.insert(Dlls::value_type(name, new Dll)).first; 691 HMODULE h = LoadLibraryW(reinterpret_cast<LPCWSTR>(name.getStr())); 692 if (h == 0) { 693 dlls.erase(i); 694 return 0; 695 } 696 i->second->handle = h; 697 } 698 return i->second.get(); 699 } 700 701 SbError SbiDllMgr::Call( 702 rtl::OUString const & function, rtl::OUString const & library, 703 SbxArray * arguments, SbxVariable & result, bool cdeclConvention) 704 { 705 if (cdeclConvention) { 706 return ERRCODE_BASIC_NOT_IMPLEMENTED; 707 } 708 rtl::OUString dllName(fullDllName(library)); 709 Dll * dll = impl_->getDll(dllName); 710 if (dll == 0) { 711 return ERRCODE_BASIC_BAD_DLL_LOAD; 712 } 713 ProcData proc; 714 SbError e = dll->getProc(function, &proc); 715 if (e != ERRCODE_NONE) { 716 return e; 717 } 718 return call(dllName, proc, arguments, result); 719 } 720 721 void SbiDllMgr::FreeDll(rtl::OUString const & library) { 722 impl_->dlls.erase(library); 723 } 724 725 #else 726 727 struct SbiDllMgr::Impl {}; 728 729 SbError SbiDllMgr::Call( 730 rtl::OUString const &, rtl::OUString const &, SbxArray *, SbxVariable &, 731 bool) 732 { 733 return ERRCODE_BASIC_NOT_IMPLEMENTED; 734 } 735 736 void SbiDllMgr::FreeDll(rtl::OUString const &) {} 737 738 #endif 739 740 SbiDllMgr::SbiDllMgr(): impl_(new Impl) {} 741 742 SbiDllMgr::~SbiDllMgr() {} 743