Referring documentation: https://learn.microsoft.com/en-us/windows/win32/api/lmwksta/nf-lmwksta-netwkstauserenum
Steps to reproduce the bug:
- you need a server with many simultaneously concurrent logged users
- call NetWkstaUserEnum, with prefmaxlen set to a size in bytes able to contain a partial set of users. For example if you have 100 users, and a user structure occupy 100 bytes, set to 2000 bytes to return 20 user structures.
- NetWkstaUserEnum returns ERROR_MORE_DATA, and sets resumeHandle to a value to be passed again to retrieve the rest of users.
- resumeHandle value is wrong and NetWkstaUserEnum returns the same set of users
- Loop would be blocked in an infinte loop without ever be able to retrieve the complete list of users.
Simple explanation:
The internal implementation of NetWkstaUserEnum relies on
MSV1_0_ENUMUSERS_RESPONSE::EnumHandles that retrieves users in descending order, but then WsEnumUserInfo assumes users in ascending order.
You can find a detailed explanation of this bug here: https://stackoverflow.com/a/79271924/5081328
If you don't have a server with many users, you can try by setting a small buffer just be able hold one user, for example 100 bytes, assuming is enough to hold at least one user.
The code to reproduce the bug is the same as Microsoft example:
#ifndef UNICODE
#define UNICODE
#endif
#pragma comment(lib, "netapi32.lib")
#include <stdio.h>
#include <assert.h>
#include <windows.h>
#include <lm.h>
int wmain(int argc, wchar_t *argv[])
{
LPWKSTA_USER_INFO_0 pBuf = NULL;
LPWKSTA_USER_INFO_0 pTmpBuf;
DWORD dwLevel = 0;
DWORD dwPrefMaxLen = 100;
DWORD dwEntriesRead = 0;
DWORD dwTotalEntries = 0;
DWORD dwResumeHandle = 0;
DWORD i;
DWORD dwTotalCount = 0;
NET_API_STATUS nStatus;
LPWSTR pszServerName = NULL;
if (argc > 2)
{
fwprintf(stderr, L"Usage: %s [\\\\ServerName]\n", argv[0]);
exit(1);
}
// The server is not the default local computer.
//
if (argc == 2)
pszServerName = argv[1];
fwprintf(stderr, L"\nUsers currently logged on %s:\n", pszServerName);
//
// Call the NetWkstaUserEnum function, specifying level 0.
//
do // begin do
{
nStatus = NetWkstaUserEnum( pszServerName,
dwLevel,
(LPBYTE*)&pBuf,
dwPrefMaxLen,
&dwEntriesRead,
&dwTotalEntries,
&dwResumeHandle);
//
// If the call succeeds,
//
if ((nStatus == NERR_Success) || (nStatus == ERROR_MORE_DATA))
{
if ((pTmpBuf = pBuf) != NULL)
{
//
// Loop through the entries.
//
for (i = 0; (i < dwEntriesRead); i++)
{
assert(pTmpBuf != NULL);
if (pTmpBuf == NULL)
{
//
// Only members of the Administrators local group
// can successfully execute NetWkstaUserEnum
// locally and on a remote server.
//
fprintf(stderr, "An access violation has occurred\n");
break;
}
//
// Print the user logged on to the workstation.
//
wprintf(L"\t-- %s\n", pTmpBuf->wkui0_username);
pTmpBuf++;
dwTotalCount++;
}
}
}
//
// Otherwise, indicate a system error.
//
else
fprintf(stderr, "A system error has occurred: %d\n", nStatus);
//
// Free the allocated memory.
//
if (pBuf != NULL)
{
NetApiBufferFree(pBuf);
pBuf = NULL;
}
}
//
// Continue to call NetWkstaUserEnum while
// there are more entries.
//
while (nStatus == ERROR_MORE_DATA); // end do
//
// Check again for allocated memory.
//
if (pBuf != NULL)
NetApiBufferFree(pBuf);
//
// Print the final count of workstation users.
//
fprintf(stderr, "\nTotal of %d entries enumerated\n", dwTotalCount);
return 0;
}
This issue has been around for more than 20 years affecting windows since windows server 2003.
Now I don't expect a quick solution by Microsoft, even if they will solve in next windows version I will still need to rely on other ways to retrieve currently logged users in my application, because I will still need to support old windows versions for years.
But I still post here hoping it will get the deserved attention by Microsoft for a serious issue that has been around by so many years.
The bug has already been reported as feedback on Microsoft documentation page, on Microsoft Feedback hub and here : https://github.com/microsoft/WindowsAppSDK/issues/4955
Let me know if there is a better channel to report bugs in windows OS API,