I'm trying to make a program that gets a process name, finds it's ID,
and then finds the language with the function GetKeyboardLayout.
Although I'm having difficulties and it seem not to work.
It finds the processID although the language that return is always 00000000.
That is my code :
#include <iostream>
#include <windows.h>
#include <string>
#include <tlhelp32.h>
DWORD FindProcessId(LPCTSTR ProcessName);
int main() {
HKL currentKBLayout;
DWORD processID;
LPCTSTR processName = "chrome.exe";
while (true) {
processID = FindProcessId(processName);
if (processID == 0); // TODO: pause system for 5 seconds
else {
currentKBLayout = GetKeyboardLayout(processID);
std::cout << processID << " | "<< currentKBLayout << std::endl;
}
}
system("pause");
return 0;
}
DWORD FindProcessId(LPCTSTR ProcessName)
{
PROCESSENTRY32 pt;
HANDLE hsnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
pt.dwSize = sizeof(PROCESSENTRY32);
if (Process32First(hsnap, &pt)) { // must call this first
do {
if (!lstrcmpi(pt.szExeFile, ProcessName)) {
CloseHandle(hsnap);
return pt.th32ProcessID;
}
} while (Process32Next(hsnap, &pt));
}
CloseHandle(hsnap); // close handle on failure
return 0;
}
I agree with Remys comment about using a simpler way to get the keyboard layout for the processes if that's all you need. If you however are interested in adding more information to your current approach using snapshots, this could be a way to start. It takes a snapshot of all processes and threads. Each Process has a vector of Thread objects. Adding Thread objects to each Process is done via an unordered_map<processId, Process>. To get a unique set of keyboard layouts for each process (since each thread may in theory have its own), an unordered_set<HKL> is used.
#include "pch.h"
#include <iostream>
#include <string>
#include <vector>
#include <unordered_set>
#include <unordered_map>
#include <windows.h>
#include <tlhelp32.h>
struct Thread {
DWORD m_id;
HKL m_keyboard_layout;
Thread(DWORD Id) :
m_id(Id), m_keyboard_layout(GetKeyboardLayout(m_id))
{}
};
struct Process {
std::vector<Thread> m_threads;
DWORD m_id;
std::wstring m_exefile;
Process() = default;
Process(DWORD Id, std::wstring Name) :
m_id(Id), m_exefile(Name)
{}
// get a unique set of HKL:s from all the threads in the process
std::unordered_set<HKL> GetKeyboardLayouts() const {
std::unordered_set<HKL> rv;
for (auto& t : m_threads) {
if(t.m_keyboard_layout) // does it have a keyboard layout?
rv.emplace(t.m_keyboard_layout);
}
return rv;
}
// if you'd like to iterate over the individual threads
std::vector<Thread>::iterator begin() { return m_threads.begin(); }
std::vector<Thread>::iterator end() { return m_threads.end(); }
};
class Snapshot {
HANDLE hSnap;
std::unordered_map<DWORD, Process> m_processes;
void GetProcesses() {
PROCESSENTRY32 pt;
pt.dwSize = sizeof(pt);
if (Process32First(hSnap, &pt)) { // must call this first
do {
m_processes[pt.th32ProcessID] = Process(pt.th32ProcessID, pt.szExeFile);
} while (Process32Next(hSnap, &pt));
}
}
void GetThreads() {
THREADENTRY32 pt;
pt.dwSize = sizeof(pt);
if (Thread32First(hSnap, &pt)) { // must call this first
do {
m_processes[pt.th32OwnerProcessID].m_threads.emplace_back(pt.th32ThreadID);
} while (Thread32Next(hSnap, &pt));
}
}
void Populate() {
GetProcesses();
GetThreads();
}
public:
Snapshot() :
hSnap(CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD, 0)),
m_processes()
{
// TODO: make this exception better
if (hSnap == INVALID_HANDLE_VALUE) throw GetLastError();
Populate();
CloseHandle(hSnap);
}
std::unordered_map<DWORD, Process>::iterator begin() { return m_processes.begin(); }
std::unordered_map<DWORD, Process>::iterator end() { return m_processes.end(); }
};
int main() {
Snapshot snap;
// show processes with keyboard layouts
for (const auto& m : snap) { // std::pair m.first = processId, m.second = Process
const Process& p = m.second;
auto layouts = p.GetKeyboardLayouts();
if (layouts.size()) { // only show processes with keyboard layouts
std::wcout << p.m_id << L" " << p.m_exefile << L"\n";
for (const auto& l : layouts) {
std::wcout << L" layout " << l << L"\n";
}
}
}
return 0;
}
Related
I called IMFSourceReader::ReadSample and I found it was stuck if it cannot read data.
So I tried to terminate the thread by TerminateThread() but it returned 0 as a fail.
How could I terminate the stuck thread?
This is my sample code:
#include <iostream>
#include <vector>
#include <codecvt>
#include <string>
#include <thread>
#include <mutex>
#include <chrono>
#include <condition_variable>
#include <Windows.h>
using namespace std::chrono_literals;
class MyObject
{
private:
...
std::thread *t;
std::mutex m;
std::condition_variable cv;
std::thread::native_handle_type handle;
int getsample(uint8_t* data)
{
// call a ReadSample
hr = pVideoReader->ReadSample(
MF_SOURCE_READER_ANY_STREAM, // Stream index.
0, // Flags.
&streamIndex, // Receives the actual stream index.
&flags, // Receives status flags.
&llTimeStamp, // Receives the time stamp.
&pSample // Receives the sample or NULL.
);
...
return 0;
}
int myfunc_wrapper(uint8_t* data)
{
int ret = 0;
BOOL bpass = 0;
if (t == nullptr) {
t = new std::thread([this, &data, &ret]()
{
ret = this->getsample(data);
this->cv.notify_one();
});
handle = t->native_handle();
t->detach();
}
{
std::unique_lock<std::mutex> l(this->m);
if (this->cv.wait_for(l, 2500ms) == std::cv_status::timeout) {
bpass = TerminateThread(handle, 0);
if (bpass == 0) {
std::cout << "TerminateThread Fail! " << GetLastError() << std::endl;
}
throw std::runtime_error("Timeout Fail 2500 ms");
}
}
delete t;
t = nullptr;
}
public:
int my_func(uint8_t* raw_data)
{
bool timedout = false;
try {
if (myfunc_wrapper(raw_data) != 0)
return -1;
}
catch (std::runtime_error& e) {
std::cout << e.what() << std::endl;
timedout = true;
}
if (timedout)
return -1;
return 0;
}
};
int main()
{
uint8_t data[512];
MyObject* obj = new MyObject();
while (true)
{
obj->my_func(data);
}
return 0;
}
Output:
TerminateThread Fail! 6
Timeout Fail 2500 ms
TerminateThread Fail! 6
Timeout Fail 2500 ms
...
I also tried to use pthread_cancel but it cannot be compiled because there is a type error.
no suitable constructor exists to convert from "std::thread::native_handle_type" to "__ptw32_handle_t"
handle = t->native_handle();
...
pthread_cancel(handle); // no suitable constructor exists to convert
The reason it failed to terminate is that the native handle is no longer valid after detaching, one way you could do this is to OpenThread using the thread id to get a new handle.
To get the thread id, you could use its handle before detaching like this:
DWORD nativeId = GetThreadId(t->native_handle());
t->detach();
After that, just open a new handle to the thread to terminate it:
HANDLE hThread = OpenThread(THREAD_TERMINATE, FALSE, nativeId);
if (hThread)
{
BOOL result = TerminateThread(hThread, 0);
CloseHandle(hThread);
}
But you should not do this, consider other ways to signal the thread to terminate on its own.
I am trying to detect how many instances of a application person is running. Did he open my application once? Twice? Thrice?
I tried to detect it by checking it's instances by process names, but in windows it is pointles - people might change .exe name and it won't count towards final number.
How would I proceed then? I thought about searching it by className (HWND?) rather by processName, but how would I do it?
This is the code I am using for detecting by process name:
int Platform::getMulticlientCount(const std::string& ProcessName)
{
PROCESSENTRY32 pe32 = { sizeof(PROCESSENTRY32) };
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
const char *cstr = ProcessName.c_str();
int counter = 0;
if (Process32First(hSnapshot, &pe32))
{
do
{
if (_tcsicmp(pe32.szExeFile, cstr) == 0)
{
counter++;
}
} while (Process32Next(hSnapshot, &pe32));
}
CloseHandle(hSnapshot);
return counter;
}
The instance of the Remy:
static int counter;
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{
char classname[MAX_PATH] = { 0 };
GetClassNameA(hwnd,classname, MAX_PATH);
if (_tcsicmp(classname, (char*)lParam) == 0)
counter++;
return true;
}
int Platform::getMulticlientCount(const std::string& ClassName)
{
counter = 0;
const char *cstr = ClassName.c_str();
EnumWindows(EnumWindowsProc, (LPARAM)cstr);
return counter;
}
If you also need to get the instance, in EnumWindowsProc:
HINSTANCE instance = (HINSTANCE)GetWindowLongPtr(hwnd, GWLP_HINSTANCE);
If you also need to get the processId, in EnumWindowsProc:
DWORD pid;
GetWindowThreadProcessId(hwnd, &pid);
Here is an example code that counts the instances that are running. While the application count the instances it self it does not matter if the binary is renamed. I used a file to keep the example simple but registry would work too. The only thing that is missing is a global mutex to protect the file against concurrent access. HTH
#include <iostream>
#include <thread>
#include <fstream>
#include <Windows.h>
class GlobalCounter
{
public:
GlobalCounter(const std::string& id)
: _id(id)
{
const auto filename = "C:\\users\\twollgam\\" + id + ".counter";
if (GlobalFindAtomA(id.c_str()) == 0)
{
std::ofstream(filename) << 1;
std::cout << "I am the first instance." << std::endl;
}
else
{
auto counter = 0;
std::ifstream(filename) >> counter;
++counter;
std::ofstream(filename) << counter;
std::cout << "I am the " << counter << " instance." << std::endl;
}
_atom = GlobalAddAtomA(id.c_str());
}
~GlobalCounter()
{
const auto filename = "C:\\users\\twollgam\\" + _id + ".counter";
auto counter = 0;
std::ifstream(filename) >> counter;
--counter;
std::ofstream(filename) << counter;
GlobalDeleteAtom(_atom);
}
private:
const std::string _id;
ATOM _atom;
};
int main()
{
const auto globalCounter = GlobalCounter("test");
std::cout << "Hello World!\n";
std::this_thread::sleep_for(std::chrono::seconds(30));
return 0;
}
i searched for an answer but couldnt find it. im working on threads. i have a thread class and 3 subclass of it. when i call one of these 3 subclass i have to create a thread in thread class and use their main(since threads main is pure virtual abstract) but the problem is before it call the Create Thread function(c'tor of thread) it calls those sub-mains.
thread.h
#ifndef _THREAD_H_
#define _THREAD_H_
#include <string>
#include <Windows.h>
#include <iosfwd>
#include "Mutex.h"
#include "SynchronizedArray.h"
#include "SynchronizedCounter.h"
std::string message = "";
class Thread{
private:
HANDLE hThread;
int idThread;
protected:
SynchronizedArray *arr;
int size;
SynchronizedCounter *counter;
public:
Thread(DWORD funct){ //creates a thread by calling subclasses main functions appropriately
hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) funct, NULL, 0, (LPDWORD)&idThread);
}
virtual DWORD WINAPI main(void*) = 0; // pure virtual abstract class
void suspend(){ //suspent the thread
SuspendThread(hThread);
}
void resume(){// retume the thread
ResumeThread(hThread);
}
void terminate(){ // terminates the thread
TerminateThread(hThread,0);
}
void join(){ // joins the thread
Mutex mut;
mut.lock();
}
static void sleep(int sec){ //wrapper of sleep by sec
Sleep(sec*1000);
}
};
#endif
1 of 3 inherited class of Thread as example (all of them do the same)
PrintThread.h
#ifndef _PRINTINGTHREAD_H_
#define _PRINTINGTHREAD_H_
#include "SynchronizedArray.h"
#include "SynchronizedCounter.h"
#include "Thread.h"
#include <iostream>
#include "SortingThread.h"
#include "CountingThread.h"
#include <string>
#include <Windows.h>
extern CountingThread counterThread1;
extern CountingThread counterThread2;
extern SortingThread sortingThread1;
extern SortingThread sortingThread2;
class PrintingThread:public Thread{
private:
char temp;
public:
PrintingThread() :Thread(main(&temp)){
}
DWORD WINAPI main(void* param)
{
std::cout << "Please enter an operation ('showcounter1','showcounter2','showarray1','showarray2' or 'quit')" << std::endl;
std::cin >> message;
while (message != "quit")
{
if (message == "showcounter1")
{
std::cout << counterThread1<<std::endl;
}
else if (message == "showcounter2")
{
std::cout << counterThread2 << std::endl;
}
else if (message == "showarray1")
{
std::cout << sortingThread1 << std::endl;
}
else if (message == "showarray2")
{
std::cout << sortingThread2 << std::endl;
}
else {
std::cout << "Invalid operation";
}
std::cout << "Please enter an operation ('show counter 1','show counter 2','show array 1','show array 2' or 'quit')" << std::endl;
std::cin >> message;
}
return 0;
}
};
#endif
Why its calling mains before it calls the c'tor of thread.
Your PrintingThread constructor initializer list is actually calling PrintingThread::main and passing the result of that to the Thread constructor. That means that the CreateThread call is receiving a DWORD (in this case, 0) as its function argument.
You need to change your class design to actually pass the function pointer and context argument around, e.g.:
Thread(DWORD funct){
hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) funct, NULL, 0, (LPDWORD)&idThread);
should be:
Thread(LPTHREAD_START_ROUTINE funct, LPVOID arg) {
hThread = CreateThread(NULL, 0, funct, arg, 0, (LPDWORD)&idThread);
(The fact that you had to cast funct should have been a giant red flag.)
Likewise, the subclass constructors will change from:
PrintingThread() :Thread(main(&temp)){
to:
PrintingThread(): Thread(main, &temp) {
Note that your code will still have other issues, like the fact that the thread functions should be static (so you can't try to access member functions).
you need to do something more like this instead:
thread.h:
#ifndef _THREAD_H_
#define _THREAD_H_
#include <string>
#include <Windows.h>
#include <iosfwd>
#include "Mutex.h"
#include "SynchronizedArray.h"
#include "SynchronizedCounter.h"
class Thread
{
private:
HANDLE hThread;
DWORD idThread;
void *pParam;
static DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
Thread *pThis = (Thread*) lpParameter;
return pThis->main(pThis->pParam);
}
protected:
SynchronizedArray *arr;
int size;
SynchronizedCounter *counter;
public:
Thread(void *aParam)
{
//creates a thread by calling subclasses main functions appropriately
pParam = aParam;
hThread = CreateThread(NULL, 0, &ThreadProc, this, 0, &idThread);
}
virtual DWORD main(void*) = 0; // pure virtual abstract class
void suspend()
{
//suspent the thread
SuspendThread(hThread);
}
void resume()
{
// resume the thread
ResumeThread(hThread);
}
void terminate()
{
// terminates the thread
TerminateThread(hThread, 0);
}
void join()
{
// joins the thread
Mutex mut;
mut.lock();
}
static void sleep(int sec)
{
//wrapper of sleep by sec
Sleep(sec*1000);
}
};
#endif
PrintThread.h:
include <iostream>
#include "SortingThread.h"
#include "CountingThread.h"
#include <string>
#include <Windows.h>
extern CountingThread counterThread1;
extern CountingThread counterThread2;
extern SortingThread sortingThread1;
extern SortingThread sortingThread2;
class PrintingThread : public Thread
{
private:
char temp;
public:
PrintingThread() : Thread(&temp)
{
}
virtual DWORD main(void* param)
{
std::string message;
do
{
std::cout << "Please enter an operation ('showcounter1','showcounter2','showarray1','showarray2' or 'quit')" << std::endl;
std::cin >> message;
if (message == "quit")
{
break;
}
if (message == "showcounter1")
{
std::cout << counterThread1 << std::endl;
}
else if (message == "showcounter2")
{
std::cout << counterThread2 << std::endl;
}
else if (message == "showarray1")
{
std::cout << sortingThread1 << std::endl;
}
else if (message == "showarray2")
{
std::cout << sortingThread2 << std::endl;
}
else
{
std::cout << "Invalid operation";
}
}
while (true);
return 0;
}
};
#endif
I was wondering what this function here does:
ralloc_t(HWND hwnd) : proc_(0)
{
DWORD pid = 0;
if (!GetWindowThreadProcessId(hwnd, &pid)) {
throw exception("dang, no dice");
}
proc_ = OpenProcess(
PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION, FALSE, pid);
if (!proc_) {
throw exception("no open for me!");
}
}
~ralloc_t()
{
buffers_t::reverse_iterator i = buffers_.rbegin(), e = buffers_.rend();
for (; i != e; ++i) {
free(i->first);
}
}
I honestly don't know where function starts and where it ends and if it's returning anything.
The full code is below. I got a decent start on it, in that I have all the winapi functions in use below converted already to js-ctypes. This is what I have so far: https://gist.github.com/Noitidart/f691ab9a750f24be346f
#include <windows.h>
#include <commctrl.h>
#include <iostream>
#include <cstdio>
#include <stdexcept>
#include <map>
using namespace std;
/**
* Allocate/read/write remote process memory.
* The implementation is pretty crappy, as it isn't intelligent at all:
* It always allocates at least a full page per allocation :(
* Do something more clever in production!
* Also, type safety and convenience are pretty lacking.
* But again, this is test code, so it sucks!
*/
class ralloc_t
{
public:
ralloc_t(HWND hwnd) : proc_(0)
{
DWORD pid = 0;
if (!GetWindowThreadProcessId(hwnd, &pid)) {
throw exception("dang, no dice");
}
proc_ = OpenProcess(
PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION, FALSE, pid);
if (!proc_) {
throw exception("no open for me!");
}
}
~ralloc_t()
{
buffers_t::reverse_iterator i = buffers_.rbegin(), e = buffers_.rend();
for (; i != e; ++i) {
free(i->first);
}
}
void* alloc(size_t size)
{
void* rv = VirtualAllocEx(
proc_, 0, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (!rv) {
throw bad_alloc();
}
buffers_.insert(make_pair(rv, size));
return rv;
}
template <typename T>
T* create(size_t elems = 1)
{
return (T*)alloc(elems * sizeof(T));
}
void free(void* p)
{
buffers_t::iterator i = buffers_.find(p);
if (i == buffers_.end()) {
throw exception("invalid buffer");
}
VirtualFreeEx(proc_, i->first, i->second, MEM_RELEASE);
buffers_.erase(i);
}
void read(void* remote, void* local)
{
buffers_t::iterator i = buffers_.find(remote);
if (i == buffers_.end()) {
throw exception("invalid remote read buffer");
}
if (!ReadProcessMemory(proc_, i->first, local, i->second, 0)) {
throw exception("failed to read remote buffer");
}
}
void write(void* remote, const void* local)
{
buffers_t::iterator i = buffers_.find(remote);
if (i == buffers_.end()) {
throw exception("invalid remote write buffer");
}
if (!WriteProcessMemory(proc_, i->first, local, i->second, 0)) {
throw exception("failed to write remote buffer");
}
}
private:
typedef map<void*, size_t> buffers_t;
buffers_t buffers_;
HANDLE proc_;
};
int main()
{
typedef HWND(WINAPI * GetTaskmanWindowPtr)();
try
{
HMODULE user32 = LoadLibrary(L"user32");
GetTaskmanWindowPtr GetTaskmanWindow =
(GetTaskmanWindowPtr)GetProcAddress(user32, "GetTaskmanWindow");
if (!GetTaskmanWindow) {
throw exception("Failed to get GetTaskmanWindow!");
}
HWND htm = GetTaskmanWindow();
if (!htm) {
throw exception("Failed to get taskman window");
}
HWND htb = FindWindowEx(htm, 0, L"ToolbarWindow32", 0);
if (!htb) {
throw exception("Failed to get toolbar window");
}
ralloc_t ralloc(htb);
int count = SendMessage(htb, TB_BUTTONCOUNT, 0, 0);
cout << count << endl;
for (int i = 0; i < count; ++i) {
TBBUTTON tbb;
TBBUTTON* rtbb = ralloc.create<TBBUTTON>();
BOOL rv = SendMessage(htb, TB_GETBUTTON, i, (LPARAM)rtbb);
ralloc.read(rtbb, &tbb);
ralloc.free(rtbb);
cout << rv << " " << sizeof(tbb) << " " << tbb.idCommand << " "
<< tbb.iString << endl << flush;
int chars = SendMessage(htb, TB_GETBUTTONTEXT, tbb.idCommand, (LPARAM)0);
if (chars <= 0) {
continue;
}
chars++;
wchar_t* rbuf = ralloc.create<wchar_t>(chars);
if (SendMessage(htb, TB_GETBUTTONTEXT, tbb.idCommand, (LPARAM)rbuf) > 0) {
wchar_t* buf = new wchar_t[chars];
ralloc.read(rbuf, buf);
wcout << buf << endl << flush;
delete[] buf;
}
}
}
catch (const exception& ex)
{
cerr << "Error: " << ex.what() << endl;
}
// Sleep
getchar();
return 0;
}
It seems you need to learn about classes in C++. What you are looking at is two functions. The first is called ralloc_t and is the class's constructor. The second is called ~ralloc_t and is the class's destructor.
// This is the constructor
ralloc_t(HWND hwnd) : proc_(0)
{
DWORD pid = 0;
if (!GetWindowThreadProcessId(hwnd, &pid)) {
throw exception("dang, no dice");
}
proc_ = OpenProcess(
PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION, FALSE, pid);
if (!proc_) {
throw exception("no open for me!");
}
}
// This is the destructor
~ralloc_t()
{
buffers_t::reverse_iterator i = buffers_.rbegin(), e = buffers_.rend();
for (; i != e; ++i) {
free(i->first);
}
}
Note that the constructor has the same name as the class (see the line class ralloc_t), and the destructor has the same name but prefixed with a tilde (~).
These functions, by their very nature, do not return anything. The purpose of the constructor is to initialise objects of type ralloc_t when they are constructed, and the purpose of the destructor is to clean up when they are destroyed.
For example, you might have a block of code that looks something like this:
{
ralloc_t my_ralloc(some_hwnd);
// ...
}
The constructor gets called as a result of the declaration of my_ralloc (passing some_hwnd as the constructor's argument). The destructor will get called at the end of the block, which is when the variable goes out of scope and is destroyed.
When unhandled exception occured i want to print a stacktrace instead of just terminating. I've tried to do that using SetUnhandledExceptionFilter:
SetUnhandledExceptionFilter(UnhandledException);
...
LONG WINAPI UnhandledException(LPEXCEPTION_POINTERS exceptionInfo)
{
printf("An exception occurred which wasn't handled!\nCode: 0x%08X\nAddress: 0x%08X",
exceptionInfo->ExceptionRecord->ExceptionCode,
exceptionInfo->ExceptionRecord->ExceptionAddress);
return EXCEPTION_EXECUTE_HANDLER;
}
This code, i've found, works fine. However there are no addition information because ExceptionCode and ExceptionAddress are printed in system "Event Viewer" anyway.
If it is possible to print a full stack trace so I can determine the exact point where exception occured?
I've found this code https://code.google.com/p/m0d-s0beit-sa/source/browse/src/main.cpp?r=9ceb4fec21d647b169c72851d7882bef2b9c5a8a It partly solves my problem. Only method where exception occured is printed. But type of exception and line number is not printed.
Here's some stack-walk code for Windows I wrote some years ago. Here's the kind of output it produces:
Walking stack.
0 DebugBreak
1 ThreadFunc2 e:\c\source\stackwalk2a.cpp(72)
2 ThreadFunc1 e:\c\source\stackwalk2a.cpp(79)
3 TargetThread e:\c\source\stackwalk2a.cpp(86)
4 BaseThreadInitThunk
5 RtlUserThreadStart
End of stack walk.
The main thing that's missing is anything about the exception type. If you're talking about a native structured/vectored exception, I'm pretty sure that should be retrievable too. Retrieving types of C++ exceptions might be a little more difficult (but I'm not really sure -- it might be pretty easy).
#include <windows.h>
#include <winnt.h>
#include <string>
#include <vector>
#include <Psapi.h>
#include <algorithm>
#include <iomanip>
#include <iostream>
#include <stdexcept>
#include <iterator>
#pragma comment(lib, "psapi.lib")
#pragma comment(lib, "dbghelp.lib")
// Some versions of imagehlp.dll lack the proper packing directives themselves
// so we need to do it.
#pragma pack( push, before_imagehlp, 8 )
#include <imagehlp.h>
#pragma pack( pop, before_imagehlp )
struct module_data {
std::string image_name;
std::string module_name;
void *base_address;
DWORD load_size;
};
typedef std::vector<module_data> ModuleList;
HANDLE thread_ready;
bool show_stack(std::ostream &, HANDLE hThread, CONTEXT& c);
DWORD __stdcall TargetThread( void *arg );
void ThreadFunc1();
void ThreadFunc2();
DWORD Filter( EXCEPTION_POINTERS *ep );
void *load_modules_symbols( HANDLE hProcess, DWORD pid );
int main( void ) {
DWORD thread_id;
thread_ready = CreateEvent( NULL, false, false, NULL );
HANDLE thread = CreateThread( NULL, 0, TargetThread, NULL, 0, &thread_id );
WaitForSingleObject( thread_ready, INFINITE );
CloseHandle(thread_ready);
return 0;
}
// if you use C++ exception handling: install a translator function
// with set_se_translator(). In the context of that function (but *not*
// afterwards), you can either do your stack dump, or save the CONTEXT
// record as a local copy. Note that you must do the stack dump at the
// earliest opportunity, to avoid the interesting stack-frames being gone
// by the time you do the dump.
DWORD Filter(EXCEPTION_POINTERS *ep) {
HANDLE thread;
DuplicateHandle(GetCurrentProcess(), GetCurrentThread(),
GetCurrentProcess(), &thread, 0, false, DUPLICATE_SAME_ACCESS);
std::cout << "Walking stack.";
show_stack(std::cout, thread, *(ep->ContextRecord));
std::cout << "\nEnd of stack walk.\n";
CloseHandle(thread);
return EXCEPTION_EXECUTE_HANDLER;
}
void ThreadFunc2() {
__try { DebugBreak(); }
__except (Filter(GetExceptionInformation())) { }
SetEvent(thread_ready);
}
void ThreadFunc1(void (*f)()) {
f();
}
// We'll do a few levels of calls from our thread function so
// there's something on the stack to walk...
//
DWORD __stdcall TargetThread(void *) {
ThreadFunc1(ThreadFunc2);
return 0;
}
class SymHandler {
HANDLE p;
public:
SymHandler(HANDLE process, char const *path=NULL, bool intrude = false) : p(process) {
if (!SymInitialize(p, path, intrude))
throw(std::logic_error("Unable to initialize symbol handler"));
}
~SymHandler() { SymCleanup(p); }
};
#ifdef _M_X64
STACKFRAME64 init_stack_frame(CONTEXT c) {
STACKFRAME64 s;
s.AddrPC.Offset = c.Rip;
s.AddrPC.Mode = AddrModeFlat;
s.AddrStack.Offset = c.Rsp;
s.AddrStack.Mode = AddrModeFlat;
s.AddrFrame.Offset = c.Rbp;
s.AddrFrame.Mode = AddrModeFlat;
return s;
}
#else
STACKFRAME64 init_stack_frame(CONTEXT c) {
STACKFRAME64 s;
s.AddrPC.Offset = c.Eip;
s.AddrPC.Mode = AddrModeFlat;
s.AddrStack.Offset = c.Esp;
s.AddrStack.Mode = AddrModeFlat;
s.AddrFrame.Offset = c.Ebp;
s.AddrFrame.Mode = AddrModeFlat;
return s;
}
#endif
void sym_options(DWORD add, DWORD remove=0) {
DWORD symOptions = SymGetOptions();
symOptions |= add;
symOptions &= ~remove;
SymSetOptions(symOptions);
}
class symbol {
typedef IMAGEHLP_SYMBOL64 sym_type;
sym_type *sym;
static const int max_name_len = 1024;
public:
symbol(HANDLE process, DWORD64 address) : sym((sym_type *)::operator new(sizeof(*sym) + max_name_len)) {
memset(sym, '\0', sizeof(*sym) + max_name_len);
sym->SizeOfStruct = sizeof(*sym);
sym->MaxNameLength = max_name_len;
DWORD64 displacement;
if (!SymGetSymFromAddr64(process, address, &displacement, sym))
throw(std::logic_error("Bad symbol"));
}
std::string name() { return std::string(sym->Name); }
std::string undecorated_name() {
std::vector<char> und_name(max_name_len);
UnDecorateSymbolName(sym->Name, &und_name[0], max_name_len, UNDNAME_COMPLETE);
return std::string(&und_name[0], strlen(&und_name[0]));
}
};
bool show_stack(std::ostream &os, HANDLE hThread, CONTEXT& c) {
HANDLE process = GetCurrentProcess();
int frame_number=0;
DWORD offset_from_symbol=0;
IMAGEHLP_LINE64 line = {0};
SymHandler handler(process);
sym_options(SYMOPT_LOAD_LINES | SYMOPT_UNDNAME);
void *base = load_modules_symbols(process, GetCurrentProcessId());
STACKFRAME64 s = init_stack_frame(c);
line.SizeOfStruct = sizeof line;
IMAGE_NT_HEADERS *h = ImageNtHeader(base);
DWORD image_type = h->FileHeader.Machine;
do {
if (!StackWalk64(image_type, process, hThread, &s, &c, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL))
return false;
os << std::setw(3) << "\n" << frame_number << "\t";
if ( s.AddrPC.Offset != 0 ) {
std::cout << symbol(process, s.AddrPC.Offset).undecorated_name();
if (SymGetLineFromAddr64( process, s.AddrPC.Offset, &offset_from_symbol, &line ) )
os << "\t" << line.FileName << "(" << line.LineNumber << ")";
}
else
os << "(No Symbols: PC == 0)";
++frame_number;
} while (s.AddrReturn.Offset != 0);
return true;
}
class get_mod_info {
HANDLE process;
static const int buffer_length = 4096;
public:
get_mod_info(HANDLE h) : process(h) {}
module_data operator()(HMODULE module) {
module_data ret;
char temp[buffer_length];
MODULEINFO mi;
GetModuleInformation(process, module, &mi, sizeof(mi));
ret.base_address = mi.lpBaseOfDll;
ret.load_size = mi.SizeOfImage;
GetModuleFileNameEx(process, module, temp, sizeof(temp));
ret.image_name = temp;
GetModuleBaseName(process, module, temp, sizeof(temp));
ret.module_name = temp;
std::vector<char> img(ret.image_name.begin(), ret.image_name.end());
std::vector<char> mod(ret.module_name.begin(), ret.module_name.end());
SymLoadModule64(process, 0, &img[0], &mod[0], (DWORD64)ret.base_address, ret.load_size);
return ret;
}
};
void *load_modules_symbols(HANDLE process, DWORD pid) {
ModuleList modules;
DWORD cbNeeded;
std::vector<HMODULE> module_handles(1);
EnumProcessModules(process, &module_handles[0], module_handles.size() * sizeof(HMODULE), &cbNeeded);
module_handles.resize(cbNeeded/sizeof(HMODULE));
EnumProcessModules(process, &module_handles[0], module_handles.size() * sizeof(HMODULE), &cbNeeded);
std::transform(module_handles.begin(), module_handles.end(), std::back_inserter(modules), get_mod_info(process));
return modules[0].base_address;
}