Global keyboard hook in C using DLL doesn't work - c

I tried to do global keyboard hooking in C and got in trouble.
My purpose is that when I type F2 key or Ctrl key, some lines will be printed to check whether it works or not, and if I press the q key, it will stop hooking.
To test the whole code, I've used printf in some parts.
I have no idea how to figure out this problem, but really want to solve it.
.cpp code:
#include "stdio.h"
#include "conio.h"
#include "windows.h"
#define DEF_HOOKSTART "HookStart"
#define DEF_HOOKSTOP "HookStop"
typedef void(*PFN_HOOKSTART)();
typedef void(*PFN_HOOKSTOP)();
void main() {
HMODULE hDll = NULL;
PFN_HOOKSTART HookStart = NULL;
PFN_HOOKSTOP HookStop = NULL;
char ch = 0;
// load Dll
hDll = LoadLibrary(L"HookTest.dll");
if (hDll == NULL) {
printf("load fail \n");
return;
}
else {
printf("load success \n");
}
HookStart = (PFN_HOOKSTART)GetProcAddress(hDll, DEF_HOOKSTART);
HookStop = (PFN_HOOKSTOP)GetProcAddress(hDll, DEF_HOOKSTOP);
HookStart();
printf("press 'q' to quit!\n");
// if user push 'q' key, HookStop
while (1) {
if (_getch() == 'q') {
break;
}
printf("Test Typing \n");
}
HookStop();
FreeLibrary(hDll);
}
.dll code:
#include <stdio.h>
#include <windows.h>
#pragma data_seg(".kbdata")
HINSTANCE g_hInstance = NULL;
HHOOK g_hHook = NULL;
HWND g_hWnd = NULL;
#pragma data_seg()
#pragma comment(linker, "/SECTION:.kbdata,RWS")
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpvReserved) {
switch (dwReason) {
case DLL_PROCESS_ATTACH:
g_hInstance = hinstDLL;
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
extern "C" {
LRESULT __declspec(dllexport) CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) {
printf("Test 1 --- KeyboardProc sucess \n");
if (nCode >= 0) {
printf("Test 2 --- \n");
if (wParam == VK_F2) {
printf("F2 Key Typed ");
}
else if (wParam == VK_CONTROL) {
printf("Ctrl Key Typed ");
}
else {
printf("Other Key Typed");
}
}
return CallNextHookEx(g_hHook, nCode, wParam, lParam);
}
}
extern "C" __declspec(dllexport) void HookStart() {
g_hHook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, g_hInstance, 0);
printf("Test 3 --- HookStart Call \n");
}
extern "C" __declspec(dllexport) void HookStop() {
printf("Test 4 --- HookStop Call \n");
if (g_hHook) {
UnhookWindowsHookEx(g_hHook);
g_hHook = NULL;
}
}

Related

SetWindowsHookEx - difference between VK_LSHIFT and "Search" keyboard button

I use SetWindowsHookEx to catch keyboard events
SetWindowsHookEx(WH_KEYBOARD_LL, HookCallback, NULL, 0)
Here is HookCallback
LRESULT __stdcall HookCallback(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode >= 0)
{
// the action is valid: HC_ACTION.
if (wParam == WM_KEYDOWN)
{
kbdStruct = *((KBDLLHOOKSTRUCT*)lParam);
printf("%ld\n", kbdStruct.vkCode);
}
}
}
When press "left shift" output is
160
when press "search" button (button with loupe icon on notebooks) output is
160
91
132
How to check if "left shift" or "search" button is pressed inside HookCallback ?
160 is VK_LSHIFT, 91 is VK_LWIN, and 132 is VK_F21. See Virtual-Key Codes. The only standardized Search key is VK_BROWSER_SEARCH, so clearly your keyboard manufacturer is using a non-standard key for its Search key.
You need to remember the keys you see from one event to the next as needed, eg:
bool LeftShiftIsDown = false;
bool LeftWinIsDown = false;
bool F21IsDown = false;
bool SearchIsDown = false;
LRESULT __stdcall HookCallback(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode == HC_ACTION)
{
KBDLLHOOKSTRUCT *kbdStruct = (KBDLLHOOKSTRUCT*) lParam;
if (wParam == WM_KEYDOWN)
{
printf("%ld is down\n", kbdStruct->vkCode);
switch (kbdStruct->vkCode)
{
case VK_LSHIFT:
LeftShiftIsDown = true;
break;
case VK_LWIN:
LeftWinIsDown = true;
break;
case VK_F21:
F21IsDown = true;
break;
}
if (LeftShiftIsDown && LeftWinIsDown && F21IsDown)
{
if (!SearchIsDown)
{
SearchIsDown = true;
printf("Search is down\n");
}
}
}
else if (wParam == WM_KEYUP)
{
printf("%ld is up\n", kbdStruct->vkCode);
switch (kbdStruct->vkCode)
{
case VK_LSHIFT:
LeftShiftIsDown = false;
break;
case VK_LWIN:
LeftWinIsDown = false;
break;
case VK_F21:
F21IsDown = false;
break;
}
if (!(LeftShiftIsDown && LeftWinIsDown && F21IsDown))
{
if (SearchIsDown)
{
SearchIsDown = false;
printf("Search is up\n");
}
}
}
}
return CallNextHookEx(0, nCode, wParam, lParam);
}

Custom WM_APP message isn't posted in the message queue

I am trying to send a custom message (WM_APP + 1) when WM_SIZE is send to the window procedure. I want to be able to catch it from an other function using PeekMessage and do somethings. But when I test it the messages seem to not be send to the queue. Adding some printf statements shows me that it goes to the window procedure. The weird thing is that when I step through the code in the debugger it works fine but when I'm running normally, it goes back to not working.
Working example program with the problem, resize the window to test:
#include <windows.h>
#include <stdio.h>
#pragma comment(lib, "user32.lib")
#define OBG_EVENT_QUIT 0
#define OBG_EVENT_RESIZE 1
#define OBG_EVENT_NO -1
#define OBG_EVENT_UNKNOWN -2
//user defined event
#define OBG_WM_RESIZE (WM_APP + 1)
typedef union
{
int type;
struct
{
int type;
int width;
int height;
} resizeEvent;
} obg_event;
LRESULT CALLBACK obgpf_DefaultWindowCallback(HWND window, UINT message, WPARAM wParam, LPARAM lParam)
{
LRESULT result = 0;
switch(message)
{
case WM_CLOSE:
{
PostMessageA(window, WM_QUIT, 0, 0);
} break;
//this should be handled by OBGGetEvent
case OBG_WM_RESIZE:
{
printf("MESSAGE WENT THROUGH. DON'T WANT THIS\n");
} break;
case WM_SIZE:
{
PostMessageA(window, OBG_WM_RESIZE, wParam, lParam);
} break;
default:
{
result = DefWindowProc(window, message, wParam, lParam);
} break;
}
return result;
}
int OBGGetEvent(obg_event *event)
{
int moreMessages = 0;
MSG message;
if(PeekMessage(&message, 0, 0, 0, PM_REMOVE))
{
moreMessages = 1;
switch(message.message)
{
case WM_QUIT:
{
event->type = OBG_EVENT_QUIT;
} break;
case OBG_WM_RESIZE:
{
event->type = OBG_EVENT_RESIZE;
event->resizeEvent.type = OBG_EVENT_RESIZE;
event->resizeEvent.width = LOWORD(message.lParam);
event->resizeEvent.height = HIWORD(message.lParam);
} break;
default:
{
event->type = OBG_EVENT_UNKNOWN;
TranslateMessage(&message);
DispatchMessage(&message);
} break;
}
}
else
{
event->type = OBG_EVENT_NO;
}
return moreMessages;
}
int main()
{
HINSTANCE instance = GetModuleHandleA(0);
WNDCLASSEX windowClass = {0};
windowClass.cbSize = sizeof(windowClass);
windowClass.style = CS_HREDRAW | CS_VREDRAW;
windowClass.lpfnWndProc = obgpf_DefaultWindowCallback;
windowClass.hInstance = instance;
windowClass.lpszClassName = "testClass";
windowClass.hIcon = LoadIcon(0, IDI_APPLICATION);
windowClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
windowClass.hIconSm = LoadIcon(0, IDI_APPLICATION);
windowClass.hCursor = LoadCursorA(0, IDC_ARROW);
HWND window;
if(RegisterClassEx(&windowClass))
{
window = CreateWindowEx(0,
windowClass.lpszClassName,
"test window",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT,
CW_USEDEFAULT,
500,
300,
0,
0,
instance,
0);
if(window)
{
int appIsRunning = 1;
obg_event event = {0};
event.type = -1;
while(appIsRunning)
{
while(OBGGetEvent(&event))
{
if(event.type == OBG_EVENT_QUIT)
{
printf("event quit\n");
appIsRunning = 0;
break;
}
else if(event.type == OBG_EVENT_RESIZE)
{
printf("window resized: width %d height %d\n", event.resizeEvent.width, event.resizeEvent.height);
}
}
Sleep(33);
}
}
else
{
printf("window error\n");
}
}
else
{
printf("windowClass error\n");
}
return 0;
}
I tried doing this with SendMessage instead of PeekMessage but the same thing happened. Not sure what I'm missing or misunderstanding but any help is appreciated!
EDIT: added a complete working program that reproduces the problem
Thank you for the example code.
The behaviour you're seeing is because when the window is being resized using the mouse, the OS enters a modal message loop to process mouse input. During this loop, your message loop doesn't run - which means your PeekMessage() and the special message handling doesn't run either. Instead, messages are simply dispatched as normal to your window procedure.
There are two solutions that come to mind immediately but how you deal with this really depends on the design of your program and why you want to process size events in this way.
The first idea I had is to keep track of whether you're in a modal sizing loop or not, and defer posting the notification message until the loop is finished. An example of how to do that using your provided window procedure is below.
The second solution is to simply call your resize event handler directly whenever you get WM_SIZE (or, if you must go through the event system, put the handler for it in the window procedure rather than using PostMessage).
Example code for the first suggestion:
LRESULT CALLBACK obgpf_DefaultWindowCallback(HWND window, UINT message, WPARAM wParam, LPARAM lParam)
{
LRESULT result = 0;
static bool fResized = false;
static bool fInSizeLoop = false;
switch(message)
{
case WM_CLOSE:
{
PostMessageA(window, WM_QUIT, 0, 0);
} break;
//this should be handled by OBGGetEvent
case OBG_WM_RESIZE:
{
printf("MESSAGE WENT THROUGH. DON'T WANT THIS\n");
} break;
case WM_SIZE:
{
if (fInSizeLoop) // in modal size loop, defer notification
fResized = true;
else
PostMessageA(window, OBG_WM_RESIZE, wParam, lParam);
} break;
case WM_ENTERSIZEMOVE:
fInSizeLoop = true; // begin modal size loop
break;
case WM_EXITSIZEMOVE:
fInSizeLoop = false; // left modal size loop
// post resize notification now
if (fResized) {
RECT rc;
GetClientRect(window, &rc);
PostMessageA(window, OBG_WM_RESIZE, 0, MAKELPARAM(rc.right - rc.left, rc.bottom - rc.top));
fResized = false;
}
break;
default:
{
result = DefWindowProc(window, message, wParam, lParam);
} break;
}
return result;
}
(sorry, this is C++ code and I just noticed you had tagged as C - but the principle is the same).

Window GUI for DirectX Game seems to be faulty

I wanna developp a game using DirectX in Windows10. I use to write code with CodeBlocks. Following code will throw out error like this:
undefined reference to `GetStockObject#4'
What is problem with this Windowsfunction? I don't have to initialize or to write this function seperatly, don't I? Commenting out error code will compile code, but there is no gui...
Here is code:
#include <windows.h>
#include"Frame.h"
//global definitions
BOOL done;
int Spiel_Zustand;
HWND hwnd;
//Function:CallBack
LRESULT CALLBACK WindowProc (HWND hwnd,UINT message, WPARAM wparam,LPARAM lparam)
{
switch(message)
{
case WM_DESTROY :
{
done=TRUE;
PostQuitMessage(0);
return 0;
}
break;
case WM_KEYDOWN:
switch(wparam)
{
case VK_ESCAPE:
{
PostMessage(hwnd,WM_CLOSE,0,0);
return 0;
}
break;
}
break;
default:
break;
}//End of switch
return DefWindowProc(hwnd,message,wparam,lparam);
}//End of CallBack
//Function:Start
int WINAPI WinMain(HINSTANCE hinst,HINSTANCE hprevinst,LPSTR lpcmdline,int ncmdshow)
{
WNDCLASSEX winclass;
MSG message;
const char szclassname[]="Klassenname";
DWORD loop_start_time;
winclass.cbSize=sizeof(WNDCLASSEX);
winclass.style=CS_HREDRAW|CS_VREDRAW;
winclass.lpfnWndProc=WindowProc;
winclass.cbClsExtra=0;
winclass.cbWndExtra=0;
winclass.hInstance=hinst;
winclass.hIcon=LoadIcon(NULL,IDI_APPLICATION);
winclass.hCursor=LoadCursor(NULL,IDC_ARROW);
winclass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);//this will throw out error
winclass.lpszClassName=szclassname;
winclass.hIconSm=LoadIcon(NULL,IDI_APPLICATION);
if(!RegisterClassEx(&winclass))
return 0;
//Create handle of windows ans save it globally
if(!(hwnd=CreateWindowEx(NULL,szclassname,"3D Gameprograming - Title",WS_POPUPWINDOW|WS_VISIBLE,0,0,400,300,NULL,NULL,hinst,NULL)))
return 0;
done=FALSE;
Spiel_Zustand=SPIEL_AUSWAHL;
//Phase 1.1:
Spiel_Initialisieren();
//Phase 1.2:: Main Looping
while(!done)
{
//Any message to handle?
while(GetMessage(&message,NULL,0,0))
{
TranslateMessage(&message);
DispatchMessage(&message);
}
//Start time of Main Loop
loop_start_time=GetTickCount;
switch(Spiel_Zustand)
{
case SPIEL_AUSWAHL:
{
//[....]
Spiel_Zustand=SPIEL_START;
}
break;
case SPIEL_START:
{
//[...]
Spiel_Zustand=SPIEL_LAEUFT;
}
break;
case SPIEL_LAEUFT:
{
//waiting for User input
//Manage User input
//Artificial Intelligence and game logic
//Rendering Frame
} break;
case SPIEL_NEUSTART:
{
//[...]
Spiel_Zustand=SPIEL_START;
}
break;
default:
break;
}//End of switch
//Restricting Frame rate
while((GetTickCount()-loop_start_time)<40);
}//End of Main Looping
//Phase 1.3
Spiel_Beenden();
return message.wParam;
}//End of Start
//Phase 2.1
int Spiel_Initialisieren(void)
{
//ToDO:initializations etc..
return 1;
}
//Phase 2.2
int Spiel_Beenden(void)
{
//ToDO:Free Memory etc..
return 1;
}
You need to link gdi32.lib when you compile -lgdi32
Undefined reference to GetStockObject#4

Simple if comparison doesn't work with char

I am creating a DLL to check when the user presses the close button on the application window, it works fine, then I want to do it so that when _text global variable is empty, no message will be shown.
I can't understand why a simple if (_text != "") doesn't work... am I doing something wrong?
#include <windows.h>
#define export extern "C" __declspec (dllexport)
WNDPROC GameWndProc = NULL;
HWND GameHwnd = NULL;
double _button_result = 0;
char* _text;
char* _title;
LRESULT CALLBACK SubClassWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg) {
case WM_CLOSE:
if (_text != "") {
if (MessageBox(GameHwnd, (LPSTR)_text, (LPSTR)_title, MB_YESNO|MB_APPLMODAL) == IDYES) {
_button_result = 1;
return 0;
} else {
return 0;
}
}
_button_result = 1;
return 0;
break;
}
return CallWindowProc(GameWndProc, hwnd, uMsg, wParam, lParam);
}
export double _window_check_close_init(double window_handle, char* _msg_text, char* _msg_title)
{
GameHwnd = (HWND)(int)window_handle;
GameWndProc = (WNDPROC)SetWindowLongPtr(GameHwnd, GWL_WNDPROC, (LONG)SubClassWndProc);
_text = _msg_text;
_title = _msg_title;
if (!GameWndProc) {
return 0;
}
return 1;
}
export double _window_check_close()
{
if (_button_result == 1) {
_button_result = 0;
return 1;
} else {
return 0;
}
}
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
if (fdwReason == DLL_PROCESS_DETACH) {
if (IsWindow(GameHwnd) && GameWndProc) {
SetWindowLongPtr(GameHwnd, GWL_WNDPROC, (LONG)GameWndProc);
}
}
return TRUE;
}
You're checking if the pointer _text is equal to the empty string, not if the string pointed to by _text is equal to the empty string. You probably want to do something like:
if (strlen(_text) !=0)
... rest of your code...
Use strcmp() to compare strings:
if (strcmp(_text, "") != 0) {
}
Just to clarify the difference between pointers and contents of the location to which pointer points:
char* _text : The variable 'text' is a pointer. If you look at it in a debugger it would hold a hex value which would be a memory address. In your case _text holds the address of the string _msg_text
_text != "" : You are comparing two pointers, not their contents. "" in this statement is returning the location of the null string which would again be some hex value which is a memory address.
What you need to do is compare the contents of _text with the null string ""
The above answers are correct.

Trouble using Windows MIDI API (no callbacks when playing)

I have an USB-connected MIDI-keyboard. It works fine in other applications. However, in my own program it does not. The midiInOpen() call goes through, I get one callback (from opening the device) but I don't get any callbacks when playing the keyboard.
By using midiInGetDevCaps() I can see that I'm using the correct device.
Any ideas? Could the other (commercial) applications use some other API?
static void CALLBACK myFunc(HMIDIIN handle, UINT uMsg,
DWORD dwInstance, DWORD dwParam1, DWORD dwParam2) {
printf("Callback!"\n);
}
int main() {
unsigned long result;
HMIDIIN inHandle;
result = midiInOpen(&inHandle, 0, (DWORD)myFunc, 0, CALLBACK_FUNCTION);
if (result)
{
printf("There was an error opening MIDI\n");
}
while(1) { Sleep(1); }
}
You need to call midiInstart. You also need to pump messages. Nothing is going to happen if you're calling Sleep and not servicing input.
Here's a bit of a tutorial on Windows MIDI.
Here's an extract of a class (WinMidiIn) I wrote for Win MIDI input (lots of error handling removed, class member types can be inferred from the calls they are passed to as params):
{
MMRESULT res = ::midiInOpen(&mMidiIn, mDeviceIdx, (DWORD_PTR)MidiInCallbackProc, (DWORD_PTR)this,
CALLBACK_FUNCTION | MIDI_IO_STATUS);
if (MMSYSERR_NOERROR != res)
return;
const int kDataBufLen = 512;
int idx;
for (idx = 0; idx < MIDIHDR_CNT; ++idx)
{
mMidiHdrs[idx].lpData = (LPSTR) ::malloc(kDataBufLen);
mMidiHdrs[idx].dwBufferLength = kDataBufLen;
res = ::midiInPrepareHeader(mMidiIn, &mMidiHdrs[idx], (UINT)sizeof(MIDIHDR));
res = ::midiInAddBuffer(mMidiIn, &mMidiHdrs[idx], sizeof(MIDIHDR));
}
res = ::midiInStart(mMidiIn);
for (;;)
{
DWORD result;
MSG msg;
// Read all of the messages in this next loop,
// removing each message as we read it.
while (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
// If it is a quit message, exit.
if (msg.message == WM_QUIT)
break;
// Otherwise, dispatch the message.
::DispatchMessage(&msg);
}
// Wait for any message sent or posted to this queue
// or for one of the passed handles be set to signaled.
result = ::MsgWaitForMultipleObjects(1, &mDoneEvent, FALSE, INFINITE, QS_ALLINPUT);
// The result tells us the type of event we have.
if (result == (WAIT_OBJECT_0 + 1))
{
// New messages have arrived.
// Continue to the top of the always while loop to
// dispatch them and resume waiting.
continue;
}
else if (WAIT_TIMEOUT == result)
continue;
else if (WAIT_OBJECT_0 == result)
break; // done event fired
else
break; // ??
}
res = ::midiInReset(mMidiIn);
for (idx = 0; idx < MIDIHDR_CNT; ++idx)
{
res = ::midiInUnprepareHeader(mMidiIn, &mMidiHdrs[idx], (UINT)sizeof(MIDIHDR));
::free(mMidiHdrs[idx].lpData);
mMidiHdrs[idx].lpData = NULL;
}
res = ::midiInClose(mMidiIn);
mMidiIn = NULL;
}
void CALLBACK
MidiInCallbackProc(HMIDIIN hmi,
UINT wMsg,
DWORD dwInstance,
DWORD dwParam1,
DWORD dwParam2)
{
MMRESULT res;
LPMIDIHDR hdr;
WinMidiIn * _this = (WinMidiIn *) dwInstance;
switch (wMsg)
{
case MIM_DATA:
// dwParam1 is the midi event with status in the low byte of the low word
// dwParam2 is the event time in ms since the start of midi in
// data: LOBYTE(dwParam1), HIBYTE(dwParam1), LOBYTE(HIWORD(dwParam1))
break;
case MIM_ERROR:
break;
case MIM_LONGDATA:
// dwParam1 is the lpMidiHdr
// dwParam2 is the event time in ms since the start of midi in
hdr = (LPMIDIHDR) dwParam1;
// sysex: (byte*)hdr->lpData, (int)hdr->dwBytesRecorded
res = ::midiInAddBuffer(_this->mMidiIn, hdr, sizeof(MIDIHDR));
break;
case MIM_LONGERROR:
hdr = (LPMIDIHDR) dwParam1;
res = ::midiInAddBuffer(_this->mMidiIn, hdr, sizeof(MIDIHDR));
break;
}
}
You can find an example here https://gist.github.com/yoggy/1485181
I post the code here in case the link becomes dead
You may be missing the midiInStart()
#include <SDKDDKVer.h>
#include <Windows.h>
#include <stdio.h>
#include <conio.h>
#include <mmsystem.h>
#pragma comment(lib, "winmm.lib")
void PrintMidiDevices()
{
UINT nMidiDeviceNum;
MIDIINCAPS caps;
nMidiDeviceNum = midiInGetNumDevs();
if (nMidiDeviceNum == 0) {
fprintf(stderr, "midiInGetNumDevs() return 0...");
return;
}
printf("== PrintMidiDevices() == \n");
for (unsigned int i = 0; i < nMidiDeviceNum; ++i) {
midiInGetDevCaps(i, &caps, sizeof(MIDIINCAPS));
printf("\t%d : name = %s\n", i, caps.szPname);
}
printf("=====\n");
}
void CALLBACK MidiInProc(HMIDIIN hMidiIn, UINT wMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)
{
switch(wMsg) {
case MIM_OPEN:
printf("wMsg=MIM_OPEN\n");
break;
case MIM_CLOSE:
printf("wMsg=MIM_CLOSE\n");
break;
case MIM_DATA:
printf("wMsg=MIM_DATA, dwInstance=%08x, dwParam1=%08x, dwParam2=%08x\n", dwInstance, dwParam1, dwParam2);
break;
case MIM_LONGDATA:
printf("wMsg=MIM_LONGDATA\n");
break;
case MIM_ERROR:
printf("wMsg=MIM_ERROR\n");
break;
case MIM_LONGERROR:
printf("wMsg=MIM_LONGERROR\n");
break;
case MIM_MOREDATA:
printf("wMsg=MIM_MOREDATA\n");
break;
default:
printf("wMsg = unknown\n");
break;
}
return;
}
int main(int argc, char* argv[])
{
HMIDIIN hMidiDevice = NULL;;
DWORD nMidiPort = 0;
UINT nMidiDeviceNum;
MMRESULT rv;
PrintMidiDevices();
nMidiDeviceNum = midiInGetNumDevs();
if (nMidiDeviceNum == 0) {
fprintf(stderr, "midiInGetNumDevs() return 0...");
return -1;
}
rv = midiInOpen(&hMidiDevice, nMidiPort, (DWORD)(void*)MidiInProc, 0, CALLBACK_FUNCTION);
if (rv != MMSYSERR_NOERROR) {
fprintf(stderr, "midiInOpen() failed...rv=%d", rv);
return -1;
}
midiInStart(hMidiDevice);
while(true) {
if (!_kbhit()) {
Sleep(100);
continue;
}
int c = _getch();
if (c == VK_ESCAPE) break;
if (c == 'q') break;
}
midiInStop(hMidiDevice);
midiInClose(hMidiDevice);
hMidiDevice = NULL;
return 0;
}

Resources