Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

.NET

Bridging the Gap With Resident_c


MAY92: BRIDGING THE GAP WITH RESIDENT_C

Charles is a software consultant in New Jersey and holds an MS degree in computer engineering from Rutgers University. He can be reached at 73 Montgomery Street, Piscataway, NJ 08854.


If you're running Windows in 386 Enhanced Mode, cutting from a WinApp and pasting to a DOS application is easy--simply run the DOS app in a Window and cut to and paste from the clipboard. But life isn't that simple if you're stuck in real or standard mode. In this case you must first copy from your clipboard, switch to the DOS application, select the insertion point where you want the information from the clipboard to appear, press Alt-Esc to switch back to Windows without quitting the application, click the application icon to open the control menu for the destination application, and finally choose Paste.

The main drawback to this procedure is that it requires you to exit from the application before pasting the data in. But that's not all. If you are running in real or standard mode, copying the entire screen using Print Screen is the only method for copying information from a non-Windows application. This doesn't provide much flexibility--copying even a single line of text requires capturing the entire screen.

This article presents a TSR that bridges the gap between DOS and real- or standard-mode Windows. This TSR is first loaded under DOS. Upon startup, Windows preserves the contents of occupied real memory, including the TSR and DOS. Windows will be configured so that a tiny program, WINDOS, runs automatically on startup. The user will never interact with WINDOS. Instead, WINDOS appears as an icon at the bottom of the screen to let us know that improved data exchange with DOS programs is available.

The improved method doesn't require an exit before pasting into the DOS application, and will allow the exchange of text blocks of any size. The method works only in real- or standard-mode Windows. (In 386 enhanced mode, text-mode DOS programs run in a window, making the Windows cut/paste relatively efficient.) You can still copy data from graphics-mode DOS program using the Print Screen method described above.

/*resident_C*/

The tricky part of any TSR is knowing when it is safe to interrupt DOS. If TSRs issued only BIOS calls, writing them would be relatively simple. To become a TSR, a program would simply hook the keyboard interrupt (9h) and issue an int 31h to terminate resident. However, if the TSR uses DOS services, which is likely, it must not take control of the machine while the processor is executing DOS system code. This is because DOS is a non-reentrant operating system.

South Mountain Software's /*resident_C*/ library (which I'll refer to as simply "resident_C") shields the TSR programmer from pop-up pitfalls. The resident_C library frees the programmer to concentrate on procedural details without worrying about when it is safe for the TSR to take control.

TSR.C (Listing Two) hooks several interrupts, among them the keyboard interrupt, timer interrupt, and software interrupts 0x60 and 0x61. The keyboard interrupt detects when hot keys are pressed. LShift-Alt-C copies text from the screen; the user highlights the text to copy (Lotus 1-2-3 style), then presses the Enter key. LShift-Alt-P pastes text at the location of the text cursor. LShift-Alt-R, which is generally used once, instructs the TSR to unhook all interrupts and remove itself from memory.

Besides cutting and pasting text, the TSR must do one other important thing: transfer text between its internal buffer and the Windows clipboard. Actually, there is no way the TSR can directly access the Windows clipboard. (An obscure method exists, but it only works under enhanced-mode Windows and is poorly documented.) But the TSR can respond to interrupts 0x60 and 0x61 issued from the WINDOS program, which has access to the clipboard. First the TSR must hook these interrupts in the initialization procedure tsrpre(); see Listing One and Listing Two. resident_C has a convenient function initisrN() (where N is an integer from 1 to 9) for hooking interrupts; see Figure 1(a). The first parameter is the interrupt number to hook, the second parameter is a pointer to the handler function, and the third is nonzero if we want to chain to the previous interrupt handler when done processing the interrupt.

Figure 1: (a) resident_C's function for hooking interrupts; (b) defining a hot key using resident_C; (c) responding to additional hot keys.

    (a)

    initis1 (0x60, isr60, 1);
    initis2 (0x61, isr61, 1);

    (b)

    inittsr2 (LEFT_SHIFT|ALT_KEY, SCAN_P, vResprog, 1000, uiCCSig, SIG,
               (void far *) 0L);

    (c)

    tsrhotkeys (uiCCSig, (struct HKEYS far *)ahkHotKeys, 2);

WINDOS issues interrupts 0x60 and 0x61 to initiate the transfer of text to and from the TSR, respectively. In particular, when the TSR receives an interrupt 0x60 from WINDOS, it forms a pointer to the transfer buffer from registers DS:DX. (resident_C fills the variables isrX_ds and sirX_dx with the contents of these registers before passing control to the interrupt service routines.) The TSR then copies the text from the transfer buffer into its own internal buffer. When the TSR receives an interrupt 0x61 from WINDOS, it again forms a pointer to the transfer buffer from DS:DX. This time, however, it copies data from its internal buffer to the transfer buffer.

The resident_C library simplifies hot-key and interrupt processing within the TSR. One of the first resident_C functions we call is inittsr2(); see Figure 1(b). The first two parameters define the primary hot key to invoke the TSR (LShift-Alt-P in this case). There is nothing special about the primary hot key; it is simply the first one defined. The third parameter to inittsr2() points to a funciton that gets control when a hot key is pressed. The fourth parameter is the number of paragraphs of extra memory to allocate when the TSR goes resident. The amount used is excessive--in this case 16K. This particular TSR does not do dynamic allocations (malloc or calloc), so the number of extra paragraphs could be reduced to something close to the 2-4K required by the Microsoft runtime libraries. (I used 16K to put the issue of memory allocation out of mind in the even of future enhancements.)

The fifth and sixth parameters to inittsr2() define the signature of the TSR in memory; DOS maintains an internal list of resident programs by their signature. resident_C checks the DOS signature list when we try to load the TSR. If the TSR is already loaded, resident_C forces an exit without loading the TSR a second time. The signature for our program is held in the variable uiCC_Sig, defined as a random four-digit integer. The last parameter to inittsr2() is not used for our purposes and should be set to NULL.

The inittsr2() function is important because it does so much to define the behavior of the TSR. Not only does it set the primary hot-key and control function, signature, and extra memory space; it also hooks the keyboard interrupt and timer interrupts. Furthermore, the function actually loads our program in the resident state. Before terminating resident, however, it calls the user-defined function tsrpre(). This is the function in which we perform any initializations before the TSR terminates resident--in this case, to compute the dimensions of the screen and print a greeting message.

We also call another resident_C function, tsrhotkeys(), see Figure 1(c), which tells resident_C to respond to two additional hot keys. These additional hot keys are defined in an array, ahkHotKeys[], as LShift-Alt-C and LShift-Alt-R. The first parameter to tsrhotkeys() is the TSR's signature, the second is the array of hot keys, and the last is the number of hot keys defined in the array. Now the TSR will get control when any of the three hot keys are pressed.

When one of these key sequences is detected, resident_C takes control and calls the TSR control function, vResprog(). (Recall that a pointer to this function can be found in the third parameter to inittsr2().) When invoked, vResprog() determines whether a hot key was pressed by checking the global variable _hotkey_hit. This variable is set by the resident_C startup code; it is 0 if no hot key was pressed, and non-zero otherwise.

vResprog() next determines which hot key was pressed by checking the global variable _hotkey_num. The resident_C startup code sets _hotkey_num to a unique number representing the hot key. A value of 0 indicates the primary hot key as defined by parameters one and two in function inittsr2(). A non-zero value represents one of the other hot keys, those defined in the call to function tsrhotkeys(). The primary hot key (LShift-Alt-P) initiates stuffing of the keyboard buffer with text from the TSR's internal buffer. A _hotkey_num value of 1 indicates that hot key LShift-Alt-R was pressed, in which case the TSR removes itself from memory. A _hotkey_num value of 2 means that LShift-Alt-C was pressed, and the TSR proceeds to highlight text on the screen for copying.

Pasting Text

Perhaps the most fascinating aspect of this TSR is how it handles the pasting of text into an application. The standard method for pasting text into DOS applications (which I use here) is stuffing the keyboard buffer. When LShift-Alt-P is pressed, vResprog() sets some global variables and calls the resident_C function kb_init(); see Figure 2

Figure 2: Using the hook function kybdstuf2() to stuff all the bytes in the buffer into the application

    kb_str_ptr = szDataBuffer;
    kb_in_progress = strlen(szDataBuffer);
    kb_init();

Function kb_init() hooks the system timer to a resident_C internal function called kybdstuf2(), which uses two global variables. The global variable kb_str_ptr points to the TSR's internal buffer, which contains the array of keystrokes to stuff. The global variable kb_in_progress is set to the number of bytes in the array to stuff. The call to kb_init() tells the library function kybdstuf2() to begin stuffing the keyboard buffer. As each byte is stuffed, kybdstuf2() decrements kb_in_progress. When the whole buffer is done, the value of kb_in_progress is 0. Because a maximum of 16 bytes can be fit into the keyboard buffer at a time, kybdstuf2() stuffs only a few bytes at a time. But because it is invoked repeatedly from the timer interrupt, function kbdstuf2() eventually finishes the entire array.

Notice that this method for pasting text assumes that the DOS program reads the keys from the keyboard buffer as they are stuffed. It is therefore imperative that the TSR periodically relinquish control so that the foreground program can get control to read the keystrokes. When all the text has been stuffed, the TSR calls _kb_close() to disable the stuffing function kybdstuf2().

WINDOS

You may have noticed that very little of the TSR is dedicated to interfacing with WINDOS. Only the two small interrupt service routines isr60()and isr61() are needed for this purpose. Most of the code in the TSR is used to copy and paste text. A similar imbalance holds for WlNDOS; most of its code (see Listing Three and Listing Four) is used to exchange data with the Windows clipboard. Relatively little is used to interface with the TSR.

The initialization code in WINDOS is executed when the window procedure mAINwNDpROC() receives a WM_CREATE message. The initialization code tells Windows to report WM_TIMER messages every half-second. It also registers a proprietary "signature" format with the clipboard. Finally, it allocates the transfer buffer using GlobalDosAlloc(). This buffer is where WINDOS and the TSR swap text back and forth. The transfer buffer must reside in system memory below the 1-Mbyte real-mode ceiling, making it addressable by the TSR.

With every timer message, WINDOS polls the clipboard to see if any new data is waiting. It polls the clipboard by checking for the signature on the clipboard. If the signature is present, the clipboard contains "stale" data (data not to be copied to the TSR), either because it came from the TSR to begin with, or because WINDOS copied it to the TSR during a previous timer message. If the signature is missing from the clipboard, it means another Windows application has overwritten the clipboard with new data. In this case, WINDOS checks that the new data is in the CF_TEXT format, and if so, copies it to the TSR by simulating real-mode interrupt 0x60.

Next WINDOS polls the TSR by simulating real-mode interrupt 0x61. If the TSR's internal buffer contains new text copied from the screen, then isr61() copies the text to the transfer buffer pointed to by DS:DX and then returns 1 in AX. WINDOS checks AX when the simulated interrupt returns, and if AX is equal to 1, it copies text from the transfer buffer to the clipboard. Otherwise, it closes the clipboard without doing anything.

Notice that if Windows is running in real mode, there is no need to simulate real-mode interrupts using DPMI because it is not available. WINDOS checks the Windows mode with a call to GetWinFlags(). If Windows is running in real mode, WINDOS uses the old Microsoft int86x() function instead.

Conclusion

To set up for improved text exchange, run TSR.EXE before running Windows. Then run Windows using windos on the command line. That's all there is to it. Remember that the hot keys LShift-Alt-P and LShift-Alt-C are for use only within DOS applications and have no effect in Windows applications; use the standard cut/paste in Windows applications.

Products Mentioned

/*resident_C*/ South Mountain Software 76 South Orange Ave. South Orange, NJ 07079 201-762-6965 $249 Requires a supported C compiler

DDJ



_BRIDGING THE GAP WITH RESIDENT_C_
by Charles Albert Mirho



[LISTING ONE]
<a name="011b_000c">


/* tsr.h */
#define  FALSE 0
#define  TRUE  1

/* Some commonly used scan codes */
#define SC_ESCAPE      0x01
#define SC_PRTSC       0x37
#define SC_SPACE_BAR   0x39
#define SC_CAP_LOCK    0x3A
#define SC_NUM_LOCK    0x45
#define SC_SCROLL_LOCK 0x46

/* toggle key codes for inittsr() */
#define ALT_KEY     0x08
#define CTRL_KEY    0x04

#define LEFT_SHIFT  0x02
#define RIGHT_SHIFT 0x01

/* TSR signature */
#define SIG            *((unsigned int *)"ESI")

extern unsigned char Tsr24Result, Tsr24Error;   /* used for int24 */

struct HKEYS{
    unsigned char hotscancode;
    unsigned char hottoggle;
    unsigned char key_identifier;
    };

struct sharedata{                /* shared structure definition */
    unsigned oldpsp;
    unsigned ourpsp;
    unsigned ourextra;
    unsigned tsr_size;
    unsigned tsr_stat;
    unsigned char hottoggle;
    unsigned char hotscancode;
    unsigned char safewait;
    unsigned char oldbreak;
    unsigned char freeflag;
    unsigned char beepflag;
    unsigned char more_hotkeys;
    unsigned char cdummy;
    struct HKEYS far *hotkey_data;
    int far *indosflag;
    int far *critflag;
    int far *oldint8;
    int far *oldint9;
    int far *oldint10;
    int far *oldint13;
    int far *oldint16;
    int far *oldint1b;
    int far *oldint1c;
    int far *oldint21;
    int far *oldint23;
    int far *oldint24;
    int far *oldint28;
    int far *shareptr;
    unsigned int oldss;
    unsigned int oldsp;
    unsigned int ourss;
    unsigned int oursp;
    int far *resident;
    int far *dosstack;
    int far *olddta;
    int far *ourdta;
        unsigned int ourcs;
    unsigned long vec_table[256];
    unsigned long our_floats[44];
    unsigned long float_hold[44];
    };





<a name="011b_000d">
<a name="011b_000e">
[LISTING TWO]
<a name="011b_000e">

/*************************************************************************
*  File Name: tsr.c
*  Description: Main body of the TSR. Traps hot keys LSHIFT-ALT-C (copy text),
*  LSHIFT-ALT-P (paste text), LSHIFT-ALT-R (remove TSR from memory).
*************************************************************************/
#include <stdio.h>
#include <dos.h>
#include "tsr.h"
#include "isr.h"
#include "clip.h"

extern unsigned long cculMode;
extern int cciColumns;
extern int cciRows;
extern BYTE far *ccfpVidMem;
extern ATTRIBUTE  ccatDef25x80;
extern ATTRIBUTE  ccatSecond25x80;
extern BYTE *ccabAttrBuffer;                    //Undo buffer of attributes
char szDataBuffer[3000] = "Yo de do de do.";    //Holds transfer data
BOOL bNewData = FALSE;

#define SCAN_C                    46       //popup scan code
#define SCAN_P                    25       //popup scan code
#define SCAN_R                    19       //menu scan code
unsigned int uiCCSig = 0x9191;             //randomly selected signature code
extern int _hotkey_num;                    //will hold value of hot key pressed
                                           //additional hot keys to trap
extern char *kb_str_ptr;                   //points to buffer for the
                                           //keystuffer routine to use
extern unsigned kb_in_progress;            //non-zero if more bytes waiting
                                           //in buffer to be stuffed
extern int _hotkey_hit;                    //true if a hot key was pressed
struct HKEYS ahkHotKeys[] =
{
    SCAN_R, LEFT_SHIFT | ALT_KEY, 1,
    SCAN_C, LEFT_SHIFT | ALT_KEY, 2,
};
void tsrpost ();
void tsrpre ();
void vResprog();                 //function which gets control upon invocation
int isr60();
int isr61();

RECTANGLE rtScreen;
RECTANGLE rtRect = {20,5,20,5};

/**************************************************************************
    FUNCTION: Main
    PURPOSE:  Main of TSR program
**************************************************************************/
main (int argc, char *argv[])
{
int irc;
    cciColumns = 80;
    cciRows = 25;
    ccfpVidMem = (unsigned char far *) 0xB8000000;
    MAKEATTRIBUTE (ccatDef25x80, COLOR_TEXT_BLUE, COLOR_TEXT_RED,
                                                      INTENSITY_HIGH, NOBLINK);
    MAKEATTRIBUTE (ccatSecond25x80, COLOR_TEXT_RED, COLOR_TEXT_BLUE,
                                                      INTENSITY_HIGH, NOBLINK);
    if ((ccabAttrBuffer = (BYTE *) calloc (cciColumns*cciRows,
                                                          sizeof(BYTE)))==NULL)
    {
        printf ("Allocation error\n");
    }  //End if (not enough memory)
       //Test if TSR already loaded.  If not, do initialization code
    if (!tsrloaded (uiCCSig))
    {
            //initialization code
            //initializes a TSR that works off the system timer
    if ((irc=inittsr (LEFT_SHIFT|ALT_KEY, SCAN_P, vResprog, 1000, uiCCSig, SIG,
             (void far *) 0L)) != 0)
        {
            printf ("Error loading TSR: Error %d\n", irc);
            return 1;
        } //End if (failed to initialize TSR)
    }  //End if (TSR not loaded already)

} //End function (main)

/* This function is called on every clock tick, approx. 18 times per sec.
   It should do its processing and exit as quickly as possible */
void vResprog ()
{
static bStuffing = FALSE;
        //The variable kb_in_progress holds the number of bytes
        //remaining in the buffer that need to be stuffed into application.
    if(kb_in_progress)

    {
        return;
    } //End if (in the middle of stuffing keystrokes)
    if (!_hotkey_hit)
    {
        return;
    } //End if (no hot key pressed)
      //If we were previously stuffing but now we are done, clean up
    if (bStuffing && !kb_in_progress)
    {
        bStuffing = FALSE;
        kb_close ();
    } //End if (done stuffing)
    if (_hotkey_hit)
    {
        _hotkey_hit = 0;
        if (_hotkey_num == 0)
        {
                //Now initiate the hook function kybdstuf2() to stuff
                //all the bytes in the buffer into the application
            kb_str_ptr = szDataBuffer;
            kb_in_progress = strlen(szDataBuffer);
            kb_init();
            bStuffing = TRUE;
            return;
        } //End if (hot key to paste)
        if (_hotkey_num == 1)
        {
            if (freetsr())
            {
                printf ("Error - could not free tsr.\n");
            }  //End if (failed to free)
            freeisr1 ();
            freeisr2 ();
        } //End if (hot key to free tsr)
        if (_hotkey_num == 2)
        {
            cciSaveAttrRect(&rtScreen);
            if (cciHilightRect(&rtRect) == ccSUCCESS)
            {
                cciSaveCharRect (&rtRect);
                bNewData = TRUE;
            } //End if (sucessful highlight)
            cciRestoreAttrRect(&rtScreen);
        } //End if (hot key to clip screen)
    } //End if (hot key hit)
} //End function (vRespProg)

void tsrpre ()
{
    tsrhotkeys (uiCCSig, (struct HKEYS far *)ahkHotKeys, 2);
    MAKERECTANGLE (rtScreen, 0, 0, cciColumns-1, cciRows-1);
    initisr1 (0x60, isr60, 1);
    initisr2 (0x61, isr61, 1);
    printf ("TSR Loaded.\n");
    printf ("LSHIFT-ALT-C to copy\n");
    printf ("LSHIFT-ALT-P to paste\n");
    printf ("LSHIFT-ALT-R to remove from memory\n");
    return;
} //End function (tsrpre)
void tsrpost ()
{
    if (ccabAttrBuffer)
    {
        free (ccabAttrBuffer);
    }  //End if (attribute buffer defined)
    printf ("\n** TSR removed from memory. **");
    printf ("\n** Hit enter to continue. **\n");
    return;
} //End function (tsrpost)
char far *szTransferBuf;
int i;
isr60 ()
{
    FP_SEG (szTransferBuf) = isr1_ds;
    FP_OFF (szTransferBuf) = isr1_dx;
    i = 0;
    while (szTransferBuf[i] != 0)
    {
        szDataBuffer[i++] = szTransferBuf[i];
    } //End while (more bytes to transfer)
    szDataBuffer[i] = 0;
    return 0;
} //End function (isr60)
isr61 ()
{
    if (!bNewData)
    {
        isr2_ax = 0;
        return 0;
    } //End if (no new data to copy)
    bNewData = FALSE;
    FP_SEG (szTransferBuf) = isr2_ds;
    FP_OFF (szTransferBuf) = isr2_dx;
    i = 0;
    while (szDataBuffer[i] != 0)
    {
        szTransferBuf[i] = szDataBuffer[i];
        i++;
    } //End while (more bytes to transfer)
    szTransferBuf[i] = 0;
    isr2_ax = 1;
    return 0;
} //End function (isr61)




<a name="011b_000f">
<a name="011b_0010">
[LISTING THREE]
<a name="011b_0010">

/* Windos.h */
int PASCAL WinMain(HANDLE, HANDLE, LPSTR, int);
long FAR PASCAL MainWndProc(HWND, unsigned, WORD, LONG);
int      pmiIntX (int iIntNo, LPSTR szSwapData);

#define  BUFFER_SIZE      3000L              //size of transfer buffer
#define  TEXT_INC          15                //space between lines of text
#define  SIG              "windos"           //Signature to place on clipboard




<a name="011b_0011">
<a name="011b_0012">
[LISTING FOUR]
<a name="011b_0012">

/***************************************************************************
    PROGRAM: WINDOS
    PURPOSE: Reads text from the clipboard and writes it to a DOS TSR using
    DPMI. Or, reads text from a DOS TSR and writes it to clipboard.
***************************************************************************/
#include "windows.h"
#include "windos.h"
#include "dpmi.h"
#include <dos.h>
#include <string.h>
#include <stdlib.h>

HWND     hwndMain;                      //Global handle to main window
WORD     cfSignature;                   //signature format (proprietary)
char     szClipData[3000];
SELECTOR PMSelector;                    //protected mode selector
SEGMENT  RMSegment;                     //real-mode segment
DWORD    dwSegSelector;                 //seg:selector pair returned by
                                        //GlobalDosAlloc()
LPSTR    lpTransferBuf;                 //Points to transfer buffer

/***************************************************************************
    FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int)
    PURPOSE: Calls initialization functions and processes message loop
    Only one instance of the program is allowed to run.
***************************************************************************/
int PASCAL WinMain(HANDLE hInstance, HANDLE hPrevInstance, LPSTR lpCmdLine,
                   int nCmdShow)
{
MSG msg;

    if (!hPrevInstance)
    {
        if (!InitApplication(hInstance))
        {
            return (FALSE);
        } //End if (failed initializing application params)
    } //End if (no previous instance running)
    else
    {
        return FALSE;
    } //End if (previous instance already running)
    if (!InitInstance(hInstance, nCmdShow))
    {
        return (FALSE);
    } //End if (failed initializing instance data)
    while (GetMessage(&msg, NULL, NULL, NULL))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    } //End while (not a quit message)
    return (msg.wParam);
}  //End function (WinMain)
/***************************************************************************
    FUNCTION: InitApplication(HANDLE)
    PURPOSE: Initializes window data and registers window classes
***************************************************************************/
BOOL InitApplication(HANDLE hInstance)
{
WNDCLASS  wc;
    wc.style = NULL;
    wc.lpfnWndProc = MainWndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon =   LoadIcon(hInstance, "SMILEY");
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName =  NULL;
    wc.lpszClassName = "WinDos";
    return (RegisterClass(&wc));
} //End function (InitApplication)
/***************************************************************************
    FUNCTION:  InitInstance(HANDLE, int)
    PURPOSE:  Saves instance handle and creates main window
***************************************************************************/
BOOL InitInstance(HANDLE hInstance, int nCmdShow)
{
HDC         hDC;
TEXTMETRIC  tm;
    hwndMain = CreateWindow(
        "WinDos",
        "WinDos",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        NULL,
        NULL,
        hInstance,
        NULL
    );

    if (!hwndMain)
    {
        return (FALSE);
    } //End if (error creating main window)
    ShowWindow(hwndMain, SW_SHOWMINIMIZED);
    UpdateWindow(hwndMain);
    return (TRUE);
} //End function (InitInstance)
/***************************************************************************
    FUNCTION: MainWndProc(HWND, unsigned, WORD, LONG)
    PURPOSE:  Processes messages for WINDOS application.
***************************************************************************/
long FAR PASCAL MainWndProc(HWND hWnd, unsigned message, WORD wParam, LONG lParam)
{
HANDLE hClipData, hSig;
LPSTR lpClipData, lpSig;
int i, j;
    switch (message)
    {
        case WM_CREATE:
            if (GetWinFlags() & WF_ENHANCED)
            {
   MessageBox (hWnd, "Cannot use WINDOS in 386 enhanced mode", "Error", MB_OK);
                PostQuitMessage(0);
                break;
            } //End if (running 386 enhanced mode)
                // Allocate the transfer buffer
                // Take selector from low word of return value
                // (real-mode segment is in high word)
            if ((dwSegSelector = GlobalDosAlloc (BUFFER_SIZE+1))==NULL)
            {
         MessageBox (hWnd, "Unable to allocate transfer buf", "Error", MB_OK);
                PostQuitMessage(0);
                break;
            } //End if (could not allocate transfer buf)
            PMSelector = LOWORD (dwSegSelector);
            RMSegment  = HIWORD (dwSegSelector);
                // Set pointer to transfer buffer
            lpTransferBuf = (LPSTR) MAKELONG (0, PMSelector);
                //Register our proprietary format with the clipboard
            if (!(cfSignature = RegisterClipboardFormat("Sig")))
            {
         MessageBox (hWnd, "Unable to register clipbd format", "Error", MB_OK);
                PostQuitMessage(0);
                break;
            } //End if (error registering proprietary clipboard format)
                //Receive timer messages every 1/2 second
            if (!SetTimer (hWnd, 1, 500, NULL))
            {
                MessageBox (hWnd, "Unable to create timer", "Error", MB_OK);
                PostQuitMessage(0);
                break;
            } //End if (could not create timer)
            break;
        case WM_TIMER:
                //Check the clipboard;  if signature not present, then copy
                //data.
            do
            {
                if (OpenClipboard(hWnd))
                {
                    if (IsClipboardFormatAvailable (cfSignature))
                    {
                        CloseClipboard ();
                        break;
                    } //End if (signature present - stale data)
                    else
                    {
                        if (IsClipboardFormatAvailable (CF_TEXT))
                        {
                            if (!(hClipData = GetClipboardData (CF_TEXT)))
                            {
                                CloseClipboard ();
                                break;
                            } //End if (error getting data from clipboard)
                            if (!(lpClipData = GlobalLock (hClipData)))
                            {
                                CloseClipboard ();
                                break;
                            } //End if (error dereferencing clip handle)
                        } //End if (text available on clipboard)
                        else
                        {
                            CloseClipboard ();
                            break;
                        } //End if (no text on clipboard)
                    } //End if (new data on the clipboard)
                    CloseClipboard ();
                } //End if (able to open clipboard)
                else
                {
                    break;
                } //End if (unable to open clipboard)

                    //Now copy the data to the TSR
                    //Do the real-mode interrupt to get the TSR's attention.
                pmiIntX (0x60, lpClipData);
                    //Now copy signature to clipboard
    if (!(hSig = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, (DWORD)lstrlen(SIG))))
                {
                    break;
                } //End if (couldn't find the memory)
                if (!(lpSig = GlobalLock(hSig)))
                {
                    GlobalFree(hSig);
                    break;
                } //End if (couldn't lock the memory)
                lstrcpy (lpSig, SIG);
                GlobalUnlock(hSig);
                if (OpenClipboard(hWnd))
                {
                    SetClipboardData(cfSignature, hSig);
                    CloseClipboard();
                } //End if (clipboard open)
            } while (FALSE);
            do
            {
                if (!pmiIntX (0x61, szClipData))
                {
                    if (OpenClipboard(hWnd))
                    {
                            //Now copy data to clipboard; allocate a few extra
                            //bytes for CR - CR-LF combos.
      if (!(hClipData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, (DWORD) 3300)))
                        {
                            CloseClipboard ();
                            break;
                        } //End if (couldn't find the memory)
                        if (!(lpClipData = GlobalLock(hClipData)))
                        {
                            CloseClipboard ();
                            GlobalFree(hClipData);
                            break;
                        } //End if (couldn't lock the memory)
                            //Copy the data to clipboard buf, translating
                            //CR to CR-LF combo; this is the clipboard
                            //TEXT format.
                        i=0;
                        j=0;
                        while (szClipData[j] != 0)
                        {
                            if (szClipData[j] == 0x0D)
                            {
                                lpClipData[i] = 0x0D;
                                i++;
                                lpClipData[i] = 0x0A;
                            } //End if (CR found)
                            else
                            {
                                lpClipData[i] = szClipData[j];
                            } //End if (normal byte)
                            i++;
                            j++;
                        } //End while (more bytes to copy)
                        lpClipData[i] = 0;
                        GlobalUnlock(hClipData);
                            //Now copy signature to clipboard
    if (!(hSig = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, (DWORD)lstrlen(SIG))))
                        {
                            CloseClipboard ();
                            break;
                        } //End if (couldn't find the memory)
                        if (!(lpSig = GlobalLock(hSig)))
                        {
                            CloseClipboard ();
                            GlobalFree(hSig);
                            break;
                        } //End if (couldn't lock the memory)
                        lstrcpy (lpSig, SIG);
                        GlobalUnlock(hSig);
                        EmptyClipboard();
                        SetClipboardData(CF_TEXT, hClipData);
                        SetClipboardData(cfSignature, hSig);
                        CloseClipboard();
                    } //End if (clipboard opened)
                } //End if (got data from TSR)
            } while (FALSE);
            break;
        case WM_DESTROY:                  /* message: window being destroyed */
            KillTimer (hWnd, 1);
            GlobalDosFree (PMSelector);
            PostQuitMessage(0);
            break;
        default:                          /* Passes it on if unproccessed    */
            return (DefWindowProc(hWnd, message, wParam, lParam));
    } //End switch (on message)
    return (NULL);
} //End function (MainWndProc)
/***************************************************************************
    FUNCTION:  pmiIntX
    PURPOSE:  High level: Performs real mode interrupt
***************************************************************************/
int pmiIntX (int iIntNo, LPSTR szSwapData)
{
    if (iIntNo == 0x60)
    {
            //Copy data to transfer buffer, including trailing NULL
        _fmemcpy (lpTransferBuf, (LPSTR) szSwapData, lstrlen (szSwapData)+1);
    } //End if (writing data TO TSR)
        //If running protected mode, simulate real mode interrupt
        //Otherwise do an int86x()
    if (GetWinFlags() & WF_PMODE)
    {
    RM386_INT rmInt;                        //structure for making real-mode
                                            //interrupts from protected mode
        memset (&rmInt, 0, sizeof(rmInt));
        rmInt.edx = 0x0000;
        rmInt.ds =  RMSegment;
        dpmiRMInt (iIntNo, 0, &rmInt, 0);
        if (iIntNo == 0x61)
        {
            if (rmInt.eax == 0)
            {
                return -1;
            } //End if (no TSR data to read)
        } //End if (reading data FROM TSR)
    } //End if (running in protected mode)
    else
    {
    union REGS inregs, outregs;
    struct SREGS sregs;
        sregs.ds =        RMSegment;
        inregs.x.dx =     0;
        int86x (iIntNo, &inregs, &outregs, &sregs);
        if (iIntNo==0x61 && outregs.x.ax == 0)
        {
            return -1;
        } //End if (no TSR data to read)
    } //End if (running in real mode)
    if (iIntNo == 0x61)
    {
            //Copy data from transfer buffer, including trailing NULL
       _fmemcpy ((LPSTR) szSwapData, lpTransferBuf, lstrlen (lpTransferBuf)+1);
    } //End if (reading data FROM TSR)
    return 0;
} //End function (pmiDPMIIntX)
/***************************************************************************
    FUNCTION:  pmiDPMIIntX
    PURPOSE:  Low level: Performs simulated real mode interrupt using DPMI
***************************************************************************/
BOOL dpmiRMInt(WORD wIntno, WORD wFlags, RM386_INT far *rmInt, WORD wStackVals)
{
    if (wFlags)
    {
        wIntno |= 0x100;
    } //End if (flags set)
    _asm
    {
        push di
        mov  ax, 0300h
        mov  bx, word ptr wIntno
        mov  cx, word ptr wStackVals
        les  di, dword ptr rmInt
        int  31h
        jc   error
        mov  ax, 1
        jmp  short done
    } //End (low-level dpmi real-mode interrupt)
error:
    _asm xor ax, ax
done:
    _asm pop di
} //End function (dpmiRMInt)


Copyright © 1992, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.