/****************************************************************************
*                                                                           *
* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY     *
* KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE       *
* IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR     *
* PURPOSE.                                                                  *
*                                                                           *
* Copyright (C) 1993-95  Microsoft Corporation.  All Rights Reserved.       *
*                                                                           *
****************************************************************************/

/** posthost.c                                                          **/


/* the includes required to build this file */
    #include <windows.h>
    #include "posthost.h"


/* crude (very) debugging stuff */
    #ifdef DEBUG
        #define D(x)    {x;}
        #define ODS(x)  OutputDebugString(x)
    #else
        #define D(x)
        #define ODS(x)
    #endif


/* defines the device ID of VPOSTD... */
    #define VPOSTD_DEV_ID       0x7fe8


/* internal function prototypes */
    void FAR PASCAL phCallBack( HWND hWnd, WORD wVMID, DWORD lParam );


/* need to guarantee that phCallBack() is in FIXED (page-locked) segment */
    #pragma alloc_text( CALLBACK, phCallBack )


/* global variables */
    HANDLE          ghModule;           /* handle (instance?) of .DLL   */
    WORD            gwError;            /* status info on POSTHOST.DLL  */
    DWORD           VPOSTD_API = NULL;  /* addr of VPOSTD PM API        */




/** void FAR PASCAL phCallBack( HWND hWnd, WORD wVMID, DWORD lParam )
 *
 *  DESCRIPTION:
 *      This is the callback for the VPOSTD.  It should be treated as
 *      an ISR--all rules of ISR's apply here.  You may ONLY call
 *      PostMessage() and PostAppMessage() in an ISR.
 *
 *  ARGUMENTS:
 *      ( HWND hWnd, WORD wVMID, DWORD lParam )
 *
 *  RETURN (void FAR PASCAL):
 *      None.  We were 'far called' (simulated), so we must RETF.  Also
 *      note that we are using the PL/M calling convention, so *we* pop
 *      the stuff off the stack--not the VPOSTD (RETF 8).
 *
 *  NOTES:
 *
 ** cjp */

void FAR PASCAL phCallBack( HWND hWnd, WORD wVMID, DWORD lParam )
{
    ODS( "POSTHOST: phCallBack() called...\r\n" );

    /* quick sanity check--do *NOT* call IsWindow()!!! */
    if ( hWnd )
    {
        /* post the message with the caller's VM ID and CX:DX lParam */
        PostMessage( hWnd, WM_POSTHOSTPOST, wVMID, lParam );
    }
} /* phCallBack() */



/** WORD FAR PASCAL phGetPostHostError( void )
 *
 *  DESCRIPTION: 
 *      This function returns the error status of POSTHOST.DLL.  It should
 *      be used to determine if the .DLL is ready for business or if 
 *      something is hosed.  This is actually quite superfluous; the .DLL
 *      should just fail the LibMain(), but that is harsh for this example.
 *      This works well enough for this example.
 *
 *  ARGUMENTS:
 *      None.
 *
 *  RETURN (WORD FAR PASCAL):
 *      The return value is one of the following:
 *
 *          PHERR_NOERROR  (0)  :   Everything is cool.
 *          PHERR_NO386ENH (1)  :   You gotta run 386 Enh mode!
 *          PHERR_NOVPOSTD (2)  :   VPOSTD.VXD is not installed.
 *
 *  NOTES:
 *
 ** cjp */

WORD FAR PASCAL phGetPostHostError( void )
{
    /* simply return gwError--was filled in during LibMain() */
    return ( gwError );
} /* phGetPostHostError() */



/** DWORD FAR PASCAL phGetVPOSTDAPIAddr void )
 *
 *  DESCRIPTION: 
 *      This function is used to get the far pointer to the API that
 *      is exported by VPOSTD.
 *
 *  ARGUMENTS:
 *      None.
 *
 *  RETURN (DWORD FAR PASCAL):
 *      The return value is the address of the *Protect Mode* API that
 *      VPOSTD exports.  It is NULL if VPOSTD is not available.
 *
 *  NOTES:
 *
 ** cjp */

DWORD FAR PASCAL phGetVPOSTDAPIAddr( void )
{
    /* return the far pointer to PM API entry */
    return ( VPOSTD_API );
} /* phGetVPOSTDAPIAddr() */



/** WORD FAR PASCAL phGetVPOSTDVersion( void )
 *
 *  DESCRIPTION: 
 *      This function returns the version of VPOSTD.  The HIBYTE() of the
 *      return value is the major version number; the LOBYTE() is the 
 *      minor version number.
 *
 *  ARGUMENTS:
 *      None.
 *
 *  RETURN (WORD FAR PASCAL):
 *      The return value is the version of the VPOSTD.  The HIBYTE() of the
 *      return value is the major version number; the LOBYTE() is the 
 *      minor version number.  The return value is zero if VPOSTD is not
 *      available.
 *
 *  NOTES:
 *
 ** cjp */

WORD FAR PASCAL phGetVPOSTDVersion( void )
{
    WORD    wReturn = 0x0000;           /* assume the worst         */

    /* sanity check */
    if ( VPOSTD_API )
    {
        _asm
        {
            mov     ax, 0                   ; func 0 is 'Get Version'
            call    dword ptr [VPOSTD_API]  ; call VPOSTD
            mov     wReturn, ax             ; stuff the return value
        }
    }

    /* return the result */
    return ( wReturn );
} /* phGetVPOSTDVersion() */



/** BOOL FAR PASCAL phRegisterWindow( HWND hWnd )
 *
 *  DESCRIPTION: 
 *      This function is used to register a window handle with VPOSTD
 *      to receive posts.  In this example, only one window may receive
 *      messages at a time.
 *
 *  ARGUMENTS:
 *      HWND hWnd   :   Window to receive posts.  If NULL, the callback
 *                      will be de-registered.
 *
 *  RETURN (BOOL FAR PASCAL):
 *      The return value is TRUE if this function succeeds in setting
 *      the window handle in VPOSTD to hWnd.  It is FALSE if it fails.
 *
 *  NOTES:
 *
 ** cjp */

BOOL FAR PASCAL phRegisterWindow( HWND hWnd )
{
    BOOL    fReturn     = FALSE;        /* assume bad things        */
    DWORD   dwCallBack;
    
    
    dwCallBack = hWnd ? (DWORD)phCallBack : NULL;

    /* make sure we have the API entry point to VPOSTD */
    if ( VPOSTD_API )
    {
        _asm
        {
            mov     ax, 1                   ; func 1 is 'Register Callback'
            mov     bx, hWnd                ; hWnd in bx
            les     di, dwCallBack          ; callback addr in es:di

            call    dword ptr [VPOSTD_API]  ; hello VPOSTD...

            mov     fReturn, ax             ; return result
        }
    }

    /* return outcome of the function */
    return ( fReturn );
} /* phRegisterWindow() */



/** BOOL FAR PASCAL phCallPostHost( DWORD lParam )
 *
 *  DESCRIPTION: 
 *      This function will call VPOSTD to post a message to the current
 *      hWnd that has been registered with phRegisterWindow.  You could
 *      say that this is doing it the hard way, but it demonstrates what
 *      it is intended to demonstrate...
 *
 *  ARGUMENTS:
 *      DWORD lParam    :   Arbitrary DWORD to be posted as the lParam
 *                          to the current registered window.
 *
 *  RETURN (BOOL FAR PASCAL):
 *      The return value is TRUE if this function succeeds.  It is FALSE
 *      if VPOSTD is not available.
 *
 *  NOTES:
 *
 ** cjp */

BOOL FAR PASCAL phCallPostHost( DWORD lParam )
{
    BOOL    fReturn = FALSE;            /* assume bad things        */

    /* make sure we have the API entry point to VPOSTD */
    if ( VPOSTD_API )
    {
        _asm
        {
            mov     ax, 2                       ; func 2 is 'Call PostHost'
            mov     dx, word ptr [lParam + 0]   ; low word in DX
            mov     cx, word ptr [lParam + 2]   ; high word in CX

            call    dword ptr [VPOSTD_API]      ; call VPOSTD

            mov     fReturn, ax                 ; return result
        }
    }

    /* return the outcome of the function */
    return ( fReturn );
} /* phCallPostHost() */



/** BOOL FAR PASCAL WEP( int nArgument )
 *
 *  DESCRIPTION:
 *      Performs cleanup tasks when the .DLL is unloaded.  The WEP() is
 *      called automatically by Windows when the .DLL is unloaded (no
 *      remaining tasks still have the .DLL loaded).  It is strongly
 *      recommended that a .DLL have a WEP() function, even if it does
 *      nothing but return, as in this example.
 *
 *      Make sure that the WEP() is @1 RESIDENTNAME in the EXPORTS
 *      section of the .DEF file.  This ensures that the WEP() can
 *      be called as quickly as possible.  Incidently, this is why
 *      the WEP() is called the WEP() instead of WindowsExitProcedure().
 *      It takes up the minimum amount of space and is quickly located.
 *
 *
 ** cjp */

BOOL FAR PASCAL WEP( int nArgument )
{
    switch ( nArgument )
    {
        case WEP_SYSTEM_EXIT:
            ODS( "POSTHOST: WEP_SYSTEM_EXIT\r\n" );
        break;

        case WEP_FREE_DLL:
            ODS( "POSTHOST: WEP_FREE_DLL\r\n" );
        break;

        default:
            ODS( "POSTHOST: WEP_UNDEFINED!!!\r\n" );
        break;
    }

    /* always return TRUE (1) */
    return ( TRUE );
} /* WEP() */



/** BOOL FAR PASCAL LibMain( HANDLE, WORD, WORD, LPSTR )
 *
 *  DESCRIPTION:
 *      Is called by LibInit().  LibInit() is called by Windows when
 *      the .DLL is loaded.  LibInit() initializes the DLL's heap, if
 *      a HEAPSIZE value is specified in the DLL's .DEF file.  Then
 *      LibInit() calls LibMain().  The LibMain() function below
 *      satisfies that call.
 *
 *      The LibMain() function should perform additional initialization
 *      tasks required by the .DLL.  In this example, the hModule is
 *      made global and the VPOSTD_API is justified.  If the latter
 *      fails, the .DLL will be unloaded...
 *
 ** cjp */

BOOL FAR PASCAL LibMain( HANDLE hModule,
                         WORD   wDataSeg,
                         WORD   cbHeapSize,
                         LPSTR  lpszCmdLine )
{
    /* make instance handle global */
    ghModule = hModule;

    /* get API entry to VPOSTD--if it is installed */
    _asm
    {
        mov     ax, 1600h               ; enhanced mode?
        int     2Fh                     ; api call
        test    al, 7Fh                 ; enhance mode running?
        jz      not_running_enhanced    ; no

        mov     ax, 1684h               ; get device API call
        mov     bx, VPOSTD_DEV_ID       ; for the VPOSTD VxD
        int     2Fh                     ; get api entry point
        mov     word ptr VPOSTD_API, di ; save the callback address
        mov     word ptr VPOSTD_API + 2, es

        mov     ax, es                  ; is VPOSTD installed?
        or      ax, di
        jz      vxd_not_installed       ; if not, split

        mov     gwError, 0              ; show success (PHERR_NOERROR)
        jmp     get_out

not_running_enhanced:
        mov     gwError, 1              ; no enh windows! (PHERR_NO386ENH)
        jmp     get_out                 ; return our error code

vxd_not_installed:
        mov     gwError, 2              ; VPOSTD?  (PHERR_NOVPOSTD)

get_out:
    }

    /* jolly good show... */
    ODS( "POSTHOST: LibMain() succeeded...\r\n" );

    /* return success--even though we may have failed */
    return ( TRUE );
} /* LibMain() */


/** EOF: posthost.c **/

