I'm trying to get the int value of a processes' memory address.
I have been able to write to the memory address can't read the memory address value.
#include <iostream>
#include <windows.h>
#include <chrono>
#include <ctime>
using namespace std;
int main(void) {
int nVal = 40000;
HWND hWnd = FindWindowA(0, "Crusader");
if(hWnd == 0){
cerr << "Could not find window." << endl;
} else {
DWORD PID;
GetWindowThreadProcessId(hWnd, &PID);
HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, false, PID);
if(!hProc) {
cerr << "Cannot open process." << endl;
} else {
int buffer = 0;
int stat = WriteProcessMemory(hProc, (LPVOID)0x0115FCF8, &nVal, (DWORD)sizeof(nVal), NULL);
SIZE_T NumberOfBytesToRead = sizeof(buffer); //this is equal to 4
SIZE_T NumberOfBytesActuallyRead;
int stat2 = ReadProcessMemory(hProc, (LPVOID)0x0115FCF8, &buffer, NumberOfBytesToRead, &NumberOfBytesActuallyRead);
std:cout<< *stat2;
/* if(stat > 0){
clog << "Memory written to process." << endl;
} else {
cerr << "Memory couldn't be written to process." << endl;
}
*/
CloseHandle(hProc);
cin.get();
}
}
return 0;
}
I can assign a value to the memory address, but reading the memory address returns 1.
How can i get the value of the address
stat and stat2 represent the return value of the Read/Write ProcessMemory calls, these are used in error checking. You appear to be thinking they are the values of the addresses you're trying to read and write.
Assuming your target process is x86, you're compiling for x86, you're running as admin and all your addresses are correct then just change that last line to:
std:cout<< buffer;
This should output the integer representation of the data in 0x0115FCF8
If 0x0115FCF8 is data, not code this should work fine. If it's code, the page will not have write permissions and you will have to change the page permission with VirtualProtectEx to include WRITE.
Related
I'd like to transmit a shared_ptr object via boost asio from a client to a server. Here is my code:
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/asio.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <chrono>
#include <iostream>
#include <sstream>
#include <string>
#include <thread>
using namespace std;
class Message {
public:
Message() {
}
virtual ~Message() {
}
string text;
private:
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive &ar, const unsigned int version) {
ar &text;
}
};
BOOST_CLASS_EXPORT(Message)
void runClient() {
// Give server time to startup
this_thread::sleep_for(chrono::milliseconds(3000));
boost::asio::ip::tcp::iostream stream("localhost", "3000");
boost::archive::text_oarchive archive(stream);
for (int i = 0; i < 10; i++) {
std::shared_ptr<Message> dl = std::make_shared<Message>();
stringstream ss;
ss << "Hello " << i;
dl->text = ss.str();
archive << dl;
}
stream.close();
cout << "Client shutdown" << endl;
}
void handleIncommingClientConnection(boost::asio::ip::tcp::acceptor &acceptor) {
boost::asio::ip::tcp::iostream stream;
acceptor.accept(*stream.rdbuf());
boost::archive::text_iarchive archive(stream);
while (true) {
std::shared_ptr<Message> m;
try {
archive >> m;
cout << m->text << endl;
} catch (std::exception &ex) {
cout << ex.what() << endl;
if (stream.eof()) {
cout << "eof" << endl;
stream.close();
cout << "Server: shutdown client handling..." << endl;
break;
} else
throw ex;
}
}
}
void runServer() {
boost::asio::io_service ios;
boost::asio::ip::tcp::endpoint endpoint = boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), 3000);
boost::asio::ip::tcp::acceptor acceptor(ios, endpoint);
handleIncommingClientConnection(acceptor);
}
int main(int argc, char **argv) {
thread clientThread(runClient);
thread serverThread(runServer);
clientThread.join();
serverThread.join();
return 0;
}
Here is the program output:
Hello 0
Hello 1
Hello 2
Hello 3
Hello 3
Hello 3
Hello 3
Hello 3
Client shutdown
Hello 3
Hello 3
input stream error
eof
Server: shutdown client handling...
I am expecting the following output:
Hello 0
Hello 1
Hello 2
Hello 3
Hello 4
Hello 5
Hello 6
Hello 7
Client shutdown
Hello 8
Hello 9
input stream error
eof
Server: shutdown client handling...
When changing the shared_ptr to a simple object (std::shared_ptr<Message> m; to Message m) everything works as expected. I want to stick to the shared_ptr. What do I need to change?
Serialization alone seems to work:
stringstream stream;
{
boost::archive::text_oarchive archive(stream);
std::shared_ptr<Message> dl = std::make_shared<Message>();
stringstream ss;
ss << "Hello World!";
dl->text = ss.str();
archive << dl;
}
{
boost::archive::text_iarchive archive(stream);
std::shared_ptr<Message> m;
archive >> m;
cout << m->text << endl;
}
Output: Hello World!
The issues you're encountering are due to object tracking done by Boost.Serialization.
Depending on how the class is used and other factors, serialized
objects may be tracked by memory address. This prevents the same
object from being written to or read from an archive multiple times.
These stored addresses can also be used to delete objects created
during a loading process that has been interrupted by throwing of an
exception.
The documentation actually foreshadows this specific issue happening:
This could cause problems in progams[sic] where the copies of different
objects are saved from the same address.
Furthermore, the Class Serialization Traits documentation on object tracking tells us that in this particular situation, object tracking is enabled:
Default tracking traits are:
For primitive, track_never.
For pointers, track_never. That is, addresses of addresses are not tracked by default.
All current serialization wrappers such as boost::serialization::nvp, track_never.
For all other types, track_selectively. That is addresses of serialized objects are tracked if and only if one or more of the
following is true:
an object of this type is anywhere in the program serialized through a pointer.
the class is explicitly "exported" - see below.
the class is explicitly "registered" in the archive
Going back to your situation -- in the client, due to how your loop body is written, the 5th (and following) Message instance were allocated at the same address as the 4th Message instance. You can verify this by inspecting the values of dl.get() in each iteration. (In my tests on coliru, all of the instances were allocated at the same address, so YMMV).
Due to how object tracking works, all those shared_ptr instances were considered to point to the same Message instance (even though you changed the value meanwhile -- the library does not expect this happening), so the additional occurrences were just serialized as additional references. Upon deserialization... to be honest this smells of memory leaks and/or dangling reference issues (opinion, haven't investigated this in detail).
Summed up, the main issue with the code as shown is that it breaks a prerequisite of the serialization library, which is that you're serializing some constant state, and on deserialization you recreate that same state.
One way to address this would be to have an initialized std::vector of shared_ptr<Message> containing all the messages to transmit in this particular transaction. Similarly, you'd deserialize the whole vector on the other side. If you expect to have some persistent connection, then add framing to the protocol, with each frame containing an archive that contains one sequence of messages.
Minimal code modifications to make this work -- add include
#include <boost/serialization/vector.hpp>
Change runClient() as such:
void runClient() {
// Give server time to startup
this_thread::sleep_for(chrono::milliseconds(3000));
boost::asio::ip::tcp::iostream stream("127.0.0.1", "3000");
std::vector<std::shared_ptr<Message>> messages;
for (int i = 0; i < 10; i++) {
std::shared_ptr<Message> dl = std::make_shared<Message>();
stringstream ss;
ss << "Hello " << i;
dl->text = ss.str();
messages.emplace_back(dl);
}
boost::archive::text_oarchive archive(stream);
archive << messages;
stream.close();
cout << "Client shutdown" << endl;
}
And change handleIncommingClientConnection(...) as such:
void handleIncommingClientConnection(boost::asio::ip::tcp::acceptor &acceptor) {
boost::asio::ip::tcp::iostream stream;
acceptor.accept(*stream.rdbuf());
boost::archive::text_iarchive archive(stream);
while (true) {
try {
std::vector<std::shared_ptr<Message>> messages;
archive >> messages;
for (auto const& m : messages) {
cout << m->text << endl;
}
} catch (std::exception &ex) {
cout << ex.what() << endl;
if (stream.eof()) {
cout << "eof" << endl;
stream.close();
cout << "Server: shutdown client handling..." << endl;
break;
} else
throw ex;
}
}
}
NB: This doesn't add any support for multiple frames -- the client is expected to close the connection after it sent one vector of messages, otherwise the behaviour is undefined.
Sample on Coliru
Further resources:
boost serialization multiple objects
I am trying to use boost to communicate serially between my desktop and an arduino. In arduino space, I can ask the serial port whether or not there are bytes available before trying to perform a read.
I am having trouble finding the equivalent for boost::asio::serial_port.
While Boost.Asio does not provide direct support for this, one can still accomplish this by using serial port's native_handle() with system specific calls. Consult the system's documentation to determine how to query for the available bytes ready to be read, but it is often ioctl(..., FIONREAD, ...) on Linux, and ClearCommError() on Windows.
Here is a complete minimal example that uses system specific calls to get the number of bytes available. The example program will continue to query the serial port until there are greater than 20 bytes available, at which point it will read all but 5 bytes:
#include <iostream>
#include <vector>
#include <boost/asio.hpp>
#include <boost/thread.hpp>
/// #brief Returns the number of bytes available for reading from a serial
/// port without blocking.
std::size_t get_bytes_available(
boost::asio::serial_port& serial_port,
boost::system::error_code& error)
{
error = boost::system::error_code();
int value = 0;
#if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__)
COMSTAT status;
if (0 != ::ClearCommError(serial_port.lowest_layer().native_handle(),
NULL, &status))
{
value = status.cbInQue;
}
// On error, set the error code.
else
{
error = boost::system::error_code(::GetLastError(),
boost::asio::error::get_system_category());
}
#else // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__)
if (0 == ::ioctl(serial_port.lowest_layer().native_handle(),
FIONREAD, &value))
{
error = boost::system::error_code(errno,
boost::asio::error::get_system_category());
}
#endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__)
return error ? static_cast<std::size_t>(0)
: static_cast<size_t>(value);
}
/// #brief Returns the number of bytes available for reading from a serial
/// port without blocking. Throws on error.
std::size_t get_bytes_available(boost::asio::serial_port& serial_port)
{
boost::system::error_code error;
std::size_t bytes_available = get_bytes_available(serial_port, error);
if (error)
{
boost::throw_exception((boost::system::system_error(error)));
}
return bytes_available;
}
int main(int argc, char* argv[])
{
if (argc < 2)
{
std::cerr << "Usage: " << argv[0] << " <device_name>" << std::endl;
return 1;
}
// Create all I/O objects.
boost::asio::io_service io_service;
boost::asio::serial_port serial_port(io_service, argv[1]);
// Continue quering the serial port until at least 20 bytes are available
// to be read.
std::size_t bytes_available = 0;
while (bytes_available < 20)
{
bytes_available = get_bytes_available(serial_port);
std::cout << "available: " << bytes_available << std::endl;
boost::this_thread::sleep_for(::boost::chrono::seconds(3));
}
// Read all but 5 available bytes.
std::vector<char> buffer(bytes_available - 5);
std::size_t bytes_transferred =
read(serial_port, boost::asio::buffer(buffer));
bytes_available = get_bytes_available(serial_port);
// Print results.
std::cout << "Read " << bytes_transferred << " bytes\n";
std::cout.write(&buffer[0], bytes_transferred);
std::cout << "\navailable: " << bytes_available << std::endl;
}
Create virtual serial ports with socat:
$ socat -d -d PTY: PTY
2015/02/01 21:12:31 socat[3056] N PTY is /dev/pts/2
2015/02/01 21:12:31 socat[3056] N PTY is /dev/pts/3
2015/02/01 21:12:31 socat[3056] N starting data transfer loop
with FDs [3,3] and [5,5]
After starting the program in one terminal, I write to /dev/pts/3 in another terminal:
$ echo -n "This is" > /dev/pts/3
$ echo -n " an example" > /dev/pts/3
$ echo -n " with asio." > /dev/pts/3
And the resulting output from the program:
$ ./a.out /dev/pts/2
available: 0
available: 7
available: 18
available: 29
Read 24 bytes
This is an example with
available: 5
I don't know of such a thing in asio, but as comments above have already stated, you don't really need it. I have an example of how to use boost asio serial at:
https://github.com/cdesjardins/ComBomb/blob/master/TargetConnection/TgtSerialConnection.cpp
It uses async_read_some to fill a buffer with serial data, the buffer data is then queued up for other parts of the program to process.
I am trying to get the VolumeDetails of my WINDOWS system- Drive label plus its respective Volume Serial number. I've tried since an hour and built a code which gone wrong in syntax. At present I am getting the following error with it-
error C2664: 'GetVolumeInformationW' : cannot convert parameter 1 from 'char []' to 'LPCWSTR'
Here is my code:
// getVolDrive.cpp : Defines the entry point for the console application.
#include "stdafx.h"
#include <iostream>
#include <windows.h>
#include <direct.h>
#include <stdio.h>
#include <conio.h>
#include <tchar.h>
#include <sstream>
#include <string>
#include <ctype.h>
#include <algorithm>
using namespace std;
//wchar_t mydrives[5];// = " A: ";
char mydrives[] = " A: ";
string retVolSno(char drives[]) //wchar_t drives[]
{
DWORD dwSerial;
stringstream ss;
cout<<drives<<endl;
if(!GetVolumeInformation(drives, NULL, 0, &dwSerial, NULL, NULL, NULL, 0))
{
ss<<"Error: "<<GetLastError();
}
else
{
ss<<hex<<dwSerial;
}
return ss.str();
}
int _tmain(int argc, _TCHAR* argv[])
{
string cVolSno;
ULONG DriveMask = _getdrives();
if(DriveMask == 0)
printf("_getdrives() failed with failure code: %d\n", GetLastError());
else
{
printf("This machine has the following logical drives:\n");
while (DriveMask)
{
cout << "In While" << endl;
if(DriveMask & 1)
printf("%s", mydrives);
wcout << mydrives << endl;
cVolSno = retVolSno(mydrives);
cout<<cVolSno<<endl;
++mydrives[1];
DriveMask >>= 1;
}
}
//std::transform(cVolSno.begin(), cVolSno.end(),cVolSno.begin(), ::toupper);
//cout<<cVolSno<<endl;
_getch();
return 0;
}
I've also tried replacing char with wchar_t, I didn't got any build errors, but while executing the application, got Error Code 3- Path not found!.
CODE MODIFIED:
// getVolDrive.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <iostream>
#include <windows.h>
#include <direct.h>
#include <stdio.h>
#include <conio.h>
#include <tchar.h>
#include <sstream>
#include <string>
#include <ctype.h>
#include <algorithm>
using namespace std;
//wchar_t mydrives[5];// = " A: ";
char mydrives[] = " A:\\\\ ";
string retVolSno(char drives[]) //wchar_t drives[]
{
DWORD dwSerial;
stringstream ss;
wchar_t text[10];
mbstowcs(text,drives,100); //strlen(drives)+1
LPWSTR ptr = text;
if(!GetVolumeInformation(ptr, NULL, 0, &dwSerial, NULL, NULL, NULL, 0))
{
ss<<"Error: "<<GetLastError();
}
else
{
ss<<hex<<dwSerial;
}
return ss.str();
}
int _tmain(int argc, _TCHAR* argv[])
{
string cVolSno;
ULONG DriveMask = _getdrives();
if(DriveMask == 0)
printf("_getdrives() failed with failure code: %d\n", GetLastError());
else
{
printf("This machine has the following logical drives:\n");
while (DriveMask)
{
if(DriveMask & 1)
printf("%s \n", mydrives);
cVolSno = retVolSno(mydrives);
std::transform(cVolSno.begin(), cVolSno.end(),cVolSno.begin(), ::toupper);
cout<<cVolSno<<endl;
++mydrives[1];
DriveMask >>= 1;
}
}
//std::transform(cVolSno.begin(), cVolSno.end(),cVolSno.begin(), ::toupper);
//cout<<cVolSno<<endl;
_getch();
return 0;
}
OUTPUT:
This machine has the following logical drives:
ERROR: 123
ERROR: 123
C:\\
ERROR: 123
D:\\
ERROR: 123
E:\\
ERROR: 123
I see at least these main issues:
1) wchar_t is the right type because you're compiling for UNICODE, you can write generic code using TCHAR macro or explicitly declare your buffer as wchar_t but that's what to do.
2) You have that error because you're passing wrong path to GetVolumeInformation() (trailing backslash is required so A: must become A:\).
Moreover please note that you have a little bit more easy way to achieve same result, you can use GetLogicalDriveStrings() to directly get a NULL delimited string list. Split it using, for example, this (don't forget UNICODE) and use c_str() with each entry.
EDIT about your modified code:
Why you drive path is A:\\ (escaped to A:\\\\)? Just one trailing backslash is needed so mydrives has to be declared as:
wchar_t mydrives[] = L"A:\\";
EDIT 2: there are more errors in your code so I'll post a reviewed version. There are more things I'd change but I'll point out just what doesn't actually work.
Function retVolSno to read volume serial number. Original version were almost right, in your modified version you perform useless character conversion. What you had to do was just to accept a wchar_t drive path.
Global variable mydrives. You actually don't need any global variable for that. It must be wchar_t and space before/after path are useless. One trailing backslash is needed. Line where you increment character value (++mydrives[0];) must be changed accordingly (index 0 instead of 1).
Check for drive availability. After if(DriveMask & 1) you did forget { then you won't print drive name but you'll perform GetVolumeInformation() even on unavailable drives (error 123). That's why indentation is important...
You're mixing UNICODE/NOT UNICODE and C/C++ stuff. I strongly suggest you pick one of them and you keep it (C or C++? UNICODE or NOT UNICODE?). For example you used C function printf() to print stuff and you have both std::string and wchar_t things.
Let's put everything together to have a working version. First the function to read serial number given drive path:
wstring getVolumeSerialNumber(const wchar_t* drivePath)
{
DWORD dwSerial;
wstringstream ss;
if (!GetVolumeInformation(drivePath, NULL, 0, &dwSerial, NULL, NULL, NULL, 0))
ss << L"Error: " << GetLastError();
else
ss << hex << dwSerial;
return ss.str();
}
It's almost the same as your original version, just changed to work with UNICODE characters. Then main function that cycles through available drives and print out their serial number:
int _tmain(int argc, _TCHAR* argv[])
{
wchar_t drive[] = L"A:\\";
ULONG driveMask = _getdrives();
if (driveMask == 0)
wcout << L"_getdrives() failed with failure code: " << GetLastError() << endl;
else
{
wcout << L"This machine has the following logical drives:" << endl;
while (driveMask)
{
if (driveMask & 1)
{
wcout << drive << endl;
wcout << getVolumeSerialNumber(drive) << endl;
}
++drive[0];
driveMask >>= 1;
}
}
wcin.ignore();
return 0;
}
From the documentation , the first parameters should be with trailing slash if drive letter is passed.
lpRootPathName [in, optional]
A pointer to a string that contains the root directory of the volume to be described.
If this parameter is NULL, the root of the current directory is used.
A trailing backslash is required.
For example, you specify \\MyServer\MyShare as \\MyServer\MyShare\, or the C drive as C:\
I'm using ReadDirectoryChangesW to monitor a directory.
Here's my simple code
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <tchar.h>
#include <iostream>
#include <string>
#include <cwctype>
using namespace std;
wstring getname(FILE_NOTIFY_INFORMATION *tmp)
{
wstring s = L"";
for (int i = 0;i < tmp->FileNameLength / 2;i++)
s += tmp->FileName[i];
return s;
}
void _tmain(int argc, TCHAR *argv[])
{
HANDLE hDir;
char notify[1024];
DWORD cbBytes,i;
char AnsiChar[3];
wchar_t UnicodeChar[2];
LPTSTR path;
FILE_NOTIFY_INFORMATION *pnotify=(FILE_NOTIFY_INFORMATION *)notify;
FILE_NOTIFY_INFORMATION *tmp ;
// GetCurrentDirectory(MAX_PATH,path.GetBuffer(MAX_PATH+1));
wcout.imbue(locale("chs"));
path = argv[1];
hDir = CreateFile( path, FILE_LIST_DIRECTORY,
FILE_SHARE_READ |
FILE_SHARE_WRITE |
FILE_SHARE_DELETE, NULL,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS |
FILE_FLAG_OVERLAPPED, NULL);
wcout << L"===CreateFile complete===" << endl;
if (hDir == INVALID_HANDLE_VALUE)
{
wcout << L"invalid handle value" << endl;
return;
}
FILE_NOTIFY_INFORMATION buffer[1024];
FILE_NOTIFY_INFORMATION *pbuffer;
while (TRUE)
{
wcout << L"waiting..." << endl;
if(ReadDirectoryChangesW(hDir, &buffer, sizeof(buffer),
TRUE, FILE_NOTIFY_CHANGE_FILE_NAME| FILE_NOTIFY_CHANGE_LAST_WRITE,
&cbBytes, NULL, NULL))
{
pbuffer = buffer;
do{
tmp = pbuffer;
switch(tmp->Action)
{
case FILE_ACTION_ADDED:
wcout << L"Directory/File added - " << getname(tmp) << endl;
break;
case FILE_ACTION_REMOVED:
wcout << L"Directory/File removed - " << getname(tmp) << endl;
break;
case FILE_ACTION_MODIFIED:
wcout << L"Directory/File modfied - " << getname(tmp) << endl;
break;
case FILE_ACTION_RENAMED_OLD_NAME:
wcout << L"Directory/File old name - " << getname(tmp) << endl;
break;
case FILE_ACTION_RENAMED_NEW_NAME:
wcout << L"Directory/File new name - " << getname(tmp) << endl;
break;
default:
wcout << L"unknown action\n" << endl;
break;
}
pbuffer += pbuffer->NextEntryOffset;
}while(pbuffer->NextEntryOffset);
} else
{
wcout << "readChangesW failed now return" << endl;
return;
}
}
}
It looks fine, however, when I'm adding or deleting a large number of files in my directory, it will not report some of the changes, how can I fix this?
Try making your buffer bigger.
From the documentation for the ReadDirectoryChangesW function:
When you first call ReadDirectoryChangesW, the system allocates a buffer to store change information. This buffer is associated with the directory handle until it is closed and its size does not change during its lifetime. Directory changes that occur between calls to this function are added to the buffer and then returned with the next call. If the buffer overflows, the entire contents of the buffer are discarded and the lpBytesReturned parameter contains zero.
The buffer size that the system allocates is based on the size of the buffer you pass in. If you pass in a bigger size the system will allocate a bigger buffer to store changes that occur while you are processing the previous lot of changes, which means there's less chance of the buffer overflowing and those changes being lost.
alt text http://www.freeimagehosting.net/uploads/b350914deb.png
i want to retrieve the list of user and local service and network service
WMI has a Win32_UserAccount class, but enumerating it looks like it produces the same list as NetEnumUsers, which only produces (more or less) "Normal" accounts, not the built in security principals like "Local Service" and "Network Service".
You can retrieve everything with NetLocalGroupEnum and NetLocalGroupGetMembers, but you'd have to do it from something that lets you work with the Win32 API directly, not (at least AFAIK) via WMI. In case that's still useful, here's a bit of sample code that lists groups and members:
#define UNICODE
#include <windows.h>
#include <lmaccess.h>
#include <lmapibuf.h>
#include <iostream>
int main() {
LOCALGROUP_INFO_0 *l_info;
DWORD read;
DWORD total;
NetLocalGroupEnum(NULL,
0,
(unsigned char **)&l_info,
MAX_PREFERRED_LENGTH,
&read,
&total,
NULL);
std::wcout << L"Local Groups\n";
for (int i=0; i<read; i++) {
std::wcout << l_info[i].lgrpi0_name << std::endl;
LOCALGROUP_MEMBERS_INFO_1 *members;
DWORD entries, total_entries;
NetLocalGroupGetMembers(NULL,
l_info[i].lgrpi0_name,
1,
reinterpret_cast<BYTE **>(&members),
MAX_PREFERRED_LENGTH,
&entries,
&total_entries,
NULL);
for (int mem_num = 0; mem_num<entries; mem_num++)
std::wcout << L"\t" << members[mem_num].lgrmi1_name << L"\n";
NetApiBufferFree(members);
}
NetApiBufferFree(l_info);
GROUP_INFO_0 *g_info;
NetGroupEnum(NULL,
0,
(unsigned char **)&g_info,
MAX_PREFERRED_LENGTH,
&read,
&total,
NULL);
std::wcout << L"\nGlobal Groups\n";
for (i=0; i<read; i++)
std::wcout << g_info[i].grpi0_name << std::endl;
NetApiBufferFree(g_info);
return 0;
}
You can do this via WSH. Here is an example in JavaScript: http://www.winscripter.com/WSH/ADSI/56.aspx
and a sample in C#: http://www.geekzone.co.nz/chakkaradeep/3938
I found several answers on BING by searching win32 list user accounts
And finally a sample from Microsoft: http://gallery.technet.microsoft.com/ScriptCenter/en-us/827623f5-eb55-4035-8f57-25c4afb444cd