C++ Procedura CALLBACK dla MCI

0

Witam
Mam projekt aplikacji konsolowej i próbuję bezskutecznie podpiąć pod polecenie mciSendString lub mciSendCommand procedurę callback. Mój CALLBACK siedzi jako statyczny w klasie.


class SoundManager
{
private:

	static LRESULT CALLBACK PlayCallback(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
	{
		MessageBox(0, "Tekst1", "Tekst2", 0);
		
		switch(uMsg)
		{
		case MM_MCINOTIFY:
			MessageBox(0, "Tekst1", "Tekst2", 0);
			//return 0;
		default:
			MessageBox(0, "Tekst1", "Tekst2", 0);
			//return WndProc(hwnd,uMsg,wParam,lParam);
		}
		return PlayCallback(hwnd,uMsg,wParam,lParam);
	}

public:

	static void PlaySound (unsigned int soundID, bool repeat = false)
	{
		if (SoundIsPlaying(soundID))
			return;

		playingSounds->push_back(&soundID);
		std::string filePath = GetSoundFilePathByID(soundID);
		std::string soundAlias = "S" + std::to_string(soundID);

		MCI_OPEN_PARMS mci_op;

		mci_op.lpstrAlias = soundAlias.c_str();
		mci_op.dwCallback = (DWORD)SoundManager::PlayCallback;
		mci_op.lpstrDeviceType = "waveaudio";
		mci_op.lpstrElementName = filePath.c_str();

		if (!mciSendCommand(NULL, MCI_OPEN, MCI_OPEN_TYPE | MCI_OPEN_ELEMENT, (DWORD)(LPVOID)&mci_op))
		{
			MCI_PLAY_PARMS mci_pp;
			mci_pp.dwCallback = (DWORD)SoundManager::PlayCallback;
			UINT deviceID = mci_op.wDeviceID;
			mciSendCommand(deviceID, MCI_PLAY, MCI_NOTIFY, (DWORD)(LPVOID)&mci_pp);
		}
	}
}

To jest sposób z mciSendCommand, robiłem też z mciSendString tak, że dopisywałem do komendy parametr " notify", a ostatni 4 parametr ustawiałem (DWORD)SoundManager::PlayCallback ale to też nie działało.
Znalazłem gdzieś sposób, żeby pobrać HWND okna i ustawić inną procedurę WndProc i próbowałem to robić pobierałem HWND GetConsoleWindow() i ustawiałem SoundManager::PlayCallback w funkcji SetWindowLong, ale też nie działało. Potem odnalazłem funkcję mciSetYieldProc ale nie wiem czy ta funkcja to jest właśnie to czego chcę (potrzebuję wiedzieć kiedy odtwarzanie się skończyło)

1

Hmm, niezależnie od całego MCI... Gdyby twój PlayCallback ruszył, to następnie rekurencyjnie by się wywoływał w nieskończoność, aż do przepełnienia stosu :|

static LRESULT CALLBACK PlayCallback(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        // coś tam robisz w switch... a potem zawsze dochodzisz to linijki:
        return PlayCallback(hwnd,uMsg,wParam,lParam);
    }

Ogólnie sprawa rysuje się tak: Musisz stworzyć sobie w tle wątek (funkcja CreateThread). W w tym wątku musisz sobie utworzyć pomocnicze okno, a w obsłudze tego okna odbierać MM_MCINOTIFY. Klasę okna rejestrujesz funkcją RegisterClassEx, wypełniając strukturę WNDCLASSEX. Większość pól domyślnie, z istotnych to będzie hInstance oraz lpfnWndProc. Pierwsze ustawiasz na GetModuleHandle(NULL) - jako aplikacja konsolowa, to chyba innego sposobu nie ma. A drugie pole to na swoją funkcję callbackową. Okno sobie tworzysz funkcją CreateWindowEx. Z ciekawszych argumentów to będzie na pewno styl okna jako WS_EX_TOOLWINDOW, uchwyt rodzica to możesz sobie dać na HWND_DESKTOP, a hInstance znowu na GetModuleHandle(NULL). No i oczywiście lpszClassName podajesz taką samą, jak klasa okna, którą rejestrowałeś przed chwilą.

Nie wiem, na jakim etapie jesteś - taki ogólny zarys może będzie Ci wygodniej wykorzystać w swoim programie, niż gotowy kod. Kluczowe sprawy są. Zarys napisałem po wygrzebaniu i przetrawieniu programiku, który sobie lata temu uklepałem w ramach eksperymentów. Nie jest długi, więc też dołączę - może będzie łatwiej Ci przykładowy kod przerobić.

Disclaimer: przykład nie ma obsługi błędów, zwalniania zasobów, może w ogóle jest nieczytelny... nie wiem, nie znam się, takie miałem, takie daję ;)

#include <conio.h>
#include <cstdio>
#include <windows.h>

class MCIPlayer {
    private:
        HINSTANCE mInstance;
        LPCSTR mClassName;
        HANDLE mThread;

        WNDCLASSEX mWindowClass;
        HWND mWindowHandle;

        void init();

        static DWORD WINAPI procThread(void* param);
        static LRESULT CALLBACK procWindow(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);

    public:
        MCIPlayer(HINSTANCE instance, LPCSTR className);
        ~MCIPlayer();

        void send(const char* command);

};

MCIPlayer::MCIPlayer(HINSTANCE instance, LPCSTR className) {
    mInstance = instance;
    mClassName = className;
    mThread = CreateThread(NULL, 0, &procThread, this, 0, NULL);
}

MCIPlayer::~MCIPlayer() {
    SendMessage(mWindowHandle, WM_DESTROY, 0, 0);
}

void MCIPlayer::init() {
    memset(&mWindowClass, 0, sizeof(mWindowClass) );

    mWindowClass.hInstance = mInstance;
    mWindowClass.lpszClassName = mClassName;
    mWindowClass.lpfnWndProc = &MCIPlayer::procWindow;
    mWindowClass.style = CS_DBLCLKS;
    mWindowClass.cbSize = sizeof (WNDCLASSEX);
    mWindowClass.hIcon = NULL;
    mWindowClass.hIconSm = NULL;
    mWindowClass.hCursor = NULL;
    mWindowClass.lpszMenuName = NULL;
    mWindowClass.cbClsExtra = 0;
    mWindowClass.cbWndExtra = 0;
    mWindowClass.hbrBackground = (HBRUSH) COLOR_BACKGROUND;

    RegisterClassEx(&mWindowClass);

    mWindowHandle = CreateWindowEx (
        0,
        mWindowClass.lpszClassName,
        "",
        WS_EX_TOOLWINDOW,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        0,
        0,
        HWND_DESKTOP,
        NULL,
        mWindowClass.hInstance,
        NULL
    );
}


void MCIPlayer::send(const char* command) {
    char response[128];
    mciSendString(command, response, 128, mWindowHandle);
}

DWORD WINAPI MCIPlayer::procThread(void* param) {
    MCIPlayer* that = (MCIPlayer*)param;

    that->init();

    MSG message;
    while( GetMessage(&message, NULL, 0, 0) ){
        TranslateMessage(&message);
        DispatchMessage(&message);
    }

    return 0;
}

LRESULT CALLBACK MCIPlayer::procWindow(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
    switch (message) {
        case MM_MCINOTIFY:
            printf("MCI > NOTIFY : %lx %lx\n", (long)wParam, (long)lParam);
            break;
        default:
            printf("MSG > %08lx : %lx %lx\n", (long)message, (long)wParam, (long)lParam);
            return DefWindowProc(hwnd, message, wParam, lParam);
    }
    return 0;
}

int main() {

    printf(
        " [0] exit\n"
        " [1] play\n"
        " [2] resume\n"
        " [3] stop\n"
        " [4] pause\n"
        " [5] rewind\n"
    );

    MCIPlayer mci(GetModuleHandle(NULL), "MCIPlayerHelper");

    mci.send("open music.mp3 type mpegvideo alias track1");
    //mci.send("open alarm.wav type waveaudio alias track1");

    while(true) switch(getch()) {
        case 27 :
        case '0': // stop & exit
            mci.send("close track1 notify");
            return 0;
        case '1': // play
            mci.send("play track1 notify");
            break;
        case '2': // resume
            mci.send("resume track1 notify");
            break;
        case '3': // stop
            mci.send("stop track1 notify");
            break;
        case '4': // pause
            mci.send("pause track1 notify");
            break;
        case '5': // rewind
            mci.send("seek track1 to start notify");
            break;
    }

    return 0;
}
1

Tylko dlaczego MCI, skoro od dawna jest DirectShow?

#pragma comment (lib, "strmiids.lib")

#define _WIN32_WINNT 0x0501
#include <Windows.h>
#include <dshow.h>

int main()
{
	HRESULT hr;
	IGraphBuilder *graph;	
    IMediaControl *control;
    IMediaEvent *event;

	// dodać sprawdzanie błędów (hr)

	hr = CoInitialize(NULL);
	hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (LPVOID*)&graph);
	hr = graph->QueryInterface(IID_IMediaControl, (LPVOID*)&control);
	hr = graph->QueryInterface(IID_IMediaEvent, (LPVOID*)&event);
	
	hr = graph->RenderFile(L"Murray Gold - I Am The Doctor.mp3", NULL);
	hr = control->Run();

	long dummy;
	hr = event->WaitForCompletion(INFINITE, &dummy);

	event->Release();
	control->Release();
	graph->Release();
	CoUninitialize();
	return 0;
}
0

Dzięki za pomoc :)
@Ranides, Procedurę CALLBACK skopiowałem gdzieś z internetu i też mi na początku te wywołanie do samej siebie nie pasowało ale uznałem, że być może tak ma być (w WinAPI jestem czysto zielony). Zresztą ona nawet nie była wywoływana, bo jakby była to pojawiłby mi się MessageBox który wsadziłem na początku tej procedury.

1 użytkowników online, w tym zalogowanych: 0, gości: 1