| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| #include "precompiled_basic.hxx" |
| #include "sal/config.h" |
| |
| #include <algorithm> |
| #include <cstddef> |
| #include <list> |
| #include <map> |
| #include <vector> |
| |
| #include "basic/sbx.hxx" |
| #include "basic/sbxvar.hxx" |
| #include "runtime.hxx" |
| #include "osl/thread.h" |
| #include "rtl/ref.hxx" |
| #include "rtl/string.hxx" |
| #include "rtl/ustring.hxx" |
| #include "salhelper/simplereferenceobject.hxx" |
| #include "tools/svwin.h" |
| |
| #undef max |
| |
| #include "dllmgr.hxx" |
| |
| /* Open issues: |
| |
| Only 32-bit Windows for now. |
| |
| Missing support for functions returning structs (see TODO in call()). |
| |
| Missing support for additional data types (64 bit integers, Any, ...; would |
| trigger OSL_ASSERT(false) in various switches). |
| |
| It is assumed that the variables passed into SbiDllMgr::Call to represent |
| the arguments and return value have types that exactly match the Declare |
| statement; it would be better if this code had access to the function |
| signature from the Declare statement, so that it could convert the passed |
| variables accordingly. |
| */ |
| |
| #if defined WNT // only 32-bit Windows, actually |
| |
| extern "C" { |
| |
| int __stdcall DllMgr_call32(FARPROC, void const * stack, std::size_t size); |
| double __stdcall DllMgr_callFp(FARPROC, void const * stack, std::size_t size); |
| |
| } |
| |
| namespace { |
| |
| char * address(std::vector< char > & blob) { |
| return blob.empty() ? 0 : &blob[0]; |
| } |
| |
| SbError convert(rtl::OUString const & source, rtl::OString * target) { |
| return |
| source.convertToString( |
| target, osl_getThreadTextEncoding(), |
| (RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR | |
| RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR)) |
| ? ERRCODE_NONE : ERRCODE_BASIC_BAD_ARGUMENT; |
| //TODO: more specific errcode? |
| } |
| |
| SbError convert(char const * source, sal_Int32 length, rtl::OUString * target) { |
| return |
| rtl_convertStringToUString( |
| &target->pData, source, length, osl_getThreadTextEncoding(), |
| (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR | |
| RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR | |
| RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR)) |
| ? ERRCODE_NONE : ERRCODE_BASIC_BAD_ARGUMENT; |
| //TODO: more specific errcode? |
| } |
| |
| struct UnmarshalData { |
| UnmarshalData(SbxVariable * theVariable, void * theBuffer): |
| variable(theVariable), buffer(theBuffer) {} |
| |
| SbxVariable * variable; |
| void * buffer; |
| }; |
| |
| struct StringData: public UnmarshalData { |
| StringData(SbxVariable * theVariable, void * theBuffer, bool theSpecial): |
| UnmarshalData(theVariable, theBuffer), special(theSpecial) {} |
| |
| bool special; |
| }; |
| |
| class MarshalData: private boost::noncopyable { |
| public: |
| std::vector< char > * newBlob() { |
| blobs_.push_front(std::vector< char >()); |
| return &blobs_.front(); |
| } |
| |
| std::vector< UnmarshalData > unmarshal; |
| |
| std::vector< StringData > unmarshalStrings; |
| |
| private: |
| std::list< std::vector< char > > blobs_; |
| }; |
| |
| std::size_t align(std::size_t address, std::size_t alignment) { |
| // alignment = 2^k for some k >= 0 |
| return (address + (alignment - 1)) & ~(alignment - 1); |
| } |
| |
| char * align( |
| std::vector< char > & blob, std::size_t alignment, std::size_t offset, |
| std::size_t add) |
| { |
| std::vector< char >::size_type n = blob.size(); |
| n = align(n - offset, alignment) + offset; //TODO: overflow in align() |
| blob.resize(n + add); //TODO: overflow |
| return address(blob) + n; |
| } |
| |
| template< typename T > void add( |
| std::vector< char > & blob, T const & data, std::size_t alignment, |
| std::size_t offset) |
| { |
| *reinterpret_cast< T * >(align(blob, alignment, offset, sizeof (T))) = data; |
| } |
| |
| std::size_t alignment(SbxVariable * variable) { |
| OSL_ASSERT(variable != 0); |
| if ((variable->GetType() & SbxARRAY) == 0) { |
| switch (variable->GetType()) { |
| case SbxINTEGER: |
| return 2; |
| case SbxLONG: |
| case SbxSINGLE: |
| case SbxSTRING: |
| return 4; |
| case SbxDOUBLE: |
| return 8; |
| case SbxOBJECT: |
| { |
| std::size_t n = 1; |
| SbxArray * props = PTR_CAST(SbxObject, variable->GetObject())-> |
| GetProperties(); |
| for (sal_uInt16 i = 0; i < props->Count(); ++i) { |
| n = std::max(n, alignment(props->Get(i))); |
| } |
| return n; |
| } |
| case SbxBOOL: |
| case SbxBYTE: |
| return 1; |
| default: |
| OSL_ASSERT(false); |
| return 1; |
| } |
| } else { |
| SbxDimArray * arr = PTR_CAST(SbxDimArray, variable->GetObject()); |
| int dims = arr->GetDims(); |
| std::vector< sal_Int32 > low(dims); |
| for (int i = 0; i < dims; ++i) { |
| sal_Int32 up; |
| arr->GetDim32(i + 1, low[i], up); |
| } |
| return alignment(arr->Get32(&low[0])); |
| } |
| } |
| |
| SbError marshal( |
| bool outer, SbxVariable * variable, bool special, |
| std::vector< char > & blob, std::size_t offset, MarshalData & data); |
| |
| SbError marshalString( |
| SbxVariable * variable, bool special, MarshalData & data, void ** buffer) |
| { |
| OSL_ASSERT(variable != 0 && buffer != 0); |
| rtl::OString str; |
| SbError e = convert(variable->GetString(), &str); |
| if (e != ERRCODE_NONE) { |
| return e; |
| } |
| std::vector< char > * blob = data.newBlob(); |
| blob->insert( |
| blob->begin(), str.getStr(), str.getStr() + str.getLength() + 1); |
| *buffer = address(*blob); |
| data.unmarshalStrings.push_back(StringData(variable, *buffer, special)); |
| return ERRCODE_NONE; |
| } |
| |
| SbError marshalStruct( |
| SbxVariable * variable, std::vector< char > & blob, std::size_t offset, |
| MarshalData & data) |
| { |
| OSL_ASSERT(variable != 0); |
| SbxArray * props = PTR_CAST(SbxObject, variable->GetObject())-> |
| GetProperties(); |
| for (sal_uInt16 i = 0; i < props->Count(); ++i) { |
| SbError e = marshal(false, props->Get(i), false, blob, offset, data); |
| if (e != ERRCODE_NONE) { |
| return e; |
| } |
| } |
| return ERRCODE_NONE; |
| } |
| |
| SbError marshalArray( |
| SbxVariable * variable, std::vector< char > & blob, std::size_t offset, |
| MarshalData & data) |
| { |
| OSL_ASSERT(variable != 0); |
| SbxDimArray * arr = PTR_CAST(SbxDimArray, variable->GetObject()); |
| int dims = arr->GetDims(); |
| std::vector< sal_Int32 > low(dims); |
| std::vector< sal_Int32 > up(dims); |
| for (int i = 0; i < dims; ++i) { |
| arr->GetDim32(i + 1, low[i], up[i]); |
| } |
| for (std::vector< sal_Int32 > idx = low;;) { |
| SbError e = marshal( |
| false, arr->Get32(&idx[0]), false, blob, offset, data); |
| if (e != ERRCODE_NONE) { |
| return e; |
| } |
| int i = dims - 1; |
| while (idx[i] == up[i]) { |
| idx[i] = low[i]; |
| if (i == 0) { |
| return ERRCODE_NONE; |
| } |
| --i; |
| } |
| ++idx[i]; |
| } |
| } |
| |
| // 8-aligned structs are only 4-aligned on stack, so alignment of members in |
| // such structs must take that into account via "offset" |
| SbError marshal( |
| bool outer, SbxVariable * variable, bool special, |
| std::vector< char > & blob, std::size_t offset, MarshalData & data) |
| { |
| OSL_ASSERT(variable != 0); |
| |
| SbxDataType eVarType = variable->GetType(); |
| bool bByVal = (variable->GetFlags() & SBX_REFERENCE) == 0; |
| if( !bByVal && !SbiRuntime::isVBAEnabled() && eVarType == SbxSTRING ) |
| bByVal = true; |
| |
| if (bByVal) { |
| if ((eVarType & SbxARRAY) == 0) { |
| switch (eVarType) { |
| case SbxINTEGER: |
| add(blob, variable->GetInteger(), outer ? 4 : 2, offset); |
| break; |
| case SbxLONG: |
| add(blob, variable->GetLong(), 4, offset); |
| break; |
| case SbxSINGLE: |
| add(blob, variable->GetSingle(), 4, offset); |
| break; |
| case SbxDOUBLE: |
| add(blob, variable->GetDouble(), outer ? 4 : 8, offset); |
| break; |
| case SbxSTRING: |
| { |
| void * p; |
| SbError e = marshalString(variable, special, data, &p); |
| if (e != ERRCODE_NONE) { |
| return e; |
| } |
| add(blob, p, 4, offset); |
| break; |
| } |
| case SbxOBJECT: |
| { |
| align(blob, outer ? 4 : alignment(variable), offset, 0); |
| SbError e = marshalStruct(variable, blob, offset, data); |
| if (e != ERRCODE_NONE) { |
| return e; |
| } |
| break; |
| } |
| case SbxBOOL: |
| add(blob, variable->GetBool(), outer ? 4 : 1, offset); |
| break; |
| case SbxBYTE: |
| add(blob, variable->GetByte(), outer ? 4 : 1, offset); |
| break; |
| default: |
| OSL_ASSERT(false); |
| break; |
| } |
| } else { |
| SbError e = marshalArray(variable, blob, offset, data); |
| if (e != ERRCODE_NONE) { |
| return e; |
| } |
| } |
| } else { |
| if ((eVarType & SbxARRAY) == 0) { |
| switch (eVarType) { |
| case SbxINTEGER: |
| case SbxLONG: |
| case SbxSINGLE: |
| case SbxDOUBLE: |
| case SbxBOOL: |
| case SbxBYTE: |
| add(blob, variable->data(), 4, offset); |
| break; |
| case SbxSTRING: |
| { |
| std::vector< char > * blob2 = data.newBlob(); |
| void * p; |
| SbError e = marshalString(variable, special, data, &p); |
| if (e != ERRCODE_NONE) { |
| return e; |
| } |
| add(*blob2, p, 4, 0); |
| add(blob, address(*blob2), 4, offset); |
| break; |
| } |
| case SbxOBJECT: |
| { |
| std::vector< char > * blob2 = data.newBlob(); |
| SbError e = marshalStruct(variable, *blob2, 0, data); |
| if (e != ERRCODE_NONE) { |
| return e; |
| } |
| void * p = address(*blob2); |
| if (outer) { |
| data.unmarshal.push_back(UnmarshalData(variable, p)); |
| } |
| add(blob, p, 4, offset); |
| break; |
| } |
| default: |
| OSL_ASSERT(false); |
| break; |
| } |
| } else { |
| std::vector< char > * blob2 = data.newBlob(); |
| SbError e = marshalArray(variable, *blob2, 0, data); |
| if (e != ERRCODE_NONE) { |
| return e; |
| } |
| void * p = address(*blob2); |
| if (outer) { |
| data.unmarshal.push_back(UnmarshalData(variable, p)); |
| } |
| add(blob, p, 4, offset); |
| } |
| } |
| return ERRCODE_NONE; |
| } |
| |
| template< typename T > T read(void const ** pointer) { |
| T const * p = static_cast< T const * >(*pointer); |
| *pointer = static_cast< void const * >(p + 1); |
| return *p; |
| } |
| |
| void const * unmarshal(SbxVariable * variable, void const * data) { |
| OSL_ASSERT(variable != 0); |
| if ((variable->GetType() & SbxARRAY) == 0) { |
| switch (variable->GetType()) { |
| case SbxINTEGER: |
| variable->PutInteger(read< sal_Int16 >(&data)); |
| break; |
| case SbxLONG: |
| variable->PutLong(read< sal_Int32 >(&data)); |
| break; |
| case SbxSINGLE: |
| variable->PutSingle(read< float >(&data)); |
| break; |
| case SbxDOUBLE: |
| variable->PutDouble(read< double >(&data)); |
| break; |
| case SbxSTRING: |
| read< char * >(&data); // handled by unmarshalString |
| break; |
| case SbxOBJECT: |
| { |
| data = reinterpret_cast< void const * >( |
| align( |
| reinterpret_cast< sal_uIntPtr >(data), |
| alignment(variable))); |
| SbxArray * props = PTR_CAST(SbxObject, variable->GetObject())-> |
| GetProperties(); |
| for (sal_uInt16 i = 0; i < props->Count(); ++i) { |
| data = unmarshal(props->Get(i), data); |
| } |
| break; |
| } |
| case SbxBOOL: |
| variable->PutBool(read< sal_Bool >(&data)); |
| break; |
| case SbxBYTE: |
| variable->PutByte(read< sal_uInt8 >(&data)); |
| break; |
| default: |
| OSL_ASSERT(false); |
| break; |
| } |
| } else { |
| SbxDimArray * arr = PTR_CAST(SbxDimArray, variable->GetObject()); |
| int dims = arr->GetDims(); |
| std::vector< sal_Int32 > low(dims); |
| std::vector< sal_Int32 > up(dims); |
| for (int i = 0; i < dims; ++i) { |
| arr->GetDim32(i + 1, low[i], up[i]); |
| } |
| for (std::vector< sal_Int32 > idx = low;;) { |
| data = unmarshal(arr->Get32(&idx[0]), data); |
| int i = dims - 1; |
| while (idx[i] == up[i]) { |
| idx[i] = low[i]; |
| if (i == 0) { |
| goto done; |
| } |
| --i; |
| } |
| ++idx[i]; |
| } |
| done:; |
| } |
| return data; |
| } |
| |
| SbError unmarshalString(StringData const & data, SbxVariable & result) { |
| rtl::OUString str; |
| if (data.buffer != 0) { |
| char const * p = static_cast< char const * >(data.buffer); |
| sal_Int32 len; |
| if (data.special) { |
| len = static_cast< sal_Int32 >(result.GetULong()); |
| if (len < 0) { // i.e., DWORD result >= 2^31 |
| return ERRCODE_BASIC_BAD_ARGUMENT; |
| //TODO: more specific errcode? |
| } |
| } else { |
| len = rtl_str_getLength(p); |
| } |
| SbError e = convert(p, len, &str); |
| if (e != ERRCODE_NONE) { |
| return e; |
| } |
| } |
| data.variable->PutString(String(str)); |
| return ERRCODE_NONE; |
| } |
| |
| struct ProcData { |
| rtl::OString name; |
| FARPROC proc; |
| }; |
| |
| SbError call( |
| rtl::OUString const & dll, ProcData const & proc, SbxArray * arguments, |
| SbxVariable & result) |
| { |
| std::vector< char > stack; |
| MarshalData data; |
| // For DWORD GetLogicalDriveStringsA(DWORD nBufferLength, LPSTR lpBuffer) |
| // from kernel32, upon return, filled lpBuffer length is result DWORD, which |
| // requires special handling in unmarshalString; other functions might |
| // require similar treatment, too: |
| bool special = |
| dll.equalsIgnoreAsciiCaseAsciiL( |
| RTL_CONSTASCII_STRINGPARAM("KERNEL32.DLL")) && |
| (proc.name == |
| rtl::OString(RTL_CONSTASCII_STRINGPARAM("GetLogicalDriveStringsA"))); |
| for (sal_uInt16 i = 1; i < (arguments == 0 ? 0 : arguments->Count()); ++i) { |
| SbError e = marshal( |
| true, arguments->Get(i), special && i == 2, stack, stack.size(), |
| data); |
| if (e != ERRCODE_NONE) { |
| return e; |
| } |
| align(stack, 4, 0, 0); |
| } |
| switch (result.GetType()) { |
| case SbxEMPTY: |
| DllMgr_call32(proc.proc, address(stack), stack.size()); |
| break; |
| case SbxINTEGER: |
| result.PutInteger( |
| static_cast< sal_Int16 >( |
| DllMgr_call32(proc.proc, address(stack), stack.size()))); |
| break; |
| case SbxLONG: |
| result.PutLong( |
| static_cast< sal_Int32 >( |
| DllMgr_call32(proc.proc, address(stack), stack.size()))); |
| break; |
| case SbxSINGLE: |
| result.PutSingle( |
| static_cast< float >( |
| DllMgr_callFp(proc.proc, address(stack), stack.size()))); |
| break; |
| case SbxDOUBLE: |
| result.PutDouble( |
| DllMgr_callFp(proc.proc, address(stack), stack.size())); |
| break; |
| case SbxSTRING: |
| { |
| char const * s1 = reinterpret_cast< char const * >( |
| DllMgr_call32(proc.proc, address(stack), stack.size())); |
| rtl::OUString s2; |
| SbError e = convert(s1, rtl_str_getLength(s1), &s2); |
| if (e != ERRCODE_NONE) { |
| return e; |
| } |
| result.PutString(String(s2)); |
| break; |
| } |
| case SbxOBJECT: |
| //TODO |
| DllMgr_call32(proc.proc, address(stack), stack.size()); |
| break; |
| case SbxBOOL: |
| result.PutBool( |
| static_cast< sal_Bool >( |
| DllMgr_call32(proc.proc, address(stack), stack.size()))); |
| break; |
| case SbxBYTE: |
| result.PutByte( |
| static_cast< sal_uInt8 >( |
| DllMgr_call32(proc.proc, address(stack), stack.size()))); |
| break; |
| default: |
| OSL_ASSERT(false); |
| break; |
| } |
| for (sal_uInt16 i = 1; i < (arguments == 0 ? 0 : arguments->Count()); ++i) { |
| arguments->Get(i)->ResetFlag(SBX_REFERENCE); |
| //TODO: skipped for errors?!? |
| } |
| for (std::vector< UnmarshalData >::iterator i(data.unmarshal.begin()); |
| i != data.unmarshal.end(); ++i) |
| { |
| unmarshal(i->variable, i->buffer); |
| } |
| for (std::vector< StringData >::iterator i(data.unmarshalStrings.begin()); |
| i != data.unmarshalStrings.end(); ++i) |
| { |
| SbError e = unmarshalString(*i, result); |
| if (e != ERRCODE_NONE) { |
| return e; |
| } |
| } |
| return ERRCODE_NONE; |
| } |
| |
| SbError getProcData(HMODULE handle, rtl::OUString const & name, ProcData * proc) |
| { |
| OSL_ASSERT(proc != 0); |
| if ( !name.isEmpty() && name[0] == '@' ) { //TODO: "@" vs. "#"??? |
| sal_Int32 n = name.copy(1).toInt32(); //TODO: handle bad input |
| if (n <= 0 || n > 0xFFFF) { |
| return ERRCODE_BASIC_BAD_ARGUMENT; //TODO: more specific errcode? |
| } |
| FARPROC p = GetProcAddress(handle, reinterpret_cast< LPCSTR >(n)); |
| if (p != 0) { |
| proc->name = rtl::OString(RTL_CONSTASCII_STRINGPARAM("#")) + |
| rtl::OString::valueOf(n); |
| proc->proc = p; |
| return ERRCODE_NONE; |
| } |
| } else { |
| rtl::OString name8; |
| SbError e = convert(name, &name8); |
| if (e != ERRCODE_NONE) { |
| return e; |
| } |
| FARPROC p = GetProcAddress(handle, name8.getStr()); |
| if (p != 0) { |
| proc->name = name8; |
| proc->proc = p; |
| return ERRCODE_NONE; |
| } |
| sal_Int32 i = name8.indexOf('#'); |
| if (i != -1) { |
| name8 = name8.copy(0, i); |
| p = GetProcAddress(handle, name8.getStr()); |
| if (p != 0) { |
| proc->name = name8; |
| proc->proc = p; |
| return ERRCODE_NONE; |
| } |
| } |
| rtl::OString real( |
| rtl::OString(RTL_CONSTASCII_STRINGPARAM("_")) + name8); |
| p = GetProcAddress(handle, real.getStr()); |
| if (p != 0) { |
| proc->name = real; |
| proc->proc = p; |
| return ERRCODE_NONE; |
| } |
| real = name8 + rtl::OString(RTL_CONSTASCII_STRINGPARAM("A")); |
| p = GetProcAddress(handle, real.getStr()); |
| if (p != 0) { |
| proc->name = real; |
| proc->proc = p; |
| return ERRCODE_NONE; |
| } |
| } |
| return ERRCODE_BASIC_PROC_UNDEFINED; |
| } |
| |
| struct Dll: public salhelper::SimpleReferenceObject { |
| private: |
| typedef std::map< rtl::OUString, ProcData > Procs; |
| |
| virtual ~Dll(); |
| |
| public: |
| Dll(): handle(0) {} |
| |
| SbError getProc(rtl::OUString const & name, ProcData * proc); |
| |
| HMODULE handle; |
| Procs procs; |
| }; |
| |
| Dll::~Dll() { |
| if (handle != 0 && !FreeLibrary(handle)) { |
| OSL_TRACE("FreeLibrary(%p) failed with %u", handle, GetLastError()); |
| } |
| } |
| |
| SbError Dll::getProc(rtl::OUString const & name, ProcData * proc) { |
| Procs::iterator i(procs.find(name)); |
| if (i != procs.end()) { |
| *proc = i->second; |
| return ERRCODE_NONE; |
| } |
| SbError e = getProcData(handle, name, proc); |
| if (e == ERRCODE_NONE) { |
| procs.insert(Procs::value_type(name, *proc)); |
| } |
| return e; |
| } |
| |
| rtl::OUString fullDllName(rtl::OUString const & name) { |
| rtl::OUString full(name); |
| if (full.indexOf('.') == -1) { |
| full += rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(".DLL")); |
| } |
| return full; |
| } |
| |
| } |
| |
| struct SbiDllMgr::Impl: private boost::noncopyable { |
| private: |
| typedef std::map< rtl::OUString, rtl::Reference< Dll > > Dlls; |
| |
| public: |
| Dll * getDll(rtl::OUString const & name); |
| |
| Dlls dlls; |
| }; |
| |
| Dll * SbiDllMgr::Impl::getDll(rtl::OUString const & name) { |
| Dlls::iterator i(dlls.find(name)); |
| if (i == dlls.end()) { |
| i = dlls.insert(Dlls::value_type(name, new Dll)).first; |
| HMODULE h = LoadLibraryW(reinterpret_cast<LPCWSTR>(name.getStr())); |
| if (h == 0) { |
| dlls.erase(i); |
| return 0; |
| } |
| i->second->handle = h; |
| } |
| return i->second.get(); |
| } |
| |
| SbError SbiDllMgr::Call( |
| rtl::OUString const & function, rtl::OUString const & library, |
| SbxArray * arguments, SbxVariable & result, bool cdeclConvention) |
| { |
| if (cdeclConvention) { |
| return ERRCODE_BASIC_NOT_IMPLEMENTED; |
| } |
| rtl::OUString dllName(fullDllName(library)); |
| Dll * dll = impl_->getDll(dllName); |
| if (dll == 0) { |
| return ERRCODE_BASIC_BAD_DLL_LOAD; |
| } |
| ProcData proc; |
| SbError e = dll->getProc(function, &proc); |
| if (e != ERRCODE_NONE) { |
| return e; |
| } |
| return call(dllName, proc, arguments, result); |
| } |
| |
| void SbiDllMgr::FreeDll(rtl::OUString const & library) { |
| impl_->dlls.erase(library); |
| } |
| |
| #else |
| |
| struct SbiDllMgr::Impl {}; |
| |
| SbError SbiDllMgr::Call( |
| rtl::OUString const &, rtl::OUString const &, SbxArray *, SbxVariable &, |
| bool) |
| { |
| return ERRCODE_BASIC_NOT_IMPLEMENTED; |
| } |
| |
| void SbiDllMgr::FreeDll(rtl::OUString const &) {} |
| |
| #endif |
| |
| SbiDllMgr::SbiDllMgr(): impl_(new Impl) {} |
| |
| SbiDllMgr::~SbiDllMgr() {} |