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