#include "pch.h"

#pragma warning(disable:4091)   // dbghelp.h in Win8.1 has unnamed typedef'd enums
#include <dbghelp.h>
#pragma warning(default:4091)

typedef BOOL
(WINAPI *PMiniDumpWriteDump)(
    IN HANDLE hProcess,
    IN DWORD ProcessId,
    IN HANDLE hFile,
    IN MINIDUMP_TYPE DumpType,
    IN CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, OPTIONAL
    IN CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, OPTIONAL
    IN CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam OPTIONAL
    );

DWORD ExceptionMessage(LPSTR Proc, LPEXCEPTION_POINTERS ep) {
#ifndef _DEBUG
  UNREFERENCED_PARAMETER(Proc);
#endif

  MINIDUMP_EXCEPTION_INFORMATION mei;
  char filename[MAX_PATH], temppath[MAX_PATH];
  if (GetTempPath(MAX_PATH, temppath) == 0 ||
      GetTempFileName(temppath, "kmc", 0, filename) == 0) {
    OutputThreadDebugString("Minidump failed to generate temp file name\n");
  } 
  else {
    HANDLE hFile = CreateFile(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
    if (!hFile) {
      OutputThreadDebugString("Minidump failed to create file ");
      OutputThreadDebugString(filename);
    }
    else {
      mei.ClientPointers = TRUE;
      mei.ExceptionPointers = ep;
      mei.ThreadId = GetCurrentThreadId();
      HMODULE hDbgHelp = LoadLibrary("dbghelp.dll");
      if (!hDbgHelp) {
        OutputThreadDebugString("dbghelp.dll not available");
      }
      else {
        PMiniDumpWriteDump mdwd = (PMiniDumpWriteDump) GetProcAddress(hDbgHelp, "MiniDumpWriteDump");
        if (!mdwd) {
          OutputThreadDebugString("MiniDumpWriteDump not available");
        }
        else {
          if (!(*mdwd)(GetCurrentProcess(), GetCurrentProcessId(), hFile,
            (MINIDUMP_TYPE)(MiniDumpWithDataSegs | MiniDumpWithHandleData),
            &mei, NULL, NULL)) {
            DebugLastError("MiniDumpWriteDump");
          } 
          else {
            OutputThreadDebugString("Minidump written to ");
            OutputThreadDebugString(filename);
            HKEY hkey;
            if(RegCreateKeyEx(HKEY_CURRENT_USER, REGSZ_KeymanEngineDiag, 0, NULL, 0, KEY_ALL_ACCESS,
              NULL, &hkey, NULL) == ERROR_SUCCESS) {
              DWORD v = 0;
              RegSetValueEx(hkey, filename, 0, REG_DWORD, (PBYTE)&v, sizeof(DWORD));
              RegCloseKey(hkey);
            }
          }
        }
        FreeLibrary(hDbgHelp);
      }
      CloseHandle(hFile);
    }
  }

  if(!ep || !ep->ExceptionRecord) {
    OutputThreadDebugString("CAUGHT UNKNOWN EXCEPTION");
    return EXCEPTION_CONTINUE_SEARCH;
  }
  LPEXCEPTION_RECORD er = ep->ExceptionRecord;
  
  while(er != NULL) {
    if (er->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) {
      OutputThreadDebugString("EXCEPTION_ACCESS_VIOLATION in ");
      OutputThreadDebugString(Proc);
      /*SendDebugMessageFormat(0, sdmGlobal, 0, "CAUGHT EXCEPTION %d (ACCESS VIOLATION) IN %s AT %x; attempted to %s %x",
        er->ExceptionCode, Proc, er->ExceptionAddress,
        er->ExceptionInformation[0] == 0 ? "read from" : "write to",
        er->ExceptionInformation[1]);*/
    }
    else {
      OutputThreadDebugString("CAUGHT EXCEPTION");
      /*SendDebugMessageFormat(0, sdmGlobal, 0, "CAUGHT EXCEPTION %d IN %s AT %x",
        er->ExceptionCode, Proc, er->ExceptionAddress);*/
    }
    er = er->ExceptionRecord;
  }

  //StackWalker sw(StackWalker::RetrieveModuleInfo | StackWalker::RetrieveFileVersion, NULL, GetCurrentProcessId(), GetCurrentProcess());
  //sw.ShowCallstack(GetCurrentThread(), ep->ContextRecord);

  return EXCEPTION_CONTINUE_SEARCH;
}
