xref: /aoo41x/main/basic/source/runtime/dllmgr.cxx (revision e1f63238)
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