home *** CD-ROM | disk | FTP | other *** search
/ Visual Basic 4 Unleashed / Visual_Basic_4_Unleashed_SAMS_Publishing_1995.iso / repease / demo.c < prev    next >
C/C++ Source or Header  |  1995-08-14  |  46KB  |  1,193 lines

  1. /*==============================================================================
  2.    DEMO.C :- FORM Demo Program (MS Windows Version)
  3.    Sub Systems, Inc.
  4.    Software License Agreement  (1989)              
  5.    ----------------------------------
  6.    This is a demo program.  The program shows how you can utilize the Report 
  7.    Ease routines to perform develop report templates and execute the reports.
  8. ===============================================================================*/
  9.  
  10. #include "windows.h"
  11. #include "stdio.h"
  12. #include "stdlib.h"
  13. #include "fcntl.h"
  14. #include "sys\types.h"
  15. #include "sys\stat.h"
  16. #include "io.h"
  17. #include "dos.h"
  18. #include "string.h"
  19. #include "math.h"
  20.  
  21. #if defined(__TURBOC__)
  22.    #include "dir.h"
  23.    #include "mem.h"
  24. #else
  25.    #include "memory.h"
  26. #endif
  27.  
  28. /******************************************************************************
  29.     WIN32 specific defines
  30. *******************************************************************************/
  31. #if defined (_WIN32)
  32.    #if !defined(WIN32)
  33.      #define WIN32
  34.    #endif
  35. #endif
  36. #if defined (WIN32)
  37.    #undef  huge
  38.    #define huge
  39.    #undef  FreeProcInstance 
  40.    #define FreeProcInstance(x) 
  41.    #define COMMAND_ID(wp,lp)        LOWORD(wp)
  42.    #define CONTROL_ID(wp,lp)        LOWORD(wp)
  43.    #define NOTIFY_MSG(wp,lp)        HIWORD(wp)
  44.    #undef  farmalloc
  45.    #define farmalloc  malloc
  46.    #undef  farrealloc
  47.    #define farrealloc realloc
  48.    #undef  farfree
  49.    #define farfree    free
  50. #else
  51.    #define COMMAND_ID(wp,lp)        (wp)
  52.    #define CONTROL_ID(wp,lp)        (wp)
  53.    #define NOTIFY_MSG(wp,lp)        HIWORD(lp)
  54. #endif
  55.  
  56. /******************************************************************************
  57.     DLL and demo program specific includes
  58. *******************************************************************************/
  59. #include "rep.h"
  60. #include "demo.h"
  61.  
  62. #include "util.h"      // need only by the demo program
  63.  
  64. /****************************************************************************
  65.         Main Fuction
  66. ******************************************************************************/
  67. int PASCAL WinMain(HANDLE hInstance,HANDLE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)
  68. {
  69.     MSG msg;
  70.  
  71.     hPrevInst=hPrevInstance;
  72.     if (!hPrevInstance)
  73.         if (!InitApplication(hInstance))
  74.             return (FALSE);
  75.  
  76.     if (!InitInstance(hInstance, nCmdShow))
  77.         return (FALSE);
  78.  
  79.     while (GetMessage(&msg, NULL, 0, 0)) {
  80.         TranslateMessage(&msg);
  81.         DispatchMessage(&msg);
  82.     }
  83.     
  84.     return (msg.wParam);
  85. }
  86.  
  87. /****************************************************************************
  88.    Initialize window data and register window class
  89. ****************************************************************************/
  90.  
  91. BOOL InitApplication(HANDLE hInstance)
  92. {
  93.     WNDCLASS  wc;
  94.  
  95.     wc.style = 0;
  96.     wc.lpfnWndProc = (void far *)DemoWndProc;
  97.     wc.cbClsExtra = 0;
  98.     wc.cbWndExtra = 0;
  99.     wc.hInstance = hInstance;
  100.     wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
  101.     wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  102.     wc.hbrBackground = GetStockObject(WHITE_BRUSH); 
  103.     wc.lpszMenuName =  "DemoMenu";
  104.     wc.lpszClassName = "ReDemoClass";
  105.  
  106.     return (RegisterClass(&wc));
  107. }
  108.  
  109.  
  110. /****************************************************************************
  111.     Save instance handle and create main window
  112. ****************************************************************************/
  113.  
  114. BOOL InitInstance(HANDLE hInstance,int nCmdShow)
  115. {
  116.     HWND            hDemoWin;
  117.     int             i;
  118.     
  119.  
  120.     hInst = hInstance;
  121.  
  122.     hDemoWin = CreateWindow(
  123.         "ReDemoClass",
  124.         "Report Ease Demo",
  125.         WS_OVERLAPPEDWINDOW,
  126.         CW_USEDEFAULT,
  127.         CW_USEDEFAULT,
  128.         CW_USEDEFAULT,
  129.         CW_USEDEFAULT,
  130.         NULL,
  131.         NULL,
  132.         hInstance,
  133.         NULL
  134.     );
  135.  
  136.     if (!hDemoWin)
  137.         return (FALSE);
  138.  
  139.     ShowWindow(hDemoWin, nCmdShow);
  140.     UpdateWindow(hDemoWin);
  141.     
  142.     //****** initialize the structure variables *********
  143.     FormParm.open=FALSE;           // all windows closed in the beginning 
  144.     FormParm.x=0;                  // Initial X position of FORM edit window 
  145.     FormParm.y=0;                  // Initial Y position of FORM edit window 
  146.     FormParm.width=GetSystemMetrics(SM_CXSCREEN);        // Initial edit window width 
  147.     FormParm.height=(GetSystemMetrics(SM_CYSCREEN)*9)/10; // Initial edit window height 
  148.     FormParm.ShowMenu=TRUE;        // display menu 
  149.     FormParm.ShowVerBar=TRUE;      // display vertical scroll bar 
  150.     FormParm.ShowHorBar=TRUE;      // display horizontal scroll bar (N/A with word wrap) 
  151.     strcpy(FormParm.file,"");      // Text file name if the input type is 'F' 
  152.     FormParm.hInst=hInst;          // Current application instant handle 
  153.     FormParm.hPrevInst=hPrevInst;  // Previous application instant handle 
  154.     FormParm.hParentWnd=hDemoWin;  // let this be the parent of the editor window 
  155.     FormParm.style=WS_OVERLAPPEDWINDOW; //  Editor window style 
  156.     strcpy(FormParm.DataSetName,"CUSTOMER");
  157.  
  158.  
  159.     //***** pass the pointer to the call back routines 
  160.     /** The field selection and verification routines must be included in the 
  161.         export section of your applications definition file **/
  162.  
  163.     FormParm.UserSelection=(USER_SELECTION)MakeProcInstance(UserFieldSelection,hInst); // field selection routine 
  164.     FormParm.VerifyField=(VERIFY_FIELD)MakeProcInstance(VerifyField,hInst);          // field verification routine 
  165.     if (!FormParm.UserSelection || !FormParm.VerifyField) return FALSE;
  166.  
  167.     //**** copy the parameters to the RepParam structure *********
  168.     RepParm.hInst=FormParm.hInst;
  169.     RepParm.hPrevInst=FormParm.hPrevInst;
  170.     RepParm.hParentWnd=FormParm.hParentWnd;
  171.     RepParm.style=FormParm.style;
  172.     strcpy(RepParm.SwapDir,"");         // screen page swap location
  173.  
  174.     RepParm.DrawPicture=(DRAW_PICTURE)MakeProcInstance(DrawPicture,hInst);          // field verification routine 
  175.     if (!RepParm.DrawPicture) return FALSE;
  176.  
  177.     //********** read field name for the files *******************
  178.     strcpy(DataFile[0].name,"CUSTOMER");
  179.     strcpy(DataFile[1].name,"SALES");
  180.  
  181.     for (i=0;i<MAX_FILES;i++) {
  182.        if (!ReadFields(i)) return FALSE;
  183.     }
  184.  
  185.     // load the customer logo bitmap
  186.     hLogoBM=LoadBitmap(hInst,"LogoBM");
  187.     Bitmap2DIB();                      // extract bits from the bitmap
  188.  
  189.     return (TRUE);
  190. }
  191.  
  192. /****************************************************************************
  193.     Process Main Window messages
  194. ****************************************************************************/
  195.  
  196. long FAR PASCAL _export DemoWndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
  197. {
  198.     FARPROC lpProcParam;
  199.  
  200.  
  201.     switch (message) {
  202.         case WM_COMMAND:
  203.            switch (COMMAND_ID(wParam,lParam)) {
  204.               case IDM_EDITOR:                 // open a new window 
  205.                 if (FormParm.open) {
  206.                    MessageBox(hDemoWin,"A Form Editor Window Already Open!",NULL,MB_OK);
  207.                    break;
  208.                 }
  209.                 CallEditor();             // call the form editor 
  210.                 break;
  211.             
  212.               case IDM_EXECUTER:              // run a form template 
  213.                 if (FormParm.open) {
  214.                    MessageBox(hWnd,"A Form Editor Window Already Open!",NULL,MB_OK);
  215.                    break;
  216.                 }
  217.                 RunReport();              // run the report 
  218.                 break;
  219.             
  220.               case IDM_OPTIONS:          // accept editor window options 
  221.                 if (FormParm.open) {
  222.                    MessageBox(hWnd,"A Form Editor Window Already Open!",NULL,MB_OK);
  223.                    break;
  224.                 }
  225.  
  226.                 lpProcParam = MakeProcInstance(DemoOptions, hInst);
  227.                 DialogBox(hInst,"DemoOptions",hWnd,lpProcParam);
  228.                 FreeProcInstance(lpProcParam);
  229.                 break;
  230.             
  231.               case IDM_EXIT:             // quit demo 
  232.                 if (FormParm.open) {
  233.                    if (!SendMessage(FormParm.hFrWnd,WM_QUERYENDSESSION,0,0L)) break;
  234.                 }
  235.                 PostQuitMessage(0);
  236.  
  237.               default:
  238.                 return (DefWindowProc(hWnd, message, wParam, lParam));
  239.            }
  240.            break;
  241.  
  242.         case WM_DESTROY:
  243.             if (FormParm.open) {
  244.                if (!SendMessage(FormParm.hFrWnd,WM_QUERYENDSESSION,0,0L)) break;
  245.             }
  246.             
  247.             // release demo bitmap data
  248.             if (hLogoBM) DeleteObject(hLogoBM);
  249.             GlobalUnlock(hImage);
  250.             GlobalUnlock(hInfo);
  251.             GlobalFree(hImage);
  252.             GlobalFree(hInfo);
  253.  
  254.             PostQuitMessage(0);
  255.             break;
  256.  
  257.         /********************************************************************
  258.                       Messages coming from the form editor Window
  259.         *********************************************************************/
  260.         case REP_CLOSE:                      // REP sends this message before closing 
  261.             FormParm.open=FALSE;             // mark Report Ease as closed 
  262.             break;
  263.  
  264.         default:
  265.             return (DefWindowProc(hWnd, message, wParam, lParam));
  266.     }
  267.     return (LRESULT)(NULL);
  268. }
  269.  
  270. /****************************************************************************
  271.     CallEditor:
  272.     This function calls the form editor.
  273. ****************************************************************************/
  274. CallEditor()
  275. {
  276.     int result;
  277.     char string[80];
  278.  
  279.     if (GetFormSelection(FormParm.file,TRUE)>=0) {  // select a form file 
  280.        
  281.        //*** let form designer use the default fonts ******
  282.        FormParm.FontTypeFace[0]=0;    // default font type faces for report 
  283.  
  284.        result=form(&FormParm);        // call the form editor 
  285.        if (result!=0) {               // print error if any 
  286.           wsprintf(string,"Error calling the form editor, code: %d",result);
  287.           MessageBox(NULL,string,NULL,MB_OK);
  288.        }
  289.     }
  290.  
  291.     return TRUE;
  292. }
  293.  
  294.  
  295.  
  296. /****************************************************************************
  297.     DemoOptions:
  298.     This dialog box allows the user to change the editor window options.
  299. ****************************************************************************/
  300.  
  301. BOOL FAR PASCAL _export DemoOptions(HWND hDlg,unsigned message,WPARAM wParam,LPARAM lParam)
  302. {
  303.  
  304.  
  305.     switch (message) {
  306.         case WM_INITDIALOG:
  307.             // Set initial values of the parameters 
  308.             SetDlgItemInt(hDlg,IDC_X,FormParm.x,0);
  309.             SetDlgItemInt(hDlg,IDC_Y,FormParm.y,0);
  310.             SetDlgItemInt(hDlg,IDC_WIDTH,FormParm.width,0);
  311.             SetDlgItemInt(hDlg,IDC_HEIGHT,FormParm.height,0);
  312.             SendMessage(GetDlgItem(hDlg,IDC_MENU),BM_SETCHECK, FormParm.ShowMenu, 0L);
  313.             SendMessage(GetDlgItem(hDlg,IDC_VER_BAR),BM_SETCHECK, FormParm.ShowVerBar, 0L);
  314.             SendMessage(GetDlgItem(hDlg,IDC_HOR_BAR),BM_SETCHECK, FormParm.ShowHorBar, 0L);
  315.             return (TRUE);
  316.  
  317.         case WM_COMMAND:
  318.             switch (CONTROL_ID(wParam,lParam)) {
  319.                 case IDOK:
  320.                    if (!GetNumVal(hDlg,IDC_X,&FormParm.x)) return (TRUE);
  321.                    if (!GetNumVal(hDlg,IDC_Y,&FormParm.y)) return (TRUE);
  322.                    if (!GetNumVal(hDlg,IDC_WIDTH,&FormParm.width)) return (TRUE);
  323.                    if (!GetNumVal(hDlg,IDC_HEIGHT,&FormParm.height)) return (TRUE);
  324.                    FormParm.ShowMenu=SendMessage(GetDlgItem(hDlg,IDC_MENU),BM_GETCHECK, 0, 0L);
  325.                    FormParm.ShowVerBar=SendMessage(GetDlgItem(hDlg,IDC_VER_BAR),BM_GETCHECK, 0, 0L);
  326.                    FormParm.ShowHorBar=SendMessage(GetDlgItem(hDlg,IDC_HOR_BAR),BM_GETCHECK, 0, 0L);
  327.                    EndDialog(hDlg, TRUE);
  328.                    return (TRUE);
  329.         
  330.                 case IDCANCEL:
  331.                    EndDialog(hDlg, FALSE);
  332.                    return (TRUE);
  333.                 default:
  334.                    ;
  335.             }
  336.             break;
  337.     }
  338.     return (FALSE);
  339. }
  340.  
  341. /******************************************************************************
  342.     GetFormSelection:
  343.     Shows available report forms and let user select one.
  344.     In form edit mode, the user has two additional selections to
  345.     create a new report form.
  346. ******************************************************************************/
  347. GetFormSelection(char *CurFile,int FormEdit)
  348. {
  349.      int  select;
  350.      FARPROC lpProcParam;
  351.  
  352.      TotalForms=0;
  353.      
  354.      strcpy(CurFile," ");
  355.      
  356.      #if defined (WIN32)
  357.         GetFormFiles("*.FPC");          // get the report form files 
  358.      #else
  359.         GetFormFiles("*.FP");          // get the report form files 
  360.      #endif
  361.  
  362.      if (FormEdit) {                // allow the user to create new forms 
  363.          NewReport=TotalForms;      // append the new report selection 
  364.          TotalForms++;
  365.          strcpy(FormName[NewReport],"New Report Form");
  366.          strcpy(FormFile[NewReport]," ");
  367.      }
  368.      else NewReport=TotalForms;
  369.  
  370.      //***** show the file seletion box and let user select a file *****
  371.      lpProcParam = MakeProcInstance(DemoForms, hInst);
  372.      select=DialogBox(hInst,"DemoForms",hDemoWin,lpProcParam);
  373.      FreeProcInstance(lpProcParam);
  374.  
  375.      if (select>=0) {
  376.         strcpy(CurFile,"");
  377.         if (select!=NewReport) strcpy(CurFile,FormFile[select]);  // pass the file name 
  378.      }
  379.  
  380.      return select;
  381. }
  382.  
  383. /****************************************************************************
  384.     DemoForms:
  385.     This dialog box allows the user to select a form file.
  386. ****************************************************************************/
  387.  
  388. BOOL FAR PASCAL _export DemoForms(HWND hDlg,unsigned message,WPARAM wParam,LPARAM lParam)
  389. {
  390.     int i,idx;
  391.  
  392.     switch (message) {
  393.         case WM_INITDIALOG:
  394.             // Set initial values of the parameters 
  395.             for (i=0;i<TotalForms;i++) {
  396.                idx=(int)SendMessage(GetDlgItem(hDlg,IDC_FILE_BOX),LB_ADDSTRING, 0, (DWORD)(LPSTR)FormName[i]);
  397.                SendMessage(GetDlgItem(hDlg,IDC_FILE_BOX),LB_SETITEMDATA, idx, i);
  398.             }
  399.             SendMessage(GetDlgItem(hDlg,IDC_FILE_BOX),LB_SETCURSEL, 0,0L);
  400.             SetFocus(GetDlgItem(hDlg,IDC_FILE_BOX));
  401.             return (FALSE);
  402.  
  403.         case WM_COMMAND:
  404.             switch (CONTROL_ID(wParam,lParam)) {
  405.                 case IDC_FILE_BOX:
  406.                     if (NOTIFY_MSG(wParam,lParam)!=LBN_DBLCLK) break;
  407.  
  408.                 case IDOK:
  409.                    i=SendMessage(GetDlgItem(hDlg,IDC_FILE_BOX),LB_GETCURSEL, 0,0L);     // index into the list box
  410.                    i=SendMessage(GetDlgItem(hDlg,IDC_FILE_BOX),LB_GETITEMDATA, i,0L);   // index into our table
  411.                    EndDialog(hDlg, i);
  412.                    return (TRUE);
  413.         
  414.                 case IDCANCEL:
  415.                    EndDialog(hDlg, -1);
  416.                    return (TRUE);
  417.                 default:
  418.                    ;
  419.             }
  420.             break;
  421.     }
  422.     return (FALSE);
  423. }
  424.  
  425. /****************************************************************************
  426.     DemoFileField:
  427.     This dialog box allows the user to select a data file or to select
  428.     a field for a data file.  When lParam is -1, the dialog box shows 
  429.     the data files to select from.  When lParam is zero or greater than 
  430.     zero, the dialog box shows the fields for the file to select from.  The
  431.     lParam, then specifies the index of the data file to choose the
  432.     fields from.
  433. ****************************************************************************/
  434.  
  435. int FAR PASCAL _export DemoFileField(HWND hDlg,unsigned message,WPARAM wParam,LPARAM lParam)
  436. {
  437.     int i;
  438.  
  439.     switch (message) {
  440.         case WM_INITDIALOG:
  441.             // Set initial values of the parameters 
  442.             if (lParam<0) {                            // stuff the list box with data file names 
  443.                SetWindowText(hDlg,"Select Data File"); // set the header 
  444.                for (i=0;i<MAX_FILES;i++) {
  445.                   SendMessage(GetDlgItem(hDlg,IDC_FILE_BOX),LB_ADDSTRING, 0, (DWORD)(LPSTR)DataFile[i].name);
  446.                }
  447.             }
  448.             else {                                     // stuff the list box with data field names 
  449.                if (GetSortField) SetWindowText(hDlg,"Select Sort Field");// set the header 
  450.                else              SetWindowText(hDlg,"Select Data Field");// set the header 
  451.                for (i=0;i<DataFile[lParam].TotalFields;i++) {
  452.                   SendMessage(GetDlgItem(hDlg,IDC_FILE_BOX),LB_ADDSTRING, 0, (DWORD)(LPSTR)DataField[lParam][i].ShortName);  // load the field names 
  453.                }
  454.             }
  455.             SendMessage(GetDlgItem(hDlg,IDC_FILE_BOX),LB_SETCURSEL, 0,0L);
  456.             SetFocus(GetDlgItem(hDlg,IDC_FILE_BOX));
  457.             return (FALSE);
  458.  
  459.         case WM_COMMAND:
  460.             switch (CONTROL_ID(wParam,lParam)) {
  461.                 case IDC_FILE_BOX:
  462.                     if (NOTIFY_MSG(wParam,lParam)!=LBN_DBLCLK) break;
  463.  
  464.                 case IDOK:
  465.                    i=SendMessage(GetDlgItem(hDlg,IDC_FILE_BOX),LB_GETCURSEL, 0,0L);
  466.                    EndDialog(hDlg, i);
  467.                    return (TRUE);
  468.         
  469.                 case IDCANCEL:
  470.                    EndDialog(hDlg, -1);
  471.                    return (TRUE);
  472.                 default:
  473.                    ;
  474.             }
  475.             break;
  476.     }
  477.     return (FALSE);
  478. }
  479.  
  480. /******************************************************************************
  481.     GetFormFiles:
  482.     This routine scans the current directory for the files matching the
  483.     specified template.
  484. ******************************************************************************/
  485. GetFormFiles(char *template)
  486. {
  487.     unsigned done;
  488.     FILE *iStream;
  489.     struct StrFormHdr hdr;
  490.  
  491.     #if defined (WIN32) 
  492.        HANDLE hFindFile;
  493.        WIN32_FIND_DATA blk;
  494.        done=FALSE;
  495.        if (INVALID_HANDLE_VALUE==(hFindFile=FindFirstFile(template,&blk))) done=TRUE;
  496.     #else
  497.        #if defined(__TURBOC__)
  498.           struct ffblk blk;
  499.           done=findfirst(template,&blk,FA_RDONLY);
  500.        #else
  501.           struct find_t blk;
  502.           done=_dos_findfirst(template,_A_NORMAL,&blk);
  503.        #endif
  504.     #endif
  505.  
  506.     
  507.     while(!done) {
  508.        if (TotalForms==MAX_FORMS) {
  509.           return FALSE;
  510.        }
  511.  
  512.        #if defined (WIN32)
  513.           strcpy(FormFile[TotalForms],blk.cFileName);
  514.           done=!FindNextFile(hFindFile,&blk);
  515.        #else
  516.           #if defined(__TURBOC__)
  517.              strcpy(FormFile[TotalForms],blk.ff_name);
  518.              done=findnext(&blk);         // get the next file 
  519.           #else
  520.              strcpy(FormFile[TotalForms],blk.name);
  521.              done=_dos_findnext(&blk);
  522.           #endif
  523.        #endif
  524.        
  525.        //*************** get the form name ****************************
  526.        if (NULL==(iStream=fopen(FormFile[TotalForms],"r+b"))) continue;
  527.        
  528.        if (fread(&hdr,sizeof(struct StrFormHdr),1,iStream)!=1
  529.           || hdr.FormSign!=FORM_SIGN ) {
  530.            fclose(iStream);
  531.            continue;
  532.        }
  533.        fclose(iStream);
  534.  
  535.        strcpy(FormName[TotalForms],hdr.name);
  536.  
  537.        TotalForms++;
  538.     }
  539.  
  540.     return TRUE;
  541. }
  542.  
  543. /******************************************************************************
  544.     ReadFields:
  545.     Read the field definitions for a file.  The argument specifies the index 
  546.     into the DataFile structure.
  547.     The field definitions are stored in files with the extension .DF, whereas
  548.     the data is stored in the files with the extension .DB.  
  549. ******************************************************************************/
  550. ReadFields(int idx)
  551. {
  552.     char name[64],line[300],CharReturn[300],*result;
  553.     FILE *iStream;
  554.     int  CurLen,CurField=0,LineIdx;
  555.     int  ExtractField(char *,int *,int,char *);
  556.  
  557.     strcpy(name,DataFile[idx].name);
  558.     strcat(name,".DF");              // file extension 
  559.  
  560.     iStream=fopen(name,"r+t");
  561.     if (iStream==NULL) {
  562.         wsprintf(line,"Error while opening file %s ",(LPSTR)name);
  563.         MessageBox(hDemoWin,line,NULL,MB_OK);
  564.         return FALSE;
  565.     }
  566.  
  567.     READ_LINE:
  568.     result=fgets(line,300,iStream);
  569.     if (ferror(iStream)) {
  570.        wsprintf(line,"Error while reading file %s ",(LPSTR)name);
  571.        MessageBox(hDemoWin,line,NULL,MB_OK);
  572.        return FALSE;
  573.     }
  574.     if (feof(iStream))   goto END_FILE; // end of file 
  575.     if (result==NULL) {
  576.        wsprintf(line,"Error while reading file %s ",(LPSTR)name);
  577.        MessageBox(hDemoWin,line,NULL,MB_OK);
  578.        return FALSE;
  579.     }
  580.     
  581.     CurLen=strlen(line);             // exclude new line character 
  582.     if (CurLen>0 && line[CurLen-1]==0xA) CurLen--;
  583.     line[CurLen]=0;
  584.  
  585.     LineIdx=0;                       // prepare to scan the line 
  586.  
  587.     // extract the field name 
  588.     if (!ExtractField(line,&LineIdx,CurLen,CharReturn)) { // extract field name 
  589.        wsprintf(line,"Invalid format, file: %s, field# %d\n",(LPSTR)name,1);
  590.        MessageBox(hDemoWin,line,NULL,MB_OK);
  591.        return FALSE;
  592.     }
  593.     StringTrim(CharReturn);              // trim space 
  594.     strcpy(DataField[idx][CurField].ShortName,CharReturn);
  595.     strcpy(DataField[idx][CurField].FullName,DataFile[idx].name);
  596.     strcat(DataField[idx][CurField].FullName,"->");
  597.     strcat(DataField[idx][CurField].FullName,CharReturn);
  598.  
  599.     // extract the field width 
  600.     if (!ExtractField(line,&LineIdx,CurLen,CharReturn)) { // extract field name 
  601.        wsprintf(line,"Invalid format, file: %s, field# %d\n",(LPSTR)name,1);
  602.        MessageBox(hDemoWin,line,NULL,MB_OK);
  603.        return FALSE;
  604.     }
  605.     DataField[idx][CurField].width=atoi(CharReturn);
  606.  
  607.     // extract the field type 
  608.     if (!ExtractField(line,&LineIdx,CurLen,CharReturn)) { // extract field name 
  609.        wsprintf(line,"Invalid format, file: %s, field# %d\n",(LPSTR)name,1);
  610.        MessageBox(hDemoWin,line,NULL,MB_OK);
  611.        return FALSE;
  612.     }
  613.     if      (CharReturn[0]=='T') DataField[idx][CurField].type=TYPE_TEXT;
  614.     else if (CharReturn[0]=='N') DataField[idx][CurField].type=TYPE_NUM;
  615.     else if (CharReturn[0]=='F') DataField[idx][CurField].type=TYPE_DBL;
  616.     else if (CharReturn[0]=='D') DataField[idx][CurField].type=TYPE_DATE;
  617.     else if (CharReturn[0]=='L') DataField[idx][CurField].type=TYPE_LOGICAL;
  618.     else if (CharReturn[0]=='P') DataField[idx][CurField].type=TYPE_PICT;
  619.     else {
  620.        wsprintf(line,"Invalid field type, file: %s, field# %d\n",(LPSTR)name,1);
  621.        MessageBox(hDemoWin,line,NULL,MB_OK);
  622.        return FALSE;
  623.     }
  624.  
  625.     // extract the default decimal places 
  626.     if (!ExtractField(line,&LineIdx,CurLen,CharReturn)) { // extract field name 
  627.        wsprintf(line,"Invalid format, file: %s, field# %d\n",(LPSTR)name,1);
  628.        MessageBox(hDemoWin,line,NULL,MB_OK);
  629.        return FALSE;
  630.     }
  631.     DataField[idx][CurField].DecPlaces=atoi(CharReturn);
  632.  
  633.     CurField++;
  634.     
  635.     goto READ_LINE;
  636.  
  637.     END_FILE:
  638.     fclose(iStream);
  639.     DataFile[idx].TotalFields=CurField;
  640.  
  641.     return TRUE;
  642. }
  643.  
  644. /******************************************************************************
  645.     ExtractField:
  646.     This routine scans a text line to extract the next field.
  647.     The fields are assumed to be of character type.
  648. ******************************************************************************/
  649. ExtractField(char *line,int *idx,int LineLen,char *CharReturn)
  650. {
  651.     char quote='"',comma=',',string[300],*ptr,SaveChar;
  652.     int  i,j;
  653.  
  654.     i=(*idx);
  655.     while (i<LineLen && line[i]==' ') i++;  // go past the spaces 
  656.  
  657.     if (i>=LineLen) return FALSE;           // past the end of the line 
  658.  
  659.     if (line[i]==quote) {
  660.        i++;                                 // skip over the first quote 
  661.        if (NULL==(ptr=memchr(&line[i],quote,LineLen-i))) return FALSE;
  662.        
  663.        j=1;
  664.        while (ptr[j]!=',' && ptr[j]!=0) j++;// locate the next comma 
  665.        
  666.        SaveChar=ptr[0];                     // extract the string 
  667.        ptr[0]=0;        
  668.        strcpy(string,&line[i]);
  669.        ptr[0]=SaveChar;
  670.        (*idx)=i+strlen(string)+1+j;         // index to the next field        
  671.     }
  672.     else {
  673.        if (NULL==(ptr=memchr(&line[i],comma,LineLen-i))) ptr=&line[LineLen];  // field spans the end of the line 
  674.        
  675.        SaveChar=ptr[0];                     // extract the string 
  676.        ptr[0]=0;        
  677.        strcpy(string,&line[i]);
  678.        ptr[0]=SaveChar;
  679.        (*idx)=i+strlen(string)+1;           // index to the next field        
  680.     }
  681.  
  682.     strcpy(CharReturn,string);
  683.     return TRUE;
  684. }
  685.  
  686.  
  687. /******************************************************************************
  688.     Call back routines.
  689.     The following routines are called by Report Ease to select an application
  690.     field or to verify an application field.
  691. ******************************************************************************/
  692.  
  693. /******************************************************************************
  694.     UserFieldSelection:
  695.     This routine is called by the FORM_FLD module to allow user to select 
  696.     a data field.  This routine can be programmed by your application in
  697.     any way as long as it returns the data about the selected field using
  698.     the argument pointer.  If the user chose not to select a field after all,
  699.     the function should return with a FALSE value. Otherwise it should return
  700.     with a TRUE value.
  701.  
  702.     In this routine, we will allow the user to first select a file, and
  703.     then select a field from the chosen file.  The required data about the
  704.     selected field is filled into the 'field' structure. Your program may
  705.     also optionally fill other remaining 'field' structure variables.
  706.  
  707.     The first parameter contains the handle of the Form Editor window.
  708.     The third parameter specifies the sort field number if this field will
  709.     be used for section breaks. If your application will not sort on all
  710.     fields,  you can restrict the fields that can be selected by the user.
  711.     For non-sort fields, this paramter is set to 0.  In this demo program, 
  712.     we will allow only the fields from the primary file (CUSTOMER) to be 
  713.     used as sort fields. 
  714. ******************************************************************************/
  715. int FAR PASCAL _export UserFieldSelection(HWND hWnd,struct StrField huge *field,int SortFieldNo)
  716. {
  717.     int CurFile,CurField;
  718.     FARPROC lpProc;
  719.  
  720.     SELECT_FILE:
  721.     if (SortFieldNo==0) {
  722.         lpProc = MakeProcInstance(DemoFileField, hInst);
  723.         CurFile=DialogBoxParam(hInst,"DemoFileField",hWnd,lpProc,(long)-1);
  724.         FreeProcInstance(lpProc);           // set back the old window 
  725.         if (CurFile<0) return FALSE;
  726.     }
  727.     else CurFile=0;                         // Allow sort fields only from the customer file 
  728.  
  729.     // SELECT FIELD
  730.     if (SortFieldNo==0) GetSortField=FALSE; // global flag 
  731.     else                GetSortField=TRUE;
  732.  
  733.     lpProc = MakeProcInstance(DemoFileField, hInst);
  734.     CurField=DialogBoxParam(hInst,"DemoFileField",hWnd,lpProc,(long)CurFile);
  735.     FreeProcInstance(lpProc);              // set back the old window 
  736.     
  737.     if (CurField<0) {
  738.        if (GetSortField) return FALSE;
  739.        else              goto SELECT_FILE;
  740.     }
  741.  
  742.     // fill the required 'field' variables 
  743.  
  744.     lstrcpy(field->name,DataField[CurFile][CurField].FullName); // field name 
  745.     field->type=DataField[CurFile][CurField].type;             // alpha/num etc 
  746.     field->width=DataField[CurFile][CurField].width;           // display width 
  747.     field->DecPlaces=DataField[CurFile][CurField].DecPlaces;   // decimal places for display 
  748.     
  749.     // specify new paragraph indicator field.  Used only for a word/wrapped
  750.     // text type fields.
  751.     field->ParaChar[0]='|';
  752.  
  753.     /****** fill up these OPTIONAL fields also. This information will be 
  754.             used by our report executer demo program to identfy the
  755.             fields quickly.  *****/
  756.  
  757.     field->FileId=CurFile;                    // Information only 
  758.     field->FieldId=CurField;                  // information only 
  759.     
  760.     return TRUE;
  761. }
  762.  
  763. /******************************************************************************
  764.     VerifyField:
  765.     This routine is called by the FORM1 module to validate a field.  The field
  766.     name is given by the 'name' variable in the StrField structure.  It 
  767.     contains the full name including the file prefix (if your application allows
  768.     it). The input field is always in the upper case. The required data about the
  769.     current field is filled into the 'field' structure. Your program may
  770.     also optionally fill other remaining 'field' structure variables. 
  771.     
  772.     The second argument indicates if the field can be 
  773.     used as a sort field.  This parameter indicates the sort field number.
  774.     1 indicates the first sort field, 2 the second, ... A zero for this 
  775.     field indicates a non-sort field.  In this demo program, we will allow
  776.     only the fields from the primary file (CUSTOMER) to be used as sort fields.
  777.  
  778.     The function returns TRUE if the field is valid, otherwise it returns
  779.     a FALSE value.
  780. ******************************************************************************/
  781. int FAR PASCAL _export VerifyField(struct StrField huge *field,int SortFieldNo)
  782. {
  783.     int  CurFile,CurField,MaxFiles;
  784.  
  785.     if (SortFieldNo==0) MaxFiles=MAX_FILES;
  786.     else                MaxFiles=1;         // allow sort fields from the customer file only 
  787.  
  788.     for (CurFile=0;CurFile<MaxFiles;CurFile++) {
  789.        for (CurField=0;CurField<DataFile[CurFile].TotalFields;CurField++) {
  790.          if (lstrcmp(DataField[CurFile][CurField].FullName,field->name)==0) {
  791.             //* fill up the mandatory data *
  792.             field->type=DataField[CurFile][CurField].type;             // alpha/num etc 
  793.             field->width=DataField[CurFile][CurField].width;           // display width 
  794.             field->DecPlaces=DataField[CurFile][CurField].DecPlaces;   // decimal places for display 
  795.  
  796.             /****** fill up these OPTIONAL fields also. This information will be 
  797.                     used by our report executer demo program to identfy the
  798.                     fields quickly.  *****/
  799.  
  800.             field->FileId=CurFile;                    // Information only 
  801.             field->FieldId=CurField;                  // information only 
  802.             
  803.             return TRUE;
  804.          }
  805.        }
  806.     }
  807.  
  808.     return FALSE;                                     // not a valid field 
  809. }
  810.  
  811. /******************************************************************************
  812.     DrawPicture:
  813.     This routine is called by the report executor to draw a specified
  814.     picture type field.  The first argument specifies the device context
  815.     of the reporting device.  If the report output is directed to a printer,
  816.     this device context belongs to a printer, otherwise it specifies a 
  817.     device context for a metafile.  You application should draw the picture
  818.     on this device context within the specified rectangle (last argument).
  819.  
  820.     The device context is in ANISOTROPIC mode.  The resolution in the X and
  821.     Y direction is given by the UNITS_PER_INCH constant.
  822.  
  823.     The second argument is the picture id.  The third and fourth arguments
  824.     are the file and field id for the picture field.
  825.  
  826.     The function returns TRUE if successful.
  827.  
  828.     In this demo program, this routine draws a logo for a given picture id.
  829.     This program uses only one bitmap, and draws different part of the same
  830.     bitmap for different picture id.
  831.  
  832. ******************************************************************************/
  833. int FAR PASCAL _export DrawPicture(HDC hDC, int PictId, int FileId, int FieldId, RECT far * rect)
  834. {
  835.     BITMAP bm;
  836.     int SourceWidth,SourceX;
  837.  
  838.     // get the bitmap information
  839.     GetObject(hLogoBM,sizeof(BITMAP),&bm);
  840.  
  841.     // Select part of the bitmap to display for this picture id
  842.     SourceWidth=bm.bmWidth/10;          // divide picture into 10 equal parts
  843.     if (PictId<1)  PictId=1;
  844.     if (PictId>10) PictId=10;
  845.     SourceX=SourceWidth*(PictId-1);
  846.  
  847.     // copy the bitmap
  848.     StretchDIBits(hDC,rect->left,rect->top,rect->right-rect->left,rect->bottom-rect->top,
  849.                    SourceX,0,SourceWidth,bm.bmHeight,pImage,(LPBITMAPINFO)pInfo,DIB_RGB_COLORS,SRCCOPY);
  850.  
  851.     return TRUE;
  852. }
  853.  
  854.  
  855. /******************************************************************************
  856.     Report Executer Interface Routines.
  857. ******************************************************************************/
  858.  
  859. /******************************************************************************
  860.     RunReport: The following routines demonstrate the process of calling the 
  861.     report executor.
  862. ******************************************************************************/
  863. RunReport()
  864. {
  865.    int  PrintRecords(struct StrField huge *,int);
  866.  
  867.    if (GetFormSelection(RepParm.file,FALSE)>=0) { // select a form file 
  868.  
  869.        //********* Initialize the argument parameters *************                                                              
  870.        RepParm.device='A';            // ask user  
  871.  
  872.        RepParm.x=FormParm.x;          // specify the window coordinates for screen output 
  873.        RepParm.y=FormParm.y;          // these fields needed only for screen output 
  874.        RepParm.width=FormParm.width; 
  875.        RepParm.height=FormParm.height; 
  876.  
  877.        if (RepInit(&RepParm)!=0) return FALSE;// Intialize the report 
  878.  
  879.        PrepareFile();                         // sort and join files if needed    
  880.  
  881.        PrintRecords(RepParm.field,RepParm.TotalFields);  // read and print each record 
  882.  
  883.        RepExit();                            // print footers and exit 
  884.    }
  885.  
  886.    return TRUE;
  887. }
  888.  
  889. /******************************************************************************
  890.     PrepareFile:  Sort and join the CUSTOMER and SALES files if needed.
  891. ******************************************************************************/
  892. PrepareFile()
  893. {
  894.     int i,SalesFileUsed,SortKey[10];
  895.     char InputFile1[80],InputFile2[80],OutputFile[80];
  896.  
  897.     lstrcpy(InputFile1,DataFile[0].name);
  898.     lstrcat(InputFile1,".DB ");              // apped the data file extension 
  899.  
  900.     for (i=0;i<RepParm.TotalSortFields;i++) {// extract each sort field 
  901.        SortKey[i]=RepParm.SortField[i].FieldId+1;
  902.     }
  903.     FileSort(InputFile1,RepParm.TotalSortFields,SortKey);
  904.                          // the output is in the CUSTOMER.SRT file 
  905.  
  906.     // determine if the sales file is used 
  907.     SalesFileUsed=FALSE;
  908.     for (i=0;i<RepParm.TotalFields;i++) {
  909.        if ((RepParm.field[i].FileId)==1) {   /* id =0 is customer file, and 
  910.                                                 id=1 is the sales file, 
  911.                                                 see the UserFieldSeletion routine */
  912.            SalesFileUsed=TRUE;
  913.            break;
  914.        }
  915.     }
  916.  
  917.     if (SalesFileUsed) {                     // join SALES file with CUSTOMER FILE 
  918.         lstrcpy(InputFile1,DataFile[0].name);
  919.         lstrcat(InputFile1,".SRT ");         // first file 
  920.         
  921.         lstrcpy(InputFile2,DataFile[1].name);
  922.         lstrcat(InputFile2,".DB ");          // second file 
  923.         
  924.         lstrcpy(OutputFile,DataFile[0].name);
  925.         lstrcat(OutputFile,".SRT ");         // output file 
  926.  
  927.         FileJoin(InputFile1,1,InputFile2,1,OutputFile);
  928.         
  929.         /* customer and sales file have field number 1 in common. The output
  930.         is stored in the "CUSTOMER.SRT file.
  931.         */
  932.     }
  933.  
  934.     return TRUE;
  935. }
  936.  
  937. /******************************************************************************
  938.     PrintRecords:
  939.     This routine follows these step:
  940.     1. Open the data set file.
  941.     2. For each record of data set:
  942.          a. Initialize the fields in the field structure.  IMPORTANT:  Initialize
  943.             only the fields where source = SRC_APPL.  All other fields are
  944.             for internal use of ReportEase.
  945.          b. Parse each field from the data record and stuff into the 
  946.             field structure.  One field may be stuffed in more than one
  947.             place in field structure.  Stuff only those structure fields where
  948.             source = SRC_APPL
  949.          c. Call the RepRec routine to print this record.
  950.  
  951.     This routine returns a TRUE value after all records are printed.  If 
  952.     the user hit escape during printing, then the return result will be a FALSE
  953.     value.
  954. ******************************************************************************/
  955. PrintRecords(struct StrField huge *field,int TotalFields)
  956. {
  957.     FILE *iStream;
  958.     int i,CurLen,FileNo,FieldNo,RecNo=1,LineIdx;
  959.     char DataSetName[64],line[300],string[300],*result;
  960.  
  961.     //*** open the data set file ***
  962.     strcpy(DataSetName,DataFile[0].name);
  963.     strcat(DataSetName,".SRT");
  964.  
  965.     if (NULL==(iStream=fopen(DataSetName,"rt"))) {
  966.        MessageBox(hDemoWin,"Can Not Open The Data Set File!",NULL,MB_OK);
  967.        return FALSE;
  968.     }
  969.     
  970.     
  971.     READ_LINE:
  972.     result=fgets(line,300,iStream);
  973.     if (ferror(iStream)) {
  974.        MessageBox(hDemoWin,"Error in reading file",NULL,MB_OK);return FALSE;
  975.     }
  976.     if (feof(iStream))   goto END_FILE; // end of file 
  977.     if (result==NULL) {
  978.        MessageBox(hDemoWin,"Error in reading file",NULL,MB_OK);return FALSE;
  979.     }
  980.     
  981.     CurLen=strlen(line);               // exclude new line character 
  982.     if (CurLen>0 && line[CurLen-1]==0xA) CurLen--;
  983.     line[CurLen]=0;
  984.  
  985.     //********** initialize the field structure *************
  986.     for (i=0;i<TotalFields;i++) {
  987.        if (field[i].source==SRC_APPL) {
  988.           if (field[i].type==TYPE_TEXT)  field[i].CharData[0]=0;
  989.           if (field[i].type==TYPE_NUM)   field[i].NumData=0;
  990.           if (field[i].type==TYPE_PICT)  field[i].NumData=0;
  991.           if (field[i].type==TYPE_DBL)   field[i].DblData=0;
  992.        }
  993.     }
  994.  
  995.     //************ parse the record to get each field ********
  996.     FileNo=0;
  997.     FieldNo=0;
  998.     LineIdx=0;                         // prepare to scan the line 
  999.  
  1000.     while (TRUE) {
  1001.        // extract the fields 
  1002.        if (!ExtractField(line,&LineIdx,CurLen,string)) break;
  1003.           
  1004.        if (FileNo>=MAX_FILES) {
  1005.           wsprintf(line,"Too many fields in the data record number: %d\n",RecNo);
  1006.           MessageBox(hDemoWin,line,NULL,MB_OK);
  1007.           goto END_FILE;
  1008.        }
  1009.     
  1010.        // stuff this field into the field structure 
  1011.        for (i=0;i<TotalFields;i++) {
  1012.           if (field[i].source==SRC_APPL
  1013.                    && field[i].FileId==FileNo && field[i].FieldId==FieldNo) {
  1014.              if (field[i].type==TYPE_TEXT) {
  1015.                  if ((int)strlen(string)>field[i].width) string[field[i].width]=0; // truncate oversize data 
  1016.                  lstrcpy(field[i].CharData,string);
  1017.              }
  1018.              else if (field[i].type==TYPE_NUM)   field[i].NumData=atol(string);
  1019.              else if (field[i].type==TYPE_DBL)   field[i].DblData=(double)atof(string);
  1020.              else if (field[i].type==TYPE_DATE)  field[i].NumData=atol(string); // date shoule be YYMMDD or YYYYMMDD 
  1021.              else if (field[i].type==TYPE_PICT)  field[i].NumData=atol(string); // date shoule be YYMMDD or YYYYMMDD 
  1022.              else if (field[i].type==TYPE_LOGICAL) {
  1023.                 if (string[0]=='Y' || string[0]=='y') field[i].NumData=1;
  1024.                 else                                  field[i].NumData=0;
  1025.              }
  1026.           }
  1027.        }
  1028.  
  1029.        // advance to the next field number 
  1030.        FieldNo++;
  1031.        if (FieldNo>=DataFile[FileNo].TotalFields) {
  1032.           FieldNo=0;
  1033.           FileNo++;
  1034.        }
  1035.  
  1036.     }
  1037.  
  1038.     if (RepRec()!=0)  goto END_FILE;     // print this record 
  1039.  
  1040.     RecNo++;
  1041.  
  1042.     goto READ_LINE;
  1043.  
  1044.     END_FILE:
  1045.     fclose(iStream);
  1046.     return TRUE;
  1047. }
  1048.  
  1049. /******************************************************************************
  1050.     Support Routines.
  1051. ******************************************************************************/
  1052.  
  1053. /*****************************************************************************
  1054.     StringTrim:
  1055.     Remove spaces from the left and right of a NULL terminated string.
  1056. ******************************************************************************/
  1057. void StringTrim(LPSTR string)  
  1058. {
  1059.     RightTrim(string);LeftTrim(string);
  1060. }
  1061.  
  1062. /******************************************************************************
  1063.     RightTrim:
  1064.     Remove spaces on the right of a string.
  1065. ******************************************************************************/
  1066. void RightTrim(LPSTR string)  
  1067. {
  1068.     int i,TempLen;
  1069.  
  1070.     TempLen=lstrlen(string);
  1071.     for (i=TempLen-1;i>=0 && string[i] == ' ';i--);
  1072.     string[i+1] = '\0';
  1073. }
  1074.  
  1075. /******************************************************************************
  1076.     LeftTrim:
  1077.     Trim initial spaces from the string.
  1078. *******************************************************************************/
  1079. void LeftTrim(LPSTR string)    
  1080. {
  1081.     int i,TempLen,BeginPoint;char TempStr[300];
  1082.  
  1083.     TempLen=lstrlen(string);
  1084.     for (i=0;i < TempLen && string[i] == ' ';i++);
  1085.     BeginPoint=i;
  1086.     for (i=BeginPoint;i<TempLen;i++) TempStr[i-BeginPoint]=string[i];
  1087.     TempStr[TempLen-BeginPoint] = '\0';
  1088.     lstrcpy(string,TempStr);
  1089. }
  1090.  
  1091. /******************************************************************************
  1092.      GetNumVal:
  1093.      Extract a numeric value from the dialog item.  Display error if a non-
  1094.      numeric value is encountered and return a false result.
  1095. *******************************************************************************/
  1096. BOOL GetNumVal(HWND hWin,int ControlId,int *pInt)
  1097. {
  1098.     BOOL ErrorFlag;
  1099.  
  1100.     *pInt=GetDlgItemInt(hWin,ControlId,&ErrorFlag,(BOOL) 0);
  1101.     if (!ErrorFlag) {
  1102.        MessageBox(hWin,"Must Enter a Valid Number",NULL,MB_OK);
  1103.        SetFocus(GetDlgItem(hWin,ControlId));
  1104.        return (FALSE);
  1105.     }
  1106.     return TRUE;
  1107. }
  1108.  
  1109. /******************************************************************************
  1110.      Bitmap2DIB:
  1111.      Extract device indepedent bits from the bitmap.
  1112. *******************************************************************************/
  1113. Bitmap2DIB()
  1114. {
  1115.     DWORD  InfoSize,ImageSize;
  1116.     BITMAP bm;
  1117.     HDC hDC;
  1118.  
  1119.     GetObject(hLogoBM,sizeof(BITMAP),(LPSTR)&bm);     // get the dimension of bit map 
  1120.    
  1121.     //********** create the device independent bitmap ***********************
  1122.     InfoSize=sizeof(BITMAPINFOHEADER)+256*sizeof(RGBQUAD);  // create a 256 color bitmap structure 
  1123.  
  1124.     if (NULL==(hInfo=GlobalAlloc(GMEM_MOVEABLE,InfoSize))
  1125.       ||NULL==(pInfo=(LPBITMAPINFOHEADER)GlobalLock(hInfo)) ) {
  1126.         MessageBox(hDemoWin,"Ran out of memory for Bitmap Info!",NULL,MB_OK);
  1127.         return -1;
  1128.     }
  1129.  
  1130.     //***** fill in the info structure ******
  1131.     pInfo->biSize=sizeof(BITMAPINFOHEADER);
  1132.     pInfo->biWidth=bm.bmWidth;
  1133.     pInfo->biHeight=bm.bmHeight;
  1134.     pInfo->biPlanes=1;                       // DIB have plane = 1 
  1135.     pInfo->biBitCount=8;                     // 8 bits per pixel or color 
  1136.     pInfo->biCompression=BI_RGB;             // no compression - needed for compatibility for all devices
  1137.     pInfo->biSizeImage=0;                    // initialize to zero 
  1138.     pInfo->biXPelsPerMeter=0;
  1139.     pInfo->biYPelsPerMeter=0;
  1140.     pInfo->biClrUsed=0;                      // depends on biBitCount 
  1141.     pInfo->biClrImportant=0;                 // all colors important 
  1142.  
  1143.     hDC=GetDC(hDemoWin);
  1144.  
  1145.     if (!GetDIBits(hDC,hLogoBM,0,bm.bmHeight,NULL,(LPBITMAPINFO)pInfo,DIB_RGB_COLORS)) {
  1146.         MessageBox(hDemoWin,"Error Creating Device Independent Bitmap Structure!",NULL,MB_OK);
  1147.         return -1;
  1148.     }
  1149.  
  1150.     ImageSize=pInfo->biSizeImage;
  1151.     
  1152.     if (NULL==(hImage=GlobalAlloc(GMEM_MOVEABLE,ImageSize))
  1153.       ||NULL==(pImage=GlobalLock(hImage)) ) {
  1154.         MessageBox(hDemoWin,"Ran out of memory for Bitmap Image!",NULL,MB_OK);
  1155.         return -1;
  1156.     }
  1157.  
  1158.     if (!GetDIBits(hDC,hLogoBM,0,bm.bmHeight,pImage,(LPBITMAPINFO)pInfo,DIB_RGB_COLORS)) {
  1159.         MessageBox(hDemoWin,"Error Creating Device Independent Bitmap Image!",NULL,MB_OK);
  1160.         return -1;
  1161.     }
  1162.     
  1163.     // release the common device context
  1164.     ReleaseDC(hDemoWin,hDC);
  1165.  
  1166.     return TRUE;
  1167. }
  1168.  
  1169.  
  1170. /******************************************************************************
  1171.     OurPrintf:
  1172.     This routine formats and display a given set of arguments.  The format
  1173.     is given by the first argument.  The argument 2 to n contain the
  1174.     arguments for the specified format.  The function uses MessageBox to
  1175.     display the formatted string.
  1176. ******************************************************************************/
  1177. OurPrintf(LPSTR fmt,...)
  1178. {
  1179.     LPSTR ArgStart;
  1180.     char string[256];
  1181.  
  1182.     ArgStart=(LPSTR) &fmt;  // pointer to first argument 
  1183.     ArgStart=&ArgStart[4];  // go past the first argument 
  1184.     wvsprintf(string,fmt,ArgStart);
  1185.     if (FindWindow("DBWin",NULL)) { // debug window open
  1186.         lstrcat(string,"\n");
  1187.         OutputDebugString(string);  // send to the debug terminal
  1188.     }
  1189.     else MessageBox(NULL,string,NULL,MB_OK);
  1190.     return TRUE;
  1191. }
  1192.  
  1193.