Introduction
This is my first article on CodeProject. So please don't expect too much. ;-)
Given scenario
An old workstation running Windows 2000 acts as a "file server". Users can access shared folders over the network by connecting using a username and password. Unfortunately, in a workgroup environment, there is no built-in way to change the passwords of these local users from a remote machine.
Tasks to solve
The described problem can be divided into three main parts:
- How to get a list of all the computers in our workgroup?
- How to get a list of all users from a specific computer?
- How to change a user's password on a remote computer?
The API is your friend
In the MSDN are three corresponding network management functions mentioned. Their usage is well explained, so I won't recite the MSDN.
NetServerEnum
NetQueryDisplayInformation
(see: How to get a list of users from a server)NetUserChangePassword
But the simple NetUserChangePassword
function prototype should be self-explanatory.
DWORD NetUserChangePassword(
LPCWSTR domainname,
LPCWSTR username,
LPCWSTR oldpassword,
LPCWSTR newpassword
);
Naming conventions
The different strings (e.g., usernames, passwords) are limited to a certain number of characters. It seems that these limitations are a relic from the NT LAN Manager.
Name | Length |
Domain/Workgroup | 15 |
Server | 15 |
User | 20 |
Password | 14 |
Security concerns
NetQueryDisplayInformation
requires anonymous access to the remote machine. Furthermore, an established NULL session connection (e.g., to $IPC) is required.
Using the code
The server and the user enumeration are wrapped into several classes.
Iterating through the result list shouldn't be a big deal.
NetObjectEnumerator *noe =
new [ NetServerEnumerator(Domain) | NetUserEnumerator(Server) ] ;
if(noe->GetError() == ERROR_SUCCESS){
while(noe->HasNext()){
NetObject *no = noe->GetNext();
... = no->name;
}
}
else
... = NetObjectEnumerator::GetErrorMessage(noe->GetError());
Error codes and message strings
The FormatMessage
function maps an error code to a message. For network management errors, the netmsg.dll module is used to look up the specified errors.
CString GetErrorMessage(DWORD dwLastError){
HMODULE hModule = NULL;
DWORD dwFormatFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM;
LPTSTR lpMessageBuffer = NULL;
if((dwLastError >= NERR_BASE) && (dwLastError <= MAX_NERR)){
hModule = LoadLibraryEx(_T("netmsg.dll"), NULL, LOAD_LIBRARY_AS_DATAFILE);
if(hModule != NULL)
dwFormatFlags |= FORMAT_MESSAGE_FROM_HMODULE;
}
FormatMessage(
dwFormatFlags,
hModule,
dwLastError,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMessageBuffer,
0,
NULL);
CString szMessage;
szMessage.Format(_T("%s (%u)"), lpMessageBuffer, dwLastError);
LocalFree(lpMessageBuffer);
if(hModule != NULL)
FreeLibrary(hModule);
return szMessage;
}
Credits
Credits go to Chris Maunder for his great HyperLink static control.
Notice
The application has not been tested in a domain environment. Any volunteers?
Please feel free to send your feedback to me via email. Bug reports, spelling corrections, opinions, and comments would be much appreciated.
History
- 05/16/2006:
- Original article published.
- Version 1.0.0.1 released
The newest Password Changer version is also available on my website.