1/////////////////////////////////////////////////////////////////////////////
2// Name:         src/common/dynload.cpp
3// Purpose:      Dynamic loading framework
4// Author:       Ron Lee, David Falkinder, Vadim Zeitlin and a cast of 1000's
5//               (derived in part from dynlib.cpp (c) 1998 Guilhem Lavaux)
6// Modified by:
7// Created:      03/12/01
8// RCS-ID:       $Id: dynload.cpp 40943 2006-08-31 19:31:43Z ABX $
9// Copyright:    (c) 2001 Ron Lee <ron@debian.org>
10// Licence:      wxWindows licence
11/////////////////////////////////////////////////////////////////////////////
12
13// ----------------------------------------------------------------------------
14// headers
15// ----------------------------------------------------------------------------
16
17#include "wx/wxprec.h"
18
19#ifdef __BORLANDC__
20    #pragma hdrstop
21#endif
22
23#if wxUSE_DYNAMIC_LOADER
24
25#ifdef __WINDOWS__
26    #include "wx/msw/private.h"
27#endif
28
29#ifndef WX_PRECOMP
30    #include "wx/log.h"
31    #include "wx/intl.h"
32    #include "wx/hash.h"
33    #include "wx/utils.h"
34    #include "wx/module.h"
35#endif
36
37#include "wx/strconv.h"
38
39#include "wx/dynload.h"
40
41
42// ---------------------------------------------------------------------------
43// wxPluginLibrary
44// ---------------------------------------------------------------------------
45
46
47wxDLImports*  wxPluginLibrary::ms_classes = NULL;
48
49class wxPluginLibraryModule : public wxModule
50{
51public:
52    wxPluginLibraryModule() { }
53
54    // TODO: create ms_classes on demand, why always preallocate it?
55    virtual bool OnInit()
56    {
57        wxPluginLibrary::ms_classes = new wxDLImports;
58        wxPluginManager::CreateManifest();
59        return true;
60    }
61
62    virtual void OnExit()
63    {
64        delete wxPluginLibrary::ms_classes;
65        wxPluginLibrary::ms_classes = NULL;
66        wxPluginManager::ClearManifest();
67    }
68
69private:
70    DECLARE_DYNAMIC_CLASS(wxPluginLibraryModule )
71};
72
73IMPLEMENT_DYNAMIC_CLASS(wxPluginLibraryModule, wxModule)
74
75
76wxPluginLibrary::wxPluginLibrary(const wxString &libname, int flags)
77        : m_linkcount(1)
78        , m_objcount(0)
79{
80    m_before = wxClassInfo::sm_first;
81    Load( libname, flags );
82    m_after = wxClassInfo::sm_first;
83
84    if( m_handle != 0 )
85    {
86        UpdateClasses();
87        RegisterModules();
88    }
89    else
90    {
91        // Flag us for deletion
92        --m_linkcount;
93    }
94}
95
96wxPluginLibrary::~wxPluginLibrary()
97{
98    if( m_handle != 0 )
99    {
100        UnregisterModules();
101        RestoreClasses();
102    }
103}
104
105wxPluginLibrary *wxPluginLibrary::RefLib()
106{
107    wxCHECK_MSG( m_linkcount > 0, NULL,
108                 _T("Library had been already deleted!") );
109
110    ++m_linkcount;
111    return this;
112}
113
114bool wxPluginLibrary::UnrefLib()
115{
116    wxASSERT_MSG( m_objcount == 0,
117                  _T("Library unloaded before all objects were destroyed") );
118
119    if ( m_linkcount == 0 || --m_linkcount == 0 )
120    {
121        delete this;
122        return true;
123    }
124
125    return false;
126}
127
128// ------------------------
129// Private methods
130// ------------------------
131
132void wxPluginLibrary::UpdateClasses()
133{
134    for (wxClassInfo *info = m_after; info != m_before; info = info->m_next)
135    {
136        if( info->GetClassName() )
137        {
138            // Hash all the class names into a local table too so
139            // we can quickly find the entry they correspond to.
140            (*ms_classes)[info->GetClassName()] = this;
141        }
142    }
143}
144
145void wxPluginLibrary::RestoreClasses()
146{
147    // Check if there is a need to restore classes.
148    if (!ms_classes)
149        return;
150
151    for(wxClassInfo *info = m_after; info != m_before; info = info->m_next)
152    {
153        ms_classes->erase(ms_classes->find(info->GetClassName()));
154    }
155}
156
157void wxPluginLibrary::RegisterModules()
158{
159    // Plugin libraries might have wxModules, Register and initialise them if
160    // they do.
161    //
162    // Note that these classes are NOT included in the reference counting since
163    // it's implicit that they will be unloaded if and when the last handle to
164    // the library is.  We do have to keep a copy of the module's pointer
165    // though, as there is currently no way to Unregister it without it.
166
167    wxASSERT_MSG( m_linkcount == 1,
168                  _T("RegisterModules should only be called for the first load") );
169
170    for ( wxClassInfo *info = m_after; info != m_before; info = info->m_next)
171    {
172        if( info->IsKindOf(CLASSINFO(wxModule)) )
173        {
174            wxModule *m = wxDynamicCast(info->CreateObject(), wxModule);
175
176            wxASSERT_MSG( m, _T("wxDynamicCast of wxModule failed") );
177
178            m_wxmodules.push_back(m);
179            wxModule::RegisterModule(m);
180        }
181    }
182
183    // FIXME: Likewise this is (well was) very similar to InitializeModules()
184
185    for ( wxModuleList::iterator it = m_wxmodules.begin();
186          it != m_wxmodules.end();
187          ++it)
188    {
189        if( !(*it)->Init() )
190        {
191            wxLogDebug(_T("wxModule::Init() failed for wxPluginLibrary"));
192
193            // XXX: Watch this, a different hash implementation might break it,
194            //      a good hash implementation would let us fix it though.
195
196            // The name of the game is to remove any uninitialised modules and
197            // let the dtor Exit the rest on shutdown, (which we'll initiate
198            // shortly).
199
200            wxModuleList::iterator oldNode = m_wxmodules.end();
201            do {
202                ++it;
203                if( oldNode != m_wxmodules.end() )
204                    m_wxmodules.erase(oldNode);
205                wxModule::UnregisterModule( *it );
206                oldNode = it;
207            } while( it != m_wxmodules.end() );
208
209            --m_linkcount;     // Flag us for deletion
210            break;
211        }
212    }
213}
214
215void wxPluginLibrary::UnregisterModules()
216{
217    wxModuleList::iterator it;
218
219    for ( it = m_wxmodules.begin(); it != m_wxmodules.end(); ++it )
220        (*it)->Exit();
221
222    for ( it = m_wxmodules.begin(); it != m_wxmodules.end(); ++it )
223        wxModule::UnregisterModule( *it );
224
225    // NB: content of the list was deleted by UnregisterModule calls above:
226    m_wxmodules.clear();
227}
228
229
230// ---------------------------------------------------------------------------
231// wxPluginManager
232// ---------------------------------------------------------------------------
233
234wxDLManifest*   wxPluginManager::ms_manifest = NULL;
235
236// ------------------------
237// Static accessors
238// ------------------------
239
240wxPluginLibrary *
241wxPluginManager::LoadLibrary(const wxString &libname, int flags)
242{
243    wxString realname(libname);
244
245    if( !(flags & wxDL_VERBATIM) )
246        realname += wxDynamicLibrary::GetDllExt();
247
248    wxPluginLibrary *entry;
249
250    if ( flags & wxDL_NOSHARE )
251    {
252        entry = NULL;
253    }
254    else
255    {
256        entry = FindByName(realname);
257    }
258
259    if ( entry )
260    {
261        wxLogTrace(_T("dll"),
262                   _T("LoadLibrary(%s): already loaded."), realname.c_str());
263
264        entry->RefLib();
265    }
266    else
267    {
268        entry = new wxPluginLibrary( libname, flags );
269
270        if ( entry->IsLoaded() )
271        {
272            (*ms_manifest)[realname] = entry;
273
274            wxLogTrace(_T("dll"),
275                       _T("LoadLibrary(%s): loaded ok."), realname.c_str());
276
277        }
278        else
279        {
280            wxLogTrace(_T("dll"),
281                       _T("LoadLibrary(%s): failed to load."), realname.c_str());
282
283            // we have created entry just above
284            if ( !entry->UnrefLib() )
285            {
286                // ... so UnrefLib() is supposed to delete it
287                wxFAIL_MSG( _T("Currently linked library is not loaded?") );
288            }
289
290            entry = NULL;
291        }
292    }
293
294    return entry;
295}
296
297bool wxPluginManager::UnloadLibrary(const wxString& libname)
298{
299    wxString realname = libname;
300
301    wxPluginLibrary *entry = FindByName(realname);
302
303    if ( !entry )
304    {
305        realname += wxDynamicLibrary::GetDllExt();
306
307        entry = FindByName(realname);
308    }
309
310    if ( !entry )
311    {
312        wxLogDebug(_T("Attempt to unload library '%s' which is not loaded."),
313                   libname.c_str());
314
315        return false;
316    }
317
318    wxLogTrace(_T("dll"), _T("UnloadLibrary(%s)"), realname.c_str());
319
320    if ( !entry->UnrefLib() )
321    {
322        // not really unloaded yet
323        return false;
324    }
325
326    ms_manifest->erase(ms_manifest->find(realname));
327
328    return true;
329}
330
331// ------------------------
332// Class implementation
333// ------------------------
334
335bool wxPluginManager::Load(const wxString &libname, int flags)
336{
337    m_entry = wxPluginManager::LoadLibrary(libname, flags);
338
339    return IsLoaded();
340}
341
342void wxPluginManager::Unload()
343{
344    wxCHECK_RET( m_entry, _T("unloading an invalid wxPluginManager?") );
345
346    for ( wxDLManifest::iterator i = ms_manifest->begin();
347          i != ms_manifest->end();
348          ++i )
349    {
350        if ( i->second == m_entry )
351        {
352            ms_manifest->erase(i);
353            break;
354        }
355    }
356
357    m_entry->UnrefLib();
358
359    m_entry = NULL;
360}
361
362#endif  // wxUSE_DYNAMIC_LOADER
363