Reorganization of the loader to correctly load and free libraries and
implementation of load order to load different types of libraries.

diff --git a/loader/module.c b/loader/module.c
index 8ba903c..30e5bdc 100644
--- a/loader/module.c
+++ b/loader/module.c
@@ -29,6 +29,8 @@
 #include "task.h"
 #include "debug.h"
 #include "callback.h"
+#include "loadorder.h"
+#include "elfdll.h"
 
 
 /*************************************************************************
@@ -53,10 +55,55 @@
 }
 
 /*************************************************************************
- *		MODULE_InitializeDLLs
+ *		MODULE_InitDll
+ */
+static BOOL MODULE_InitDll( WINE_MODREF *wm, DWORD type, LPVOID lpReserved )
+{
+    BOOL retv = TRUE;
+
+    static LPCSTR typeName[] = { "PROCESS_DETACH", "PROCESS_ATTACH", 
+                                 "THREAD_ATTACH", "THREAD_DETACH" };
+    assert( wm );
+
+
+    /* Skip calls for modules loaded with special load flags */
+
+    if (    ( wm->flags & WINE_MODREF_DONT_RESOLVE_REFS )
+         || ( wm->flags & WINE_MODREF_LOAD_AS_DATAFILE ) )
+        return TRUE;
+
+
+    TRACE( module, "(%s,%s,%p) - CALL\n", 
+           wm->modname, typeName[type], lpReserved );
+
+    /* Call the initialization routine */
+    switch ( wm->type )
+    {
+    case MODULE32_PE:
+        retv = PE_InitDLL( wm, type, lpReserved );
+        break;
+
+    case MODULE32_ELF:
+        /* no need to do that, dlopen() already does */
+        break;
+
+    default:
+        ERR( module, "wine_modref type %d not handled.\n", wm->type );
+        retv = FALSE;
+        break;
+    }
+
+    TRACE( module, "(%s,%s,%p) - RETURN %d\n", 
+           wm->modname, typeName[type], lpReserved, retv );
+
+    return retv;
+}
+
+/*************************************************************************
+ *		MODULE_DllProcessAttach
  * 
- * Call the initialization routines of all DLLs belonging to the
- * current process. This is somewhat complicated due to the fact that
+ * Send the process attach notification to all DLLs the given module
+ * depends on (recursively). This is somewhat complicated due to the fact that
  *
  * - we have to respect the module dependencies, i.e. modules implicitly
  *   referenced by another module have to be initialized before the module
@@ -69,171 +116,159 @@
  * (Note that this routine can be recursively entered not only directly
  *  from itself, but also via LoadLibrary from one of the called initialization
  *  routines.)
+ *
+ * Furthermore, we need to rearrange the main WINE_MODREF list to allow
+ * the process *detach* notifications to be sent in the correct order.
+ * This must not only take into account module dependencies, but also 
+ * 'hidden' dependencies created by modules calling LoadLibrary in their
+ * attach notification routine.
+ *
+ * The strategy is rather simple: we move a WINE_MODREF to the head of the
+ * list after the attach notification has returned.  This implies that the
+ * detach notifications are called in the reverse of the sequence the attach
+ * notifications *returned*.
+ *
+ * NOTE: Assumes that the process critical section is held!
+ *
  */
-static void MODULE_DoInitializeDLLs( WINE_MODREF *wm,
-                                     DWORD type, LPVOID lpReserved )
+BOOL MODULE_DllProcessAttach( WINE_MODREF *wm, LPVOID lpReserved )
 {
-    WINE_MODREF *xwm;
-    int i, skip = FALSE;
+    BOOL retv = TRUE;
+    int i;
+    assert( wm );
 
-    assert( wm && !(wm->flags & WINE_MODREF_MARKER) );
-    TRACE( module, "(%s,%08x,%ld,%p) - START\n", 
-           wm->modname, wm->module, type, lpReserved );
+    /* prevent infinite recursion in case of cyclical dependencies */
+    if (    ( wm->flags & WINE_MODREF_MARKER )
+         || ( wm->flags & WINE_MODREF_PROCESS_ATTACHED ) )
+        return retv;
+
+    TRACE( module, "(%s,%p) - START\n", 
+           wm->modname, lpReserved );
 
     /* Tag current MODREF to prevent recursive loop */
     wm->flags |= WINE_MODREF_MARKER;
 
-    switch ( type )
+    /* Recursively attach all DLLs this one depends on */
+    for ( i = 0; retv && i < wm->nDeps; i++ )
+        if ( wm->deps[i] )
+            retv = MODULE_DllProcessAttach( wm->deps[i], lpReserved );
+
+    /* Call DLL entry point */
+    if ( retv )
     {
-    default:
-    case DLL_PROCESS_ATTACH:
-    case DLL_THREAD_ATTACH:
-        /* Recursively attach all DLLs this one depends on */
-        for ( i = 0; i < wm->nDeps; i++ )
-            if ( wm->deps[i] && !(wm->deps[i]->flags & WINE_MODREF_MARKER) )
-                MODULE_DoInitializeDLLs( wm->deps[i], type, lpReserved );
-        break;
-
-    case DLL_PROCESS_DETACH:
-    case DLL_THREAD_DETACH:
-        /* Recursively detach all DLLs that depend on this one */
-        for ( xwm = PROCESS_Current()->modref_list; xwm; xwm = xwm->next )
-            if ( !(xwm->flags & WINE_MODREF_MARKER) )
-                for ( i = 0; i < xwm->nDeps; i++ )
-                    if ( xwm->deps[i] == wm )
-                    {
-                        MODULE_DoInitializeDLLs( xwm, type, lpReserved );
-                        break;
-                    }
-        break;
-    }
-
-    /* Evaluate module flags */
-
-    if (    ( wm->flags & WINE_MODREF_NO_DLL_CALLS )
-         || ( wm->flags & WINE_MODREF_DONT_RESOLVE_REFS )
-         || ( wm->flags & WINE_MODREF_LOAD_AS_DATAFILE ) )
-        skip = TRUE;
-
-    if ( type == DLL_PROCESS_ATTACH )
-    {
-        if ( wm->flags & WINE_MODREF_PROCESS_ATTACHED )
-            skip = TRUE;
-        else
+        retv = MODULE_InitDll( wm, DLL_PROCESS_ATTACH, lpReserved );
+        if ( retv )
             wm->flags |= WINE_MODREF_PROCESS_ATTACHED;
     }
 
-    if ( type == DLL_PROCESS_DETACH )
+    /* Re-insert MODREF at head of list */
+    if ( retv && wm->prev )
     {
-        if ( wm->flags & WINE_MODREF_PROCESS_DETACHED )
-            skip = TRUE;
-        else
-            wm->flags |= WINE_MODREF_PROCESS_DETACHED;
+        wm->prev->next = wm->next;
+        if ( wm->next ) wm->next->prev = wm->prev;
+
+        wm->prev = NULL;
+        wm->next = PROCESS_Current()->modref_list;
+        PROCESS_Current()->modref_list = wm->next->prev = wm;
     }
 
-    if ( !skip )
-    {
-        /* Now we can call the initialization routine */
-        TRACE( module, "(%s,%08x,%ld,%p) - CALL\n", 
-               wm->modname, wm->module, type, lpReserved );
+    /* Remove recursion flag */
+    wm->flags &= ~WINE_MODREF_MARKER;
 
-        switch ( wm->type )
-        {
-        case MODULE32_PE:
-            PE_InitDLL( wm, type, lpReserved );
-            break;
+    TRACE( module, "(%s,%p) - END\n", 
+           wm->modname, lpReserved );
 
-        case MODULE32_ELF:
-            /* no need to do that, dlopen() already does */
-            break;
-
-        default:
-           ERR(module, "wine_modref type %d not handled.\n", wm->type);
-           break;
-        }
-    }
-
-    TRACE( module, "(%s,%08x,%ld,%p) - END\n", 
-           wm->modname, wm->module, type, lpReserved );
+    return retv;
 }
 
-void MODULE_InitializeDLLs( HMODULE root, DWORD type, LPVOID lpReserved )
+/*************************************************************************
+ *		MODULE_DllProcessDetach
+ * 
+ * Send DLL process detach notifications.  See the comment about calling 
+ * sequence at MODULE_DllProcessAttach.  Unless the bForceDetach flag
+ * is set, only DLLs with zero refcount are notified.
+ *
+ * NOTE: Assumes that the process critical section is held!
+ *
+ */
+void MODULE_DllProcessDetach( BOOL bForceDetach, LPVOID lpReserved )
 {
-    BOOL inProgress = FALSE;
     WINE_MODREF *wm;
 
-    /* Grab the process critical section to protect the recursion flags */
-    /* FIXME: This is probably overkill! */
-    EnterCriticalSection( &PROCESS_Current()->crit_section );
-
-    TRACE( module, "(%08x,%ld,%p) - START\n", root, type, lpReserved );
-
-    /* First, check whether initialization is currently in progress */
-    for ( wm = PROCESS_Current()->modref_list; wm; wm = wm->next )
-        if ( wm->flags & WINE_MODREF_MARKER )
+    do
+    {
+        for ( wm = PROCESS_Current()->modref_list; wm; wm = wm->next )
         {
-            inProgress = TRUE;
+            /* Check whether to detach this DLL */
+            if ( !(wm->flags & WINE_MODREF_PROCESS_ATTACHED) )
+                continue;
+            if ( wm->refCount > 0 && !bForceDetach )
+                continue;
+
+            /* Call detach notification */
+            wm->flags &= ~WINE_MODREF_PROCESS_ATTACHED;
+            MODULE_InitDll( wm, DLL_PROCESS_DETACH, lpReserved );
+
+            /* Restart at head of WINE_MODREF list, as entries might have
+               been added and/or removed while performing the call ... */
             break;
         }
+    } while ( wm );
+}
 
-    if ( inProgress )
+/*************************************************************************
+ *		MODULE_DllThreadAttach
+ * 
+ * Send DLL thread attach notifications. These are sent in the
+ * reverse sequence of process detach notification.
+ *
+ */
+void MODULE_DllThreadAttach( LPVOID lpReserved )
+{
+    WINE_MODREF *wm;
+
+    EnterCriticalSection( &PROCESS_Current()->crit_section );
+
+    for ( wm = PROCESS_Current()->modref_list; wm; wm = wm->next )
+        if ( !wm->next )
+            break;
+
+    for ( ; wm; wm = wm->prev )
     {
-        /* 
-         * If this a LoadLibrary call from within an initialization routine,
-         * treat it analogously to an implicitly referenced DLL.
-         * Anything else may not happen at this point!
-         */
-        if ( root )
-        {
-            wm = MODULE32_LookupHMODULE( root );
-            if ( wm && !(wm->flags & WINE_MODREF_MARKER) )
-                MODULE_DoInitializeDLLs( wm, type, lpReserved );
-        }
-        else
-            FIXME(module, "Invalid recursion!\n");
-    }
-    else
-    {
-        /* If we arrive here, this is the start of an initialization run */
-        if ( !root )
-        {
-            /* If called for main EXE, initialize all DLLs */
-            switch ( type )
-            {
-            default:  /* Hmmm. */
-            case DLL_PROCESS_ATTACH:
-            case DLL_THREAD_ATTACH:
-                for ( wm = PROCESS_Current()->modref_list; wm; wm = wm->next )
-                    if ( !wm->next )
-                        break;
-                for ( ; wm; wm = wm->prev )
-                    if ( !(wm->flags & WINE_MODREF_MARKER) )
-                        MODULE_DoInitializeDLLs( wm, type, lpReserved );
-                break;
+        if ( !(wm->flags & WINE_MODREF_PROCESS_ATTACHED) )
+            continue;
+        if ( wm->flags & WINE_MODREF_NO_DLL_CALLS )
+            continue;
 
-            case DLL_PROCESS_DETACH:
-            case DLL_THREAD_DETACH:
-                for ( wm = PROCESS_Current()->modref_list; wm; wm = wm->next )
-                    if ( !(wm->flags & WINE_MODREF_MARKER) )
-                        MODULE_DoInitializeDLLs( wm, type, lpReserved );
-                break;
-            }
-        }
-        else
-        {
-            /* If called for a specific DLL, initialize only it and its children */
-            wm = MODULE32_LookupHMODULE( root );
-            if (wm) MODULE_DoInitializeDLLs( wm, type, lpReserved );
-        }
-
-        /* We're finished, so we reset all recursion flags */
-        for ( wm = PROCESS_Current()->modref_list; wm; wm = wm->next )
-            wm->flags &= ~WINE_MODREF_MARKER;
+        MODULE_InitDll( wm, DLL_THREAD_ATTACH, lpReserved );
     }
 
-    TRACE( module, "(%08x,%ld,%p) - END\n", root, type, lpReserved );
+    LeaveCriticalSection( &PROCESS_Current()->crit_section );
+}
 
-    /* Release critical section */
+/*************************************************************************
+ *		MODULE_DllThreadDetach
+ * 
+ * Send DLL thread detach notifications. These are sent in the
+ * same sequence as process detach notification.
+ *
+ */
+void MODULE_DllThreadDetach( LPVOID lpReserved )
+{
+    WINE_MODREF *wm;
+
+    EnterCriticalSection( &PROCESS_Current()->crit_section );
+
+    for ( wm = PROCESS_Current()->modref_list; wm; wm = wm->next )
+    {
+        if ( !(wm->flags & WINE_MODREF_PROCESS_ATTACHED) )
+            continue;
+        if ( wm->flags & WINE_MODREF_NO_DLL_CALLS )
+            continue;
+
+        MODULE_InitDll( wm, DLL_THREAD_DETACH, lpReserved );
+    }
+
     LeaveCriticalSection( &PROCESS_Current()->crit_section );
 }
 
@@ -244,48 +279,20 @@
  */
 BOOL WINAPI DisableThreadLibraryCalls( HMODULE hModule )
 {
-    WINE_MODREF *wm = MODULE32_LookupHMODULE( hModule );
-    if ( !wm ) return FALSE;
-
-    wm->flags |= WINE_MODREF_NO_DLL_CALLS;
-    return TRUE;
-}
-
-/****************************************************************************
- *              MODULE_IncRefCount
- */
-static void MODULE_IncRefCount( HMODULE hModule )
-{
     WINE_MODREF *wm;
+    BOOL retval = TRUE;
 
     EnterCriticalSection( &PROCESS_Current()->crit_section );
-    wm = MODULE32_LookupHMODULE( hModule );
-    if( wm )
-    {
-        wm->refCount++;
-        TRACE(module, "(%08x) count now %d\n", hModule, wm->refCount);
-    }
-    LeaveCriticalSection( &PROCESS_Current()->crit_section );
-}
 
-/****************************************************************************
- *              MODULE_DecRefCount
- */
-static int MODULE_DecRefCount( HMODULE hModule )
-{
-    int retv = 0;
-    WINE_MODREF *wm;
-
-    EnterCriticalSection( &PROCESS_Current()->crit_section );
     wm = MODULE32_LookupHMODULE( hModule );
-    if( wm && ( retv = wm->refCount ) > 0 )
-    {
-        wm->refCount--;
-        TRACE(module, "(%08x) count now %d\n", hModule, wm->refCount);
-    }
+    if ( !wm )
+        retval = FALSE;
+    else
+        wm->flags |= WINE_MODREF_NO_DLL_CALLS;
+
     LeaveCriticalSection( &PROCESS_Current()->crit_section );
 
-    return retv;
+    return retval;
 }
 
 
@@ -449,7 +456,7 @@
  *	the module handle if found
  * 	0 if not
  */
-HMODULE MODULE_FindModule(
+WINE_MODREF *MODULE_FindModule(
 	LPCSTR path	/* [in] pathname of module/library to be found */
 ) {
     LPSTR	filename;
@@ -477,7 +484,7 @@
 	if (!strcasecmp( filename, xmodname)) {
 	    HeapFree( GetProcessHeap(), 0, filename );
 	    HeapFree( GetProcessHeap(), 0, xmodname );
-	    return wm->module;
+	    return wm;
 	}
 	if (dotptr) *dotptr='.';
 	/* FIXME: add paths, shortname */
@@ -504,14 +511,14 @@
 	if (!strcasecmp( filename, xlname)) {
 	    HeapFree( GetProcessHeap(), 0, filename );
 	    HeapFree( GetProcessHeap(), 0, xlname );
-	    return wm->module;
+	    return wm;
 	}
 	if (dotptr) *dotptr='.';
 	/* FIXME: add paths, shortname */
 	HeapFree( GetProcessHeap(), 0, xlname );
     }
     HeapFree( GetProcessHeap(), 0, filename );
-    return 0;
+    return NULL;
 }
 
 /***********************************************************************
@@ -1121,10 +1128,14 @@
  */
 HMODULE WINAPI GetModuleHandleA(LPCSTR module)
 {
-    if (module == NULL)
-    	return PROCESS_Current()->exe_modref->module;
+    WINE_MODREF *wm;
+
+    if ( module == NULL )
+        wm = PROCESS_Current()->exe_modref;
     else
-	return MODULE_FindModule( module );
+        wm = MODULE_FindModule( module );
+
+    return wm? wm->module : 0;
 }
 
 HMODULE WINAPI GetModuleHandleW(LPCWSTR module)
@@ -1186,43 +1197,116 @@
 }
 
 /***********************************************************************
- *           LoadLibraryEx32A   (KERNEL32)
+ *           LoadLibraryExA   (KERNEL32)
  */
-HMODULE WINAPI LoadLibraryExA(LPCSTR libname,HFILE hfile,DWORD flags)
+HMODULE WINAPI LoadLibraryExA(LPCSTR libname, HFILE hfile, DWORD flags)
 {
-    HMODULE hmod;
+	WINE_MODREF *wm;
 
-    if (!libname) {
-    	SetLastError(ERROR_INVALID_PARAMETER);
-	return 0;
-    }
-    hmod = MODULE_LoadLibraryExA( libname, hfile, flags );
+	if(!libname)
+	{
+		SetLastError(ERROR_INVALID_PARAMETER);
+		return 0;
+	}
 
-    if ( hmod >= 32 )
-    {
-        /* Initialize DLL just loaded */
-        MODULE_InitializeDLLs( hmod, DLL_PROCESS_ATTACH, NULL );
-	/* FIXME: check for failure, SLE(ERROR_DLL_INIT_FAILED) */
-    }
+	wm = MODULE_LoadLibraryExA( libname, hfile, flags );
 
-    return hmod;
+	return wm ? wm->module : 0;
 }
 
-HMODULE MODULE_LoadLibraryExA( LPCSTR libname, HFILE hfile, DWORD flags )
+/***********************************************************************
+ *	MODULE_LoadLibraryExA	(internal)
+ *
+ * Load a PE style module according to the load order.
+ *
+ * The HFILE parameter is not used and marked reserved in the SDK. I can
+ * only guess that it should force a file to be mapped, but I rather
+ * ignore the parameter because it would be extremely difficult to
+ * integrate this with different types of module represenations.
+ *
+ */
+WINE_MODREF *MODULE_LoadLibraryExA( LPCSTR libname, HFILE hfile, DWORD flags )
 {
-    HMODULE hmod;
-    
-    hmod = ELF_LoadLibraryExA( libname, hfile, flags );
-    if(hmod < (HMODULE)32)
-        hmod = PE_LoadLibraryExA( libname, hfile, flags );
+	DWORD err;
+	WINE_MODREF *pwm;
+	int i;
+	module_loadorder_t *plo;
 
-    if(hmod >= (HMODULE)32)
-    {
-        /* Increment RefCount */
-        MODULE_IncRefCount( hmod );
-    }
+	EnterCriticalSection(&PROCESS_Current()->crit_section);
 
-    return hmod;
+	/* Check for already loaded module */
+	if((pwm = MODULE_FindModule(libname))) 
+	{
+		pwm->refCount++;
+		TRACE(module, "Already loaded module '%s' at 0x%08x, count=%d, \n", libname, pwm->module, pwm->refCount);
+		LeaveCriticalSection(&PROCESS_Current()->crit_section);
+		return pwm;
+	}
+
+	plo = MODULE_GetLoadOrder(libname);
+
+	for(i = 0; i < MODULE_LOADORDER_NTYPES; i++)
+	{
+		switch(plo->loadorder[i])
+		{
+		case MODULE_LOADORDER_DLL:
+			TRACE(module, "Trying native dll '%s'\n", libname);
+			pwm = PE_LoadLibraryExA(libname, flags, &err);
+			break;
+
+		case MODULE_LOADORDER_ELFDLL:
+			TRACE(module, "Trying elfdll '%s'\n", libname);
+			pwm = ELFDLL_LoadLibraryExA(libname, flags, &err);
+			break;
+
+		case MODULE_LOADORDER_SO:
+			TRACE(module, "Trying so-library '%s'\n", libname);
+			pwm = ELF_LoadLibraryExA(libname, flags, &err);
+			break;
+
+		case MODULE_LOADORDER_BI:
+			TRACE(module, "Trying built-in '%s'\n", libname);
+			pwm = BUILTIN32_LoadLibraryExA(libname, flags, &err);
+			break;
+
+		default:
+			ERR(module, "Got invalid loadorder type %d (%s index %d)\n", plo->loadorder[i], plo->modulename, i);
+		/* Fall through */
+
+		case MODULE_LOADORDER_INVALID:	/* We ignore this as it is an empty entry */
+			pwm = NULL;
+			break;
+		}
+
+		if(pwm)
+		{
+			/* Initialize DLL just loaded */
+			TRACE(module, "Loaded module '%s' at 0x%08x, \n", libname, pwm->module);
+
+			/* Set the refCount here so that an attach failure will */
+			/* decrement the dependencies through the MODULE_FreeLibrary call. */
+			pwm->refCount++;
+
+			if(!MODULE_DllProcessAttach(pwm, NULL))
+			{
+				WARN(module, "Attach failed for module '%s', \n", libname);
+				MODULE_FreeLibrary(pwm);
+				SetLastError(ERROR_DLL_INIT_FAILED);
+				pwm = NULL;
+			}
+
+			LeaveCriticalSection(&PROCESS_Current()->crit_section);
+			return pwm;
+		}
+
+		if(err != ERROR_FILE_NOT_FOUND)
+			break;
+	}
+
+	ERR(module, "Failed to load module '%s'; error=0x%08lx, \n", libname, err);
+	SetLastError(err);
+	LeaveCriticalSection(&PROCESS_Current()->crit_section);
+	return NULL;
 }
 
 /***********************************************************************
@@ -1252,97 +1336,121 @@
     return ret;
 }
 
-
 /***********************************************************************
- *	MODULE_FreeLibrary		(internal)
+ *           MODULE_FlushModrefs
  *
- * Decrease the loadcount of the dll.
- * If the count reaches 0, then notify the dll and free all dlls
- * that depend on this one.
+ * NOTE: Assumes that the process critical section is held!
  *
+ * Remove all unused modrefs and call the internal unloading routines
+ * for the library type.
  */
-static void MODULE_FreeLibrary(WINE_MODREF *wm, BOOL first)
+static void MODULE_FlushModrefs(void)
 {
-    int i;
+	WINE_MODREF *wm, *next;
 
-    assert(wm != NULL);
+	for(wm = PROCESS_Current()->modref_list; wm; wm = next)
+	{
+		next = wm->next;
 
-    /* Don't do anything if there still are references */
-    if( MODULE_DecRefCount(wm->module) > 1 )
-        return;
+		if(wm->refCount)
+			continue;
 
-    TRACE( module, "(%s, %08x, %d) - START\n", wm->modname, wm->module, first);
+		/* Unlink this modref from the chain */
+		if(wm->next)
+                        wm->next->prev = wm->prev;
+		if(wm->prev)
+                        wm->prev->next = wm->next;
+		if(wm == PROCESS_Current()->modref_list)
+			PROCESS_Current()->modref_list = wm->next;
 
-    /* Evaluate module flags */
-    if(!(    ( wm->flags & WINE_MODREF_NO_DLL_CALLS )
-          || ( wm->flags & WINE_MODREF_DONT_RESOLVE_REFS )
-          || ( wm->flags & WINE_MODREF_LOAD_AS_DATAFILE ) ))
-    {
-        /* Now we can call the initialization routine */
-        TRACE( module, "(%s, %08x, %d) - CALL\n", wm->modname, wm->module, first);
+		/* 
+		 * The unloaders are also responsible for freeing the modref itself
+		 * because the loaders were responsible for allocating it.
+		 */
+		switch(wm->type)
+		{
+		case MODULE32_PE:	PE_UnloadLibrary(wm);		break;
+		case MODULE32_ELF:	ELF_UnloadLibrary(wm);		break;
+		case MODULE32_ELFDLL:	ELFDLL_UnloadLibrary(wm);	break;
+		case MODULE32_BI:	BUILTIN32_UnloadLibrary(wm);	break;
 
-        switch ( wm->type )
-        {
-        case MODULE32_PE:
-            PE_InitDLL(wm, DLL_PROCESS_DETACH, (LPVOID)(first ? 0 : 1));
-            break;
-
-        case MODULE32_ELF:
-            FIXME(module, "FreeLibrary requested on ELF module '%s'\n", wm->modname);
-            break;
-
-        default:
-           ERR(module, "wine_modref type %d not handled.\n", wm->type);
-           break;
-        }
-    }
-
-    /* Recursively free all DLLs that depend on this one */
-    for( i = 0; i < wm->nDeps; i++ )
-    {
-        if(wm->deps[i])
-        {
-            MODULE_FreeLibrary(wm->deps[i], FALSE);
-            break;
-        }
-    }
-
-    /* Be sure that the freed library and the list head are unlinked properly */
-    if(PROCESS_Current()->modref_list == wm)
-        PROCESS_Current()->modref_list = wm->next;
-    if(wm->next)
-        wm->next->prev = wm->prev;
-    if(wm->prev)
-        wm->prev->next = wm->next;
-
-    FIXME(module,"should free memory of module %08x '%s'\n", wm->module, wm->modname);
-
-    TRACE( module, "(%s, %08x, %d) - END\n", wm->modname, wm->module, first);
+		default:
+			ERR(module, "Invalid or unhandled MODREF type %d encountered (wm=%p)\n", wm->type, wm);
+		}
+	}
 }
 
-
 /***********************************************************************
  *           FreeLibrary
  */
 BOOL WINAPI FreeLibrary(HINSTANCE hLibModule)
 {
+    BOOL retv = TRUE;
     WINE_MODREF *wm;
-    BOOL retval = TRUE;
 
-    EnterCriticalSection(&PROCESS_Current()->crit_section);
+    EnterCriticalSection( &PROCESS_Current()->crit_section );
 
-    wm = MODULE32_LookupHMODULE(hLibModule);
-    if(!wm)
-    {
-        ERR(module, "(%08x) module not found in process' modref_list. Freed too many times?\n", hLibModule);
-        retval = FALSE;
-    }
+    wm = MODULE32_LookupHMODULE( hLibModule );
+    if ( !wm )
+        SetLastError( ERROR_INVALID_HANDLE );
     else
-        MODULE_FreeLibrary(wm, TRUE);	/* This always succeeds */
+        retv = MODULE_FreeLibrary( wm );
 
-    LeaveCriticalSection(&PROCESS_Current()->crit_section);
+    LeaveCriticalSection( &PROCESS_Current()->crit_section );
 
-    return retval;
+    return retv;
+}
+
+/***********************************************************************
+ *           MODULE_DecRefCount
+ *
+ * NOTE: Assumes that the process critical section is held!
+ */
+static void MODULE_DecRefCount( WINE_MODREF *wm )
+{
+    int i;
+
+    if ( wm->flags & WINE_MODREF_MARKER )
+        return;
+
+    if ( wm->refCount <= 0 )
+        return;
+
+    --wm->refCount;
+    TRACE( module, "(%s) refCount: %d\n", wm->modname, wm->refCount );
+
+    if ( wm->refCount == 0 )
+    {
+        wm->flags |= WINE_MODREF_MARKER;
+
+        for ( i = 0; i < wm->nDeps; i++ )
+            if ( wm->deps[i] )
+                MODULE_DecRefCount( wm->deps[i] );
+
+        wm->flags &= ~WINE_MODREF_MARKER;
+    }
+}
+
+/***********************************************************************
+ *           MODULE_FreeLibrary
+ *
+ * NOTE: Assumes that the process critical section is held!
+ */
+BOOL MODULE_FreeLibrary( WINE_MODREF *wm )
+{
+    TRACE( module, "(%s) - START\n", wm->modname );
+
+    /* Recursively decrement reference counts */
+    MODULE_DecRefCount( wm );
+
+    /* Call process detach notifications */
+    MODULE_DllProcessDetach( FALSE, NULL );
+
+    MODULE_FlushModrefs();
+
+    TRACE( module, "(%s) - END\n", wm->modname );
+
+    return FALSE;
 }