Example of a Callback Function

The remainder of this chapter presents code that implements a script-channel monitor. This script-channel monitor responds to the following commands:

Command Description

!b <text> Break command. This command tells the application to stop the movie and display the text string <text>.
!g <frame> Goto command. This command tells the application to jump to frame number <frame>.
!l <frame> Loop command. This command tells the application to enter a loop, returning to frame number <frame> until a Continue button (displayed in a small child window) is pressed.
!c <text> Close command. This command tells the application to display the text string <text> and then exit.

Each command begins with an exclamation point (!); this makes it easy for the callback function to recognize commands. When the callback function encounters a script-channel command that it recognizes, it copies the command text and calls another function, Parse, to check the command syntax and break the script command into the window-message format. It then sends the resulting window message and parameters to the main message handler function.

The Callback Function

The callback function is shown in the following code fragment:

#define MAX_SCRIPT 1023
char szScriptText[MAX_SCRIPT+1];

BOOL FAR PASCAL
MyFrameHook(MMPID idMovie, WORD wMsg, WORD wParam, LONG lParam)
{
    WORD wMsgOut, wParamOut;
    LONG lParamOut;

    /* If another callback function is hooked, call it first.
     */
    if(lpfnCallback)
        (*lpfnCallback)(idMovie, wMsg, wParam, lParam);

    switch(wMsg)
    {
        case MMP_HOOK_FRAME :
            return FALSE;

        case MMP_HOOK_SCRIPT :

            // Check the script-channel text. If there's no text, or the
            // command is not recognized, let the Movie Player handle it.

            if(lParam == NULL || *((LPSTR)lParam) != '!')
                return FALSE;

            if(lstrlen((LPSTR)lParam) > MAX_SCRIPT)
                return FALSE;

            // Copy the script-channel text.
            lstrcpy(szScriptText, (LPSTR)lParam);

            // Convert the script-channel text to message format.
            // If the parsing succeeds, carry out the command.
            AnsiLower(szScriptText);
            if(Parse(szScriptText, &wMsgOut, &wParamOut, &lParamOut))
            {
                if(bFullScreen && hFullWnd)
                    DoCommand(hFullWnd, wMsgOut, wParamOut, lParamOut);
                else
                    DoCommand(hMainWnd, wMsgOut, wParamOut, lParamOut);
                return TRUE;               // Application handled it.
            }
            else
                return FALSE;              // Let Movie Player handle it.
            break;

        default:
            return FALSE;
    }
}

The following DoCommand function, called in the previous frame-hook function, handles script-channel commands:

LONG DoCommand(HWND hWnd, int iMessage, WORD wParam, LONG lParam)
{
    switch(iMessage)
    {
        case WM_MMPLAY_GOTO:
            if(wParam)
                mmpGoToFrame(idMovie, wParam, MMP_DRAW_FRAME);
            break;

        case WM_MMPLAY_BREAK:
            MessageBox(hWnd, (LPSTR)lParam, szAppName, MB_OK);
            break;

        case WM_MMPLAY_LOOP:

            // The wLoopState flag indicates the current state of the loop.

            switch(wLoopState)
            {
                // If the flag is LOOP_CLEAR, the movie is just entering
                // the loop. Display the "Continue" button and begin
                // the loop.

                case LOOP_CLEAR:
                    CreateWindow(szKeytestName, szKeytestName,
                        WS_CHILD | WS_VISIBLE | WS_DLGFRAME | WS_SYSMENU |
                                                        WS_CAPTION,
                        10, 10, 100, 60, hWnd, NULL, hInst, 0L);
                    wLoopState = LOOP_WAIT;
                    mmpGoToFrame(idMovie, wParam, MMP_DRAW_FRAME);
                    break;

                // If the flag is LOOP_WAIT, the movie is in the loop.
                // The flag remains LOOP_WAIT until the user presses the
                // Continue button.

                case LOOP_WAIT:
                    mmpGoToFrame(idMovie, wParam, MMP_DRAW_FRAME);
                    break;

                // If the flag is LOOP_CONTINUE, exit the loop.

                case LOOP_CONTINUE:
                    wLoopState = LOOP_CLEAR;
                    break;
            }
            break;

        case WM_MMPLAY_CLOSE:

            MessageBox(hWnd, (LPSTR)lParam, szAppName, MB_OK);
            PostQuitMessage(1);
            break;
    }
    return 0L;
}

Hooking and Removing the Callback Function

An application can insert the callback function into the playback after opening the Movie Player instance. Notice how the following code fragment records the current callback-function address in a global variable before calling mmpSetFrameHook:

lpfnCallback = mmpGetFrameHook(idMovie);
if(lpfnMyCallback = MakeProcInstance(MyFrameHook, hInst))
    mmpSetFrameHook(idMovie, lpfnMyCallback);

The application unhooks its frame function before exiting. The following code fragment shows the WM_DESTROY block of the main message handler:

case WM_DESTROY :
    mmpSetFrameHook(idMovie, lpfnCallback);    // Restore previous callback.
    FreeProcInstance(lpfnMyCallback);

    if(!mmpClose(idMovie, 0))
        PrintError(idMovie, "Failed to stop Movie Player.");

    WriteFlags();
    break;