Commit in reactos on MAIN
include/wine/npapi.h+203added 1.1
            /unicode.h+11.7 -> 1.8
lib/mpr/mpr.rc+241.2 -> 1.3
       /mpr_En.rc+26added 1.1
       /mprres.h+23added 1.1
       /version.rc+29added 1.1
       /wnetpriv.h+27added 1.1
       /Makefile.in+7-11.1 -> 1.2
       /mpr_main.c+161.2 -> 1.3
       /winehq2ros.patch+7-71.1 -> 1.2
       /wnet.c+1499-881.2 -> 1.3
+1862-96
5 added + 6 modified, total 11 files
Sync to Wine-20040408:
Juan Lang <juan_lang@yahoo.com>
- implement loading network providers
- implement enumeration functions
- some improved error checking and A->W conversion

reactos/include/wine
npapi.h added at 1.1
diff -N npapi.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ npapi.h	15 Apr 2004 09:28:04 -0000	1.1
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2004 Juan Lang
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef __WINE_NPAPI_H__
+#define __WINE_NPAPI_H__
+
+/* capabilities */
+#define WNNC_SPEC_VERSION          0x00000001
+#define WNNC_SPEC_VERSION51        0x00050001
+#define WNNC_NET_TYPE              0x00000002
+#define WNNC_NET_NONE              0x00000000
+
+#define WNNC_DRIVER_VERSION        0x00000003
+
+#define WNNC_USER                  0x00000004
+#define WNNC_USR_GETUSER           0x00000001
+
+#define WNNC_CONNECTION            0x00000006
+#define WNNC_CON_ADDCONNECTION     0x00000001
+#define WNNC_CON_CANCELCONNECTION  0x00000002
+#define WNNC_CON_GETCONNECTIONS    0x00000004
+#define WNNC_CON_ADDCONNECTION3    0x00000008
+
+#define WNNC_DIALOG                0x00000008
+#define WNNC_DLG_DEVICEMODE        0x00000001
+#define WNNC_DLG_PROPERTYDIALOG    0x00000020
+#define WNNC_DLG_SEARCHDIALOG      0x00000040
+#define WNNC_DLG_FORMATNETWORKNAME 0x00000080
+
+#define WNNC_ADMIN                 0x00000009
+#define WNNC_ADM_GETDIRECTORYTYPE  0x00000001
+#define WNNC_ADM_DIRECTORYNOTIFY   0x00000002
+
+#define WNNC_ENUMERATION           0x0000000b
+#define WNNC_ENUM_GLOBAL           0x00000001
+#define WNNC_ENUM_LOCAL            0x00000002
+
+#define WNNC_START                 0x0000000c
+#define WNNC_WAIT_FOR_START        0x00000001
+
+typedef DWORD APIENTRY (*PF_NPGetCaps)(DWORD ndex);
+
+/* get user */
+typedef DWORD APIENTRY (*PF_NPGetUser)(LPWSTR lpName, LPWSTR lpUserName,
+ LPDWORD lpnBufferLen);
+
+/* enumeration-related */
+typedef DWORD APIENTRY (*PF_NPOpenEnum)(DWORD dwScope, DWORD dwType, DWORD dwUsage,
+ LPNETRESOURCEW lpNetResource, LPHANDLE lphEnum);
+typedef DWORD APIENTRY (*PF_NPEnumResource)(HANDLE hEnum, LPDWORD lpcCount,
+ LPVOID lpBuffer, LPDWORD lpBufferSize);
+typedef DWORD APIENTRY (*PF_NPCloseEnum)(HANDLE hEnum);
+
+/* connection-related */
+typedef DWORD APIENTRY (*PF_NPAddConnection)(LPNETRESOURCEW lpNetResource,
+ LPWSTR lpPassword, LPWSTR lpUserName);
+typedef DWORD APIENTRY (*PF_NPAddConnection3)(HWND hwndOwner,
+ LPNETRESOURCEW lpNetResource, LPWSTR lpPassword, LPWSTR lpUserName,
+ DWORD dwFlags);
+typedef DWORD APIENTRY (*PF_NPCancelConnection)(LPWSTR lpName, BOOL fForce);
+typedef DWORD APIENTRY (*PF_NPGetConnection)(LPWSTR lpLocalName,
+ LPWSTR lpRemoteName, LPDWORD lpnBufferLen);
+
+/* network name manipulation */
+typedef DWORD APIENTRY (*PF_NPGetUniversalName)(LPWSTR lpLocalPath,
+ DWORD dwInfoLevel, LPVOID lpBuffer, LPDWORD lpnBufferSize);
+typedef DWORD APIENTRY (*PF_NPFormatNetworkName)(LPWSTR lpRemoteName,
+ LPWSTR lpFormattedName, LPDWORD lpnLength, DWORD dwFlags,
+ DWORD dwAveCharPerLine);
+
+/* dialogs */
+typedef DWORD APIENTRY (*PF_NPDeviceMode)(HWND hParent);
+
+/* search dialog */
+#define WNSRCH_REFRESH_FIRST_LEVEL 0x00000001
+
+typedef DWORD APIENTRY (*PF_NPSearchDialog)(HWND hwndParent,
+ LPNETRESOURCEW lpNetResource, LPVOID lpBuffer, DWORD cbBuffer,
+ LPDWORD lpnFlags);
+
+/* property dialog */
+
+#define WNTYPE_DRIVE   1
+#define WNTYPE_FILE    2
+#define WNTYPE_PRINTER 3
+#define WNTYPE_COMM    4
+
+#define WNPS_FILE 0
+#define WNPS_DIR  1
+#define WNPS_MULT 2
+
+typedef DWORD APIENTRY (*PF_NPGetPropertyText)(DWORD iButton, DWORD nPropSel,
+ LPWSTR lpName, LPWSTR lpButtonName, DWORD nButtonNameLen, DWORD nType);
+
+typedef DWORD APIENTRY (*PF_NPPropertyDialog)(HWND hwndParent, DWORD iButtonDlg,
+ DWORD nPropSel, LPWSTR lpFileName, DWORD nType);
+
+/* admin */
+#define WNDT_NORMAL  0
+#define WNDT_NETWORK 1
+
+#define WNDN_MKDIR 1
+#define WNDN_RMDIR 2
+#define WNDN_MVDIR 3
+
+typedef DWORD APIENTRY (*PF_NPGetDirectoryType)(LPWSTR lpName, LPINT lpType,
+ BOOL bFlushCache);
+typedef DWORD APIENTRY (*PF_NPDirectoryNotify)(HWND hwnd, LPWSTR lpDir,
+ DWORD dwOper);
+
+/* permission editor dialogs */
+#define WNPERMC_PERM  0x00000001
+#define WNPERMC_AUDIT 0x00000002
+#define WNPERMC_OWNER 0x00000004
+
+typedef DWORD APIENTRY (*PF_NPFMXGetPermCaps)(LPWSTR lpDriveName);
+
+#define WNPERM_DLG_PERM  0
+#define WNPERM_DLG_AUDIT 1
+#define WNPERM_DLG_OWNER 2
+
+typedef DWORD APIENTRY (*PF_NPFMXEditPerm)(LPWSTR lpDriveName, HWND hwndFMX,
+ DWORD nDialogType);
+
+typedef DWORD APIENTRY (*PF_NPFMXGetPermHelp)(LPWSTR lpDriveName,
+ DWORD nDialogType, BOOL fDirectory, LPVOID lpFileNameBuffer,
+ LPDWORD lpBufferSize, LPDWORD lpnHelpContext);
+
+VOID WINAPI WNetSetLastErrorA(DWORD err, LPSTR lpError, LPSTR lpProviders);
+VOID WINAPI WNetSetLastErrorW(DWORD err, LPWSTR lpError, LPWSTR lpProviders);
+#define WNetSetLastError WINELIB_NAME_AW(WNetSetLastError)
+
+/* provider classes */
+#define WN_NETWORK_CLASS         0x00000001
+#define WN_CREDENTIAL_CLASS      0x00000002
+#define WN_PRIMARY_AUTHENT_CLASS 0x00000004
+#define WN_SERVICE_CLASS         0x00000008
+
+#define WN_VALID_LOGON_ACCOUNT   0x00000001
+#define WN_NT_PASSWORD_CHANGED   0x00000002
+
+/* notifications */
+typedef DWORD APIENTRY (*PF_NPLogonNotify)(PLUID lpLogonId,
+ LPCWSTR lpAuthentInfoType, LPVOID lpAuthentInfo,
+ LPCWSTR lpPreviousAuthentInfoType, LPVOID lpPreviousAuthentInfo,
+ LPWSTR lpStationName, LPVOID StationHandle, LPWSTR *lpLogonScript);
+typedef DWORD APIENTRY (*PF_NPPasswordChangeNotify)(LPCWSTR lpAuthentInfoType,
+ LPVOID lpAuthentInfo, LPCWSTR lpPreviousAuthentInfoType,
+ LPVOID lpPreviousAuthentInfo, LPWSTR lpStationName, LPVOID StationHandle,
+ DWORD dwChangeInfo);
+
+#define NOTIFY_PRE  0x00000001
+#define NOTIFY_POST 0x00000002
+
+typedef struct _NOTIFYINFO
+{
+    DWORD  dwNotifyStatus;
+    DWORD  dwOperationStatus;
+    LPVOID lpContext;
+} NOTIFYINFO, *LPNOTIFYINFO;
+
+/* FIXME: NetResource is declared as a NETRESOURCE in psdk, not a NETRESOURCEW,
+ * but how can the type change in a service provider?  Defaulting to wide-char
+ * for consistency with the rest of the api.
+ */
+typedef struct _NOTIFYADD
+{
+    HWND         hwndOwner;
+    NETRESOURCEW NetResource;
+    DWORD        dwAddFlags;
+} NOTIFYADD, *LPNOTIFYADD;
+
+/* FIXME: lpName and lpProvider are declared as LPTSTRs in psdk, but again
+ * for consistency with rest of api defaulting to LPWSTRs.
+ */
+typedef struct _NOTIFYCANCEL
+{
+    LPWSTR lpName;
+    LPWSTR lpProvider;
+    DWORD  dwFlags;
+    BOOL   fForce;
+} NOTIFYCANCEL, *LPNOTIFYCANCEL;
+
+typedef DWORD APIENTRY (*PF_AddConnectNotify)(LPNOTIFYINFO lpNotifyInfo,
+ LPNOTIFYADD lpAddInfo);
+typedef DWORD APIENTRY (*PF_CancelConnectNotify)(LPNOTIFYINFO lpNotifyInfo,
+ LPNOTIFYADD lpAddInfo);
+
+#endif /* ndef __WINE_NPAPI_H__ */

reactos/include/wine
unicode.h 1.7 -> 1.8
diff -u -r1.7 -r1.8
--- unicode.h	4 Jan 2004 20:22:02 -0000	1.7
+++ unicode.h	15 Apr 2004 09:28:04 -0000	1.8
@@ -68,6 +68,7 @@
 #define strlwrW(s) _wcslwr((wchar_t *)(s))
 #define struprW(s) _wcsupr((wchar_t *)(s))
 #define sprintfW wsprintfW
+#define snprintfW _snwprintf
 
 #ifndef WINE_UNICODE_API
 #define WINE_UNICODE_API __attribute__((dllimport))

reactos/lib/mpr
mpr.rc 1.2 -> 1.3
diff -N mpr.rc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ mpr.rc	15 Apr 2004 09:28:04 -0000	1.3
@@ -0,0 +1,24 @@
+/*
+ * MPR dll resources
+ *
+ * Copyright (C) 2004 Juan Lang
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include "windef.h"
+#include "winbase.h"
+#include "mprres.h"
+
+#include "mpr_En.rc"

reactos/lib/mpr
mpr_En.rc added at 1.1
diff -N mpr_En.rc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ mpr_En.rc	15 Apr 2004 09:28:04 -0000	1.1
@@ -0,0 +1,26 @@
+/*
+ * MPR dll resources
+ *
+ * Copyright (C) 2004 Juan Lang
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT
+
+STRINGTABLE DISCARDABLE
+{
+    IDS_ENTIRENETWORK "Entire Network"
+}

reactos/lib/mpr
mprres.h added at 1.1
diff -N mprres.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ mprres.h	15 Apr 2004 09:28:04 -0000	1.1
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2004 Juan Lang
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef __WINE_MPRRES_H__
+#define __WINE_MPRRES_H__
+
+#define IDS_ENTIRENETWORK 1
+
+#endif /* ndef __WINE_MPRRES_H__ */

reactos/lib/mpr
version.rc added at 1.1
diff -N version.rc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ version.rc	15 Apr 2004 09:28:04 -0000	1.1
@@ -0,0 +1,29 @@
+/*
+ * MPR dll version resources
+ *
+ * Copyright (C) 2004 Juan Lang
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#define WINE_FILEDESCRIPTION_STR "Wine Multiprotocol Router Library"
+#define WINE_FILENAME_STR "mpr.dll"
+#define WINE_FILEVERSION 5,0,2195,6611
+#define WINE_FILEVERSION_STR "5.00.2195.6611"
+#define WINE_PRODUCTVERSION 5,0,2195,6611
+#define WINE_PRODUCTVERSION_STR "5.00.2195.6611"
+#define WINE_PRODUCTNAME_STR "Wine"
+
+#include "wine/wine_common_ver.rc"

reactos/lib/mpr
wnetpriv.h added at 1.1
diff -N wnetpriv.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ wnetpriv.h	15 Apr 2004 09:28:04 -0000	1.1
@@ -0,0 +1,27 @@
+/*
+ * WNet private definitions
+ *
+ * Copyright (C) 2004 Juan Lang
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef __WNET_PRIV_H__
+#define __WNET_PRIV_H__
+
+void wnetInit(HINSTANCE hInstDll);
+void wnetFree(void);
+
+#endif /* ndef __WNET_PRIV_H__ */

reactos/lib/mpr
Makefile.in 1.1 -> 1.2
diff -u -r1.1 -r1.2
--- Makefile.in	1 Feb 2004 19:48:44 -0000	1.1
+++ Makefile.in	15 Apr 2004 09:28:04 -0000	1.2
@@ -3,7 +3,8 @@
 SRCDIR    = @srcdir@
 VPATH     = @srcdir@
 MODULE    = mpr.dll
-IMPORTS   = advapi32 kernel32
+IMPORTS   = user32 advapi32 kernel32
+EXTRALIBS = $(LIBUNICODE)
 
 C_SRCS = \
 	auth.c \
@@ -13,6 +14,11 @@
 	pwcache.c \
 	wnet.c
 
+RC_SRCS = \
+	mpr.rc \
+	version.rc
+
+
 @MAKE_DLL_RULES@
 
 ### Dependencies:

reactos/lib/mpr
mpr_main.c 1.2 -> 1.3
diff -u -r1.2 -r1.3
--- mpr_main.c	1 Feb 2004 19:48:44 -0000	1.2
+++ mpr_main.c	15 Apr 2004 09:28:04 -0000	1.3
@@ -24,6 +24,7 @@
 #include "winbase.h"
 #include "winnetwk.h"
 #include "wine/debug.h"
+#include "wnetpriv.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(mpr);
 
@@ -79,3 +80,18 @@
     FIXME("Stub\n");
     return S_OK;
 }
+
+BOOL WINAPI DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
+{
+    switch (fdwReason) {
+        case DLL_PROCESS_ATTACH:
+            DisableThreadLibraryCalls( hinstDLL );
+            wnetInit(hinstDLL);
+            break;
+
+        case DLL_PROCESS_DETACH:
+            wnetFree();
+            break;
+    }
+    return TRUE;
+}

reactos/lib/mpr
winehq2ros.patch 1.1 -> 1.2
diff -u -r1.1 -r1.2
--- winehq2ros.patch	1 Feb 2004 19:48:44 -0000	1.1
+++ winehq2ros.patch	15 Apr 2004 09:28:04 -0000	1.2
@@ -1,15 +1,15 @@
 Index: wnet.c
 ===================================================================
 RCS file: /home/wine/wine/dlls/mpr/wnet.c,v
-retrieving revision 1.14
-diff -u -r1.14 wnet.c
---- wnet.c	5 Sep 2003 23:08:36 -0000	1.14
-+++ wnet.c	1 Feb 2004 19:48:21 -0000
-@@ -30,6 +30,7 @@
- 
+retrieving revision 1.15
+diff -u -r1.15 wnet.c
+--- wnet.c	5 Apr 2004 23:14:26 -0000	1.15
++++ wnet.c	15 Apr 2004 09:36:14 -0000
+@@ -22,6 +22,7 @@
+ #include <stdarg.h>
  #include "windef.h"
  #include "winbase.h"
 +#include "winuser.h"
  #include "winnls.h"
  #include "winnetwk.h"
- #include "wine/debug.h"
+ #include "npapi.h"

reactos/lib/mpr
wnet.c 1.2 -> 1.3
diff -u -r1.2 -r1.3
--- wnet.c	1 Feb 2004 19:48:44 -0000	1.2
+++ wnet.c	15 Apr 2004 09:28:04 -0000	1.3
@@ -2,6 +2,7 @@
  * MPR WNet functions
  *
  * Copyright 1999 Ulrich Weigand
+ * Copyright 2004 Juan Lang
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -18,54 +19,795 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
-#include "config.h"
-
-#include <ctype.h>
 #include <stdarg.h>
-#include <string.h>
-#include <sys/types.h>
-#ifdef HAVE_UNISTD_H
-# include <unistd.h>
-#endif
-
 #include "windef.h"
 #include "winbase.h"
 #include "winuser.h"
 #include "winnls.h"
 #include "winnetwk.h"
+#include "npapi.h"
+#include "winreg.h"
+#include "winuser.h"
 #include "wine/debug.h"
+#include "wine/unicode.h"
+#include "mprres.h"
+#include "wnetpriv.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(mpr);
 
+/* Data structures representing network service providers.  Assumes only one
+ * thread creates them, and that they are constant for the life of the process
+ * (and therefore doesn't synchronize access).
+ * FIXME: only basic provider data and enumeration-related data are implemented
+ * so far, need to implement the rest too.
+ */
+typedef struct _WNetProvider
+{
+    HMODULE           hLib;
+    PWSTR             name;
+    PF_NPGetCaps      getCaps;
+    DWORD             dwSpecVersion;
+    DWORD             dwNetType;
+    DWORD             dwEnumScopes;
+    PF_NPOpenEnum     openEnum;
+    PF_NPEnumResource enumResource;
+    PF_NPCloseEnum    closeEnum;
+} WNetProvider, *PWNetProvider;
+
+typedef struct _WNetProviderTable
+{
+    LPWSTR           entireNetwork;
+    DWORD            numAllocated;
+    DWORD            numProviders;
+    WNetProvider     table[1];
+} WNetProviderTable, *PWNetProviderTable;
+
+#define WNET_ENUMERATOR_TYPE_NULL     0
+#define WNET_ENUMERATOR_TYPE_GLOBAL   1
+#define WNET_ENUMERATOR_TYPE_PROVIDER 2
+#define WNET_ENUMERATOR_TYPE_CONTEXT  3
+
+/* An WNet enumerator.  Note that the type doesn't correspond to the scope of
+ * the enumeration; it represents one of the following types:
+ * - a 'null' enumeration, one that contains no members
+ * - a global enumeration, one that's executed across all providers
+ * - a provider-specific enumeration, one that's only executed by a single
+ *   provider
+ * - a context enumeration.  I know this contradicts what I just said about
+ *   there being no correspondence between the scope and the type, but it's
+ *   necessary for the special case that a "Entire Network" entry needs to
+ *   be enumerated in an enumeration of the context scope.  Thus an enumeration
+ *   of the context scope results in a context type enumerator, which morphs
+ *   into a global enumeration (so the enumeration continues across all
+ *   providers).
+ */
+typedef struct _WNetEnumerator
+{
+    DWORD          enumType;
+    DWORD          providerIndex;
+    HANDLE         handle;
+    BOOL           providerDone;
+    DWORD          dwScope;
+    DWORD          dwType;
+    DWORD          dwUsage;
+    LPNETRESOURCEW lpNet;
+} WNetEnumerator, *PWNetEnumerator;
+
+#define BAD_PROVIDER_INDEX (DWORD)0xffffffff
+
+/* Returns an index (into the global WNetProviderTable) of the provider with
+ * the given name, or BAD_PROVIDER_INDEX if not found.
+ */
+static DWORD _findProviderIndexW(LPCWSTR lpProvider);
+
+PWNetProviderTable providerTable;
+
+/*
+ * Global provider table functions
+ */
+
+static void _tryLoadProvider(PCWSTR provider)
+{
+    static const WCHAR servicePrefix[] = { 'S','y','s','t','e','m','\\',
+     'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
+     'S','e','r','v','i','c','e','s','\\',0 };
+    static const WCHAR serviceFmt[] = { '%','s','%','s','\\',
+     'N','e','t','w','o','r','k','P','r','o','v','i','d','e','r',0 };
+    WCHAR serviceName[MAX_PATH];
+    HKEY hKey;
+
+    TRACE("%s\n", debugstr_w(provider));
+    snprintfW(serviceName, sizeof(serviceName) / sizeof(WCHAR), serviceFmt,
+     servicePrefix, provider);
+    serviceName[sizeof(serviceName) / sizeof(WCHAR) - 1] = '\0';
+    if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, serviceName, 0, KEY_READ, &hKey) ==
+     ERROR_SUCCESS)
+    {
+        static const WCHAR szProviderPath[] = { 'P','r','o','v','i','d','e','r',
+         'P','a','t','h',0 };
+        WCHAR providerPath[MAX_PATH];
+        DWORD type, size = sizeof(providerPath);
+
+        if (RegQueryValueExW(hKey, szProviderPath, NULL, &type,
+         (LPBYTE)providerPath, &size) == ERROR_SUCCESS && type == REG_SZ)
+        {
+            static const WCHAR szProviderName[] = { 'N','a','m','e',0 };
+            PWSTR name = NULL;
+           
+            size = 0;
+            RegQueryValueExW(hKey, szProviderName, NULL, NULL, NULL, &size);
+            if (size)
+            {
+                name = (PWSTR)HeapAlloc(GetProcessHeap(), 0, size);
+                if (RegQueryValueExW(hKey, szProviderName, NULL, &type,
+                 (LPBYTE)name, &size) != ERROR_SUCCESS || type != REG_SZ)
+                {
+                    HeapFree(GetProcessHeap(), 0, name);
+                    name = NULL;
+                }
+            }
+            if (name)
+            {
+                HMODULE hLib = LoadLibraryW(providerPath);
+
+                if (hLib)
+                {
+                    PF_NPGetCaps getCaps = (PF_NPGetCaps)GetProcAddress(hLib,
+                     "NPGetCaps");
+
+                    TRACE("loaded lib %p\n", hLib);
+                    if (getCaps)
+                    {
+                        PWNetProvider provider =
+                         &providerTable->table[providerTable->numProviders];
+
+                        provider->hLib = hLib;
+                        provider->name = name;
+                        TRACE("name is %s\n", debugstr_w(name));
+                        provider->getCaps = getCaps;
+                        provider->dwSpecVersion = getCaps(WNNC_SPEC_VERSION);
+                        provider->dwNetType = getCaps(WNNC_NET_TYPE);
+                        TRACE("net type is 0x%08lx\n", provider->dwNetType);
+                        provider->dwEnumScopes = getCaps(WNNC_ENUMERATION);
+                        if (provider->dwEnumScopes)
+                        {
+                            TRACE("supports enumeration\n");
+                            provider->openEnum = (PF_NPOpenEnum)
+                             GetProcAddress(hLib, "NPOpenEnum");
+                            TRACE("openEnum is %p\n", provider->openEnum);
+                            provider->enumResource = (PF_NPEnumResource)
+                             GetProcAddress(hLib, "NPEnumResource");
+                            TRACE("enumResource is %p\n",
+                             provider->enumResource);
+                            provider->closeEnum = (PF_NPCloseEnum)
+                             GetProcAddress(hLib, "NPCloseEnum");
+                            TRACE("closeEnum is %p\n", provider->closeEnum);
+                            if (!provider->openEnum || !provider->enumResource
+                             || !provider->closeEnum)
+                            {
+                                provider->openEnum = NULL;
+                                provider->enumResource = NULL;
+                                provider->closeEnum = NULL;
+                                provider->dwEnumScopes = 0;
+                                WARN("Couldn't load enumeration functions\n");
+                            }
+                        }
+                        providerTable->numProviders++;
+                    }
+                    else
+                    {
+                        WARN("Provider %s didn't export NPGetCaps\n",
+                         debugstr_w(provider));
+                        if (name)
+                            HeapFree(GetProcessHeap(), 0, name);
+                        FreeLibrary(hLib);
+                    }
+                }
+                else
+                {
+                    WARN("Couldn't load library %s for provider %s\n",
+                     debugstr_w(providerPath), debugstr_w(provider));
+                    HeapFree(GetProcessHeap(), 0, name);
+                }
+            }
+            else
+            {
+                WARN("Couldn't get provider name for provider %s\n",
+                 debugstr_w(provider));
+            }
+        }
+        else
+            WARN("Couldn't open value %s\n", debugstr_w(szProviderPath));
+        RegCloseKey(hKey);
+    }
+    else
+        WARN("Couldn't open service key for provider %s\n",
+         debugstr_w(provider));
+}
+
+void wnetInit(HINSTANCE hInstDll)
+{
+    static const WCHAR providerOrderKey[] = { 'S','y','s','t','e','m','\\',
+     'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
+     'C','o','n','t','r','o','l','\\',
+     'N','e','t','w','o','r','k','P','r','o','v','i','d','e','r','\\',
+     'O','r','d','e','r',0 };
+     static const WCHAR providerOrder[] = { 'P','r','o','v','i','d','e','r',
+      'O','r','d','e','r',0 };
+    HKEY hKey;
+
+    if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, providerOrderKey, 0, KEY_READ, &hKey)
+     == ERROR_SUCCESS)
+    {
+        DWORD size = 0;
+
+        RegQueryValueExW(hKey, providerOrder, NULL, NULL, NULL, &size);
+        if (size)
+        {
+            PWSTR providers = (PWSTR)HeapAlloc(GetProcessHeap(), 0, size);
+
+            if (providers)
+            {
+                DWORD type;
+
+                if (RegQueryValueExW(hKey, providerOrder, NULL, &type,
+                 (LPBYTE)providers, &size) == ERROR_SUCCESS && type == REG_SZ)
+                {
+                    PWSTR ptr;
+                    DWORD numToAllocate;
+
+                    TRACE("provider order is %s\n", debugstr_w(providers));
+                    /* first count commas as a heuristic for how many to
+                     * allocate space for */
+                    for (ptr = providers, numToAllocate = 1; ptr; )
+                    {
+                        ptr = strchrW(ptr, ',');
+                        if (ptr)
+                            numToAllocate++;
+                    }
+                    providerTable = (PWNetProviderTable)
+                     HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
+                     sizeof(WNetProviderTable)
+                     + (numToAllocate - 1) * sizeof(WNetProvider));
+                    if (providerTable)
+                    {
+                        PWSTR ptrPrev;
+                        int entireNetworkLen;
+
+                        entireNetworkLen = LoadStringW(hInstDll,
+                         IDS_ENTIRENETWORK, NULL, 0);
+                        providerTable->entireNetwork = (LPWSTR)HeapAlloc(
+                         GetProcessHeap(), 0, (entireNetworkLen + 1) *
+                         sizeof(WCHAR));
+                        if (providerTable->entireNetwork)
+                            LoadStringW(hInstDll, IDS_ENTIRENETWORK,
+                             providerTable->entireNetwork,
+                             entireNetworkLen + 1);
+                        providerTable->numAllocated = numToAllocate;
+                        for (ptr = providers; ptr; )
+                        {
+                            ptrPrev = ptr;
+                            ptr = strchrW(ptr, ',');
+                            if (ptr)
+                                *ptr = '\0';
+                            _tryLoadProvider(ptrPrev);
+                        }
+                    }
+                }
+                HeapFree(GetProcessHeap(), 0, providers);
+            }
+        }
+        RegCloseKey(hKey);
+    }
+}
+
+void wnetFree(void)
+{
+    if (providerTable)
+    {
+        DWORD i;
+
+        for (i = 0; i < providerTable->numProviders; i++)
+        {
+            HeapFree(GetProcessHeap(), 0, providerTable->table[i].name);
+            FreeModule(providerTable->table[i].hLib);
+        }
+        if (providerTable->entireNetwork)
+            HeapFree(GetProcessHeap(), 0, providerTable->entireNetwork);
+        HeapFree(GetProcessHeap(), 0, providerTable);
+        providerTable = NULL;
+    }
+}
+
+static DWORD _findProviderIndexW(LPCWSTR lpProvider)
+{
+    DWORD ret = BAD_PROVIDER_INDEX;
+
+    if (providerTable && providerTable->numProviders)
+    {
+        DWORD i;
+
+        for (i = 0; i < providerTable->numProviders &&
+         ret == BAD_PROVIDER_INDEX; i++)
+            if (!strcmpW(lpProvider, providerTable->table[i].name))
+                ret = i;
+    }
+    return ret;
+}
 
 /*
  * Browsing Functions
  */
 
+static LPNETRESOURCEW _copyNetResourceForEnumW(LPNETRESOURCEW lpNet)
+{
+    LPNETRESOURCEW ret;
+
+    if (lpNet)
+    {
+        ret = (LPNETRESOURCEW)HeapAlloc(GetProcessHeap(), 0,
+         sizeof(NETRESOURCEW));
+        if (ret)
+        {
+            size_t len;
+
+            memcpy(ret, lpNet, sizeof(ret));
+            ret->lpLocalName = ret->lpComment = ret->lpProvider = NULL;
+            if (lpNet->lpRemoteName)
+            {
+                len = strlenW(lpNet->lpRemoteName) + 1;
+                ret->lpRemoteName = (LPWSTR)HeapAlloc(GetProcessHeap(), 0,
+                 len * sizeof(WCHAR));
+                if (ret->lpRemoteName)
+                    strcpyW(ret->lpRemoteName, lpNet->lpRemoteName);
+            }
+        }
+    }
+    else
+        ret = NULL;
+    return ret;
+}
+
+static void _freeEnumNetResource(LPNETRESOURCEW lpNet)
+{
+    if (lpNet)
+    {
+        if (lpNet->lpRemoteName)
+            HeapFree(GetProcessHeap(), 0, lpNet->lpRemoteName);
+        HeapFree(GetProcessHeap(), 0, lpNet);
+    }
+}
+
+static PWNetEnumerator _createNullEnumerator(void)
+{
+    PWNetEnumerator ret = (PWNetEnumerator)HeapAlloc(GetProcessHeap(),
+     HEAP_ZERO_MEMORY, sizeof(WNetEnumerator));
+
+    if (ret)
+        ret->enumType = WNET_ENUMERATOR_TYPE_NULL;
+    return ret;
+}
+
+static PWNetEnumerator _createGlobalEnumeratorW(DWORD dwScope, DWORD dwType,
+ DWORD dwUsage, LPNETRESOURCEW lpNet)
+{
+    PWNetEnumerator ret = (PWNetEnumerator)HeapAlloc(GetProcessHeap(),
+     HEAP_ZERO_MEMORY, sizeof(WNetEnumerator));
+
+    if (ret)
+    {
+        ret->enumType = WNET_ENUMERATOR_TYPE_GLOBAL;
+        ret->dwScope = dwScope;
+        ret->dwType  = dwType;
+        ret->dwUsage = dwUsage;
+        ret->lpNet   = _copyNetResourceForEnumW(lpNet);
+    }
+    return ret;
+}
+
+static PWNetEnumerator _createProviderEnumerator(DWORD dwScope, DWORD dwType,
+ DWORD dwUsage, DWORD index, HANDLE handle)
+{
+    PWNetEnumerator ret;
+
+    if (!providerTable || index >= providerTable->numProviders)
+        ret = NULL;
+    else
+    {
+        ret = (PWNetEnumerator)HeapAlloc(GetProcessHeap(),
+         HEAP_ZERO_MEMORY, sizeof(WNetEnumerator));
+        if (ret)
+        {
+            ret->enumType      = WNET_ENUMERATOR_TYPE_PROVIDER;
+            ret->providerIndex = index;
+            ret->dwScope       = dwScope;
+            ret->dwType        = dwType;
+            ret->dwUsage       = dwUsage;
+            ret->handle        = handle;
+        }
+    }
+    return ret;
+}
+
+static PWNetEnumerator _createContextEnumerator(DWORD dwScope, DWORD dwType,
+ DWORD dwUsage)
+{
+    PWNetEnumerator ret = (PWNetEnumerator)HeapAlloc(GetProcessHeap(),
+     HEAP_ZERO_MEMORY, sizeof(WNetEnumerator));
+
+    if (ret)
+    {
+        ret->enumType = WNET_ENUMERATOR_TYPE_CONTEXT;
+        ret->dwScope = dwScope;
+        ret->dwType  = dwType;
+        ret->dwUsage = dwUsage;
+    }
+    return ret;
+}
+
+/* Thunks the array of wide-string LPNETRESOURCEs lpNetArrayIn into buffer
+ * lpBuffer, with size *lpBufferSize.  lpNetArrayIn contains *lpcCount entries
+ * to start.  On return, *lpcCount reflects the number thunked into lpBuffer.
+ * Returns WN_SUCCESS on success (all of lpNetArrayIn thunked), WN_MORE_DATA
+ * if not all members of the array could be thunked, and something else on
+ * failure.
+ */
+static DWORD _thunkNetResourceArrayWToA(const LPNETRESOURCEW lpNetArrayIn,
+ LPDWORD lpcCount, LPVOID lpBuffer, LPDWORD lpBufferSize)
+{
+    DWORD i, numToThunk, totalBytes, ret;
+    LPSTR strNext;
+
+    if (!lpNetArrayIn)
+        return WN_BAD_POINTER;
+    if (!lpcCount)
+        return WN_BAD_POINTER;
+    if (*lpcCount == -1)
+        return WN_BAD_VALUE;
+    if (!lpBuffer)
+        return WN_BAD_POINTER;
+    if (!lpBufferSize)
+        return WN_BAD_POINTER;
+
+    for (i = 0, numToThunk = 0, totalBytes = 0; i < *lpcCount; i++)
+    {
+        LPNETRESOURCEW lpNet = lpNetArrayIn + i;
+
+        totalBytes += sizeof(NETRESOURCEA);
+        if (lpNet->lpLocalName)
+            totalBytes += WideCharToMultiByte(CP_ACP, 0, lpNet->lpLocalName,
+             -1, NULL, 0, NULL, NULL);
+        if (lpNet->lpRemoteName)
+            totalBytes += WideCharToMultiByte(CP_ACP, 0, lpNet->lpRemoteName,
+             -1, NULL, 0, NULL, NULL);
+        if (lpNet->lpComment)
+            totalBytes += WideCharToMultiByte(CP_ACP, 0, lpNet->lpComment,
+             -1, NULL, 0, NULL, NULL);
+        if (lpNet->lpProvider)
+            totalBytes += WideCharToMultiByte(CP_ACP, 0, lpNet->lpProvider,
+             -1, NULL, 0, NULL, NULL);
+        if (totalBytes < *lpBufferSize)
+            numToThunk = i + 1;
+    }
+    strNext = (LPSTR)((LPBYTE)lpBuffer + numToThunk * sizeof(NETRESOURCEA));
+    for (i = 0; i < numToThunk; i++)
+    {
+        LPNETRESOURCEA lpNetOut = (LPNETRESOURCEA)lpBuffer + i;
+        LPNETRESOURCEW lpNetIn = lpNetArrayIn + i;
+
+        memcpy(lpNetOut, lpNetIn, sizeof(NETRESOURCEA));
+        /* lie about string lengths, we already verified how many
+         * we have space for above
+         */
+        if (lpNetIn->lpLocalName)
+        {
+            lpNetOut->lpLocalName = strNext;
+            strNext += WideCharToMultiByte(CP_ACP, 0, lpNetIn->lpLocalName, -1,
+             lpNetOut->lpLocalName, *lpBufferSize, NULL, NULL);
+        }
+        if (lpNetIn->lpRemoteName)
+        {
+            lpNetOut->lpRemoteName = strNext;
+            strNext += WideCharToMultiByte(CP_ACP, 0, lpNetIn->lpRemoteName, -1,
+             lpNetOut->lpRemoteName, *lpBufferSize, NULL, NULL);
+        }
+        if (lpNetIn->lpComment)
+        {
+            lpNetOut->lpComment = strNext;
+            strNext += WideCharToMultiByte(CP_ACP, 0, lpNetIn->lpComment, -1,
+             lpNetOut->lpComment, *lpBufferSize, NULL, NULL);
+        }
+        if (lpNetIn->lpProvider)
+        {
+            lpNetOut->lpProvider = strNext;
+            strNext += WideCharToMultiByte(CP_ACP, 0, lpNetIn->lpProvider, -1,
+             lpNetOut->lpProvider, *lpBufferSize, NULL, NULL);
+        }
+    }
+    ret = numToThunk < *lpcCount ? WN_MORE_DATA : WN_SUCCESS;
+    TRACE("numToThunk is %ld, *lpcCount is %ld, returning %ld\n", numToThunk,
+     *lpcCount, ret);
+    return ret;
+}
+
+/* Thunks the array of multibyte-string LPNETRESOURCEs lpNetArrayIn into buffer
+ * lpBuffer, with size *lpBufferSize.  lpNetArrayIn contains *lpcCount entries
+ * to start.  On return, *lpcCount reflects the number thunked into lpBuffer.
+ * Returns WN_SUCCESS on success (all of lpNetArrayIn thunked), WN_MORE_DATA
+ * if not all members of the array could be thunked, and something else on
+ * failure.
+ */
+static DWORD _thunkNetResourceArrayAToW(const LPNETRESOURCEA lpNetArrayIn,
+ LPDWORD lpcCount, LPVOID lpBuffer, LPDWORD lpBufferSize)
+{
+    DWORD i, numToThunk, totalBytes, ret;
+    LPWSTR strNext;
+
+    if (!lpNetArrayIn)
+        return WN_BAD_POINTER;
+    if (!lpcCount)
+        return WN_BAD_POINTER;
+    if (*lpcCount == -1)
+        return WN_BAD_VALUE;
+    if (!lpBuffer)
+        return WN_BAD_POINTER;
+    if (!lpBufferSize)
+        return WN_BAD_POINTER;
+
+    for (i = 0, numToThunk = 0, totalBytes = 0; i < *lpcCount; i++)
+    {
+        LPNETRESOURCEA lpNet = lpNetArrayIn + i;
+
+        totalBytes += sizeof(NETRESOURCEW);
+        if (lpNet->lpLocalName)
+            totalBytes += MultiByteToWideChar(CP_ACP, 0, lpNet->lpLocalName,
+             -1, NULL, 0) * sizeof(WCHAR);
+        if (lpNet->lpRemoteName)
+            totalBytes += MultiByteToWideChar(CP_ACP, 0, lpNet->lpRemoteName,
+             -1, NULL, 0) * sizeof(WCHAR);
+        if (lpNet->lpComment)
+            totalBytes += MultiByteToWideChar(CP_ACP, 0, lpNet->lpComment,
+             -1, NULL, 0) * sizeof(WCHAR);
+        if (lpNet->lpProvider)
+            totalBytes += MultiByteToWideChar(CP_ACP, 0, lpNet->lpProvider,
+             -1, NULL, 0) * sizeof(WCHAR);
+        if (totalBytes < *lpBufferSize)
+            numToThunk = i + 1;
+    }
+    strNext = (LPWSTR)((LPBYTE)lpBuffer + numToThunk * sizeof(NETRESOURCEW));
+    for (i = 0; i < numToThunk; i++)
+    {
+        LPNETRESOURCEW lpNetOut = (LPNETRESOURCEW)lpBuffer + i;
+        LPNETRESOURCEA lpNetIn = lpNetArrayIn + i;
+
+        memcpy(lpNetOut, lpNetIn, sizeof(NETRESOURCEW));
+        /* lie about string lengths, we already verified how many
+         * we have space for above
+         */
+        if (lpNetIn->lpLocalName)
+        {
+            lpNetOut->lpLocalName = strNext;
+            strNext += MultiByteToWideChar(CP_ACP, 0, lpNetIn->lpLocalName,
+             -1, lpNetOut->lpLocalName, *lpBufferSize);
+        }
+        if (lpNetIn->lpRemoteName)
+        {
+            lpNetOut->lpRemoteName = strNext;
+            strNext += MultiByteToWideChar(CP_ACP, 0, lpNetIn->lpRemoteName,
+             -1, lpNetOut->lpRemoteName, *lpBufferSize);
+        }
+        if (lpNetIn->lpComment)
+        {
+            lpNetOut->lpComment = strNext;
+            strNext += MultiByteToWideChar(CP_ACP, 0, lpNetIn->lpComment,
+             -1, lpNetOut->lpComment, *lpBufferSize);
+        }
+        if (lpNetIn->lpProvider)
+        {
+            lpNetOut->lpProvider = strNext;
+            strNext += MultiByteToWideChar(CP_ACP, 0, lpNetIn->lpProvider,
+             -1, lpNetOut->lpProvider, *lpBufferSize);
+        }
+    }
+    ret = numToThunk < *lpcCount ? WN_MORE_DATA : WN_SUCCESS;
+    TRACE("numToThunk is %ld, *lpcCount is %ld, returning %ld\n", numToThunk,
+     *lpcCount, ret);
+    return ret;
+}
+
 /*********************************************************************
  * WNetOpenEnumA [MPR.@]
+ *
+ * See comments for WNetOpenEnumW.
  */
 DWORD WINAPI WNetOpenEnumA( DWORD dwScope, DWORD dwType, DWORD dwUsage,
                             LPNETRESOURCEA lpNet, LPHANDLE lphEnum )
 {
-    FIXME( "(%08lX, %08lX, %08lX, %p, %p): stub\n",
+    DWORD ret;
+
+    TRACE( "(%08lX, %08lX, %08lX, %p, %p)\n",
 	    dwScope, dwType, dwUsage, lpNet, lphEnum );
 
-    SetLastError(WN_NO_NETWORK);
-    return WN_NO_NETWORK;
+    if (!lphEnum)
+        ret = WN_BAD_POINTER;
+    else if (!providerTable || providerTable->numProviders == 0)
+        ret = WN_NO_NETWORK;
+    else
+    {
+        if (lpNet)
+        {
+            LPNETRESOURCEW lpNetWide = NULL;
+            BYTE buf[1024];
+            DWORD size = sizeof(buf), count = 1;
+            BOOL allocated = FALSE;
+
+            ret = _thunkNetResourceArrayAToW(lpNet, &count, buf, &size);
+            if (ret == WN_MORE_DATA)
+            {
+                lpNetWide = (LPNETRESOURCEW)HeapAlloc(GetProcessHeap(), 0,
+                 size);
+                if (lpNetWide)
+                {
+                    ret = _thunkNetResourceArrayAToW(lpNet, &count, lpNetWide,
+                     &size);
+                    allocated = TRUE;
+                }
+                else
+                    ret = WN_OUT_OF_MEMORY;
+            }
+            else if (ret == WN_SUCCESS)
+                lpNetWide = (LPNETRESOURCEW)buf;
+            if (ret == WN_SUCCESS)
+                ret = WNetOpenEnumW(dwScope, dwType, dwUsage, lpNetWide,
+                 lphEnum);
+            if (allocated && lpNetWide)
+                HeapFree(GetProcessHeap(), 0, lpNetWide);
+        }
+        else
+            ret = WNetOpenEnumW(dwScope, dwType, dwUsage, NULL, lphEnum);
+    }
+    if (ret)
+        SetLastError(ret);
+    TRACE("Returning %ld\n", ret);
+    return ret;
 }
 
 /*********************************************************************
  * WNetOpenEnumW [MPR.@]
+ *
+ * Network enumeration has way too many parameters, so I'm not positive I got
+ * them right.  What I've got so far:
+ *
+ * - If the scope is RESOURCE_GLOBALNET, and no LPNETRESOURCE is passed,
+ *   all the network providers should be enumerated.
+ *
+ * - If the scope is RESOURCE_GLOBALNET, and LPNETRESOURCE is passed, and
+ *   and neither the LPNETRESOURCE's lpRemoteName nor the LPNETRESOURCE's
+ *   lpProvider is set, all the network providers should be enumerated.
+ *   (This means the enumeration is a list of network providers, not that the
+ *   enumeration is passed on to the providers.)
+ *
+ * - If the scope is RESOURCE_GLOBALNET, and LPNETRESOURCE is passed, and the
+ *   resource matches the "Entire Network" resource (no remote name, no
+ *   provider, comment is the "Entire Network" string), a RESOURCE_GLOBALNET
+ *   enumeration is done on every network provider.
+ *
+ * - If the scope is RESOURCE_GLOBALNET, and LPNETRESOURCE is passed, and
+ *   the LPNETRESOURCE's lpProvider is set, enumeration will be passed through
+ *   only to the given network provider.
+ *
+ * - If the scope is RESOURCE_GLOBALNET, and LPNETRESOURCE is passed, and
+ *   no lpProvider is set, enumeration will be tried on every network provider,
+ *   in the order in which they're loaded.
+ *
+ * - The LPNETRESOURCE should be disregarded for scopes besides
+ *   RESOURCE_GLOBALNET.  MSDN states that lpNet must be NULL if dwScope is not
+ *   RESOURCE_GLOBALNET, but Windows doesn't return an error if it isn't NULL.
+ *
+ * - If the scope is RESOURCE_CONTEXT, MS includes an "Entire Network" net
+ *   resource in the enumerated list, as well as any machines in your
+ *   workgroup.  The machines in your workgroup come from doing a
+ *   RESOURCE_CONTEXT enumeration of every Network Provider.
  */
 DWORD WINAPI WNetOpenEnumW( DWORD dwScope, DWORD dwType, DWORD dwUsage,
                             LPNETRESOURCEW lpNet, LPHANDLE lphEnum )
 {
-   FIXME( "(%08lX, %08lX, %08lX, %p, %p): stub\n",
+    DWORD ret;
+
+    TRACE( "(%08lX, %08lX, %08lX, %p, %p)\n",
           dwScope, dwType, dwUsage, lpNet, lphEnum );
 
-   SetLastError(WN_NO_NETWORK);
-   return WN_NO_NETWORK;
+    if (!lphEnum)
+        ret = WN_BAD_POINTER;
+    else if (!providerTable || providerTable->numProviders == 0)
+        ret = WN_NO_NETWORK;
+    else
+    {
+        switch (dwScope)
+        {
+            case RESOURCE_GLOBALNET:
+                if (lpNet)
+                {
+                    if (lpNet->lpProvider)
+                    {
+                        DWORD index = _findProviderIndexW(lpNet->lpProvider);
+
+                        if (index != BAD_PROVIDER_INDEX)
+                        {
+                            if (providerTable->table[index].openEnum &&
+                             providerTable->table[index].dwEnumScopes & dwScope)
+                            {
+                                HANDLE handle;
+
+                                ret = providerTable->table[index].openEnum(
+                                 dwScope, dwType, dwUsage, lpNet, &handle);
+                                if (ret == WN_SUCCESS)
+                                {
+                                    *lphEnum =
+                                     (HANDLE)_createProviderEnumerator(
+                                     dwScope, dwType, dwUsage, index, handle);
+                                    ret = *lphEnum ? WN_SUCCESS :
+                                     WN_OUT_OF_MEMORY;
+                                }
+                            }
+                            else
+                                ret = WN_NOT_SUPPORTED;
+                        }
+                        else
+                            ret = WN_BAD_PROVIDER;
+                    }
+                    else if (lpNet->lpRemoteName)
+                    {
+                        *lphEnum = (HANDLE)_createGlobalEnumeratorW(dwScope,
+                         dwType, dwUsage, lpNet);
+                        ret = *lphEnum ? WN_SUCCESS : WN_OUT_OF_MEMORY;
+                    }
+                    else
+                    {
+                        if (lpNet->lpComment && !strcmpW(lpNet->lpComment,
+                         providerTable->entireNetwork))
+                        {
+                            /* comment matches the "Entire Network", enumerate
+                             * global scope of every provider
+                             */
+                            *lphEnum = (HANDLE)_createGlobalEnumeratorW(dwScope,
+                             dwType, dwUsage, lpNet);
+                        }
+                        else
+                        {
+                            /* this is the same as not having passed lpNet */
+                            *lphEnum = (HANDLE)_createGlobalEnumeratorW(dwScope,
+                             dwType, dwUsage, NULL);
+                        }
+                        ret = *lphEnum ? WN_SUCCESS : WN_OUT_OF_MEMORY;
+                    }
+                }
+                else
+                {
+                    *lphEnum = (HANDLE)_createGlobalEnumeratorW(dwScope, dwType,
+                     dwUsage, lpNet);
+                    ret = *lphEnum ? WN_SUCCESS : WN_OUT_OF_MEMORY;
+                }
+                break;
+            case RESOURCE_CONTEXT:
+                *lphEnum = (HANDLE)_createContextEnumerator(dwScope, dwType,
+                 dwUsage);
+                ret = *lphEnum ? WN_SUCCESS : WN_OUT_OF_MEMORY;
+                break;
+            case RESOURCE_REMEMBERED:
+            case RESOURCE_CONNECTED:
+                *lphEnum = (HANDLE)_createNullEnumerator();
+                ret = *lphEnum ? WN_SUCCESS : WN_OUT_OF_MEMORY;
+                break;
+            default:
+                WARN("unknown scope 0x%08lx\n", dwScope);
+                ret = WN_BAD_VALUE;
+        }
+    }
+    if (ret)
+        SetLastError(ret);
+    TRACE("Returning %ld\n", ret);
+    return ret;
 }
 
 /*********************************************************************
@@ -74,11 +816,364 @@
 DWORD WINAPI WNetEnumResourceA( HANDLE hEnum, LPDWORD lpcCount,
                                 LPVOID lpBuffer, LPDWORD lpBufferSize )
 {
-    FIXME( "(%p, %p, %p, %p): stub\n",
-	    hEnum, lpcCount, lpBuffer, lpBufferSize );
+    DWORD ret;
 
-    SetLastError(WN_NO_NETWORK);
-    return WN_NO_NETWORK;
+    TRACE( "(%p, %p, %p, %p)\n", hEnum, lpcCount, lpBuffer, lpBufferSize );
+
+    if (!hEnum)
+        ret = WN_BAD_POINTER;
+    else if (!lpcCount)
+        ret = WN_BAD_POINTER;
+    if (!lpBuffer)
+        ret = WN_BAD_POINTER;
+    else if (!lpBufferSize)
+        ret = WN_BAD_POINTER;
+    else if (*lpBufferSize < sizeof(NETRESOURCEA))
+    {
+        *lpBufferSize = sizeof(NETRESOURCEA);
+        ret = WN_MORE_DATA;
+    }
+    else
+    {
+        DWORD localCount = *lpcCount, localSize = *lpBufferSize;
+        LPVOID localBuffer = HeapAlloc(GetProcessHeap(), 0, localSize);
+
+        if (localBuffer)
+        {
+            ret = WNetEnumResourceW(hEnum, &localCount, localBuffer,
+             &localSize);
+            if (ret == WN_SUCCESS || (ret == WN_MORE_DATA && localCount != -1))
+            {
+                /* FIXME: this isn't necessarily going to work in the case of
+                 * WN_MORE_DATA, because our enumerator may have moved on to
+                 * the next provider.  MSDN states that a large (16KB) buffer
+                 * size is the appropriate usage of this function, so
+                 * hopefully it won't be an issue.
+                 */
+                ret = _thunkNetResourceArrayWToA((LPNETRESOURCEW)localBuffer,
+                 &localCount, lpBuffer, lpBufferSize);
+                *lpcCount = localCount;
+            }
+            HeapFree(GetProcessHeap(), 0, localBuffer);
+        }
+        else
+            ret = WN_OUT_OF_MEMORY;
+    }
+    if (ret)
+        SetLastError(ret);
+    TRACE("Returning %ld\n", ret);
+    return ret;
+}
+
+static DWORD _countProviderBytesW(PWNetProvider provider)
+{
+    DWORD ret;
+
+    if (provider)
+    {
+        ret = sizeof(NETRESOURCEW);
+        ret += 2 * (strlenW(provider->name) + 1) * sizeof(WCHAR);
+    }
+    else
+        ret = 0;
+    return ret;
+}
+
+static DWORD _enumerateProvidersW(PWNetEnumerator enumerator, LPDWORD lpcCount,
+ LPVOID lpBuffer, LPDWORD lpBufferSize)
+{
+    DWORD ret;
+
+    if (!enumerator)
+        return WN_BAD_POINTER;
+    if (enumerator->enumType != WNET_ENUMERATOR_TYPE_GLOBAL)
+        return WN_BAD_VALUE;
+    if (!lpcCount)
+        return WN_BAD_POINTER;
+    if (!lpBuffer)
+        return WN_BAD_POINTER;
+    if (!lpBufferSize)
+        return WN_BAD_POINTER;
+    if (*lpBufferSize < sizeof(NETRESOURCEA))
+        return WN_MORE_DATA;
+
+    if (!providerTable || enumerator->providerIndex >= 
+     providerTable->numProviders)
+        ret = WN_NO_MORE_ENTRIES;
+    else
+    {
+        DWORD bytes = 0, count = 0, countLimit, i;
+        LPNETRESOURCEW resource;
+        LPWSTR strNext;
+
+        countLimit = *lpcCount == -1 ?
+         providerTable->numProviders - enumerator->providerIndex : *lpcCount;
+        while (count < countLimit && bytes < *lpBufferSize)
+        {
+            DWORD bytesNext = _countProviderBytesW(
+             &providerTable->table[count + enumerator->providerIndex]);
+
+            if (bytes + bytesNext < *lpBufferSize)
+            {
+                bytes += bytesNext;
+                count++;
+            }
+        }
+        strNext = (LPWSTR)((LPBYTE)lpBuffer + count * sizeof(NETRESOURCEW));
+        for (i = 0, resource = (LPNETRESOURCEW)lpBuffer; i < count;
+         i++, resource++)
+        {
+            resource->dwScope = RESOURCE_GLOBALNET;
+            resource->dwType = RESOURCETYPE_ANY;
+            resource->dwDisplayType = RESOURCEDISPLAYTYPE_NETWORK;
+            resource->dwUsage = RESOURCEUSAGE_CONTAINER |
+             RESOURCEUSAGE_RESERVED;
+            resource->lpLocalName = NULL;
+            resource->lpRemoteName = strNext;
+            strcpyW(resource->lpRemoteName,
+             providerTable->table[i + enumerator->providerIndex].name);
+            strNext += strlenW(resource->lpRemoteName) + 1;
+            resource->lpComment = NULL;
+            resource->lpProvider = strNext;
+            strcpyW(resource->lpProvider,
+             providerTable->table[i + enumerator->providerIndex].name);
+            strNext += strlenW(resource->lpProvider) + 1;
+        }
+        enumerator->providerIndex += count;
+        *lpcCount = count;
+        ret = count > 0 ? WN_SUCCESS : WN_MORE_DATA;
+    }
+    TRACE("Returning %ld\n", ret);
+    return ret;
+}
+
+/* Advances the enumerator (assumed to be a global enumerator) to the next
+ * provider that supports the enumeration scope passed to WNetOpenEnum.  Does
+ * not open a handle with the next provider.
+ * If the existing handle is NULL, may leave the enumerator unchanged, since
+ * the current provider may support the desired scope.
+ * If the existing handle is not NULL, closes it before moving on.
+ * Returns WN_SUCCESS on success, WN_NO_MORE_ENTRIES if there is no available
+ * provider, and another error on failure.
+ */
+static DWORD _globalEnumeratorAdvance(PWNetEnumerator enumerator)
+{
+    if (!enumerator)
+        return WN_BAD_POINTER;
+    if (enumerator->enumType != WNET_ENUMERATOR_TYPE_GLOBAL)
+        return WN_BAD_VALUE;
+    if (!providerTable || enumerator->providerIndex >=
+     providerTable->numProviders)
+        return WN_NO_MORE_ENTRIES;
+
+    if (enumerator->providerDone)
+    {
+        enumerator->providerDone = FALSE;
+        if (enumerator->handle)
+        {
+            providerTable->table[enumerator->providerIndex].closeEnum(
+             enumerator->handle);
+            enumerator->handle = NULL;
+            enumerator->providerIndex++;
+        }
+        for (; enumerator->providerIndex < providerTable->numProviders &&
+         !(enumerator->dwScope & providerTable->table
+         [enumerator->providerIndex].dwEnumScopes);
+         enumerator->providerIndex++)
+            ;
+    }
+    return enumerator->providerIndex < providerTable->numProviders ?
+     WN_SUCCESS : WN_NO_MORE_ENTRIES;
+}
+
[truncated at 1000 lines; 785 more skipped]
CVSspam 0.2.8