在WIN10平台上体验Microsoft古老的Quick C 1.0编程

前言:

在WIN10平台上体验Microsoft古老的Quick C 1.0编程_第1张图片

90年代初,微软出了Quick系统对抗Borland Turbo系列,其中包括 QuickBasic, QuickPascal和Quick C。1991年,Quick C for Windows 1.0发布,后来它被Visual C++取代。我自己觉得微软成就在那个winstub.exe桩上,后来从xp开始挖掉那个桩。为了突破DOS内存限制,DOS4GW, pharlap都有办法,但只有winstub.exe架起了微软的WINDOWS平台,其它的都被淘汰了。现在CPU有内存管理单元,这些东西都不需要了。

步骤一:QCWIN (Quick C 1.0 for Windows) 安装

在WIN10上安装VMWARE虚拟机,在虚拟机中安装XP。虚拟机挂载共享文件夹,在WIN10上将5张盘的内容放在文件夹中,XP系统中打开共享文件夹,双击运行DISK1中的SETUP.EXE

在WIN10平台上体验Microsoft古老的Quick C 1.0编程_第2张图片

安装程序会自动顺畅地安装好5张盘的内容。下图是安装后的文件分布情况。

在WIN10平台上体验Microsoft古老的Quick C 1.0编程_第3张图片

步骤二:运行QCWIN (Quick C 1.0 for Windows)

在WIN10平台上体验Microsoft古老的Quick C 1.0编程_第4张图片

界面看上去很简洁,在XP theme夹持下感观上很舒服。下面分步建立一个简单程序。

步骤三:写一个简单程序

先打开QUICK CASE,用它是做一个SDI单文档界面,也是程序的主界面。

在WIN10平台上体验Microsoft古老的Quick C 1.0编程_第5张图片

3.1 输入标题

在WIN10平台上体验Microsoft古老的Quick C 1.0编程_第6张图片

3.2 输入菜单项

在WIN10平台上体验Microsoft古老的Quick C 1.0编程_第7张图片

建完 File - Exit 后,在它旁边用同样的方法建 Help - About。 在Quick Case 的Design菜单项下可以设置图标及样式。

3.3 回到QCWIN程序(最小化但不要关闭QuickCase),在TOOLS下选用Dialog Editor在WIN10平台上体验Microsoft古老的Quick C 1.0编程_第8张图片

在WIN10平台上体验Microsoft古老的Quick C 1.0编程_第9张图片

3.4 从任务栏上恢复QuickCase窗口(最小化但不要关闭Dialog Editor),先保存文件,然后点击Build下的生成。如果保存文件名是Hello.WIN的话,则会生成Hello.C文件和Hello.H文件。

在WIN10平台上体验Microsoft古老的Quick C 1.0编程_第10张图片

把它最小化到任务栏,同时恢复任务栏上的Dialog Editor,保存Dialog文件并设置Include头文件。

在WIN10平台上体验Microsoft古老的Quick C 1.0编程_第11张图片

3.5 Open装入QuickCase生成的MAK文件,然后Build或Rebuild All生成EXE文件。

在WIN10平台上体验Microsoft古老的Quick C 1.0编程_第12张图片

步骤四:回观并感受上面的代码

生成的文件

在WIN10平台上体验Microsoft古老的Quick C 1.0编程_第13张图片

生成的代码是WIN16 API代码,与现在的WIN32 API代码基本上相同,但个别API函数没有,比如LoadImage,需要用其它函数组合实现。生成的代码基本没改,只是读取了桌面尺寸和默认生成的主窗体尺寸,然后根据尺寸参数将主窗体放在屏幕的正中间位置。代码从注册应用程序类、创建主窗口、显示主窗口、进入主消息队列,与现在的API编程是完全一样的,这套思路从1991年到现在30年间似乎没有什么改变。

/* QuickCase:W KNB Version 1.00 */
#include "HELLO.h"

/************************************************************************/
/*                                                                      */
/* Windows 3.0 Main Program Body                                        */
/*                                                                      */
/* The following routine is the Windows Main Program.  The Main Program */
/* is executed when a program is selected from the Windows Control      */
/* Panel or File Manager.  The WinMain routine registers and creates    */
/* the program's main window and initializes global objects.  The       */
/* WinMain routine also includes the applications message dispatch      */
/* loop.  Every window message destined for the main window or any      */
/* subordinate windows is obtained, possibly translated, and            */
/* dispatched to a window or dialog processing function. The dispatch   */
/* loop is exited when a WM_QUIT message is obtained.  Before exiting   */
/* the WinMain routine should destroy any objects created and free      */
/* memory and other resources.                                          */
/*                                                                      */
/************************************************************************/

int PASCAL WinMain(HANDLE hInstance, HANDLE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow)
{
 /***********************************************************************/
 /* HANDLE hInstance;       handle for this instance                    */
 /* HANDLE hPrevInstance;   handle for possible previous instances      */
 /* LPSTR  lpszCmdLine;     long pointer to exec command line           */
 /* int    nCmdShow;        Show code for main window display           */
 /***********************************************************************/

 MSG        msg;           /* MSG structure to store your messages        */
 int        nRc;           /* return value from Register Classes          */

 HWND hDesk;	//handle of DESKTOP as a root window
 int WndMainX, WndMainY;		//new centered main window
 int nWndMainWidth, nWndMainHeight;		//width and height of DESKTOP
 RECT rectWndDesk;		//rectanglur structure of DESKTOP
 RECT rectWndMain;		//rectanglur structure of main window

 strcpy(szAppName, "HELLO");
 hInst = hInstance;
 if(!hPrevInstance)
   {
    /* register window classes if first instance of application         */
    if ((nRc = nCwRegisterClasses()) == -1)
      {
       /* registering one of the windows failed                         */
       LoadString(hInst, IDS_ERR_REGISTER_CLASS, szString, sizeof(szString));
       MessageBox(NULL, szString, NULL, MB_ICONEXCLAMATION);
       return nRc;
      }
   }

hDesk = GetDesktopWindow();
GetWindowRect(hDesk, &rectWndDesk);

 /* create application's Main window                                    */
 hWndMain = CreateWindow(
                szAppName,               /* Window class name           */
                NULL,                   /* no title                     */
                WS_CAPTION      |        /* Title and Min/Max           */
                WS_SYSMENU      |        /* Add system menu box         */
                WS_MINIMIZEBOX  |        /* Add minimize box            */
                WS_MAXIMIZEBOX  |        /* Add maximize box            */
                WS_THICKFRAME   |        /* thick sizeable frame        */
                WS_CLIPCHILDREN |         /* don't draw in child windows areas */
                WS_OVERLAPPED,
                CW_USEDEFAULT, 0,        /* Use default X, Y            */
                CW_USEDEFAULT, 0,        /* Use default X, Y            */
                NULL,                    /* Parent window's handle      */
                NULL,                    /* Default to Class Menu       */
                hInst,                   /* Instance of window          */
                NULL);                   /* Create struct for WM_CREATE */


 if(hWndMain == NULL)
   {
    LoadString(hInst, IDS_ERR_CREATE_WINDOW, szString, sizeof(szString));
    MessageBox(NULL, szString, NULL, MB_ICONEXCLAMATION);
    return IDS_ERR_CREATE_WINDOW;
   }

GetWindowRect(hWndMain, &rectWndMain);

WndMainX = (rectWndDesk.right - rectWndMain.right + rectWndMain.left)/2;
WndMainY = (rectWndDesk.bottom - rectWndMain.bottom + rectWndMain.top)/2;
nWndMainWidth = rectWndMain.right - rectWndMain.left;
nWndMainHeight = rectWndMain.bottom - rectWndMain.top;

 MoveWindow(hWndMain, WndMainX, WndMainY, nWndMainWidth, nWndMainHeight, FALSE);
 ShowWindow(hWndMain, SW_SHOWNORMAL);    /* display main window      */
 UpdateWindow(hWndMain);

 while(GetMessage(&msg, NULL, 0, 0))        /* Until WM_QUIT message    */
   {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
   }

 /* Do clean up before exiting from the application                     */
 CwUnRegisterClasses();
 return msg.wParam;
} /*  End of WinMain                                                    */
/************************************************************************/
/*                                                                      */
/* Main Window Procedure                                                */
/*                                                                      */
/* This procedure provides service routines for the Windows events      */
/* (messages) that Windows sends to the window, as well as the user     */
/* initiated events (messages) that are generated when the user selects */
/* the action bar and pulldown menu controls or the corresponding       */
/* keyboard accelerators.                                               */
/*                                                                      */
/* The SWITCH statement shown below distributes the window messages to  */
/* the respective message service routines, which are set apart by the  */
/* CASE statements. The window procedures must provide an appropriate   */
/* service routine for its end user initiated messages, as well as the  */
/* general Windows messages (ie. WM_CLOSE message). If a message is     */
/* sent to this procedure for which there is no programmed CASE clause  */
/* (i.e., no service routine), the message is defaulted to the          */
/* DefWindowProc function, where it is handled by Windows               */
/*                                                                      */
/************************************************************************/

LONG FAR PASCAL WndProc(HWND hWnd, WORD Message, WORD wParam, LONG lParam)
{
 HMENU      hMenu=0;            /* handle for the menu                 */
 HBITMAP    hBitmap=0;          /* handle for bitmaps                  */
 HDC        hDC;                /* handle for the display device       */
 PAINTSTRUCT ps;                /* holds PAINT information             */
 int        nRc=0;              /* return code                         */

 switch (Message)
   {
    case WM_COMMAND:
         /* The Windows messages for action bar and pulldown menu items */
         /* are processed here.                                         */
         switch (wParam)
           {
            case IDM_F_EXIT:
                 /* Place User Code to respond to the                   */
                 /* Menu Item Named "Exit" here.                        */
                 break;

            case IDM_H_ABOUT:
                 /* Place User Code to respond to the                   */
                 /* Menu Item Named "About" here.                       */


                 {
                  FARPROC lpfnDIALOGSMsgProc;

                  lpfnDIALOGSMsgProc = MakeProcInstance((FARPROC)DIALOGSMsgProc, hInst);
                  nRc = DialogBox(hInst, (LPSTR)"AboutBox", hWnd, lpfnDIALOGSMsgProc);
                  FreeProcInstance(lpfnDIALOGSMsgProc);
                 }
                 break;

            default:
                return DefWindowProc(hWnd, Message, wParam, lParam);
           }
         break;        /* End of WM_COMMAND                             */

    case WM_CREATE:
         /* The WM_CREATE message is sent once to a window when the     */
         /* window is created.  The window procedure for the new window */
         /* receives this message after the window is created, but      */
         /* before the window becomes visible.                          */


         break;       /*  End of WM_CREATE                              */

    case WM_MOVE:     /*  code for moving the window                    */
         break;

    case WM_SIZE:     /*  code for sizing client area                   */
         break;       /* End of WM_SIZE                                 */

    case WM_PAINT:    /* code for the window's client area              */
         /* Obtain a handle to the device context                       */
         /* BeginPaint will sends WM_ERASEBKGND if appropriate          */
         memset(&ps, 0x00, sizeof(PAINTSTRUCT));
         hDC = BeginPaint(hWnd, &ps);

         /* Included in case the background is not a pure color         */
         SetBkMode(hDC, TRANSPARENT);

         /* Inform Windows painting is complete                         */
         EndPaint(hWnd, &ps);
         break;       /*  End of WM_PAINT                               */

    case WM_CLOSE:  /* close the window                                 */
         /* Destroy child windows, modeless dialogs, then, this window  */
         DestroyWindow(hWnd);
         if (hWnd == hWndMain)
           PostQuitMessage(0);  /* Quit the application                 */
        break;

    default:
         /* For any message for which you don't specifically provide a  */
         /* service routine, you should return the message to Windows   */
         /* for default message processing.                             */
         return DefWindowProc(hWnd, Message, wParam, lParam);
   }
 return 0L;
}     /* End of WndProc                                         */
/************************************************************************/
/*                                                                      */
/* Dialog Window Procedure                                              */
/*                                                                      */
/* This procedure is associated with the dialog box that is included in */
/* the function name of the procedure. It provides the service routines */
/* for the events (messages) that occur because the end user operates   */
/* one of the dialog box's buttons, entry fields, or controls.          */
/*                                                                      */
/* The SWITCH statement in the function distributes the dialog box      */
/* messages to the respective service routines, which are set apart by  */
/* the CASE clauses. Like any other Windows window, the Dialog Window   */
/* procedures must provide an appropriate service routine for their end */
/* user initiated messages as well as for general messages (like the    */
/* WM_CLOSE message).                                                   */
/* Dialog messages are processed internally by windows and passed to the*/
/* Dialog Message Procedure. IF processing is done for a Message the    */
/* Message procedure returns a TRUE, else , for messages not explicitly */
/* processed, it returns a FALSE                                        */
/*                                                                      */
/************************************************************************/

BOOL FAR PASCAL DIALOGSMsgProc(HWND hWndDlg, WORD Message, WORD wParam, LONG lParam)
{

 switch(Message)
   {
    case WM_INITDIALOG:
         cwCenter(hWndDlg, 0);
         /* initialize working variables                                */
         break; /* End of WM_INITDIALOG                                 */

    case WM_CLOSE:
         /* Closing the Dialog behaves the same as Cancel               */
         PostMessage(hWndDlg, WM_COMMAND, IDCANCEL, 0L);
         break; /* End of WM_CLOSE                                      */

    case WM_COMMAND:
         switch(wParam)
           {
            case Edit1: /* Edit Control                                 */
                 break;

            case IDS_ERR_REGISTER_CLASS: /* Button text: "Push"         */
                 break;
            case IDCANCEL:
                 /* Ignore data values entered into the controls        */
                 /* and dismiss the dialog window returning FALSE       */
                 EndDialog(hWndDlg, FALSE);
                 break;
           }
         break;    /* End of WM_COMMAND                                 */

    default:
        return FALSE;
   }
 return TRUE;
} /* End of DIALOGSMsgProc                                      */


/************************************************************************/
/*                                                                      */
/* nCwRegisterClasses Function                                          */
/*                                                                      */
/* The following function registers all the classes of all the windows  */
/* associated with this application. The function returns an error code */
/* if unsuccessful, otherwise it returns 0.                             */
/*                                                                      */
/************************************************************************/

int nCwRegisterClasses(void)
{
 WNDCLASS   wndclass;    /* struct to define a window class             */
 memset(&wndclass, 0x00, sizeof(WNDCLASS));


 /* load WNDCLASS with window's characteristics                         */
 wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_BYTEALIGNWINDOW;
 wndclass.lpfnWndProc = WndProc;
 /* Extra storage for Class and Window objects                          */
 wndclass.cbClsExtra = 0;
 wndclass.cbWndExtra = 0;
 wndclass.hInstance = hInst;
 wndclass.hIcon = LoadIcon(hInst, "HELLO");
 wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
 /* Create brush for erasing background                                 */
 wndclass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
 wndclass.lpszMenuName = szAppName;   /* Menu Name is App Name */
 wndclass.lpszClassName = szAppName; /* Class Name is App Name */
 if(!RegisterClass(&wndclass))
   return -1;


 return(0);
} /* End of nCwRegisterClasses                                          */

/************************************************************************/
/*  cwCenter Function                                                   */
/*                                                                      */
/*  centers a window based on the client area of its parent             */
/*                                                                      */
/************************************************************************/

void cwCenter(hWnd, top)
HWND hWnd;
int top;
{
 POINT      pt;
 RECT       swp;
 RECT       rParent;
 int        iwidth;
 int        iheight;

 /* get the rectangles for the parent and the child                     */
 GetWindowRect(hWnd, &swp);
 GetClientRect(hWndMain, &rParent);

 /* calculate the height and width for MoveWindow                       */
 iwidth = swp.right - swp.left;
 iheight = swp.bottom - swp.top;

 /* find the center point and convert to screen coordinates             */
 pt.x = (rParent.right - rParent.left) / 2;
 pt.y = (rParent.bottom - rParent.top) / 2;
 ClientToScreen(hWndMain, &pt);

 /* calculate the new x, y starting point                               */
 pt.x = pt.x - (iwidth / 2);
 pt.y = pt.y - (iheight / 2);

 /* top will adjust the window position, up or down                     */
 if(top)
   pt.y = pt.y + top;

 /* move the window                                                     */
 MoveWindow(hWnd, pt.x, pt.y, iwidth, iheight, FALSE);
}

/************************************************************************/
/*  CwUnRegisterClasses Function                                        */
/*                                                                      */
/*  Deletes any refrences to windows resources created for this         */
/*  application, frees memory, deletes instance, handles and does       */
/*  clean up prior to exiting the window                                */
/*                                                                      */
/************************************************************************/

void CwUnRegisterClasses(void)
{
 WNDCLASS   wndclass;    /* struct to define a window class             */
 memset(&wndclass, 0x00, sizeof(WNDCLASS));

 UnregisterClass(szAppName, hInst);
}    /* End of CwUnRegisterClasses                                      */

编译生成的EXE尺寸14KB,在WINXP平台上运行顺畅,再做点儿工作就可以放在WIN10或WIN11平台上独立运行了。

在WIN10平台上建立16位程序运行环境

在WIN10平台上体验Microsoft古老的Quick C 1.0编程_第14张图片

到Github上下载otvdm并解压到C盘,安装即是简单地运行一个inf文件,原理是把16位Windows的一些东西改动到Win10上,当16位运行在Win10上运行出错时截取下来,再用16位这些东西运行试一下,再出错再提示给用户错误信息,类似16位程序的虚拟机。

在WIN10平台上运行的效果如下图

在WIN10平台上体验Microsoft古老的Quick C 1.0编程_第15张图片

关于优化xp平台的几点说明

XP平台比较老了,在虚拟中运行的话可以优化。

1. 现在机器的分辨率都比较高,要在设置中将分辨率设在120DPI到150DPI,同时选大字体、大图标,达到比较好的外观效果。

2. 现在的3键鼠标必须在XP上安装微软4,12版的鼠标驱动程序,其它版本的不管用。用意主要是激活滚轮。

在WIN10平台上体验Microsoft古老的Quick C 1.0编程_第16张图片

3. 在控制面板中去掉Internet explorer的对勾,安装MyPal浏览器,它能正常访问现在的互联网。

4. 可以安装外挂万能无笔输入法和其它自己喜欢的输入法。与母机共享文件可以在虚拟机上外挂母机的共享文件夹,也可以通过网络邻居方式使用母机上的WebDAV空间。如何在IIS上配置WebDAV服务,在我的笔记《IIS WebDAV配置,https绑定及asp设置》中有详细记录。IIS WebDAV配置,https绑定及asp设置_Mongnewer的博客-CSDN博客

感觉IT的黄金时期是DOS到Windows的变革阶段,似乎那时全世界都在拥抱IT,甚至写个DBASE程序都能让人羡慕,那时有知识的人很受社会尊重,这与现在普遍叫喊的“内卷”似乎是完全不一样的状况。时代久远,翻遍互联网可能也找不到一篇如何用Quick C for windows的文章可参考了,我体验了就放记到CSDN笔记上吧。

你可能感兴趣的:(QCWIN,QUICK,C,OTVDM,for,WIN10)