解说一个简单的Win32程序

一、Windows程序与普通C或C++程序的不同
学过C或C++等语言的人都知道,我们写的程序都一个入口,main函数,但是在Win32程序里,我们的入口函数又是什么呢?它是怎么样运行的,跟我们用C或C++写的控制台程序又有什么不同呢?

我们先说Win32程序跟我们控制台的程序的一个很重要的不同点,就是Win32程序是一个消息响应程序,例如点击了一个按钮,就会产生一个消息onButoon,然后会这个消息会进入我们程序所维护的一个消息队列,程序运行过程中不断地取出队列中的消息,并作出相应的处理。直到取出的是结束程序的消息。

二、了解MSG的结构和组成
首先,既然Windows的程序是基于消息触发的,那么Windows是如何定义一个消息的呢?下面是在MSDN上说明文档上的定义:
typedef struct tagMSG {     // msg  
   HWND hwnd;
   UINT message;
   WPARAM wParam;
   LPARAM lParam;
   DWORD time;
   POINT pt;
} MSG;
下面我们来分析一下这个结构体:
HWND hwnd:hwnd是一个窗口的句柄,用来唯一标识一个窗口资源;至于什么是一个句柄,它有点类似对C或C++中的指针,句柄是资源的标识,根据资源的类型,又可将句柄细分成图标句柄(HICON),光标句柄(HCURSOR),窗口句柄(HWND),应用程序实例句柄(HINSTANCE)等等各种类型的句柄。操作系统给每一个窗口指定的一个唯一的标识号即窗口句柄。
 
UINT message:message是一个UINT(即C或C++中的unsign int)类型的变量,它用来标识一个具体的消息,如按键盘的消息。message用一个整数来表示,但是一个整数通常不好记忆,所以在VC++中就用微软给我们定义的一些宏来表示,如WM_KEYDOWN。
 
 
WPARAM wParam:整型参数,用来指示message的附加信息。
 
 
LPARAM lParam:跟wParam一样,是一个整型参数,用来指示message的附加信息。与wParam一样,多用来区分同一个消息的不同情况。
 
 
DWORD time:DWORD其实是C或C++中的unsigned long类型,time标识了一个消息产生时的时间。
 
 
POINT pt:POINT是一个结构体,表示现实世界里的一个点,里面有两个LONG类型的成员x和y,用来表示产生这个消息产时光标或鼠标的坐标。
由此可知一个MSG的变量所包含的信息是相当多和详细的。
 
 
三、了解WinMain函数
然后,像C或C++控制台程序的入口是main函数一样,Win32程序的入口也是main函数,不过它叫WinMain函数,它的定义如下:
int WINAPI WinMain(
  
  
  HINSTANCE hInstance,      // handle to current instance
  HINSTANCE hPrevInstance,  // handle to previous instance
  LPSTR lpCmdLine,          // command line
  int nCmdShow              // show state
);
下面我们来分析一下这个函数:
HINSTANCE hInstance: hInstance是一个指向当前应用程序实例的一个句柄。实例就是一个运行中的程序。
HINSTANCE hPrevInstance: hPrevInstance是一个指向之前应用程序实例的一个句柄。
LPSTR lpCmdLine:lpCmdLine是一个指向字符串的指针,表示一个命令行参数,什么是命令行参数呢?就是我们C或C++中的main函数中的参数 char *argv[]。
int nCmdShow:用来表示一个窗口的显示,表示它是要最大化显示,最小化显示,正常大小显示还是隐藏显示。
WinMain与main函数一样,是由操作系统进行调用的,所以这些参数也是由操作系统来赋值。
WINAPI是什么呢?其实它是一个宏,它代表的是__stdcall,表示的是参数传递的顺序,但是在VC中,参数的默认传递顺序为__cdecl。
四、创建一个窗口
那我们应该怎样设计一个窗口呢?要设计一个窗口,实际上是要设计一个窗口类,用来标记一个窗口的各种属性,在VC中已经有这样类(更正确地说是一个结构体)WNDCLASS。它的定义如下,后面的注释说明了它们的用处:
typedef struct _WNDCLASS { 
 UINT style;              //用于指定类的类型,即窗口类的类型
 WNDPROC lpfnWndProc;    //指定一个窗口回调函数,是一个函数的指针 
 int cbClsExtra;         //类的附加内存,通常数情况下为0
 int cbWndExtra;         //窗口附加内存,通常情况下为0
 HANDLE hInstance;    //当前实例句柄,用WinMain中的形参给它赋值
 HICON hIcon;         //图标句柄,用于指示应用程序所用的是什么图标,用函数LoadIcon进行赋值
 HCURSOR hCursor; //光标句柄,用于指示鼠标进入应用程序窗口区域时的显示,用函数LoadCursor进行赋值
 HBRUSH hbrBackground;     //用于指示程序的背景颜色,用函数(HBRUSH) GetStockObject赋值。
 LPCTSTR lpszMenuName;     //指定菜单的名字
 LPCTSTR lpszClassName;     //指定类的名字
 } WNDCLASS;
注:类型窗口的过程函数,也称回调函数, 原理是,当应用程序收到给某一窗口的消息时,就应该调用某一函数来处理这条消息。这一调用过程不用应用程序自己来实施,而由操作系统来完成,但是,回调函数本身的代码必须由应用程序自己完成。对于一条消息, 操作系统调用的是接受消息的窗口所属的类型中的lpfnWndProc成员指定的函数。每一种不同类型的窗口都有自己专用的回调函数,该函数就是通过lpfnWndProc成员指定的。 
在VC里或写Windows程序时,我们会经常用到一类变量,这个变量里的每一位(bit)都对应某一种特性。当该变量的某位为1时,表示有该位对应的那种特性,当该位为0时,即没有该位所对应的特性。当变量中的某几位同时为1时,就表示同时具有几种特性的组合。一个变量中的哪一位代表哪种意义,不容易记忆,所以我们经常根据特征的英文拼写的大写去定义一些宏,该宏所对应的数值中仅有与该特征相对应的那一位(bit)为1,其余的bit都为0。 其实这些宏是一个UINT类型的一个数值,所以我们可以用|运算符来把多个特性结合在一起,用&~来去掉一个特性。
所以要创建一个窗口,首先我们在WinMain函数中创建一个 WNDCLASS变量,并对 WNDCLASS变量中的成员赋值之后,就可以注册这个窗口,可调用函数R egisterClass(&wndcls)来注册一个窗口,它需要一个WNDCLASS类型变量的地址。然后定义一个窗口的句柄HWND hwnd;然后调用函数CreateWindow,把返回值赋给hwnd。最后调用函数ShowWindow(hwnd,SW_SHOWNORMAL); UpdateWindow(hwnd);来显示窗口。
五、建立消息循环
现在窗口是创建出来了,但是之前我们就说过,Windows程序是基于消息触发和处理的程序,那么我们如何让程序让系统知道我们的操作呢?例如点击了一下鼠标,按了一下键盘,那就要建立我们的消息循环了,建立方法如下。
首先,我们定义一个MSG类型的变量,如 MSG msg;
然后执行如下的循环:
while(GetMessage(&msg,NULL,0,0)) 
 TranslateMessage(&msg); 
 DispatchMessage(&msg); 
 }
解释:
GetMessage函数从我们的消息队列中取出消息,第一个参数为MSG变量,它出会自动帮我们填充msg中的成员变量;第二个参数是一个窗口句柄,NULL表示接受属于调用线程的所有消息;第三个参数指定消息的最小值;第四个参数指定消息的最大值。这两个参数若设为0,则获取所有在消息队列中的消息。它的返回值为BOOL型,只有在取出的消息为WM_QUIT时,返回FALSE;即除非关闭程序,否则将是一个死循环,一直对我们的操作进行处理。
TranslateMessage函数,用于翻译、处理和转换消息并把新消息投放到消息队列中,并且此过程不会影响原来的消息队列。
DispatechMessage函数,用于把收到的消息传到窗口回调函数进行分析和处理。即将消息传递给操作系统,让操作系统调用窗口回调函数,来对信息进行处理。
六、回调函数(窗口过程函数)
首先来看看它的定义
LRESULT CALLBACK WinSunProc( 
 HWND hwnd, // 窗口句柄 
 UINT uMsg, // 消息标志符
 WPARAM wParam, // MSG第一附加参数 
 LPARAM lParam // MSG 第二附加参数
 );
CALLBACK是一个宏表示前面所说的_stdcall,LRESULT是一个long型参数。
调用时,把窗口类WNDCLASS的参数传递过来,里面有一个switch语句,用来判断要处理的消息类型,并作出相应的处理,注意switch语句里一定有一个default:return DefWindowProc(hwnd,uMsg,wParam,lParam);表示没有在case中出现的消息将按默认处理方式来处理,不然窗口运行会出错,连窗口都不能创建和显示出来。

好的,下面是一个简单的计算器的Swing代码示例,其中包含了注释来解释每个部分的作用: ```java import javax.swing.*; import java.awt.*; import java.awt.event.*; public class Calculator extends JFrame implements ActionListener { // 定义计算器界面上的各个组件 private JTextField display; private JButton button0, button1, button2, button3, button4, button5, button6, button7, button8, button9; private JButton buttonPlus, buttonMinus, buttonMultiply, buttonDivide, buttonEquals, buttonClear; // 定义计算器内部的变量 private double num1, num2, result; private String operator; // 构造函数,用于初始化计算器界面 public Calculator() { super("Calculator"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize(200, 250); // 设置计算器界面的布局 setLayout(new BorderLayout()); // 创建显示结果的文本框,并添加到计算器界面的顶部 display = new JTextField("0"); display.setEditable(false); add(display, BorderLayout.NORTH); // 创建数字按钮,并添加到计算器界面的中间区域 JPanel numberPanel = new JPanel(new GridLayout(4, 3)); button0 = new JButton("0"); button0.addActionListener(this); numberPanel.add(button0); button1 = new JButton("1"); button1.addActionListener(this); numberPanel.add(button1); button2 = new JButton("2"); button2.addActionListener(this); numberPanel.add(button2); button3 = new JButton("3"); button3.addActionListener(this); numberPanel.add(button3); button4 = new JButton("4"); button4.addActionListener(this); numberPanel.add(button4); button5 = new JButton("5"); button5.addActionListener(this); numberPanel.add(button5); button6 = new JButton("6"); button6.addActionListener(this); numberPanel.add(button6); button7 = new JButton("7"); button7.addActionListener(this); numberPanel.add(button7); button8 = new JButton("8"); button8.addActionListener(this); numberPanel.add(button8); button9 = new JButton("9"); button9.addActionListener(this); numberPanel.add(button9); add(numberPanel, BorderLayout.CENTER); // 创建操作符按钮,并添加到计算器界面的右侧区域 JPanel operatorPanel = new JPanel(new GridLayout(5, 1)); buttonPlus = new JButton("+"); buttonPlus.addActionListener(this); operatorPanel.add(buttonPlus); buttonMinus = new JButton("-"); buttonMinus.addActionListener(this); operatorPanel.add(buttonMinus); buttonMultiply = new JButton("*"); buttonMultiply.addActionListener(this); operatorPanel.add(buttonMultiply); buttonDivide = new JButton("/"); buttonDivide.addActionListener(this); operatorPanel.add(buttonDivide); buttonEquals = new JButton("="); buttonEquals.addActionListener(this); operatorPanel.add(buttonEquals); add(operatorPanel, BorderLayout.EAST); // 创建清零按钮,并添加到计算器界面的底部 JPanel clearPanel = new JPanel(); buttonClear = new JButton("Clear"); buttonClear.addActionListener(this); clearPanel.add(buttonClear); add(clearPanel, BorderLayout.SOUTH); // 初始化计算器内部的变量 num1 = 0; num2 = 0; result = 0; operator = ""; // 显示计算器界面 setVisible(true); } // 处理各个按钮的点击事件 public void actionPerformed(ActionEvent event) { // 获取被点击的按钮的标签 String label = event.getActionCommand(); // 根据标签执行不同的操作 if (label.equals("0")) { appendDigit(0); } else if (label.equals("1")) { appendDigit(1); } else if (label.equals("2")) { appendDigit(2); } else if (label.equals("3")) { appendDigit(3); } else if (label.equals("4")) { appendDigit(4); } else if (label.equals("5")) { appendDigit(5); } else if (label.equals("6")) { appendDigit(6); } else if (label.equals("7")) { appendDigit(7); } else if (label.equals("8")) { appendDigit(8); } else if (label.equals("9")) { appendDigit(9); } else if (label.equals("+")) { setOperator("+"); } else if (label.equals("-")) { setOperator("-"); } else if (label.equals("*")) { setOperator("*"); } else if (label.equals("/")) { setOperator("/"); } else if (label.equals("=")) { calculateResult(); } else if (label.equals("Clear")) { clear(); } } // 追加一个数字到当前的操作数 private void appendDigit(int digit) { if (operator.equals("")) { num1 = num1 * 10 + digit; display.setText(Double.toString(num1)); } else { num2 = num2 * 10 + digit; display.setText(Double.toString(num2)); } } // 设置当前的操作符 private void setOperator(String op) { operator = op; } // 计算结果并显示 private void calculateResult() { if (operator.equals("+")) { result = num1 + num2; } else if (operator.equals("-")) { result = num1 - num2; } else if (operator.equals("*")) { result = num1 * num2; } else if (operator.equals("/")) { result = num1 / num2; } display.setText(Double.toString(result)); } // 清空计算器 private void clear() { num1 = 0; num2 = 0; result = 0; operator = ""; display.setText("0"); } public static void main(String[] args) { new Calculator(); } } ``` 在这个示例中,我们首先定义了计算器界面上的各个组件,包括数字按钮、操作符按钮、显示结果的文本框和清空按钮。然后在构造函数中初始化这些组件,并设置计算器界面的布局。我们使用了BorderLayout布局来将计算器界面分为顶部、中间、右侧和底部四个区域,并将各个组件添加到对应的区域中。 我们还定义了一些内部变量来保存计算器内部的状态,包括当前的操作数、操作符和计算结果。我们在点击各个按钮时,根据按钮的标签执行不同的操作,例如追加数字、设置操作符、计算结果和清空计算器等。我们还实现了一些辅助方法来简化代码,例如appendDigit方法用于将数字追加到当前的操作数中,setOperator方法用于设置当前的操作符,calculateResult方法用于计算结果并显示,以及clear方法用于清空计算器。 最后,在main方法中创建一个新的Calculator对象来启动计算器界面。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值