* Copyright 1999 Ulrich Weigand
* Copyright 2004 Juan Lang
* Copyright 2007 Maarten Lankhorst
+ * Copyright 2016 Pierre Schweitzer
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
-#include "precomp.h"
-
-#include <winioctl.h>
-#include <npapi.h>
+#include <stdarg.h>
+#include "windef.h"
+#include "winbase.h"
+#include "winnls.h"
+#include "winioctl.h"
+#include "winnetwk.h"
+#include "npapi.h"
+#include "winreg.h"
+#include "winuser.h"
#define WINE_MOUNTMGR_EXTENSIONS
-#include <ddk/mountmgr.h>
-#include <wine/unicode.h>
+#include "ddk/mountmgr.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
PF_NPEnumResource enumResource;
PF_NPCloseEnum closeEnum;
PF_NPGetResourceInformation getResourceInformation;
+ PF_NPAddConnection addConnection;
+ PF_NPAddConnection3 addConnection3;
+ PF_NPCancelConnection cancelConnection;
+#ifdef __REACTOS__
+ PF_NPGetConnection getConnection;
+#endif
} WNetProvider, *PWNetProvider;
typedef struct _WNetProviderTable
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
-
+#ifndef __REACTOS__
+#define WNET_ENUMERATOR_TYPE_NULL 0
+#define WNET_ENUMERATOR_TYPE_GLOBAL 1
+#define WNET_ENUMERATOR_TYPE_PROVIDER 2
+#define WNET_ENUMERATOR_TYPE_CONTEXT 3
+#define WNET_ENUMERATOR_TYPE_CONNECTED 4
+#else
+#define WNET_ENUMERATOR_TYPE_GLOBAL 0
+#define WNET_ENUMERATOR_TYPE_PROVIDER 1
+#define WNET_ENUMERATOR_TYPE_CONTEXT 2
+#define WNET_ENUMERATOR_TYPE_CONNECTED 3
+#define WNET_ENUMERATOR_TYPE_REMEMBERED 4
+#endif
+
+#ifndef __REACTOS__
/* 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
* into a global enumeration (so the enumeration continues across all
* providers).
*/
+#else
+/* An WNet enumerator. Note that the type doesn't correspond to the scope of
+ * the enumeration; it represents one of the following types:
+ * - 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).
+ * - a remembered enumeration, not related to providers themselves, but it
+ * is a registry enumeration for saved connections
+ */
+#endif
typedef struct _WNetEnumerator
{
DWORD enumType;
DWORD dwScope;
DWORD dwType;
DWORD dwUsage;
- LPNETRESOURCEW lpNet;
+ union
+ {
+ NETRESOURCEW* net;
+ HANDLE* handles;
+#ifdef __REACTOS__
+ struct
+ {
+ HKEY registry;
+ DWORD index;
+ } remembered;
+#endif
+ } specific;
} WNetEnumerator, *PWNetEnumerator;
#define BAD_PROVIDER_INDEX (DWORD)0xffffffff
DWORD type, size = sizeof(providerPath);
if (RegQueryValueExW(hKey, szProviderPath, NULL, &type,
- (LPBYTE)providerPath, &size) == ERROR_SUCCESS && type == REG_SZ)
+ (LPBYTE)providerPath, &size) == ERROR_SUCCESS && (type == REG_SZ || type == REG_EXPAND_SZ))
{
static const WCHAR szProviderName[] = { 'N','a','m','e',0 };
PWSTR name = NULL;
-
+
+ if (type == REG_EXPAND_SZ)
+ {
+ WCHAR path[MAX_PATH];
+ if (ExpandEnvironmentStringsW(providerPath, path, MAX_PATH)) lstrcpyW( providerPath, path );
+ }
+
size = 0;
RegQueryValueExW(hKey, szProviderName, NULL, NULL, NULL, &size);
if (size)
if (hLib)
{
- PF_NPGetCaps getCaps = (PF_NPGetCaps)GetProcAddress(hLib,
- "NPGetCaps");
+#define MPR_GETPROC(proc) ((PF_##proc)GetProcAddress(hLib, #proc))
+
+ PF_NPGetCaps getCaps = MPR_GETPROC(NPGetCaps);
TRACE("loaded lib %p\n", hLib);
if (getCaps)
{
+ DWORD connectCap;
PWNetProvider provider =
&providerTable->table[providerTable->numProviders];
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);
- provider->getResourceInformation = (PF_NPGetResourceInformation)
- GetProcAddress(hLib, "NPGetResourceInformation");
- TRACE("getResourceInformation is %p\n",
- provider->getResourceInformation);
- if (!provider->openEnum || !provider->enumResource
- || !provider->closeEnum)
+ provider->openEnum = MPR_GETPROC(NPOpenEnum);
+ TRACE("NPOpenEnum %p\n", provider->openEnum);
+ provider->enumResource = MPR_GETPROC(NPEnumResource);
+ TRACE("NPEnumResource %p\n", provider->enumResource);
+ provider->closeEnum = MPR_GETPROC(NPCloseEnum);
+ TRACE("NPCloseEnum %p\n", provider->closeEnum);
+ provider->getResourceInformation = MPR_GETPROC(NPGetResourceInformation);
+ TRACE("NPGetResourceInformation %p\n", provider->getResourceInformation);
+ if (!provider->openEnum ||
+ !provider->enumResource ||
+ !provider->closeEnum)
{
provider->openEnum = NULL;
provider->enumResource = NULL;
WARN("Couldn't load enumeration functions\n");
}
}
+ connectCap = getCaps(WNNC_CONNECTION);
+ if (connectCap & WNNC_CON_ADDCONNECTION)
+ provider->addConnection = MPR_GETPROC(NPAddConnection);
+ if (connectCap & WNNC_CON_ADDCONNECTION3)
+ provider->addConnection3 = MPR_GETPROC(NPAddConnection3);
+ if (connectCap & WNNC_CON_CANCELCONNECTION)
+ provider->cancelConnection = MPR_GETPROC(NPCancelConnection);
+#ifdef __REACTOS__
+ if (connectCap & WNNC_CON_GETCONNECTIONS)
+ provider->getConnection = MPR_GETPROC(NPGetConnection);
+#endif
+ TRACE("NPAddConnection %p\n", provider->addConnection);
+ TRACE("NPAddConnection3 %p\n", provider->addConnection3);
+ TRACE("NPCancelConnection %p\n", provider->cancelConnection);
providerTable->numProviders++;
}
else
HeapFree(GetProcessHeap(), 0, name);
FreeLibrary(hLib);
}
+
+#undef MPR_GETPROC
}
else
{
}
}
+#ifndef __REACTOS__
static PWNetEnumerator _createNullEnumerator(void)
{
PWNetEnumerator ret = HeapAlloc(GetProcessHeap(),
ret->enumType = WNET_ENUMERATOR_TYPE_NULL;
return ret;
}
+#endif
static PWNetEnumerator _createGlobalEnumeratorW(DWORD dwScope, DWORD dwType,
DWORD dwUsage, LPNETRESOURCEW lpNet)
ret->dwScope = dwScope;
ret->dwType = dwType;
ret->dwUsage = dwUsage;
- ret->lpNet = _copyNetResourceForEnumW(lpNet);
+ ret->specific.net = _copyNetResourceForEnumW(lpNet);
}
return ret;
}
return ret;
}
+static PWNetEnumerator _createConnectedEnumerator(DWORD dwScope, DWORD dwType,
+ DWORD dwUsage)
+{
+ PWNetEnumerator ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WNetEnumerator));
+ if (ret)
+ {
+ ret->enumType = WNET_ENUMERATOR_TYPE_CONNECTED;
+ ret->dwScope = dwScope;
+ ret->dwType = dwType;
+ ret->dwUsage = dwUsage;
+ ret->specific.handles = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HANDLE) * providerTable->numProviders);
+ if (!ret->specific.handles)
+ {
+ HeapFree(GetProcessHeap(), 0, ret);
+ ret = NULL;
+ }
+ }
+ return ret;
+}
+
+#ifdef __REACTOS__
+static PWNetEnumerator _createRememberedEnumerator(DWORD dwScope, DWORD dwType,
+ HKEY remembered)
+{
+ PWNetEnumerator ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WNetEnumerator));
+ if (ret)
+ {
+ ret->enumType = WNET_ENUMERATOR_TYPE_REMEMBERED;
+ ret->dwScope = dwScope;
+ ret->dwType = dwType;
+ ret->specific.remembered.registry = remembered;
+ }
+ return ret;
+}
+#endif
+
/* 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.
providerTable->table[index].dwEnumScopes & WNNC_ENUM_GLOBAL)
{
HANDLE handle;
+ PWSTR RemoteName = lpNet->lpRemoteName;
+
+ if ((lpNet->dwUsage & RESOURCEUSAGE_CONTAINER) &&
+ RemoteName && !strcmpW(RemoteName, lpNet->lpProvider))
+ lpNet->lpRemoteName = NULL;
ret = providerTable->table[index].openEnum(
dwScope, dwType, dwUsage, lpNet, &handle);
ret = *lphEnum ? WN_SUCCESS :
WN_OUT_OF_MEMORY;
}
+
+ lpNet->lpRemoteName = RemoteName;
}
else
ret = WN_NOT_SUPPORTED;
*lphEnum = _createContextEnumerator(dwScope, dwType, dwUsage);
ret = *lphEnum ? WN_SUCCESS : WN_OUT_OF_MEMORY;
break;
- case RESOURCE_REMEMBERED:
case RESOURCE_CONNECTED:
+ *lphEnum = _createConnectedEnumerator(dwScope, dwType, dwUsage);
+ ret = *lphEnum ? WN_SUCCESS : WN_OUT_OF_MEMORY;
+ break;
+ case RESOURCE_REMEMBERED:
+#ifndef __REACTOS__
*lphEnum = _createNullEnumerator();
ret = *lphEnum ? WN_SUCCESS : WN_OUT_OF_MEMORY;
+#else
+ {
+ HKEY remembered, user_profile;
+
+ ret = WN_OUT_OF_MEMORY;
+ if (RegOpenCurrentUser(KEY_READ, &user_profile) == ERROR_SUCCESS)
+ {
+ WCHAR subkey[8] = {'N', 'e', 't', 'w', 'o', 'r', 'k', 0};
+
+ if (RegOpenKeyExW(user_profile, subkey, 0, KEY_READ, &remembered) == ERROR_SUCCESS)
+ {
+ *lphEnum = _createRememberedEnumerator(dwScope, dwType, remembered);
+ ret = *lphEnum ? WN_SUCCESS : WN_OUT_OF_MEMORY;
+ }
+
+ RegCloseKey(user_profile);
+ }
+ }
+#endif
break;
default:
WARN("unknown scope 0x%08x\n", dwScope);
{
ret = providerTable->table[enumerator->providerIndex].
openEnum(enumerator->dwScope, enumerator->dwType,
- enumerator->dwUsage, enumerator->lpNet,
+ enumerator->dwUsage, enumerator->specific.net,
&enumerator->handle);
if (ret == WN_SUCCESS)
{
switch (enumerator->dwScope)
{
case RESOURCE_GLOBALNET:
- if (enumerator->lpNet)
+ if (enumerator->specific.net)
ret = _enumerateGlobalPassthroughW(enumerator, lpcCount,
lpBuffer, lpBufferSize);
else
return ret;
}
+static DWORD _copyStringToEnumW(const WCHAR *source, DWORD* left, void** end)
+{
+ DWORD len;
+ WCHAR* local = *end;
+
+ len = strlenW(source) + 1;
+ len *= sizeof(WCHAR);
+ if (*left < len)
+ return WN_MORE_DATA;
+
+ local -= (len / sizeof(WCHAR));
+ memcpy(local, source, len);
+ *left -= len;
+ *end = local;
+
+ return WN_SUCCESS;
+}
+
+static DWORD _enumerateConnectedW(PWNetEnumerator enumerator, DWORD* user_count,
+ void* user_buffer, DWORD* user_size)
+{
+ DWORD ret, index, count, total_count, size, i, left;
+ void* end;
+ NETRESOURCEW* curr, * buffer;
+ HANDLE* handles;
+
+ if (!enumerator)
+ return WN_BAD_POINTER;
+ if (enumerator->enumType != WNET_ENUMERATOR_TYPE_CONNECTED)
+ return WN_BAD_VALUE;
+ if (!user_count || !user_buffer || !user_size)
+ return WN_BAD_POINTER;
+ if (!providerTable)
+ return WN_NO_NETWORK;
+
+ handles = enumerator->specific.handles;
+ left = *user_size;
+ size = *user_size;
+ buffer = HeapAlloc(GetProcessHeap(), 0, *user_size);
+ if (!buffer)
+ return WN_NO_NETWORK;
+
+ curr = user_buffer;
+ end = (char *)user_buffer + size;
+ count = *user_count;
+ total_count = 0;
+
+ ret = WN_NO_MORE_ENTRIES;
+ for (index = 0; index < providerTable->numProviders; index++)
+ {
+ if (providerTable->table[index].dwEnumScopes)
+ {
+ if (handles[index] == 0)
+ {
+ ret = providerTable->table[index].openEnum(enumerator->dwScope,
+ enumerator->dwType,
+ enumerator->dwUsage,
+ NULL, &handles[index]);
+ if (ret != WN_SUCCESS)
+ continue;
+ }
+
+ ret = providerTable->table[index].enumResource(handles[index],
+ &count, buffer,
+ &size);
+ total_count += count;
+ if (ret == WN_MORE_DATA)
+ break;
+
+ if (ret == WN_SUCCESS)
+ {
+ for (i = 0; i < count; ++i)
+ {
+ if (left < sizeof(NETRESOURCEW))
+ {
+ ret = WN_MORE_DATA;
+ break;
+ }
+
+ memcpy(curr, &buffer[i], sizeof(NETRESOURCEW));
+ left -= sizeof(NETRESOURCEW);
+
+ ret = _copyStringToEnumW(buffer[i].lpLocalName, &left, &end);
+ if (ret == WN_MORE_DATA)
+ break;
+ curr->lpLocalName = end;
+
+ ret = _copyStringToEnumW(buffer[i].lpRemoteName, &left, &end);
+ if (ret == WN_MORE_DATA)
+ break;
+ curr->lpRemoteName = end;
+
+ ret = _copyStringToEnumW(buffer[i].lpProvider, &left, &end);
+ if (ret == WN_MORE_DATA)
+ break;
+ curr->lpProvider = end;
+
+ ++curr;
+ }
+
+ size = left;
+ }
+
+ if (*user_count != -1)
+ count = *user_count - total_count;
+ else
+ count = *user_count;
+ }
+ }
+
+ if (total_count == 0)
+ ret = WN_NO_MORE_ENTRIES;
+
+ *user_count = total_count;
+ if (ret != WN_MORE_DATA && ret != WN_NO_MORE_ENTRIES)
+ ret = WN_SUCCESS;
+
+ HeapFree(GetProcessHeap(), 0, buffer);
+
+ TRACE("Returning %d\n", ret);
+ return ret;
+}
+
+#ifdef __REACTOS__
+static const WCHAR connectionType[] = { 'C','o','n','n','e','c','t',
+ 'i','o','n','T','y','p','e',0 };
+static const WCHAR providerName[] = { 'P','r','o','v','i','d','e','r',
+ 'N','a','m','e',0 };
+static const WCHAR remotePath[] = { 'R','e','m','o','t','e','P',
+ 'a','t','h',0 };
+
+static DWORD _enumeratorRememberedW(PWNetEnumerator enumerator, DWORD* user_count,
+ void* user_buffer, DWORD* user_size)
+{
+ HKEY registry, connection;
+ WCHAR buffer[255];
+ DWORD index, ret, type, len, size, provider_size, remote_size, full_size, total_count, size_left = *user_size;
+ NETRESOURCEW * net_buffer = user_buffer;
+ WCHAR * str;
+
+ if (!enumerator)
+ return WN_BAD_POINTER;
+ if (enumerator->enumType != WNET_ENUMERATOR_TYPE_REMEMBERED)
+ return WN_BAD_VALUE;
+ if (!user_count || !user_buffer || !user_size)
+ return WN_BAD_POINTER;
+ if (!providerTable)
+ return WN_NO_NETWORK;
+
+ /* We will do the work in a single loop, so here is some things:
+ * we write netresource at the begin of the user buffer
+ * we write strings at the end of the user buffer
+ */
+ total_count = 0;
+ type = enumerator->dwType;
+ registry = enumerator->specific.remembered.registry;
+ str = (WCHAR *)((ULONG_PTR)user_buffer + *user_size - sizeof(WCHAR));
+ for (index = enumerator->specific.remembered.index; ; ++index)
+ {
+ enumerator->specific.remembered.index = index;
+
+ if (*user_count != -1 && total_count == *user_count)
+ {
+ ret = WN_SUCCESS;
+ break;
+ }
+
+ if (size_left < sizeof(NETRESOURCEW))
+ {
+ ret = WN_MORE_DATA;
+ break;
+ }
+
+ len = ARRAY_SIZE(buffer);
+ ret = RegEnumKeyExW(registry, index, buffer, &len, NULL, NULL, NULL, NULL);
+ if (ret != ERROR_SUCCESS)
+ {
+ /* We're done, that's a success! */
+ if (ret == ERROR_NO_MORE_ITEMS)
+ {
+ ret = WN_SUCCESS;
+ break;
+ }
+
+ continue;
+ }
+
+ if (RegOpenKeyExW(registry, buffer, 0, KEY_READ, &connection) != ERROR_SUCCESS)
+ {
+ continue;
+ }
+
+ size = sizeof(DWORD);
+ RegQueryValueExW(connection, connectionType, NULL, NULL, (BYTE *)&net_buffer->dwType, &size);
+ if (type != RESOURCETYPE_ANY && net_buffer->dwType != type)
+ {
+ RegCloseKey(connection);
+ continue;
+ }
+
+ net_buffer->dwScope = RESOURCE_REMEMBERED;
+ net_buffer->dwDisplayType = RESOURCEDISPLAYTYPE_GENERIC;
+ net_buffer->dwUsage = RESOURCEUSAGE_CONNECTABLE;
+
+ size_left -= sizeof(NETRESOURCEW);
+
+ /* Compute the whole size */
+ full_size = 0;
+ if (RegQueryValueExW(connection, providerName, NULL, NULL, NULL, &provider_size) != ERROR_SUCCESS)
+ {
+ RegCloseKey(connection);
+ continue;
+ }
+ full_size += provider_size;
+
+ if (RegQueryValueExW(connection, remotePath, NULL, NULL, NULL, &remote_size) != ERROR_SUCCESS)
+ {
+ RegCloseKey(connection);
+ continue;
+ }
+ full_size += remote_size;
+
+ /* FIXME: this only supports drive letters */
+ full_size += 3 * sizeof(WCHAR);
+ if (full_size > size_left)
+ {
+ RegCloseKey(connection);
+ ret = WN_MORE_DATA;
+ break;
+ }
+
+ str -= 3;
+ str[0] = buffer[0];
+ str[1] = L':';
+ str[2] = 0;
+ net_buffer->lpLocalName = str;
+ size_left -= 3 * sizeof(WCHAR);
+
+ size = provider_size;
+ str -= (provider_size / sizeof(WCHAR));
+ ret = RegQueryValueExW(connection, providerName, NULL, NULL, (BYTE *)str, &size);
+ if (ret == ERROR_MORE_DATA)
+ {
+ RegCloseKey(connection);
+ ret = WN_MORE_DATA;
+ break;
+ }
+ net_buffer->lpProvider = str;
+ size_left -= size;
+
+ size = remote_size;
+ str -= (remote_size / sizeof(WCHAR));
+ ret = RegQueryValueExW(connection, remotePath, NULL, NULL, (BYTE *)str, &size);
+ if (ret == ERROR_MORE_DATA)
+ {
+ RegCloseKey(connection);
+ ret = WN_MORE_DATA;
+ break;
+ }
+ net_buffer->lpRemoteName = str;
+ size_left -= size;
+
+ RegCloseKey(connection);
+
+ net_buffer->lpComment = NULL;
+
+ ++total_count;
+ ++net_buffer;
+ }
+
+ if (total_count == 0)
+ ret = WN_NO_MORE_ENTRIES;
+
+ *user_count = total_count;
+
+ if (ret != WN_MORE_DATA && ret != WN_NO_MORE_ENTRIES)
+ ret = WN_SUCCESS;
+
+ if (ret == WN_MORE_DATA)
+ *user_size = *user_size + full_size;
+
+ return ret;
+}
+#endif
+
/*********************************************************************
* WNetEnumResourceW [MPR.@]
*/
switch (enumerator->enumType)
{
+#ifndef __REACTOS__
case WNET_ENUMERATOR_TYPE_NULL:
ret = WN_NO_MORE_ENTRIES;
break;
+#endif
case WNET_ENUMERATOR_TYPE_GLOBAL:
ret = _enumerateGlobalW(enumerator, lpcCount, lpBuffer,
lpBufferSize);
ret = _enumerateContextW(enumerator, lpcCount, lpBuffer,
lpBufferSize);
break;
+ case WNET_ENUMERATOR_TYPE_CONNECTED:
+ ret = _enumerateConnectedW(enumerator, lpcCount, lpBuffer,
+ lpBufferSize);
+ break;
+#ifdef __REACTOS__
+ case WNET_ENUMERATOR_TYPE_REMEMBERED:
+ ret = _enumeratorRememberedW(enumerator, lpcCount, lpBuffer,
+ lpBufferSize);
+ break;
+#endif
default:
WARN("bogus enumerator type!\n");
ret = WN_NO_NETWORK;
*/
DWORD WINAPI WNetCloseEnum( HANDLE hEnum )
{
- DWORD ret;
+ DWORD ret, index;
+ HANDLE *handles;
TRACE( "(%p)\n", hEnum );
switch (enumerator->enumType)
{
+#ifndef __REACTOS__
case WNET_ENUMERATOR_TYPE_NULL:
ret = WN_SUCCESS;
break;
+#endif
case WNET_ENUMERATOR_TYPE_GLOBAL:
- if (enumerator->lpNet)
- _freeEnumNetResource(enumerator->lpNet);
+ if (enumerator->specific.net)
+ _freeEnumNetResource(enumerator->specific.net);
if (enumerator->handle)
providerTable->table[enumerator->providerIndex].
closeEnum(enumerator->handle);
closeEnum(enumerator->handle);
ret = WN_SUCCESS;
break;
+ case WNET_ENUMERATOR_TYPE_CONNECTED:
+ handles = enumerator->specific.handles;
+ for (index = 0; index < providerTable->numProviders; index++)
+ {
+ if (providerTable->table[index].dwEnumScopes && handles[index])
+ providerTable->table[index].closeEnum(handles[index]);
+ }
+ HeapFree(GetProcessHeap(), 0, handles);
+ ret = WN_SUCCESS;
+ break;
+#ifdef __REACTOS__
+ case WNET_ENUMERATOR_TYPE_REMEMBERED:
+ RegCloseKey(enumerator->specific.remembered.registry);
+ ret = WN_SUCCESS;
+ break;
+#endif
default:
WARN("bogus enumerator type!\n");
ret = WN_BAD_HANDLE;
DWORD WINAPI WNetAddConnectionA( LPCSTR lpRemoteName, LPCSTR lpPassword,
LPCSTR lpLocalName )
{
- FIXME( "(%s, %p, %s): stub\n",
- debugstr_a(lpRemoteName), lpPassword, debugstr_a(lpLocalName) );
+ NETRESOURCEA resourcesA;
- SetLastError(WN_NO_NETWORK);
- return WN_NO_NETWORK;
+ memset(&resourcesA, 0, sizeof(resourcesA));
+ resourcesA.lpRemoteName = (LPSTR)lpRemoteName;
+ resourcesA.lpLocalName = (LPSTR)lpLocalName;
+ return WNetUseConnectionA(NULL, &resourcesA, lpPassword, NULL, 0, NULL, 0, NULL);
}
/*********************************************************************
DWORD WINAPI WNetAddConnectionW( LPCWSTR lpRemoteName, LPCWSTR lpPassword,
LPCWSTR lpLocalName )
{
- FIXME( "(%s, %p, %s): stub\n",
- debugstr_w(lpRemoteName), lpPassword, debugstr_w(lpLocalName) );
+ NETRESOURCEW resourcesW;
- SetLastError(WN_NO_NETWORK);
- return WN_NO_NETWORK;
+ memset(&resourcesW, 0, sizeof(resourcesW));
+ resourcesW.lpRemoteName = (LPWSTR)lpRemoteName;
+ resourcesW.lpLocalName = (LPWSTR)lpLocalName;
+ return WNetUseConnectionW(NULL, &resourcesW, lpPassword, NULL, 0, NULL, 0, NULL);
}
/*********************************************************************
LPCSTR lpPassword, LPCSTR lpUserID,
DWORD dwFlags )
{
- FIXME( "(%p, %p, %s, 0x%08X): stub\n",
- lpNetResource, lpPassword, debugstr_a(lpUserID), dwFlags );
-
- SetLastError(WN_NO_NETWORK);
- return WN_NO_NETWORK;
+ return WNetUseConnectionA(NULL, lpNetResource, lpPassword, lpUserID, dwFlags,
+ NULL, 0, NULL);
}
/*********************************************************************
LPCWSTR lpPassword, LPCWSTR lpUserID,
DWORD dwFlags )
{
- FIXME( "(%p, %p, %s, 0x%08X): stub\n",
- lpNetResource, lpPassword, debugstr_w(lpUserID), dwFlags );
-
- SetLastError(WN_NO_NETWORK);
- return WN_NO_NETWORK;
+ return WNetUseConnectionW(NULL, lpNetResource, lpPassword, lpUserID, dwFlags,
+ NULL, 0, NULL);
}
/*********************************************************************
LPCSTR lpPassword, LPCSTR lpUserID,
DWORD dwFlags )
{
- FIXME( "(%p, %p, %p, %s, 0x%08X), stub\n",
- hwndOwner, lpNetResource, lpPassword, debugstr_a(lpUserID), dwFlags );
-
- SetLastError(WN_NO_NETWORK);
- return WN_NO_NETWORK;
+ return WNetUseConnectionA(hwndOwner, lpNetResource, lpPassword, lpUserID,
+ dwFlags, NULL, 0, NULL);
}
/*********************************************************************
LPCWSTR lpPassword, LPCWSTR lpUserID,
DWORD dwFlags )
{
- FIXME( "(%p, %p, %p, %s, 0x%08X), stub\n",
- hwndOwner, lpNetResource, lpPassword, debugstr_w(lpUserID), dwFlags );
+ return WNetUseConnectionW(hwndOwner, lpNetResource, lpPassword, lpUserID,
+ dwFlags, NULL, 0, NULL);
+}
- SetLastError(WN_NO_NETWORK);
- return WN_NO_NETWORK;
+struct use_connection_context
+{
+ HWND hwndOwner;
+ NETRESOURCEW *resource;
+ NETRESOURCEA *resourceA; /* only set for WNetUseConnectionA */
+ WCHAR *password;
+ WCHAR *userid;
+ DWORD flags;
+ void *accessname;
+ DWORD *buffer_size;
+ DWORD *result;
+ DWORD (*pre_set_accessname)(struct use_connection_context*, WCHAR *);
+ void (*set_accessname)(struct use_connection_context*, WCHAR *);
+};
+
+static DWORD use_connection_pre_set_accessnameW(struct use_connection_context *ctxt, WCHAR *local_name)
+{
+ if (ctxt->accessname && ctxt->buffer_size && *ctxt->buffer_size)
+ {
+ DWORD len;
+
+ if (local_name)
+ len = strlenW(local_name);
+ else
+ len = strlenW(ctxt->resource->lpRemoteName);
+
+ if (++len > *ctxt->buffer_size)
+ {
+ *ctxt->buffer_size = len;
+ return ERROR_MORE_DATA;
+ }
+ }
+ else
+ ctxt->accessname = NULL;
+
+ return ERROR_SUCCESS;
+}
+
+static void use_connection_set_accessnameW(struct use_connection_context *ctxt, WCHAR *local_name)
+{
+ WCHAR *accessname = ctxt->accessname;
+ if (local_name)
+ {
+ strcpyW(accessname, local_name);
+ if (ctxt->result)
+ *ctxt->result = CONNECT_LOCALDRIVE;
+ }
+ else
+ strcpyW(accessname, ctxt->resource->lpRemoteName);
+}
+
+static DWORD wnet_use_provider( struct use_connection_context *ctxt, NETRESOURCEW * netres, WNetProvider *provider, BOOLEAN redirect )
+{
+ DWORD caps, ret;
+
+ caps = provider->getCaps(WNNC_CONNECTION);
+ if (!(caps & (WNNC_CON_ADDCONNECTION | WNNC_CON_ADDCONNECTION3)))
+ return ERROR_BAD_PROVIDER;
+
+ ret = WN_ACCESS_DENIED;
+ do
+ {
+ if ((caps & WNNC_CON_ADDCONNECTION3) && provider->addConnection3)
+ ret = provider->addConnection3(ctxt->hwndOwner, netres, ctxt->password, ctxt->userid, ctxt->flags);
+ else if ((caps & WNNC_CON_ADDCONNECTION) && provider->addConnection)
+ ret = provider->addConnection(netres, ctxt->password, ctxt->userid);
+
+ if (ret == WN_ALREADY_CONNECTED && redirect)
+ netres->lpLocalName[0] -= 1;
+ } while (redirect && ret == WN_ALREADY_CONNECTED && netres->lpLocalName[0] >= 'C');
+
+ if (ret == WN_SUCCESS && ctxt->accessname)
+ ctxt->set_accessname(ctxt, netres->lpLocalName);
+
+ return ret;
+}
+
+static DWORD wnet_use_connection( struct use_connection_context *ctxt )
+{
+ WNetProvider *provider;
+ DWORD index, ret = WN_NO_NETWORK;
+ BOOL redirect = FALSE;
+ WCHAR letter[3] = {'Z', ':', 0};
+ NETRESOURCEW netres;
+
+ if (!providerTable || providerTable->numProviders == 0)
+ return WN_NO_NETWORK;
+
+ if (!ctxt->resource)
+ return ERROR_INVALID_PARAMETER;
+ netres = *ctxt->resource;
+
+ if (!netres.lpLocalName && (ctxt->flags & CONNECT_REDIRECT))
+ {
+ if (netres.dwType != RESOURCETYPE_DISK && netres.dwType != RESOURCETYPE_PRINT)
+ return ERROR_BAD_DEV_TYPE;
+
+ if (netres.dwType == RESOURCETYPE_PRINT)
+ {
+ FIXME("Local device selection is not implemented for printers.\n");
+ return WN_NO_NETWORK;
+ }
+
+ redirect = TRUE;
+ netres.lpLocalName = letter;
+ }
+
+ if (ctxt->flags & CONNECT_INTERACTIVE)
+ return ERROR_BAD_NET_NAME;
+
+ if ((ret = ctxt->pre_set_accessname(ctxt, netres.lpLocalName)))
+ return ret;
+
+ if (netres.lpProvider)
+ {
+ index = _findProviderIndexW(netres.lpProvider);
+ if (index == BAD_PROVIDER_INDEX)
+ return ERROR_BAD_PROVIDER;
+
+ provider = &providerTable->table[index];
+ ret = wnet_use_provider(ctxt, &netres, provider, redirect);
+ }
+ else
+ {
+ for (index = 0; index < providerTable->numProviders; index++)
+ {
+ provider = &providerTable->table[index];
+ ret = wnet_use_provider(ctxt, &netres, provider, redirect);
+ if (ret == WN_SUCCESS || ret == WN_ALREADY_CONNECTED)
+ break;
+ }
+ }
+
+#ifdef __REACTOS__
+ if (ret == WN_SUCCESS && ctxt->flags & CONNECT_UPDATE_PROFILE)
+ {
+ HKEY user_profile;
+
+ if (netres.dwType == RESOURCETYPE_PRINT)
+ {
+ FIXME("Persistent connection are not supported for printers\n");
+ return ret;
+ }
+
+ if (RegOpenCurrentUser(KEY_ALL_ACCESS, &user_profile) == ERROR_SUCCESS)
+ {
+ HKEY network;
+ WCHAR subkey[10] = {'N', 'e', 't', 'w', 'o', 'r', 'k', '\\', netres.lpLocalName[0], 0};
+
+ if (RegCreateKeyExW(user_profile, subkey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &network, NULL) == ERROR_SUCCESS)
+ {
+ DWORD dword_arg = RESOURCETYPE_DISK;
+ DWORD len = (strlenW(provider->name) + 1) * sizeof(WCHAR);
+
+ RegSetValueExW(network, L"ConnectionType", 0, REG_DWORD, (const BYTE *)&dword_arg, sizeof(DWORD));
+ RegSetValueExW(network, L"ProviderName", 0, REG_SZ, (const BYTE *)provider->name, len);
+ dword_arg = provider->dwNetType;
+ RegSetValueExW(network, L"ProviderType", 0, REG_DWORD, (const BYTE *)&dword_arg, sizeof(DWORD));
+ len = (strlenW(netres.lpRemoteName) + 1) * sizeof(WCHAR);
+ RegSetValueExW(network, L"RemotePath", 0, REG_SZ, (const BYTE *)netres.lpRemoteName, len);
+ len = 0;
+ RegSetValueExW(network, L"UserName", 0, REG_SZ, (const BYTE *)netres.lpRemoteName, len);
+ RegCloseKey(network);
+ }
+
+ RegCloseKey(user_profile);
+ }
+ }
+#endif
+
+ return ret;
}
/*****************************************************************
- * WNetUseConnectionA [MPR.@]
+ * WNetUseConnectionW [MPR.@]
*/
-DWORD WINAPI WNetUseConnectionA( HWND hwndOwner, LPNETRESOURCEA lpNetResource,
- LPCSTR lpPassword, LPCSTR lpUserID, DWORD dwFlags,
- LPSTR lpAccessName, LPDWORD lpBufferSize,
- LPDWORD lpResult )
+DWORD WINAPI WNetUseConnectionW( HWND hwndOwner, NETRESOURCEW *resource, LPCWSTR password,
+ LPCWSTR userid, DWORD flags, LPWSTR accessname, DWORD *buffer_size, DWORD *result )
+{
+ struct use_connection_context ctxt;
+
+ TRACE( "(%p, %p, %p, %s, 0x%08X, %p, %p, %p)\n",
+ hwndOwner, resource, password, debugstr_w(userid), flags,
+ accessname, buffer_size, result );
+
+ ctxt.hwndOwner = hwndOwner;
+ ctxt.resource = resource;
+ ctxt.resourceA = NULL;
+ ctxt.password = (WCHAR*)password;
+ ctxt.userid = (WCHAR*)userid;
+ ctxt.flags = flags;
+ ctxt.accessname = accessname;
+ ctxt.buffer_size = buffer_size;
+ ctxt.result = result;
+ ctxt.pre_set_accessname = use_connection_pre_set_accessnameW;
+ ctxt.set_accessname = use_connection_set_accessnameW;
+
+ return wnet_use_connection(&ctxt);
+}
+
+static DWORD use_connection_pre_set_accessnameA(struct use_connection_context *ctxt, WCHAR *local_name)
{
- FIXME( "(%p, %p, %p, %s, 0x%08X, %s, %p, %p), stub\n",
- hwndOwner, lpNetResource, lpPassword, debugstr_a(lpUserID), dwFlags,
- debugstr_a(lpAccessName), lpBufferSize, lpResult );
+ if (ctxt->accessname && ctxt->buffer_size && *ctxt->buffer_size)
+ {
+ DWORD len;
- SetLastError(WN_NO_NETWORK);
- return WN_NO_NETWORK;
+ if (local_name)
+ len = WideCharToMultiByte(CP_ACP, 0, local_name, -1, NULL, 0, NULL, NULL) - 1;
+ else
+ len = strlen(ctxt->resourceA->lpRemoteName);
+
+ if (++len > *ctxt->buffer_size)
+ {
+ *ctxt->buffer_size = len;
+ return ERROR_MORE_DATA;
+ }
+ }
+ else
+ ctxt->accessname = NULL;
+
+ return ERROR_SUCCESS;
+}
+
+static void use_connection_set_accessnameA(struct use_connection_context *ctxt, WCHAR *local_name)
+{
+ char *accessname = ctxt->accessname;
+ if (local_name)
+ {
+ WideCharToMultiByte(CP_ACP, 0, local_name, -1, accessname, *ctxt->buffer_size, NULL, NULL);
+ if (ctxt->result)
+ *ctxt->result = CONNECT_LOCALDRIVE;
+ }
+ else
+ strcpy(accessname, ctxt->resourceA->lpRemoteName);
+}
+
+static LPWSTR strdupAtoW( LPCSTR str )
+{
+ LPWSTR ret;
+ INT len;
+
+ if (!str) return NULL;
+ len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
+ ret = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
+ if (ret) MultiByteToWideChar( CP_ACP, 0, str, -1, ret, len );
+ return ret;
+}
+
+static void netresource_a_to_w( NETRESOURCEA *resourceA, NETRESOURCEW *resourceW )
+{
+ resourceW->dwScope = resourceA->dwScope;
+ resourceW->dwType = resourceA->dwType;
+ resourceW->dwDisplayType = resourceA->dwDisplayType;
+ resourceW->dwUsage = resourceA->dwUsage;
+ resourceW->lpLocalName = strdupAtoW(resourceA->lpLocalName);
+ resourceW->lpRemoteName = strdupAtoW(resourceA->lpRemoteName);
+ resourceW->lpComment = strdupAtoW(resourceA->lpComment);
+ resourceW->lpProvider = strdupAtoW(resourceA->lpProvider);
+}
+
+static void free_netresourceW( NETRESOURCEW *resource )
+{
+ HeapFree(GetProcessHeap(), 0, resource->lpLocalName);
+ HeapFree(GetProcessHeap(), 0, resource->lpRemoteName);
+ HeapFree(GetProcessHeap(), 0, resource->lpComment);
+ HeapFree(GetProcessHeap(), 0, resource->lpProvider);
}
/*****************************************************************
- * WNetUseConnectionW [MPR.@]
+ * WNetUseConnectionA [MPR.@]
*/
-DWORD WINAPI WNetUseConnectionW( HWND hwndOwner, LPNETRESOURCEW lpNetResource,
- LPCWSTR lpPassword, LPCWSTR lpUserID, DWORD dwFlags,
- LPWSTR lpAccessName, LPDWORD lpBufferSize,
- LPDWORD lpResult )
+DWORD WINAPI WNetUseConnectionA( HWND hwndOwner, NETRESOURCEA *resource,
+ LPCSTR password, LPCSTR userid, DWORD flags, LPSTR accessname,
+ DWORD *buffer_size, DWORD *result )
{
- FIXME( "(%p, %p, %p, %s, 0x%08X, %s, %p, %p), stub\n",
- hwndOwner, lpNetResource, lpPassword, debugstr_w(lpUserID), dwFlags,
- debugstr_w(lpAccessName), lpBufferSize, lpResult );
+ struct use_connection_context ctxt;
+ NETRESOURCEW resourceW;
+ DWORD ret;
- SetLastError(WN_NO_NETWORK);
- return WN_NO_NETWORK;
+ TRACE( "(%p, %p, %p, %s, 0x%08X, %p, %p, %p)\n", hwndOwner, resource, password, debugstr_a(userid), flags,
+ accessname, buffer_size, result );
+
+ netresource_a_to_w(resource, &resourceW);
+
+ ctxt.hwndOwner = hwndOwner;
+ ctxt.resource = &resourceW;
+ ctxt.resourceA = resource;
+ ctxt.password = strdupAtoW(password);
+ ctxt.userid = strdupAtoW(userid);
+ ctxt.flags = flags;
+ ctxt.accessname = accessname;
+ ctxt.buffer_size = buffer_size;
+ ctxt.result = result;
+ ctxt.pre_set_accessname = use_connection_pre_set_accessnameA;
+ ctxt.set_accessname = use_connection_set_accessnameA;
+
+ ret = wnet_use_connection(&ctxt);
+
+ free_netresourceW(&resourceW);
+ HeapFree(GetProcessHeap(), 0, ctxt.password);
+ HeapFree(GetProcessHeap(), 0, ctxt.userid);
+
+ return ret;
}
/*********************************************************************
*/
DWORD WINAPI WNetCancelConnectionA( LPCSTR lpName, BOOL fForce )
{
- FIXME( "(%s, %d), stub\n", debugstr_a(lpName), fForce );
-
- return WN_SUCCESS;
+ return WNetCancelConnection2A(lpName, 0, fForce);
}
/*********************************************************************
*/
DWORD WINAPI WNetCancelConnectionW( LPCWSTR lpName, BOOL fForce )
{
- FIXME( "(%s, %d), stub\n", debugstr_w(lpName), fForce );
-
- return WN_SUCCESS;
+ return WNetCancelConnection2W(lpName, 0, fForce);
}
/*********************************************************************
*/
DWORD WINAPI WNetCancelConnection2A( LPCSTR lpName, DWORD dwFlags, BOOL fForce )
{
- FIXME( "(%s, %08X, %d), stub\n", debugstr_a(lpName), dwFlags, fForce );
+ DWORD ret;
+ WCHAR * name = strdupAtoW(lpName);
+ if (!name)
+ return ERROR_NOT_CONNECTED;
- return WN_SUCCESS;
+ ret = WNetCancelConnection2W(name, dwFlags, fForce);
+ HeapFree(GetProcessHeap(), 0, name);
+
+ return ret;
}
/*********************************************************************
*/
DWORD WINAPI WNetCancelConnection2W( LPCWSTR lpName, DWORD dwFlags, BOOL fForce )
{
- FIXME( "(%s, %08X, %d), stub\n", debugstr_w(lpName), dwFlags, fForce );
+ DWORD ret = WN_NO_NETWORK;
+ DWORD index;
- return WN_SUCCESS;
+ if (providerTable != NULL)
+ {
+ for (index = 0; index < providerTable->numProviders; index++)
+ {
+ if(providerTable->table[index].getCaps(WNNC_CONNECTION) &
+ WNNC_CON_CANCELCONNECTION)
+ {
+ if (providerTable->table[index].cancelConnection)
+ ret = providerTable->table[index].cancelConnection((LPWSTR)lpName, fForce);
+ else
+ ret = WN_NO_NETWORK;
+ if (ret == WN_SUCCESS || ret == WN_OPEN_FILES)
+ break;
+ }
+ }
+ }
+#ifdef __REACTOS__
+
+ if (dwFlags & CONNECT_UPDATE_PROFILE)
+ {
+ HKEY user_profile;
+ WCHAR *coma = strchrW(lpName, ':');
+
+ if (coma && RegOpenCurrentUser(KEY_ALL_ACCESS, &user_profile) == ERROR_SUCCESS)
+ {
+ WCHAR *subkey;
+ DWORD len;
+
+ len = (ULONG_PTR)coma - (ULONG_PTR)lpName + sizeof(L"Network\\");
+ subkey = HeapAlloc(GetProcessHeap(), 0, len);
+ if (subkey)
+ {
+ strcpyW(subkey, L"Network\\");
+ memcpy(subkey + (sizeof(L"Network\\") / sizeof(WCHAR)) - 1, lpName, (ULONG_PTR)coma - (ULONG_PTR)lpName);
+ subkey[len / sizeof(WCHAR) - 1] = 0;
+
+ TRACE("Removing: %S\n", subkey);
+
+ RegDeleteKeyW(user_profile, subkey);
+ HeapFree(GetProcessHeap(), 0, subkey);
+ }
+
+ RegCloseKey(user_profile);
+ }
+ }
+
+#endif
+ return ret;
}
/*****************************************************************
/* find the network connection for a given drive; helper for WNetGetConnection */
static DWORD get_drive_connection( WCHAR letter, LPWSTR remote, LPDWORD size )
{
+#ifndef __REACTOS__
char buffer[1024];
struct mountmgr_unix_drive *data = (struct mountmgr_unix_drive *)buffer;
HANDLE mgr;
}
CloseHandle( mgr );
return ret;
+#else
+ DWORD ret = WN_NO_NETWORK;
+ DWORD index;
+ WCHAR local[3] = {letter, ':', 0};
+
+ if (providerTable != NULL)
+ {
+ for (index = 0; index < providerTable->numProviders; index++)
+ {
+ if(providerTable->table[index].getCaps(WNNC_CONNECTION) &
+ WNNC_CON_GETCONNECTIONS)
+ {
+ if (providerTable->table[index].getConnection)
+ ret = providerTable->table[index].getConnection(
+ local, remote, size);
+ else
+ ret = WN_NO_NETWORK;
+ if (ret == WN_SUCCESS || ret == WN_MORE_DATA)
+ break;
+ }
+ }
+ }
+ if (ret)
+ SetLastError(ret);
+ return ret;
+#endif
}
/**************************************************************************
break;
}
case REMOTE_NAME_INFO_LEVEL:
- err = WN_NO_NETWORK;
+ err = WN_NOT_CONNECTED;
break;
default:
return err;
}
+/*****************************************************************
+ * WNetClearConnections [MPR.@]
+ */
+DWORD WINAPI WNetClearConnections ( HWND owner )
+{
+ HANDLE connected;
+ PWSTR connection;
+ DWORD ret, size, count;
+ NETRESOURCEW * resources, * iter;
+
+ ret = WNetOpenEnumW(RESOURCE_CONNECTED, RESOURCETYPE_ANY, 0, NULL, &connected);
+ if (ret != WN_SUCCESS)
+ {
+ if (ret != WN_NO_NETWORK)
+ {
+ return ret;
+ }
+
+ /* Means no provider, then, clearing is OK */
+ return WN_SUCCESS;
+ }
+
+ size = 0x1000;
+ resources = HeapAlloc(GetProcessHeap(), 0, size);
+ if (!resources)
+ {
+ WNetCloseEnum(connected);
+ return WN_OUT_OF_MEMORY;
+ }
+
+ for (;;)
+ {
+ size = 0x1000;
+ count = -1;
+
+ memset(resources, 0, size);
+ ret = WNetEnumResourceW(connected, &count, resources, &size);
+ if (ret == WN_SUCCESS || ret == WN_MORE_DATA)
+ {
+ for (iter = resources; count; count--, iter++)
+ {
+ if (iter->lpLocalName && iter->lpLocalName[0])
+ connection = iter->lpLocalName;
+ else
+ connection = iter->lpRemoteName;
+
+ WNetCancelConnection2W(connection, 0, TRUE);
+ }
+ }
+ else
+ break;
+ }
+
+ HeapFree(GetProcessHeap(), 0, resources);
+ WNetCloseEnum(connected);
+
+ return ret;
+}
/*
*/
DWORD WINAPI WNetConnectionDialog( HWND hwnd, DWORD dwType )
{
- FIXME( "(%p, %08X): stub\n", hwnd, dwType );
+ CONNECTDLGSTRUCTW conn_dlg;
+ NETRESOURCEW net_res;
- SetLastError(WN_NO_NETWORK);
- return WN_NO_NETWORK;
+ ZeroMemory(&conn_dlg, sizeof(conn_dlg));
+ ZeroMemory(&net_res, sizeof(net_res));
+
+ conn_dlg.cbStructure = sizeof(conn_dlg);
+ conn_dlg.lpConnRes = &net_res;
+ conn_dlg.hwndOwner = hwnd;
+ net_res.dwType = dwType;
+
+ return WNetConnectionDialog1W(&conn_dlg);
}
/*********************************************************************