I'm having an issue reading the InstallTime value from the registry in Windows 11. I used this idea to read the value, but retCode gives me 2 (ERROR_FILE_NOT_FOUND). The value is there, and I guess there's no need for admin rights to read it. I'm using the following code snippet:
UINT64 RegGetQword(HKEY hKey, const std::wstring& subKey, const std::wstring& value)
{
UINT64 data{};
DWORD dataSize = sizeof(data);
LONG retCode = ::RegGetValue(hKey, subKey.c_str(), value.c_str(), RRF_RT_REG_QWORD, nullptr, &data, &dataSize);
if (retCode != ERROR_SUCCESS)
throw RegistryError{ "Cannot read QWORD from registry.", retCode };
return data;
}
int main(void)
{
UINT64 dwInst;
try
{ double nTimeExpr = (double)RegGetQword(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", L"InstallTime");
dwInst = _getSecondsFromEpoch(nTimeExpr)
}
catch (...)
{
dwInst = 0;
}
//... Do whatever needed with dwInst
return 0;
}
Related
I have a custom stream implementation that uses Win32 API functions like ::CreateFile2, ::ReadFile, ::WriteFile. Also the stream implements Flush and Truncate functions that are not supported by std::fstream (its flush() flushes its internal buffer, but not the operating system buffer):
class WinStream : public IoStream
{
public:
size_t Read(uint8_t* buffer, size_t count) override
{
const DWORD nNumberOfBytesToRead = static_cast<DWORD>(count);
assert(nNumberOfBytesToRead == count);
DWORD NumberOfBytesRead = 0;
Check(::ReadFile(m_hFile, buffer, nNumberOfBytesToRead, &NumberOfBytesRead, NULL) != FALSE);
return NumberOfBytesRead;
}
void Write(const uint8_t* buffer, size_t count) override
{
const DWORD nNumberOfBytesToWrite = static_cast<DWORD>(count);
assert(nNumberOfBytesToWrite == count);
DWORD NumberOfBytesWritten = 0;
if (::WriteFile(m_hFile, buffer, nNumberOfBytesToWrite, &NumberOfBytesWritten, NULL) == FALSE)
{
throw IoError(format()
<< _T("::WriteFile failed. This may indicate that the disk is full. Win32 Error: ")
<< ::GetLastError());
}
if (nNumberOfBytesToWrite != NumberOfBytesWritten)
{
throw IoError(format() << _T("Requested ") << nNumberOfBytesToWrite
<< _T(" bytes, but actually written ") << NumberOfBytesWritten << _T("."));
}
bool End() override
{
return GetFileSizeHelper() == GetFilePointerHelper();
}
void Seek(std::size_t pos, bool begin = true) override
{
LARGE_INTEGER li;
li.QuadPart = pos;
Check(::SetFilePointerEx(m_hFile, li, NULL, begin ? FILE_BEGIN : FILE_END) != INVALID_SET_FILE_POINTER);
}
void Move(std::ptrdiff_t offset) override
{
LARGE_INTEGER li;
li.QuadPart = offset;
Check(::SetFilePointerEx(m_hFile, li, NULL, FILE_CURRENT) != INVALID_SET_FILE_POINTER);
}
void Flush() override
{
Check(::FlushFileBuffers(m_hFile) != FALSE);
}
void Truncate() override
{
Check(::SetEndOfFile(m_hFile) != FALSE);
}
private:
void Check(bool success)
{
if (!success)
{
throw Win32Exception();
}
}
LONGLONG GetFileSizeHelper()
{
LARGE_INTEGER li;
li.QuadPart = 0;
Check(::GetFileSizeEx(m_hFile, &li) != FALSE);
return li.QuadPart;
}
LONGLONG GetFilePointerHelper()
{
LARGE_INTEGER liOfs = { 0 };
LARGE_INTEGER liNew = { 0 };
Check(::SetFilePointerEx(m_hFile, liOfs, &liNew, FILE_CURRENT) != INVALID_SET_FILE_POINTER);
return liNew.QuadPart;
}
FileHandle m_hFile;
}
inline UniqueFileHandle CreateUniqueFile(const String& file_name)
{
HANDLE hFile = ::CreateFile2(
file_name.c_str(),
GENERIC_READ | GENERIC_WRITE,
0, //FILE_SHARE_READ | FILE_SHARE_WRITE,
OPEN_ALWAYS,
NULL //&extendedParams
);
if (hFile == INVALID_HANDLE_VALUE)
{
DWORD dw_err = ::GetLastError();
throw IoError(format() << _T("Cannot open file ')" << file_name << "' for updating, error = " << dw_err));
}
return hFile;
}
What is the right (or modern) way to migrate this code to Linux? And what about Android, MacOS and iOS?
It should use non-buffered read/write functions.
As Homer512 said, you should use the POSIX file functions, like fopen, fwrite, fclose, and fread. You can find a reference for these functions here.
My personal preference for having the code work on many systems is using preprocessor directives, and having code for each os. A full list of os directives can be found here.
To separate the code for operating systems, you can do something like this:
#ifdef _WIN32
//Windows code here
#elifdef __APPLE__
//MacOS code here
#elifdef __linux__
//Linux code here
#endif
You can then use a specific set of functions, like open a file, write to a file, then define them on different systems using their specific ways. You can also easily extend this to other operating systems easily.
On running the following C++ code to extract entire registry sub-keys of HKEY_CURRENT_USER, I am getting RUN FAILED (exit value 1..) error in netbeans. Only one sub-key is being opened i.e the open() function runs once.
Link to RegOpenKeyEx function.
Any help would be highly appreciated.
#include <windows.h>
#include <iostream>
#include <string>
#define MAX_KEY_LENGTH 255
#define MAX_VALUE_NAME 16383
#define MAX_SUB_KEYS 10000
using namespace std;
void QueryKey(HKEY);
void open(HKEY,string [],int);
void open(HKEY hkey12,string subkey[],int m)
{
int u=0;
while(u<m)
{
HKEY hkey1;
const char* sub=subkey[u].c_str();
if( RegOpenKeyEx(hkey12,
sub,
0,
KEY_READ,
&hkey1) == ERROR_SUCCESS
)
{
QueryKey(hkey1);
}
else { cout<<"Subkey open failed "<<endl;}
RegCloseKey(hkey1);
u++;
}
}
int main()
{
HKEY hTestKey;
if( RegOpenKeyEx( HKEY_CURRENT_USER,
NULL,
0,
KEY_READ,
&hTestKey) == ERROR_SUCCESS
)
{
QueryKey(hTestKey);
}
RegCloseKey(hTestKey);
return 0;
}
void QueryKey(HKEY hKey) // This function fetches the actual parameters.
{
TCHAR achKey[MAX_KEY_LENGTH]; // buffer for subkey name
DWORD cbName; // size of name string
TCHAR achClass[MAX_PATH] = TEXT(""); // buffer for class name
DWORD cchClassName = MAX_PATH; // size of class string
DWORD cSubKeys=0; // number of subkeys
DWORD cbMaxSubKey; // longest subkey size
DWORD cchMaxClass; // longest class string
DWORD cValues=0; // number of values for key
DWORD cchMaxValue; // longest value name
DWORD cbMaxValueData; // longest value data
DWORD cbSecurityDescriptor; // size of security descriptor
FILETIME ftLastWriteTime; // last write time
DWORD i, retCode;
TCHAR achValue[MAX_VALUE_NAME];
DWORD valueData; // BYTE valueData[2048]; // buffer for value data
DWORD cchValue = MAX_VALUE_NAME; //pointer to size of value name
DWORD valueType; // buffer for value type
DWORD size=2049; // pointer to size of value data
// Get the class name and the value count.
retCode = RegQueryInfoKey(
hKey, // key handle
achClass, // buffer for class name
&cchClassName, // size of class string
NULL, // reserved
&cSubKeys, // number of subkeys
&cbMaxSubKey, // longest subkey size
&cchMaxClass, // longest class string
&cValues, // number of values for this key
&cchMaxValue, // longest value name
&cbMaxValueData, // longest value data
&cbSecurityDescriptor, // security descriptor
&ftLastWriteTime); // last write time
string subkey[cSubKeys];
string values[cValues];
int typev[cValues];
// Extract the subkeys, until RegEnumKeyEx fails.
if (cSubKeys)
{
for (i=0; i<cSubKeys; i++)
{
cbName = MAX_KEY_LENGTH;
retCode = RegEnumKeyEx(hKey, i,
achKey,
&cbName,
NULL,
NULL,
NULL,
&ftLastWriteTime);
if (retCode == ERROR_SUCCESS)
{
subkey[i]=achKey;
}
}
}
// Enumerate the key values.
if (cValues)
{
for (i=0, retCode=ERROR_SUCCESS; i<cValues; i++)
{
cchValue = MAX_VALUE_NAME;
achValue[0] = '\0';
retCode = RegEnumValue(hKey, i,
achValue,
&cchValue,
NULL,
&valueType,
(BYTE *)valueData,
&size);
if (retCode == ERROR_SUCCESS )
{
values[i]=achValue;
typev[i]=valueType;
}
else { cout<<"Error"<<endl;
}
}
}
int d=0;
cout<<"------------------------------------------------------------------------"<<endl;
cout<<"Sub Keys of "<<"are displayed below- "<<endl<<endl;
while(d<cSubKeys){
cout<<subkey[d]<<endl;
d++;
}
d=0;
cout<<endl;
if(!values[0].empty()) {
cout<<"Values are displayed below- "<<endl<<endl;
while(d<cValues){
cout<<values[d]<<" "<<typev[d]<<endl;
d++;
}
}
open(hKey,subkey,cSubKeys);
}
hi have this 2 pieces of code in my program:
PIMAGE_IMPORT_DESCRIPTOR PE::GetImportedLibInfo(LPSTR libName )
{
PIMAGE_DOS_HEADER doshdr = (PIMAGE_DOS_HEADER)EntryPoint;
PIMAGE_NT_HEADERS nthdr = (PIMAGE_NT_HEADERS)((DWORD)doshdr + doshdr->e_lfanew);
DWORD tmp =nthdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
DWORD Rva = RvaToOffset((DWORD)tmp);
if(Rva != -1)
{
Rva += EntryPoint;
PIMAGE_IMPORT_DESCRIPTOR iid =(PIMAGE_IMPORT_DESCRIPTOR)(DWORD)Rva;
while(iid->Characteristics)
{
char* dll = (char*)((DWORD_PTR)RvaToOffset(iid->Name)+ EntryPoint);
DWORD res=lstrcmp((LPCSTR)dll,(LPCSTR)libName);
if(res == 0)
{
return iid;
}
iid ++;
}
}
return NULL;
}
VOID FillLibInfo(PIMAGE_IMPORT_DESCRIPTOR iiD)
{
if(iiD != NULL)
{
char* buff[20];
wsprintf((LPSTR)&buff,"%08lX",(DWORD)iiD->OriginalFirstThunk);
SetDlgItemText(hImpDlg,IDC_EDIT1,(LPCSTR)&buff);
wsprintf((LPSTR)&buff,"%08lX",(DWORD)iiD->TimeDateStamp);
SetDlgItemText(hImpDlg,IDC_EDIT2,(LPCSTR)&buff);
wsprintf((LPSTR)&buff,"%08lX",(DWORD)iiD->ForwarderChain);
SetDlgItemText(hImpDlg,IDC_EDIT3,(LPCSTR)&buff);
wsprintf((LPSTR)&buff,"%08lX",(DWORD)iiD->FirstThunk);
SetDlgItemText(hImpDlg,IDC_EDIT4,(LPCSTR)&buff);
}
}
And then i use it so:
FillLibInfo(GetImportedLibInfo("MyLibName"));
what append is that my textboxes don't actualize text until i pass mouse hover them
and after a couple of calls to GetImportedLibInfo() the program crash.
i think that is something with stack corrupted...
can someone give me a hint?
#Edit:
Class PE defenition:
class PE
{
private:
DWORD ptrImgDosHeader;
DWORD RvaToOffset(DWORD Rva);
DWORD RvaToMemory(DWORD Rva);
public:
DWORD EntryPoint;
PE(DWORD ptrMemory);
~PE();
VOID EnumSections(BOOL (*ptrCallBack)(PIMAGE_SECTION_HEADER));
VOID EnumImports(BOOL (*ptrCallBack)(LPSTR,DWORD),DWORD);
VOID EnumImportedFunctionsFromLib(LPSTR,BOOL (*ptrCallBack)(LPSTR,LPSTR));
VOID EnumExportedFunctions(BOOL (*ptrCallBack)(LPSTR,LPSTR,LPSTR));
WORD GetPeType();
DWORD ValidatePE();
DWORD ValidateNtHeader();
PIMAGE_IMPORT_DESCRIPTOR GetImportedLibInfo(LPSTR lib);
};
You're writing a string to a char pointer array, not a char array, so you're writing to some random pointer (whatever the uninitialised array's first element is pointing to, which will be unallocated memory).
Try using char buff[20] instead of char* buff[20], then use wsprintf( buff, ... ) and SetDlgItemText( ..., buff ).
I am having problems with reading the registry.
This function finds the number of entries in a registry path. It works perfectly, I have tested it:
void findNumberEntries(registryTest &INSTALLKEY) {
char buffer[50];
char size = sizeof(buffer);
int index = 0;
if(RegOpenKeyEx(INSTALLKEY.hKey,(LPTSTR)(INSTALLKEY.regpath.c_str()),0,KEY_ALL_ACCESS,&INSTALLKEY.hKey) == ERROR_SUCCESS) {
DWORD readEntry;
do {
readEntry = RegEnumValue(INSTALLKEY.hKey,index,(LPTSTR)buffer,(LPDWORD)&size,NULL,NULL,NULL,NULL);
index++;
}
while(readEntry != ERROR_NO_MORE_ITEMS);
}
INSTALLKEY.number = index;
RegCloseKey(INSTALLKEY.hKey);
}
now, the main function:
std::string regpath32 = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run\\";
struct registryTest {
HKEY hKey;
std::string regpath;
int number;
};
registryTest INSTALLKEY = {HKEY_LOCAL_MACHINE, regpath32};
findNumberEntries(INSTALLKEY);
printf("%d\n",INSTALLKEY.number);
system("PAUSE");
//until here everything works as it should
HKEY hKey = INSTALLKEY.hKey;
std::string regpath = INSTALLKEY.regpath;
char buffer[50];
char size = sizeof(buffer);
std::string bufferString;
DWORD regOpen = RegOpenKeyEx(INSTALLKEY.hKey,(LPTSTR)INSTALLKEY.regpath.c_str(),0,KEY_READ,&INSTALLKEY.hKey);
if(regOpen == ERROR_SUCCESS) //this is the part that fails.
{
printf("Registry Key was successfully opened\n");
}
else
{
printf("Unable to open registry key\n");
LPVOID message;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, GetLastError(), NULL,(LPTSTR) &message, 0, NULL );
MessageBox(NULL,(LPCTSTR)message,"ERROR",MB_OK|MB_ICONINFORMATION);
}
...rest of the code
I always get "Unable to open registry" and the error message I get is "There are no more files". What is the problem??
your problem is that when you first open the registry key ,you assign it to hkey-member of your struct. So the second time this hkey doesn't contain the original basekey anymore.
change :
DWORD regOpen =
RegOpenKeyEx(INSTALLKEY.hKey,(LPTSTR)INSTALLKEY.regpath.c_str(),0,KEY_READ,&INSTALLKEY.hKey);
into
DWORD regOpen = RegOpenKeyEx(
HKEY_LOCAL_MACHINE
,(LPTSTR)INSTALLKEY.regpath.c_str(),0,KEY_READ,&INSTALLKEY.hKey);
or change this:
void findNumberEntries( registryTest &INSTALLKEY)
{
char buffer[50];
char size = sizeof(buffer);
int index = 0;
HKEY hkOpen = 0; // can't use INVALID_HANDLE_VALUE for HKEY's;
if (RegOpenKeyEx( INSTALLKEY.hKey ,(LPTSTR)(INSTALLKEY.regpath.c_str())
,0,&hkOpen ) == ERROR_SUCCESS)
{
// You should use RegQueryInfoKey for below code !
DWORD readEntry;
do {
readEntry = RegEnumValue( hkOpen ,index,(LPTSTR)buffer
,(LPDWORD size,NULL,NULL,NULL,NULL);
index++;
}
while(readEntry != ERROR_NO_MORE_ITEMS); }
INSTALLKEY.number = index;
RegCloseKey( hkOpen );
}
You may need to specify KEY_ALL_ACCESS in the second call as well, rather than just in the first. And on Win7 64-bit you may be running into the registry redirect craziness (http://msdn.microsoft.com/en-us/library/aa384232%28VS.85%29.aspx).
EDIT: ah, you might just be getting an ERROR_CANTWRITE back (error code number 5). You might be able to ignore that and see if it still works.
It's very likely that on Windows 7 64-bit that you are being redirected via Registry Virtualization. You can determine what keys are being redirected by calling RegQueryReflectionKey.
If you modify your code to output the actual integer value that is returned rather than a generic "Unable to open key", then it would be helpful. For example,
long n = RegOpenKeyEx(HKEY_LOCAL_MACHINE,TEXT("\\SOFTWARE"),
0,KEY_QUERY_VALUE, &hk );
if ( n == ERROR_SUCCESS ) {
cout << "OK" << endl;
}
else {
cout << "Failed with value " << n << endl;
}
In Windows, is there a way to check for the existence of an environment variable for another process? Just need to check existence, not necessarily get value.
I need to do this from code.
If you know the virtual address at which the environment is stored, you can use OpenProcess and ReadProcessMemory to read the environment out of the other process. However, to find the virtual address, you'll need to poke around in the Thread Information Block of one of the process' threads.
To get that, you'll need to call GetThreadContext() after calling SuspendThread(). But in order to call those, you need a thread handle, which you can get by calling CreateToolhelp32Snapshot with the TH32CS_SNAPTHREAD flag to create a snapshot of the process, Thread32First to get the thread ID of the first thread in the process, and OpenThread to get a handle to the thread.
Here is a working example which the printed output can be used to check existence as well as read the value, (build it as the same architecture as the executable's process identifier you must target):
getenv.cpp
#include <string>
#include <vector>
#include <cwchar>
#include <windows.h>
#include <winternl.h>
using std::string;
using std::wstring;
using std::vector;
using std::size_t;
// define process_t type
typedef DWORD process_t;
// #define instead of typedef to override
#define RTL_DRIVE_LETTER_CURDIR struct {\
WORD Flags;\
WORD Length;\
ULONG TimeStamp;\
STRING DosPath;\
}\
// #define instead of typedef to override
#define RTL_USER_PROCESS_PARAMETERS struct {\
ULONG MaximumLength;\
ULONG Length;\
ULONG Flags;\
ULONG DebugFlags;\
PVOID ConsoleHandle;\
ULONG ConsoleFlags;\
PVOID StdInputHandle;\
PVOID StdOutputHandle;\
PVOID StdErrorHandle;\
UNICODE_STRING CurrentDirectoryPath;\
PVOID CurrentDirectoryHandle;\
UNICODE_STRING DllPath;\
UNICODE_STRING ImagePathName;\
UNICODE_STRING CommandLine;\
PVOID Environment;\
ULONG StartingPositionLeft;\
ULONG StartingPositionTop;\
ULONG Width;\
ULONG Height;\
ULONG CharWidth;\
ULONG CharHeight;\
ULONG ConsoleTextAttributes;\
ULONG WindowFlags;\
ULONG ShowWindowFlags;\
UNICODE_STRING WindowTitle;\
UNICODE_STRING DesktopName;\
UNICODE_STRING ShellInfo;\
UNICODE_STRING RuntimeData;\
RTL_DRIVE_LETTER_CURDIR DLCurrentDirectory[32];\
ULONG EnvironmentSize;\
}\
// shortens a wide string to a narrow string
static inline string shorten(wstring wstr) {
int nbytes = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), (int)wstr.length(), NULL, 0, NULL, NULL);
vector<char> buf(nbytes);
return string { buf.data(), (size_t)WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), (int)wstr.length(), buf.data(), nbytes, NULL, NULL) };
}
// replace all occurrences of substring found in string with specified new string
static inline string string_replace_all(string str, string substr, string nstr) {
size_t pos = 0;
while ((pos = str.find(substr, pos)) != string::npos) {
str.replace(pos, substr.length(), nstr);
pos += nstr.length();
}
return str;
}
// func that splits string by first occurrence of equals sign
vector<string> string_split_by_first_equalssign(string str) {
size_t pos = 0;
vector<string> vec;
if ((pos = str.find_first_of("=")) != string::npos) {
vec.push_back(str.substr(0, pos));
vec.push_back(str.substr(pos + 1));
}
return vec;
}
// checks whether process handle is 32-bit or not
static inline bool IsX86Process(HANDLE process) {
BOOL isWow = true;
SYSTEM_INFO systemInfo = { 0 };
GetNativeSystemInfo(&systemInfo);
if (systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL)
return isWow;
IsWow64Process(process, &isWow);
return isWow;
}
// helper to open processes based on pid with full debug privileges
static inline HANDLE OpenProcessWithDebugPrivilege(process_t pid) {
HANDLE hToken;
LUID luid;
TOKEN_PRIVILEGES tkp;
OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken);
LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid);
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Luid = luid;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(hToken, false, &tkp, sizeof(tkp), NULL, NULL);
CloseHandle(hToken);
return OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
}
// get wide character string of pids environ based on handle
static inline wchar_t *GetEnvironmentStringsW(HANDLE proc) {
PEB peb;
SIZE_T nRead;
ULONG res_len = 0;
PROCESS_BASIC_INFORMATION pbi;
RTL_USER_PROCESS_PARAMETERS upp;
HMODULE p_ntdll = GetModuleHandleW(L"ntdll.dll");
typedef NTSTATUS (__stdcall *tfn_qip)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);
tfn_qip pfn_qip = tfn_qip(GetProcAddress(p_ntdll, "NtQueryInformationProcess"));
NTSTATUS status = pfn_qip(proc, ProcessBasicInformation, &pbi, sizeof(pbi), &res_len);
if (status) { return NULL; }
ReadProcessMemory(proc, pbi.PebBaseAddress, &peb, sizeof(peb), &nRead);
if (!nRead) { return NULL; }
ReadProcessMemory(proc, peb.ProcessParameters, &upp, sizeof(upp), &nRead);
if (!nRead) { return NULL; }
PVOID buffer = upp.Environment;
ULONG length = upp.EnvironmentSize;
wchar_t *res = new wchar_t[length / 2 + 1];
ReadProcessMemory(proc, buffer, res, length, &nRead);
if (!nRead) { return NULL; }
res[length / 2] = 0;
return res;
}
// get env of pid as a narrow string
string env_from_pid(process_t pid) {
string envs;
HANDLE proc = OpenProcessWithDebugPrivilege(pid);
wchar_t *wenvs = NULL;
if (IsX86Process(GetCurrentProcess())) {
if (IsX86Process(proc)) {
wenvs = GetEnvironmentStringsW(proc);
}
} else {
if (!IsX86Process(proc)) {
wenvs = GetEnvironmentStringsW(proc);
}
}
string arg;
if (wenvs == NULL) {
return "";
} else {
arg = shorten(wenvs);
}
size_t i = 0;
do {
size_t j = 0;
vector<string> envVec = string_split_by_first_equalssign(arg);
for (const string &env : envVec) {
if (j == 0) {
if (env.find_first_of("%<>^&|:") != string::npos) { continue; }
if (env.empty()) { continue; }
envs += env;
} else { envs += "=\"" + string_replace_all(env, "\"", "\\\"") + "\"\n"; }
j++;
}
i += wcslen(wenvs + i) + 1;
arg = shorten(wenvs + i);
} while (wenvs[i] != L'\0');
if (envs.back() == '\n') { envs.pop_back(); }
if (wenvs != NULL) { delete[] wenvs; }
CloseHandle(proc);
return envs;
}
// test function (can be omitted)
int main(int argc, char **argv) {
if (argc == 2) {
printf("%s", env_from_pid(stoul(string(argv[1]), nullptr, 10)).c_str());
printf("%s", "\r\n");
} else {
printf("%s", env_from_pid(GetCurrentProcessId()).c_str());
printf("%s", "\r\n");
}
return 0;
}
buildx86.sh
g++ getenv.cpp -o getenv.exe -std=c++17 -static-libgcc -static-libstdc++ -static -m32
buildx64.sh
g++ getenv.cpp -o getenv.exe -std=c++17 -static-libgcc -static-libstdc++ -static -m64
Quotes are added around the printed value for clarity, and escaping is applied to inner quotes.
With a utility:
You can use Process Explorer.
Right click on the process, go to Properties... and there is an Environment tab which lists the environment variables for that process.
With code:
There doesn't appear to be a Win32 API call to do this directly, but apparently you get fiddle with the results of GetProcessStrings to get access to this information. This CodeProject article has some code to get you started.