Edit:
The answer of this question is here:
https://stackoverflow.com/a/27317947/996540
When you create a project in msvc, the option /DYNAMICBASE is default enabled
now. Because of ASLR(Address space layout randomization, since Windows Vista),
everytime you run an exe, it's load address is random.
I am doing the DLL injection job recently, so I did some research into it on
google, and have read some projects. Get the load address (base address) of an
exe is important.
It seems there're two simple APIs to do this: EnumProcessModulesEx and
CreateToolhelp32Snapshot. But I never succeeded.
So this is the code sample:
void TestEnumProcessModulesEx(const char* app)
{
std::cout << "Begin TestEnumProcessModulesEx(" << mybit() << ")" << std::endl;
STARTUPINFOA startupInfo = {0};
startupInfo.cb = sizeof(startupInfo);
PROCESS_INFORMATION processInformation = {0};
if (CreateProcessA(app, NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &startupInfo, &processInformation))
{
std::vector<HMODULE> buf(128);
DWORD needed = 0;
for (;;) {
if (EnumProcessModulesEx(processInformation.hProcess, &buf[0], DWORD(buf.size()*sizeof(HMODULE)), &needed, LIST_MODULES_ALL) == FALSE) {
DWORD ec = GetLastError();
std::cout << "GetLastError() = " << ec << std::endl;
break;
}
else if (needed <= buf.size() * sizeof(HMODULE)) {
break;
}
else {
const size_t oldSize = buf.size();
buf.resize(oldSize * 2);
}
}
ResumeThread(processInformation.hThread);
WaitForSingleObject(processInformation.hProcess, INFINITE);
}
std::cout << "End TestEnumProcessModulesEx(" << mybit() << ")" << std::endl;
}
To reduce the length of this Question, the complete code - including the
CreateToolhelp32Snapshot's test code - is not listed here, but you can get it
from:
https://dl.dropboxusercontent.com/u/235920/enum_proc_mods_sample.7z
or
https://www.mediafire.com/?cry3pnra8392099
"If this function is called from a 32-bit application running on WOW64, it can
only enumerate the modules of a 32-bit process. If the process is a 64-bit
process, this function fails and the last error code is ERROR_PARTIAL_COPY
(299)." - from MSDN.
And this is a blog post about this question:
http://winprogger.com/getmodulefilenameex-enumprocessmodulesex-failures-in-wow64/
Unfortunately, this does not make sence, because whatever the specified
process is 32bit or 64bit, it fails with 299; whatever the caller process is
32-bit or 64bit, it fails with 299.
This is the output of my sample:
Begin TestEnumProcessModulesEx(32bit)
GetLastError() = 299
hello world 32bit
End TestEnumProcessModulesEx(32bit)
Begin TestEnumProcessModulesEx(32bit)
GetLastError() = 299
hello world 64bit
End TestEnumProcessModulesEx(32bit)
Begin TestEnumProcessModulesEx(64bit)
GetLastError() = 299
hello world 32bit
End TestEnumProcessModulesEx(64bit)
Begin TestEnumProcessModulesEx(64bit)
GetLastError() = 299
hello world 64bit
End TestEnumProcessModulesEx(64bit)
As you see, any combination is failed.
My OS is Windows 7 64bit pro and my compiler is VS2013.
So, what can I do ?
I have no idea about the unsuccess of EnumProcessModulesEx and
CreateToolhelp32Snapshot, let's leave this question to the expert.
My goal is to get the load address (base address) of the child process, find
the entry point and patch it - the reason to patch the entry point is here:
https://opcode0x90.wordpress.com/2011/01/15/injecting-dll-into-process-on-load/
Since DLL injection is the main purpose of mine, I have to reconsider this
question. I would use the "CreateRemoteThread & LoadLibrary Technique"
http://www.codeproject.com/Articles/4610/Three-Ways-to-Inject-Your-Code-into-Another-Proces#section_2
to do the DLL injection (In fact ASLR is not the barrier of this technique by the way),
Although there are so many limits in DLLMain
http://msdn.microsoft.com/en-us/library/windows/desktop/dn633971%28v=vs.85%29.aspx
, but do a little works is OK: Find the base address of an exe using
GetModuleHandleA(NULL), save the HMODULE returned into shared memory,
next, the caller process read shared memory and get the HMODULE.
Synchronization mechanism is necessary of course.
So, the answer is IPC. (not every IPC mechanism is safe in DLLMain by the way)
Related
Minimal, Complete, and Verifiable example:
Visual Studio 2017 Pro 15.9.3
Windows 10 "1803" (17134.441) x64
Environment variable OANOCACHE set to 1.
Data/Screenshots shown for a 32 bits Unicode build.
UPDATE: Exact same behavior on another machine with Windows 10 "1803" (17134.407)
UPDATE: ZERO leaks on an old Laptop with Windows Seven
UPDATE: Exact same behavior (leaks) on another machine with W10 "1803" (17134.335)
#include <windows.h>
#include <cstdio>
int main() {
getchar();
CoInitializeEx( NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE );
printf( "Launching and terminating processes...\n" );
for ( size_t i = 0; i < 64; ++i ) {
SHELLEXECUTEINFO sei;
memset( &sei, 0, sizeof( sei ) );
sei.cbSize = sizeof( sei );
sei.lpFile = L"iexplore.exe";
sei.lpParameters = L"about:blank";
sei.fMask = SEE_MASK_FLAG_NO_UI | SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NOASYNC;
BOOL bSuccess = ShellExecuteEx( &sei );
if ( bSuccess == FALSE ) {
printf( "\nShellExecuteEx failed with Win32 code %d and hInstApp %d. Exiting...\n",
GetLastError(), (int)sei.hInstApp );
CoUninitialize();
return 0;
} // endif
printf( "%d", (int)GetProcessId( sei.hProcess ) );
Sleep( 1000 );
bSuccess = TerminateProcess( sei.hProcess, 0 );
if ( bSuccess == FALSE ) {
printf( "\nTerminateProcess failed with Win32 code %d. Exiting...\n",
GetLastError() );
CloseHandle( sei.hProcess );
CoUninitialize();
return 0;
} // endif
DWORD dwRetCode = WaitForSingleObject( sei.hProcess, 5000 );
if ( dwRetCode != WAIT_OBJECT_0 ) {
printf( "\nWaitForSingleObject failed with code %x. Exiting...\n",
dwRetCode );
CloseHandle( sei.hProcess );
CoUninitialize();
return 0;
} // endif
CloseHandle( sei.hProcess );
printf( "K " );
Sleep( 1000 );
} // end for
printf( "\nDone!" );
CoUninitialize();
getchar();
} // main
The code use ShellExecuteEx to launch, in a loop, 64 instances of Internet Explorer with the about:blank URL. The SEE_MASK_NOCLOSEPROCESS is used to be able to then use the TerminateProcess API.
I notice two kinds of leaks:
Handles leaks: launching Process Explorer when the loop is finished but the program still running, I see several blocks of 64 handles (process handles, and registries handles for various keys)
Memory leaks: attaching the visual C++ 2017 debugger to the program, before the loop, I took a first Heap Snapshot, and a second one after the loop.I see 64 blocs of 8192 bytes, coming from windows.storage.dll!CInvokeCreateProcessVerb::_BuildEnvironmentForNewProcess()
You can read some information about the handles leaks here: ShellExecute leaks handles
Here are some screenshots:
First, the PID launched and terminated:
Second: the same pids, as seen in Process Explorer:
Process Explorer also shows 64*3 open registry handles, for HKCR\.exe, HKCR\exefile and HKCR\exefile\shell\open.
One of the 64 leaked "Environment" (8192 bytes and the callstack):
Last: a screen shot of Process Explorer, showing the "Private Bytes" during the execution of the MCVE modified with a 1024 loop counter. The running time is approximately 36 minutes, the PV start at 1.1 Mo (before CoInitializeEx) and end at 19 Mo (after CoUninitialize). The value then stabilizes at 18.9
What am I doing wrong?
Do I see leaks where there are none?
this is windows bug, in version 1803. minimal code for reproduce:
if (0 <= CoInitialize(0))
{
SHELLEXECUTEINFO sei = {
sizeof(sei), 0, 0, 0, L"notepad.exe", 0, 0, SW_SHOW
};
ShellExecuteEx( &sei );
CoUninitialize();
}
after execute this code, can view handles for notepad.exe process and first thread - this handles of course must not exist (be closed), not closed keys
\REGISTRY\MACHINE\SOFTWARE\Classes\.exe
\REGISTRY\MACHINE\SOFTWARE\Classes\exefile
also private memory leaks exist in process after this call.
of course this bug cause permanent resource leaks in explorer.exe and any process, which use ShellExecute[Ex]
exactly research of this bug - here
The underlying issue here appears to be in windows.storage.dll. In
particular, the CInvokeCreateProcessVerb object is never
destroyed, because the associated reference count never reaches 0.
This leaks all of the objects associated with
CInvokeCreateProcessVerb, including 4 handles and some memory.
The reason the reference count never reaches 0 appears to be related
to the argument change for ShellDDEExec::InitializeByShellInternal
from Windows 10 1709 to 1803, executed by
CInvokeCreateProcessVerb::Launch().
more concrete here we have cyclic reference of an object (CInvokeCreateProcessVerb) to itself.
more concrete error inside method CInvokeCreateProcessVerb::Launch() which call from self
HRESULT ShellDDEExec::InitializeByShellInternal(
IAssociationElement*,
CreateProcessMethod,
PCWSTR,
STARTUPINFOEXW*,
IShellItem2*,
IUnknown*, // !!!
PCWSTR,
PCWSTR,
PCWSTR);
with wrong 6 argument. the CInvokeCreateProcessVerb class containing internal ShellDDEExec sub-object. in windows 1709 CInvokeCreateProcessVerb::Launch() pass pointer to static_cast<IServiceProvider*>(pObj) in place 6 argument to ShellDDEExec::InitializeByShellInternal where pObj is point to instance of CBindAndInvokeStaticVerb class. but in 1803 version here passed pointer to static_cast<IServiceProvider*>(this) - so pointer to self. the InitializeByShellInternal store this pointer inside self and add reference to it. note that ShellDDEExec is sub-object of CInvokeCreateProcessVerb. so destructor of ShellDDEExec will not be called until destructor of CInvokeCreateProcessVerb not be called. but destructor of CInvokeCreateProcessVerb will be not called until it reference count reach 0. but this not happens until ShellDDEExec not release self pointer to CInvokeCreateProcessVerb which will be only inside it destructor ..
may be this more visible in pseudo code
class ShellDDEExec
{
CComPtr<IUnknown*> _pUnk;
HRESULT InitializeByShellInternal(..IUnknown* pUnk..)
{
_pUnk = pUnk;
}
};
class CInvokeCreateProcessVerb : CExecuteCommandBase, IServiceProvider /**/
{
IServiceProvider* _pVerb;//point to static_cast<IServiceProvider*>(CBindAndInvokeStaticVerb*)
ShellDDEExec _exec;
TRYRESULT CInvokeCreateProcessVerb::Launch()
{
// in 1709
// _exec.InitializeByShellInternal(_pVerb);
// in 1803
_exec.InitializeByShellInternal(..static_cast<IServiceProvider*>(this)..); // !! error !!
}
};
ShellDDEExec::_pUnk hold pointer to containing object CInvokeCreateProcessVerb this pointer will be released only inside CComPtr destructor, called from ShellDDEExec destructor. called from CInvokeCreateProcessVerb destructor, called when reference count became 0, but this never happens because extra reference hold ShellDDEExec::_pUnk
so object store referenced pointer to self. after this reference count to CInvokeCreateProcessVerb never reaches 0
Bugfix update:
As of Jun, 2013 FTDI did acknowledge to me that the bug was real. They have since released a new version of their driver (2.8.30.0, dated 2013-July-12) that fixes the problem. The driver made it through WHQL around the first of August, 2013 and is available via Windows Update at this time.
I've re-tested running the same test code and am not able to reproduce the problem with the new driver, so at the moment the fix seems to be 'upgrade the driver'.
The original question:
I've got an 8 port USB-serial device (from VsCOM) that is based on the FTDI FT2232D chip. When I transmit at certain settings from one of the ports, AND I use the hardware handshaking to stop and start the data flow from the other end, I get two symptoms:
1) The output data sometimes becomes garbage. There will be NUL characters, and pretty much any random thing you can think of.
2) The WriteFile call will sometimes return a number of bytes GREATER than the number I asked it to write. That's not a typo. I ask for 30 bytes to be transmitted and the number of bytes sent comes back 8192 (and yes, I do clear the number sent to 0 before I make the call).
Relevant facts:
Using FTDI drivers 2.8.24.0, which is the latest as of today.
Serial port settings are 19200, 7 data bits, odd parity, 1 stop bit.
I get this same behavior with another FTDI based serial device, this time a single port one.
I get the same behavior with another 8 port device of the same type.
I do NOT get this behavior when transmitting on the built-in serial ports (COM1).
I have a very simple 'Writer' program that just transmits continuously and a very simple 'Toggler' program that toggles RTS once per second. Together these seem to trigger the issue within 60 seconds.
I have put an issue into the manufacturer of the device, but they've not yet had much time to respond.
Compiler is mingw32, the one included with the Qt installer for Qt 4.8.1 (gcc 4.4.0)
I'd like to know first off, if there's anything that anyone can think of that I could possibly do to trigger this behavior. I can't conceive of anything, but there's always things I don't know.
Secondly, I've attached the Writer and Toggler test programs. If anyone can spot some issue that might trigger the program, I'd love to hear about it. I have a lot of trouble thinking that there is a driver bug (especially from something as mature as the FTDI chip), but the circumstances force me to think that there's at least SOME driver involvement. At the least, no matter what I do to it, it shouldn't be returning a number of bytes written greater than what I asked it to write.
Writer program:
#include <iostream>
#include <string>
using std::cerr;
using std::endl;
#include <stdio.h>
#include <windows.h>
int main(int argc, char **argv)
{
cerr << "COM Writer, ctrl-c to end" << endl;
if (argc != 2) {
cerr << "Please specify a COM port for parameter 2";
return 1;
}
char fixedbuf[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
std::string portName = "\\\\.\\";
portName += argv[1];
cerr << "Transmitting on port " << portName << endl;
HANDLE ph = CreateFileA( portName.c_str(),
GENERIC_READ | GENERIC_WRITE,
0, // must be opened with exclusive-access
NULL, // default security attributes
OPEN_EXISTING, // must use OPEN_EXISTING
0, // overlapped I/O
NULL ); // hTemplate must be NULL for comm devices
if (ph == INVALID_HANDLE_VALUE) {
cerr << "CreateFile " << portName << " failed, error " << GetLastError() << endl;
return 1;
}
COMMCONFIG ccfg;
DWORD ccfgSize = sizeof(COMMCONFIG);
ccfg.dwSize = ccfgSize;
GetCommConfig(ph, &ccfg, &ccfgSize);
GetCommState(ph, &(ccfg.dcb));
ccfg.dcb.fBinary=TRUE;
ccfg.dcb.fInX=FALSE;
ccfg.dcb.fOutX=FALSE;
ccfg.dcb.fAbortOnError=FALSE;
ccfg.dcb.fNull=FALSE;
// Camino is 19200 7-O-1
ccfg.dcb.BaudRate = 19200;
ccfg.dcb.Parity = ODDPARITY;
ccfg.dcb.fParity = TRUE;
ccfg.dcb.ByteSize = 7;
ccfg.dcb.StopBits = ONESTOPBIT;
// HW flow control
ccfg.dcb.fOutxCtsFlow=TRUE;
ccfg.dcb.fRtsControl=RTS_CONTROL_HANDSHAKE;
ccfg.dcb.fInX=FALSE;
ccfg.dcb.fOutX=FALSE;
COMMTIMEOUTS ctimeout;
DWORD tout = 10;// 10 ms
ctimeout.ReadIntervalTimeout = tout;
ctimeout.ReadTotalTimeoutConstant = tout;
ctimeout.ReadTotalTimeoutMultiplier = 0;
ctimeout.WriteTotalTimeoutMultiplier = tout;
ctimeout.WriteTotalTimeoutConstant = 0;
SetCommConfig(ph, &ccfg, sizeof(COMMCONFIG));
SetCommTimeouts(ph, &ctimeout);
DWORD nwrite = 1;
for(;;) {
nwrite++;
if (nwrite > 30) nwrite = 1;
DWORD nwritten = 0;
if (!WriteFile(ph, fixedbuf, nwrite, &nwritten, NULL)) {
cerr << "f" << endl;
}
if ((nwritten != 0) && (nwritten != nwrite)) {
cerr << "nwrite: " << nwrite << " written: " << nwritten << endl;
}
}
return 0;
}
Toggler program:
#include <iostream>
#include <string>
using std::cerr;
using std::endl;
#include <stdio.h>
#include <windows.h>
int main(int argc, char **argv)
{
cerr << "COM Toggler, ctrl-c to end" << endl;
cerr << "Flips the RTS line every second." << endl;
if (argc != 2) {
cerr << "Please specify a COM port for parameter 2";
return 1;
}
std::string portName = "\\\\.\\";
portName += argv[1];
cerr << "Toggling RTS on port " << portName << endl;
HANDLE ph = CreateFileA( portName.c_str(),
GENERIC_READ | GENERIC_WRITE,
0, // must be opened with exclusive-access
NULL, // default security attributes
OPEN_EXISTING, // must use OPEN_EXISTING
0, // overlapped I/O
NULL ); // hTemplate must be NULL for comm devices
if (ph == INVALID_HANDLE_VALUE) {
cerr << "CreateFile " << portName << " failed, error " << GetLastError() << endl;
return 1;
}
COMMCONFIG ccfg;
DWORD ccfgSize = sizeof(COMMCONFIG);
ccfg.dwSize = ccfgSize;
GetCommConfig(ph, &ccfg, &ccfgSize);
GetCommState(ph, &(ccfg.dcb));
ccfg.dcb.fBinary=TRUE;
ccfg.dcb.fInX=FALSE;
ccfg.dcb.fOutX=FALSE;
ccfg.dcb.fAbortOnError=FALSE;
ccfg.dcb.fNull=FALSE;
// Camino is 19200 7-O-1
ccfg.dcb.BaudRate = 19200;
ccfg.dcb.Parity = ODDPARITY;
ccfg.dcb.fParity = TRUE;
ccfg.dcb.ByteSize = 7;
ccfg.dcb.StopBits = ONESTOPBIT;
// no flow control (so we can do manually)
ccfg.dcb.fOutxCtsFlow=FALSE;
ccfg.dcb.fRtsControl=RTS_CONTROL_DISABLE;
ccfg.dcb.fInX=FALSE;
ccfg.dcb.fOutX=FALSE;
COMMTIMEOUTS ctimeout;
DWORD tout = 10;// 10 ms
ctimeout.ReadIntervalTimeout = tout;
ctimeout.ReadTotalTimeoutConstant = tout;
ctimeout.ReadTotalTimeoutMultiplier = 0;
ctimeout.WriteTotalTimeoutMultiplier = tout;
ctimeout.WriteTotalTimeoutConstant = 0;
SetCommConfig(ph, &ccfg, sizeof(COMMCONFIG));
SetCommTimeouts(ph, &ctimeout);
bool rts = true;// true for set
for(;;) {
if (rts)
EscapeCommFunction(ph, SETRTS);
else
EscapeCommFunction(ph, CLRRTS);
rts = !rts;
Sleep(1000);// 1 sec wait.
}
return 0;
}
I don't have a good answer yet from FTDI, but I've got the following suggestions for anyone dealing with this issue:
1) Consider switching to a non-FTDI usb-serial converter. This is what my company did, but certainly this isn't an option for everyone (we're putting the chip in our own product). We're using Silicon Labs chips now, but I think there are one or two other vendors as well.
2) Per Hans Passant in the comments - reconsider the use of RTS/CTS signalling. If the writes don't fail due to blocking, then you shouldn't trigger this bug.
3) Set all writes to infinite timeout. Again, no fail due to blocking, no triggering of the bug. This may not be appropriate for all applications, of course.
Note that if pursuing strategy #3, if Overlapped IO is used for writes, then CancelIo and it's newer cousin CancelIoEx could be used to kill off the writes if necessary. I have NOT tried doing so, but I suspect that such cancels might also result in triggering this bug. If they were only used when closing the port anyway, then it might be you could get away with it, even if they do trigger the bug.
If anyone else is still seeing this -- update your FTDI driver to 2.8.30.0 or later, as this is caused by a driver bug in earlier versions of the FTDI driver.
I'm using GetRawInputDeviceInfo to get the device name of a USB HID device name.
For some reason, when I run my code under Windows XP I get a device name which starts with \??\ and not \\?\.
This of course means, that when I try to use this device name (in CreateFile for example" it does not work. If I edit the device name and manually fix it to be \\?\ everything works great.
This does not happens in Windows 7. In Win7 everything works great.
I also test for GetLastError after every API call and no errors occur.
All my OS's are 32 bit and my project is compiling with unicode.
Any suggestions what am I doing wrong?? Here's a code snippets from my console application which gets the device name.
nResult = GetRawInputDeviceInfo( pDeviceList[i].hDevice, RIDI_DEVICENAME, NULL, &nBufferSize );
if( nResult < 0 )
{
cout << "ERR: Unable to get Device Name character count.." << endl;
return false;
}
WCHAR* wcDeviceName = new WCHAR[ nBufferSize + 1 ];
if( wcDeviceName == NULL )
{
cout << "ERR: Unable to allocate memory for Device Name.." << endl;
return false;
}
nResult = GetRawInputDeviceInfo( pDeviceList[i].hDevice, RIDI_DEVICENAME, wcDeviceName, &nBufferSize );
if( nResult < 0 )
{
cout << "ERR: Unable to get Device Name.." << endl;
delete [] wcDeviceName;
return false;
}
wcDeviceName[1]='\\';
//This is the manual fix for the device name in WinXP. How do I get rid of it????
pDesc->hHandle = CreateFile(wcDeviceName, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, NULL, NULL);
...
...
You are not doing anything wrong.
Just change the second character to \ and you are set. What you see is the raw device path in its native form (\??\...). When you have the form \\?\ that is a crutch MS invented to make long path names available on Win32 when NT arrived despite the limitation of the Win32 subsystem to the \?? object directory.
Please read a few of the chapters of "Windows Internals" by Russinovich (any old edition will do) and use winobj.exe from Sysinternals to explore the object namespace of Windows to see what I'm talking about.
Side-note: when you call CreateFile the code in kernel32.dll will literally undo the suggested change and convert it back to its native form before the native functions get to see the path. So all you are doing with this is to make the Win32 layer understand the path.
We have code that talks to our USB COMM class device, which works fine under Windows XP but is failing under Windows 7. Specifically the call to SetCommState is failing. Here's a simplified snippet. Note that in this case we don't even change any fields from GetCommState, but the result is that SetCommState fails with an error code of 87 (illegal parameter).
DCB dcb;
SecureZeroMemory(&dcb, sizeof(DCB));
dcb.DCBlength = sizeof(DCB);
if (!GetCommState(m_hIDComDev, &dcb)) {
DWORD dwError = GetLastError();
CloseHandle(m_hIDComDev);
dlDebug(5, "SerialPort::openPort") << "GetCommState failed for" << m_portName << dwError;
return 0;
}
dlDebug(5, "SerialPort::openPort") << m_portName << "rate" << dcb.BaudRate << "size" << dcb.ByteSize;
// dcb.BaudRate = baud;
// dcb.ByteSize = 8;
if (!SetCommState(m_hIDComDev, &dcb)) {
DWORD dwError = GetLastError();
CloseHandle(m_hIDComDev);
dlDebug(5, "SerialPort::openPort") << "SetCommState failed for" << m_portName << dwError;
return 0;
}
Any ideas what might be going wrong? One thought is that the USB device descriptor is incorrect and Win7 is more rigorous about double-checking (but I'm a little skeptical about that as the device works fine under MacOS X and Linux with no issues). I'm stumped!
If you are working on 64 bit, maybe you have to set dcb.DCBLength not to sizeof(DCB) but to the next highest multiple of 8.
I have an application that aids people with disabilities. In order to work, it tracks the what window is currently in the foreground. Normally, I use this function to get process executable.
bool GetWindowProcessExe2(HWND hwnd, wxString& process_exe)
//LPTSTR buf, DWORD size)
{
DWORD result = 0;
DWORD pid = 0;
GetWindowThreadProcessId(hwnd, &pid);
if (HANDLE process =
OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid))
{
char buff[512];
LPTSTR pbuff = buff;
result = GetModuleFileNameEx(process, 0, pbuff, 512);
if(result == 0)
{
//failed.
wxLogError("GetModuleFileNameEx failed with error code %d", GetLastError());
}
CloseHandle(process);
process_exe = fromCString(pbuff);
}
return result > 0 ? true : false;
}
Unfortunately, if the foreground window is the Vista file manager window (the window that opens when you click Start->Computer), GetModuleFileNameEx() fails with error code 299 which says I don't have privileges for this action. My code works for any regular application but not for the windows built in window (the file explorer). I need to know when this window is forefront. Is there another way to do it? I tried reading the window title but that just returns the current directory being shown. Any ideas?
I'm not sure why this isn't working for explorer, but error 299 is ERROR_PARTIAL_COPY, meaning that attempting to read the module name out of explorer is failing.
On Vista, prefer QueryProcessImageFileName and only open the process with PROCESS_QUERY_LIMITED_INFORMATION - your code will work in more cases.
WCHAR exeName[512];
DWORD cchExeName = 512;
HANDLE process = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, 0, pid);
QueryFullProcessImageName(process, 0, exeName, &cchExeName);
EDIT: I also got ERROR_PARTIAL_COPY with your code running on 64-bit, but only when the querying process was 32-bit. 64-bit/64-bit worked fine.
Looks like a 32 bit process can call GetModuleFileNameEx only on othere 32 bit processes. If you try to call it on 64 bit processes it fails with ERROR_PARTIAL_COPY.On a 64 bit platform make the calling process 64 bit and you should be able to call GetModuleFileNameEx on both 64 and 32 bit processes.