В большинстве случаев обратный вызов вызывается из другого потока. Чтобы доказать это, рассмотрим этот пример программы, адаптированный из midi примера программы gist :
#include <Windows.h>
#include <stdio.h>
#include <conio.h>
#include <mmsystem.h>
#pragma comment(lib, "winmm.lib")
void CALLBACK MidiInProc(HMIDIIN hMidiIn, UINT wMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
{
printf("Callback thread id=%ld\n", GetCurrentThreadId());
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=%Ix, dwParam1=%Ix, dwParam2=%Ix\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 = nullptr;;
DWORD nMidiPort = 2;
UINT nMidiDeviceNum;
MMRESULT rv;
printf("Main thread id=%ld\n", GetCurrentThreadId());
nMidiDeviceNum = midiInGetNumDevs();
if (nMidiDeviceNum == 0) {
fprintf(stderr, "midiInGetNumDevs() return 0...");
return -1;
}
rv = midiInOpen(&hMidiDevice, nMidiPort, (DWORD_PTR)(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);
return 0;
}
, выполняющего ее в моей системе с подключенными 3 MIDI-устройствами (# 2 - контроллер), Я получаю этот вывод после нажатия и отпускания одной клавиши:
Main thread id=9656
Callback thread id=9656
wMsg=MIM_OPEN
Callback thread id=5684
wMsg=MIM_DATA, dwInstance=0, dwParam1=513190, dwParam2=cfb
Callback thread id=5684
wMsg=MIM_DATA, dwInstance=0, dwParam1=403180, dwParam2=eaa
Вы можете проверить в ProcessHacker2 или в SysInternals ' ProcessExplorer вашу программу потоков во время ее работы:
Вы можете заметить, что в вашем процессе есть как минимум 2 идентификатора потока: 9656 и 5684. Ваш идентификатор потока функции main()
равен 9656, и обратные вызовы для Вызовы функций midiInOpen()
и midiInClose()
выводят один и тот же идентификатор. Но для событий примечания идентификатор потока равен 5684. И начальный адрес этого потока соответствует модулю wdmaud.drv
, который является драйвером Windows.
Это типичный сценарий для любого процесса, обрабатывающего ввод MIDI : производитель и потребитель проблема. Ваш подход обоснован: вы ставите в очередь полученные MIDI-события в функции обратного вызова (продюсер), а затем другой поток потребляет события в очереди. Вы найдете множество реализаций без блокировки кольца , которые подходят для этой задачи.
Существует другой вариант функции midiInOpen()
, который использует флаги последнего аргумента CALLBACK_WINDOW или CALLBACK_THREAD. В этом случае вместо функции обратного вызова вы предоставляете дескриптор окна или идентификатор потока для Windows, и ваша процедура окна или потока будет получать MIDI-сообщения, помещенные в очередь и чередующиеся с другими несвязанными событиями windows. Я предпочитаю использовать CALLBACK_FUNCTION.