- Implemented WNetGetUniversalName*() functions to correctly return universal/remote info levels
-- v4: mpr: WNetGetUniversalNameA() and WNetGetUniversalNameW() implemented
From: Alexey Alyaev aalyaev@etersoft.ru
--- dlls/mpr/Makefile.in | 2 +- dlls/mpr/tests/mpr.c | 33 +++-- dlls/mpr/wnet.c | 336 ++++++++++++++++++++++++++++++++++++------- 3 files changed, 312 insertions(+), 59 deletions(-)
diff --git a/dlls/mpr/Makefile.in b/dlls/mpr/Makefile.in index 8a74786cd22..97c762b8070 100644 --- a/dlls/mpr/Makefile.in +++ b/dlls/mpr/Makefile.in @@ -1,6 +1,6 @@ MODULE = mpr.dll IMPORTLIB = mpr -IMPORTS = user32 advapi32 +IMPORTS = user32 advapi32 shlwapi
C_SRCS = \ auth.c \ diff --git a/dlls/mpr/tests/mpr.c b/dlls/mpr/tests/mpr.c index ab5c47c0490..903709045a2 100644 --- a/dlls/mpr/tests/mpr.c +++ b/dlls/mpr/tests/mpr.c @@ -54,9 +54,10 @@ static void test_WNetGetUniversalName(void) fail_size = 1; ret = WNetGetUniversalNameA(driveA, UNIVERSAL_NAME_INFO_LEVEL, buffer, &fail_size); + if(drive_type == DRIVE_REMOTE) { - todo_wine ok(ret == WN_BAD_VALUE || ret == WN_MORE_DATA, "WNetGetUniversalNameA failed: %08lx\n", ret); + ok(ret == WN_MORE_DATA, "WNetGetUniversalNameA failed: %08lx\n", ret); ok(fail_size > 1, "Got %ld\n", fail_size); } else @@ -66,13 +67,15 @@ static void test_WNetGetUniversalName(void) fail_size = ARRAY_SIZE(driveA) - 1; ret = WNetGetUniversalNameA(driveA, UNIVERSAL_NAME_INFO_LEVEL, buffer, &fail_size); + if(drive_type == DRIVE_REMOTE) ok(ret == WN_MORE_DATA, "WNetGetUniversalNameA failed: %08lx\n", ret);
ret = WNetGetUniversalNameA(driveandpathA, UNIVERSAL_NAME_INFO_LEVEL, buffer, &info_size); + if(drive_type == DRIVE_REMOTE) - todo_wine ok(ret == WN_NO_ERROR, "WNetGetUniversalNameA failed: %08lx\n", ret); + ok(ret == WN_NO_ERROR, "WNetGetUniversalNameA failed: %08lx\n", ret);
info_size = sizeof(buffer); ret = WNetGetUniversalNameW(driveW, UNIVERSAL_NAME_INFO_LEVEL, @@ -84,6 +87,7 @@ static void test_WNetGetUniversalName(void) ok((ret == WN_NOT_CONNECTED) || (ret == WN_NO_NET_OR_BAD_PATH), "WNetGetUniversalNameW(%s, ...) returned %lu (drive_type: %lu)\n", wine_dbgstr_w(driveW), ret, drive_type); + if(drive_type != DRIVE_REMOTE) ok(info_size == sizeof(buffer), "Got wrong size: %lu\n", info_size); } @@ -104,8 +108,8 @@ static void test_WNetGetRemoteName(void) info_size = sizeof(buffer); ret = WNetGetUniversalNameA(driveA, REMOTE_NAME_INFO_LEVEL, buffer, &info_size); + if(drive_type == DRIVE_REMOTE) - todo_wine ok(ret == WN_NO_ERROR, "WNetGetUniversalNameA failed: %08lx\n", ret); else ok(ret == WN_NOT_CONNECTED || ret == WN_NO_NET_OR_BAD_PATH, @@ -115,21 +119,22 @@ static void test_WNetGetRemoteName(void) fail_size = 0; ret = WNetGetUniversalNameA(driveA, REMOTE_NAME_INFO_LEVEL, buffer, &fail_size); + if(drive_type == DRIVE_REMOTE) - todo_wine ok(ret == WN_BAD_VALUE || ret == WN_MORE_DATA, "WNetGetUniversalNameA failed: %08lx\n", ret); else ok(ret == WN_NOT_CONNECTED || ret == WN_NO_NET_OR_BAD_PATH, "(%s) WNetGetUniversalNameA gave wrong error: %lu\n", driveA, ret); + ret = WNetGetUniversalNameA(driveA, REMOTE_NAME_INFO_LEVEL, buffer, NULL); - todo_wine ok(ret == WN_BAD_POINTER, "WNetGetUniversalNameA failed: %08lx\n", ret); + + ok(ret == WN_BAD_POINTER, "WNetGetUniversalNameA failed: %08lx\n", ret);
ret = WNetGetUniversalNameA(driveA, REMOTE_NAME_INFO_LEVEL, NULL, &info_size);
if(drive_type == DRIVE_REMOTE) - todo_wine ok(ret == WN_BAD_POINTER || ret == WN_BAD_VALUE, "WNetGetUniversalNameA failed: %08lx\n", ret); else ok(ret == WN_NOT_CONNECTED || ret == WN_BAD_VALUE, @@ -138,24 +143,32 @@ static void test_WNetGetRemoteName(void) fail_size = ARRAY_SIZE(driveA) - 1; ret = WNetGetUniversalNameA(driveA, REMOTE_NAME_INFO_LEVEL, buffer, &fail_size); + if(drive_type == DRIVE_REMOTE) - todo_wine ok(ret == WN_MORE_DATA, "WNetGetUniversalNameA failed: %08lx\n", ret); + ok(ret == WN_MORE_DATA, "WNetGetUniversalNameA failed: %08lx\n", ret); + else + ok(ret == WN_NOT_CONNECTED || ret == WN_NO_NET_OR_BAD_PATH, + "(%s) WNetGetUniversalNameA gave wrong error: %lu\n", driveA, ret);
ret = WNetGetUniversalNameA(driveandpathA, REMOTE_NAME_INFO_LEVEL, buffer, &info_size); + if(drive_type == DRIVE_REMOTE) - todo_wine ok(ret == WN_NO_ERROR, "WNetGetUniversalNameA failed: %08lx\n", ret); + ok(ret == WN_NO_ERROR, "WNetGetUniversalNameA failed: %08lx\n", ret); + else + ok(ret == WN_NOT_CONNECTED || ret == WN_NO_NET_OR_BAD_PATH, + "(%s) WNetGetUniversalNameA gave wrong error: %lu\n", driveA, ret);
info_size = sizeof(buffer); ret = WNetGetUniversalNameW(driveW, REMOTE_NAME_INFO_LEVEL, buffer, &info_size); - todo_wine{ + if(drive_type == DRIVE_REMOTE) ok(ret == WN_NO_ERROR, "WNetGetUniversalNameW failed: %08lx\n", ret); else ok(ret == WN_NOT_CONNECTED || ret == WN_NO_NET_OR_BAD_PATH, "(%s) WNetGetUniversalNameW gave wrong error: %lu\n", driveA, ret); - } + ok(info_size == sizeof(buffer), "Got wrong size: %lu\n", info_size); } } diff --git a/dlls/mpr/wnet.c b/dlls/mpr/wnet.c index 7c5b576598b..240b388fba4 100644 --- a/dlls/mpr/wnet.c +++ b/dlls/mpr/wnet.c @@ -34,6 +34,7 @@ #include "ddk/mountmgr.h" #include "wine/debug.h" #include "mprres.h" +#include "shlwapi.h" #include "wnetpriv.h"
WINE_DEFAULT_DEBUG_CHANNEL(mpr); @@ -2483,10 +2484,9 @@ DWORD WINAPI WNetGetConnectionW( LPCWSTR lpLocalName, case DRIVE_FIXED: case DRIVE_CDROM: TRACE("file is local\n"); + default: ret = WN_NOT_CONNECTED; break; - default: - ret = WN_BAD_LOCALNAME; } } else @@ -2528,44 +2528,169 @@ DWORD WINAPI WNetSetConnectionW( LPCWSTR lpName, DWORD dwProperty, DWORD WINAPI WNetGetUniversalNameA ( LPCSTR lpLocalPath, DWORD dwInfoLevel, LPVOID lpBuffer, LPDWORD lpBufferSize ) { - DWORD err, size; + DWORD err;
- FIXME( "(%s, 0x%08lX, %p, %p): stub\n", - debugstr_a(lpLocalPath), dwInfoLevel, lpBuffer, lpBufferSize); + LPWSTR wLocalPath = NULL;
- switch (dwInfoLevel) + LPVOID wBuffer = NULL; + DWORD wBufferSize = 1024; + + UNIVERSAL_NAME_INFOW *uniInfoW = NULL; + REMOTE_NAME_INFOW *remInfoW = NULL; + + DWORD outSize; + + TRACE("(%s, %ld, %p, %p)\n", debugstr_a(lpLocalPath), dwInfoLevel, + lpBuffer, lpBufferSize); + + // Check that buffer size is provided + if (lpBufferSize == NULL) { - case UNIVERSAL_NAME_INFO_LEVEL: + err = WN_BAD_POINTER; + goto out; + } + + // Prepare parameters + wLocalPath = strdupAtoW(lpLocalPath); + if (!wLocalPath) { - LPUNIVERSAL_NAME_INFOA info = lpBuffer; + err = WN_OUT_OF_MEMORY; + goto out; + }
- if (GetDriveTypeA(lpLocalPath) != DRIVE_REMOTE) + // Get wide universal name + wBuffer = HeapAlloc(GetProcessHeap(), 0, wBufferSize); + err = WNetGetUniversalNameW(wLocalPath, dwInfoLevel, wBuffer, &wBufferSize); + if (err == WN_MORE_DATA) + { + wBuffer = HeapReAlloc(GetProcessHeap(), 0, wBuffer, wBufferSize); + if (!wBuffer) { - err = ERROR_NOT_CONNECTED; - break; + err = WN_OUT_OF_MEMORY; + goto out; }
- size = sizeof(*info) + lstrlenA(lpLocalPath) + 1; - if (*lpBufferSize < size) + err = WNetGetUniversalNameW(wLocalPath, dwInfoLevel, wBuffer, &wBufferSize); + } + + // Check for errors + if (err != WN_NO_ERROR) + goto out; + + // Check for invalid parameters + if (lpBuffer == NULL) + { + err = WN_BAD_POINTER; + goto out; + } + else if (*lpBufferSize == 0) + { + err = WN_BAD_VALUE; + goto out; + } + + // Estimate size needed to convert result + uniInfoW = wBuffer; + remInfoW = wBuffer; + + switch (dwInfoLevel) + { + case UNIVERSAL_NAME_INFO_LEVEL: { - err = WN_MORE_DATA; + outSize = sizeof(UNIVERSAL_NAME_INFOA); + outSize += WideCharToMultiByte(CP_ACP, 0, + uniInfoW->lpUniversalName, -1, NULL, 0, NULL, NULL); + break; } - info->lpUniversalName = (char *)info + sizeof(*info); - lstrcpyA(info->lpUniversalName, lpLocalPath); - err = WN_NO_ERROR; - break; + case REMOTE_NAME_INFO_LEVEL: + outSize = sizeof(REMOTE_NAME_INFOA); + outSize += WideCharToMultiByte(CP_ACP, 0, + remInfoW->lpUniversalName, -1, NULL, 0, NULL, NULL); + + outSize += WideCharToMultiByte(CP_ACP, 0, + remInfoW->lpConnectionName, -1, NULL, 0, NULL, NULL); + + outSize += WideCharToMultiByte(CP_ACP, 0, + remInfoW->lpRemainingPath, -1, NULL, 0, NULL, NULL); + + break; + + default: + err = WN_BAD_LEVEL; + goto out; + break; } - case REMOTE_NAME_INFO_LEVEL: - err = WN_NOT_CONNECTED; - break;
- default: - err = WN_BAD_VALUE; - break; + // Make sure we have enough output buffer space + if (*lpBufferSize < outSize) + { + *lpBufferSize = outSize; + err = WN_MORE_DATA; + goto out; }
- SetLastError(err); + // Convert result + switch (dwInfoLevel) + { + case UNIVERSAL_NAME_INFO_LEVEL: + { + UNIVERSAL_NAME_INFOA *out = lpBuffer; + + // Convert universal name + out->lpUniversalName = (LPSTR)(out + 1); + WideCharToMultiByte(CP_ACP, 0, uniInfoW->lpUniversalName, -1, + out->lpUniversalName, (outSize - sizeof(UNIVERSAL_NAME_INFOA)), + NULL, NULL); + + break; + } + case REMOTE_NAME_INFO_LEVEL: + { + REMOTE_NAME_INFOA *out = lpBuffer; + + int wrote = 0; + int remains = outSize - sizeof(REMOTE_NAME_INFOA); + + // Convert universal name + out->lpUniversalName = (LPSTR)(out + 1); + wrote = WideCharToMultiByte(CP_ACP, 0, remInfoW->lpUniversalName, -1, + out->lpUniversalName, remains, NULL, NULL); + + remains -= wrote; + + // Convert connection name + out->lpConnectionName = out->lpUniversalName + wrote; + wrote = WideCharToMultiByte(CP_ACP, 0, remInfoW->lpConnectionName, -1, + out->lpConnectionName, remains, NULL, NULL); + + remains -= wrote; + + // Convert remaining path + out->lpRemainingPath = out->lpConnectionName + wrote; + wrote = WideCharToMultiByte(CP_ACP, 0, remInfoW->lpRemainingPath, -1, + out->lpRemainingPath, remains, NULL, NULL); + + remains -= wrote; + break; + } + default: + err = WN_BAD_LEVEL; + goto out; + break; + } + +out: + if (wBuffer) + HeapFree(GetProcessHeap(), 0, wBuffer); + + if (wLocalPath) + HeapFree(GetProcessHeap(), 0, wLocalPath); + + if (err != WN_NO_ERROR) + SetLastError(err); + + TRACE("Returning %ld\n", err); return err; }
@@ -2575,45 +2700,160 @@ DWORD WINAPI WNetGetUniversalNameA ( LPCSTR lpLocalPath, DWORD dwInfoLevel, DWORD WINAPI WNetGetUniversalNameW ( LPCWSTR lpLocalPath, DWORD dwInfoLevel, LPVOID lpBuffer, LPDWORD lpBufferSize ) { - DWORD err, size; + DWORD err = WN_NO_ERROR;
- FIXME( "(%s, 0x%08lX, %p, %p): stub\n", - debugstr_w(lpLocalPath), dwInfoLevel, lpBuffer, lpBufferSize); + WCHAR drive[3] = {'\0'}; + LPCWSTR remainingPath = NULL;
- switch (dwInfoLevel) + int tries = 2; + + LPWSTR remoteName = NULL; + DWORD remoteNameSize = MAX_PATH; + + DWORD uniNameSize; + DWORD outSize; + + TRACE("(%s, %ld, %p, %p)\n", debugstr_w(lpLocalPath), dwInfoLevel, + lpBuffer, lpBufferSize); + + // Check that buffer size is provided + if (lpBufferSize == NULL) { - case UNIVERSAL_NAME_INFO_LEVEL: + err = WN_BAD_POINTER; + goto out; + } + + // Extract local path drive letter and remaining path + if (lpLocalPath) { - LPUNIVERSAL_NAME_INFOW info = lpBuffer; + drive[0] = lpLocalPath[0]; + drive[1] = lpLocalPath[1]; + + remainingPath = lpLocalPath + 2; + }
- if (GetDriveTypeW(lpLocalPath) != DRIVE_REMOTE) + // Try getting remote name for local device + while (tries) + { + tries--; + + remoteName = HeapAlloc(GetProcessHeap(), 0, remoteNameSize); + if (!remoteName) { - err = ERROR_NOT_CONNECTED; + err = WN_OUT_OF_MEMORY; break; }
- size = sizeof(*info) + (lstrlenW(lpLocalPath) + 1) * sizeof(WCHAR); - if (*lpBufferSize < size) + err = WNetGetConnectionW(drive, remoteName, &remoteNameSize); + if (err == WN_MORE_DATA) { - *lpBufferSize = size; - err = WN_MORE_DATA; - break; + HeapFree(GetProcessHeap(), 0, remoteName); + remoteName = NULL; + continue; } - info->lpUniversalName = (LPWSTR)((char *)info + sizeof(*info)); - lstrcpyW(info->lpUniversalName, lpLocalPath); - err = WN_NO_ERROR; - break; - } - case REMOTE_NAME_INFO_LEVEL: - err = WN_NO_NETWORK; + break; + }; + + // Check for errors + if (err != WN_NO_ERROR) + goto out;
- default: + // Check for invalid parameters + if (lpBuffer == NULL) + { + err = WN_BAD_POINTER; + goto out; + } + else if (*lpBufferSize == 0) + { err = WN_BAD_VALUE; - break; + goto out; }
- if (err != WN_NO_ERROR) SetLastError(err); + // Calculate required size for output + uniNameSize = sizeof(WCHAR) * + (lstrlenW(remoteName) + lstrlenW(remainingPath) + 1 /*NULL*/); + + switch (dwInfoLevel) + { + case UNIVERSAL_NAME_INFO_LEVEL: + outSize = sizeof(UNIVERSAL_NAME_INFOW) + uniNameSize; + break; + + case REMOTE_NAME_INFO_LEVEL: + outSize = sizeof(REMOTE_NAME_INFOW) + uniNameSize; + outSize += sizeof(WCHAR) * (lstrlenW(remoteName) + 1 /*NULL*/); + outSize += sizeof(WCHAR) * (lstrlenW(remainingPath) + 1 /*NULL*/); + break; + + default: + err = WN_BAD_LEVEL; + goto out; + break; + } + + // Check if we have enough result buffer space + if (*lpBufferSize < outSize) + { + *lpBufferSize = outSize; + err = WN_MORE_DATA; + goto out; + } + + // Proceed to copying + switch (dwInfoLevel) + { + case UNIVERSAL_NAME_INFO_LEVEL: + { + UNIVERSAL_NAME_INFOW *out = (UNIVERSAL_NAME_INFOW *)lpBuffer; + + // Copy universal name + out->lpUniversalName = (LPWSTR)(out + 1); + wnsprintfW(out->lpUniversalName, (uniNameSize / sizeof(WCHAR)), + L"%ls%ls", remoteName, remainingPath); + + break; + } + case REMOTE_NAME_INFO_LEVEL: + { + REMOTE_NAME_INFOW *out = (REMOTE_NAME_INFOW *)lpBuffer; + + // Copy universal name + out->lpUniversalName = (LPWSTR)(out + 1); + wnsprintfW(out->lpUniversalName, (uniNameSize / sizeof(WCHAR)), + L"%ls%ls", remoteName, remainingPath); + + // Copy connection name + out->lpConnectionName = + out->lpUniversalName + lstrlenW(out->lpUniversalName) + 1; + + wnsprintfW(out->lpConnectionName, (lstrlenW(remoteName) + 1 /*NULL*/), + L"%ls", remoteName); + + // Copy remaining path + out->lpRemainingPath = + out->lpConnectionName + lstrlenW(out->lpConnectionName) + 1; + + wnsprintfW(out->lpRemainingPath, (lstrlenW(remainingPath) + 1 /*NULL*/), + L"%ls", remainingPath); + + break; + } + default: + err = WN_BAD_LEVEL; + goto out; + break; + } + +out: + if (remoteName) + HeapFree(GetProcessHeap(), 0, remoteName); + + if (err != WN_NO_ERROR) + SetLastError(err); + + TRACE("Returning %ld\n", err); return err; }
Is there an application that needs this?
On Thu Oct 5 09:31:13 2023 +0000, Zebediah Figura wrote:
Is there an application that needs this?
Yes, actually, we have a number of windows application that use WNet API to connect/disconnect network disks, and they also use WNetGetUniversalName*() functions to detect whether the disk is remote, and if it is, to check its unc path.