当前位置: 首页 > news >正文

【Windows编程】Step.2 消息循环机制

DOS下编程,程序的执行过程是按照程序任意的意愿来执行的, 那时应用程序干什么,以及什么时候干什么那时由程序员说了算的, 在windows下情况就不那么妙了, windows程序是面向使用者的, windows应用程序的执行过程基本上是由使用人员的操作过程来决定的。

windows会为应用程序建立和维护一个消息队列,这个消息队列会存储预定义的消息(windows预定义了一大堆使用者使用过程中产生的消息,反正很多,没有数,估计上万吧);当操作一个应用程序的时候,windows操作系统感知使用者的操作并将这个消息放到应用程序的消息队列,应用程序根据消息进行相应的处理。这就是大体的windows消息机制,这个是我的简单理解,如果需要理解可以参考那本经典的教材

windows程序的过程是:

         程序入口

         定义窗口类

         注册窗口类

         生成窗口

         更新窗口

         显示窗口

         消息循环

         窗口消息处理(回调函数, 我不知道callback函数是不是回调函数, 不过从call back上看估计就是......哈哈哈  )

         程序执行实体结束

1、windows程序的入口:

int WINAPI WinMain(HINSTANCE hInstance,  //本程序的实例句柄
                   HINSTANCE hPrevInstance,  //程序的前一个实例的句柄
                   LPSTR lpCmdLine,  
                   int iCmdShow)//其中WINAPI是一个预定义的宏   #define WINAPI __stdcall 这个宏定义指明函数的参数的入栈和出栈顺序。

2、定义窗口类型:
windows程序主要是以窗口为对象的, 应用程序的一个按钮可以称作窗口,一个滚动条和状态条同样是窗口,而整个程序显示出来的也是窗口。在windows为窗口定义了一个结构体:

typedef struct tagWNDCLASSA
{
    UINT        style;
    WNDPROC     lpfnWndProc;
    int         cbClsExtra;
    int         cbWndExtra;
    HINSTANCE   hInstance;
    HICON       hIcon;
    HCURSOR     hCursor;
    HBRUSH      hbrBackground;
    LPCSTR      lpszMenuName;
    LPCSTR      lpszClassName;
} WNDCLASSA;

typedef WNDCLASSA WNDCLASS;

为了要使用窗口需要定义窗口变量(结构体变量);如:
Exp:

 WNDCLASS wndclass;

定义完后需要进行初始化,如下:

 wndclass.cbClsExtra =0;   //声明程序额外空间用变量
    wndclass.cbWndExtra=0;    //额外变量
    wndclass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH); //GetStockObject()获取一个对象,这里为获取一个画刷对象
                                                               //hbrBackGround字段名称中的hbr前缀代表画刷句柄,画刷是个绘图词汇,指
                                                               //用来填充一个区域的着色样式,Windows有几个标准的画刷,也称为备用Stock画刷
                                                               //这里的GetStockObject()函数呼叫返回一个白色画刷句柄,这就表示窗口的显示区域
                                                               //背景完全为白色
    wndclass.hCursor=LoadCursor(NULL,IDC_ARROW);  //加载光标
                                                  //LoadCursor()函数第一个参数是句柄,第二个参数是要加载的光标类型
    wndclass.hIcon=LoadIcon(NULL,IDI_APPLICATION); //加载程序图标
                                                   //LoadIcon()函数第一个参数是句柄,第二个参数是要加载的图标类型,这里加载预定义图标
                                                   //对于预定义的图标,第二个参数是IDI开头的标识符,在Winuser.h中定义
                                                   //当要加载用户自定义图标时,第一个参数必须设定为程序执行实体的句柄hInstance
    wndclass.hInstance=hInstance;   //系统分配给应用程序的实例句柄
    wndclass.lpfnWndProc=WndProc;    //a pointer which point to the main window's callback function函数的指针
    wndclass.lpszClassName=szAppName;  //窗口名称,创建窗口的时候需要用到
    wndclass.lpszMenuName=NULL; //菜单资源名称
    wndclass.style =CS_HREDRAW | CS_VREDRAW;  //窗口的类型,或者风格

3、注册窗口类
为了可以显示一个窗口,必须先向windows注册一个窗口类, 以此来通知windows,应用程序需要用这样一个窗口类来显示。在windows中通过 RegisterClass函数来注册窗口类。如下:

Exp:

 //注册窗口类型
    if(!RegisterClass(&wndclass))                 
        //int registerclass(WNDCALSS *wndclass)
        //函数注册窗口类型,这个函数有两个类型定义
        //RegisterClassA:  返回一个指向WNDCLASSA结构的指针
        //RegisterClassW:  返回一个指向WNDCLASSW结构的指针
        //如果用定义的UNICODE标识符编译了程序,程序将呼叫RegisterClassW,改程序可以在NT上执行,但是在98上就不能真正执行,
        //函数有一个进入点,但是会执行错误,返回一个0值,这就是为什么要进行判断的原因,
        //为了防止错误,就需要判断后退出程序。
    {
        MessageBox(NULL,TEXT("This program requires Windows NT!"),szAppName,MB_ICONERROR);
        return 0;
    }

4、生成窗口
一个窗口里面包含很多的内容,在计算机里面需要很多的内存空间来保存这些内容。同时在注册窗口类型后,并没有生成一个窗口实体,就像有了一个模具,但是还没有用模具制作工件一样。为了得到工件我们需要一个制作工件的过程。在这里就是需要生成工件,如下:

 //生成窗口
    hwnd=CreateWindow(szAppName,   //窗口类名, 在匈牙利命名规则下以sz字符开头的标识符表示的是以'\0'结束的字符串
                      TEXT("The Hello Program"), //窗口的标题栏的提示
                      WS_OVERLAPPEDWINDOW, //窗口样式,标注样式窗口,具有标题栏,并且标题栏有缩小、放大和关闭按钮。
                                            //WS_OVERLAPPEDWINDOW,其实几个位旗标的组合值
                                            // #define WS_OVERLAPPEDWINDOW (WS_OVERLAPPED | \
                                            //                              WS_CAPTION | \
                                            //                              WS_SYSMENU | \
                                            //                              WS_THICKFRAME | \
                                            //                              WS_MINIMIZEBOX | \
                                            //                              WS_MAXIMIZEBOX )
                      CW_USEDEFAULT,  //初始化窗口的X轴起始位置,
                      CW_USEDEFAULT,  //初始化窗口的Y轴起始位置
                      CW_USEDEFAULT,  // initial x size 初始的X方向的大小
                      CW_USEDEFAULT,  // initial y size 初始的Y方向的大小
                      NULL, //父窗口的句柄,通常子窗口显示在父窗口的前面
                            //应用程序的窗口显示在桌面的上面,应用程序不必为呼叫CreateWindow函数获取桌面的句柄
                      NULL, //窗口的菜单句柄
                      hInstance, //程序的执行实例句柄
                      NULL //附加参数信息
                      );
      //在调用窗口产生函数createwindow函数后,系统会为窗口分配内存,用来保存createwindow函数制定的窗口信息和其他信息,
      //createwindow返回的句柄值是程序后面获取存储在内存信息的指针值。

5、显示和更新窗口

在制作完工件后,我们需要将它拿出来才会有用;在windows下,生成窗体后,他并不会立即显示,而是在内存中占用了一段空间,我们需要利用一个工具将它显示在桌面: showwindow和updatewindow。如下:
  ShowWindow(hwnd,iCmdShow);  //显式窗口,两个参数,第一个参数是CreateWindow函数返回的窗口句柄
                                 //第二个参数,确定窗口如何显示在屏幕上,例如:最大化、最小化还是一般化
                                 //通常iCmdShow: SW_SHOWNORMAL
                                 //               SW_SHOWMAXIMIZED
                                 //               SW_SHOWMINNOACTIVE   窗口仅显示在工作列上
                                 //如果icmdshow=sw_shownormal,则窗口的显示区域会被窗口类别中定义的背景画刷所覆盖。

  UpdateWindow(hwnd);  //更新窗口, 利用updatewindow函数会重画显示区域,这里是通过给窗口处理函数发送一个WM_PAINT消息完成这一过程

6、消息循环
窗口显示到桌面上后,程序的使用者对窗口进行操作,会产生一大堆的消息, 应用程序通过消息循环从消息队列取出消息,然后进行处理,就形成了应用程序的处理过程。

消息循环通过下面的样式进行处理:

Exp:

//消息循环
     //windows为每一个应用程序维护一个消息队列,在发生输入事件后,windows将事件转化为一个消息并将消息放到消息队列中
     //应用程序通过消息循环取系统发给应用程序的消息,并对之做出响应
     //消息结构体MSG
     //
     //  typedef  struct tagMSG
     //         {
     //             HWND hwnd;   接受消息的窗口的句柄
     //             UINT message;  消息标识符,表示具体的消息信息
     //             WPARAM wParam;  32位的附加消息参数。根据消息的不同而不同
     //             LPARAM lParam;  32位的附加消息参数,其值与消息有关
     //             DWORD time; 消息放入消息队列的时间
     //             POINT pt; 消息放入消息队列时鼠标的坐标
     //         }
     //   MSG, *PMSG;
     //其中POINT也是一个结构体
     //  typedef struct tagPOINT
     //        {
     //           LONG x;
     //           LONG y;
     //         }
     //  POINT, *PPOINT;
     while(GetMessage(&msg,NULL,0,0))
         //getmessage函数取得windows系统发送给程序的消息,每次从消息队列里面取一个值
         //第二、第三、第四个参数设定为NULL,0,0 表示程序接收自己建立的所有窗口的所有消息
         //当从消息队列取回的MSG结构体的message子段位WM_QUIT消息是,GetMessage就返回一个 0 值
         //否则就返回非零值
         //在匈牙利命名法中:
         //                  WM_  开头的标识符表示的是消息
     {
         TranslateMessage(&msg); // 将消息结构体MSG回传给Windows,进行一些键盘转化
         DispatchMessage(&msg);  //将msg结构体回传给Windows。然后,Windows将该消息发送给适当的窗口处理程序,让其进行处理
                                 //即: Windows呼叫回调函数,回调函数处理完消息后,返回到Windows,此时
                                 // Windows还停留在DispatchMessage呼叫中,在结束DispatchMessage呼叫处理之后,Windows回到程序执行
                                 //实体,并进行下一轮的消息循环
     }
     return msg.wParam;
}

7、窗口消息处理函数

前面说到应用程序可以取得消息,但是我们看到消息循环并不对消息进行处理,因此为了处理消息,我们就需要建立一种机制,这就是窗口消息处理程序。

我们通过下面的方式来进行窗口消息处理:

Exp:

LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)
        //关于窗口处理函数的参数的意义
        //第一个参数:HWND hwnd, 表示的是接收消息的窗口的句柄,这个句柄值与CreateWindow函数返回的值相等
        //若用同一WNDCLASS建立多个窗口,则hwnd标识特定的某个窗体
        //第二个参数:UINT message, 为标识窗体的数值,最后两个参数都是32位的消息的附加信息参数
        //程序本身通常自己不呼叫窗口消息处理程序,而由系统呼叫窗口消息处理函数
        //通过SendMessage()函数可以实现程序自己呼叫窗口消息处理函数
        //Windows程序写作者使用switch和case结构处理消息; 窗口消息处理程序在处理消息时,必须回传一个0.
        //窗口消息处理程序不予处理的所有消息应该被传给名为DefWindowProc的Windows函数,而且从DefWindowProc传回的值必须由窗口消息处理程序传回
{
    HDC hdc;
    PAINTSTRUCT ps;
    RECT rect;

    switch (message)
    {
    //case WM_CREATE:

    case WM_PAINT:
        hdc=BeginPaint(hwnd,&ps);
        GetClientRect(hwnd,&rect);
        DrawText(hdc,TEXT("Hello win"),-1,&rect,DT_SINGLELINE | DT_CENTER | DT_VCENTER);
        EndPaint(hwnd,&ps);
        return 0; //消息处理完后必须返回0值到系统
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hwnd,message,wParam,lParam);  //这个函数的返回值必须由窗口消息处理程序返回到系统
}

我们看不到这个函数的调用,但是他是怎么执行的呢?是这样的: 当使用者进行操作后,windows操作系统感知这个操作,然后将消息投递到消息队列,同时windows会呼叫用户编制的窗口消息处理程序。
Tip:

     注意是windows呼叫用户编制的窗口消息处理程序。

通过我自己的写的一个简单的windows程序来看看一个完整的windows应用程序:

// Win32 Program

#include <windows.h>
LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);  //declare a callback function

int WINAPI WinMain(HINSTANCE hInstance,   //本程序的实例句柄
                   HINSTANCE hPrevInstance,  //程序的前一个实例的句柄
                   LPSTR lpCmdLine,  //
                   int iCmdShow)
{
    static TCHAR szAppName[]=TEXT("Hellowin"); //   这个就是窗口的caption
    HWND hwnd;  //声明句柄变量
    MSG msg;    //声明消息结构体 定义消息结构体变量

    WNDCLASS wndclass; //声明窗口结构体变量

    //声明定义窗口结构体变量后初始化结构体
    wndclass.cbClsExtra =0;   //声明程序额外空间用变量
    wndclass.cbWndExtra=0;    //额外变量
    wndclass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH); //GetStockObject()获取一个对象,这里为获取一个画刷对象
                                                               //hbrBackGround字段名称中的hbr前缀代表画刷句柄,画刷是个绘图词汇,指
                                                               //用来填充一个区域的着色样式,Windows有几个标准的画刷,也称为备用Stock画刷
                                                               //这里的GetStockObject()函数呼叫返回一个白色画刷句柄,这就表示窗口的显示区域
                                                               //背景完全为白色
    wndclass.hCursor=LoadCursor(NULL,IDC_ARROW);  //加载光标
                                                  //LoadCursor()函数第一个参数是句柄,第二个参数是要加载的光标类型
    wndclass.hIcon=LoadIcon(NULL,IDI_APPLICATION); //加载程序图标
                                                   //LoadIcon()函数第一个参数是句柄,第二个参数是要加载的图标类型,这里加载预定义图标
                                                   //对于预定义的图标,第二个参数是IDI开头的标识符,在Winuser.h中定义
                                                   //当要加载用户自定义图标时,第一个参数必须设定为程序执行实体的句柄hInstance
    wndclass.hInstance=hInstance;   //系统分配给应用程序的实例句柄
    wndclass.lpfnWndProc=WndProc;    //a pointer which point to the main window's callback function函数的指针
    wndclass.lpszClassName=szAppName;  //窗口名称,创建窗口的时候需要用到
    wndclass.lpszMenuName=NULL; //菜单资源名称
    wndclass.style =CS_HREDRAW | CS_VREDRAW;  //窗口的类型,或者风格

    //注册窗口类型
    if(!RegisterClass(&wndclass))                  
        //int registerclass(WNDCALSS *wndclass)
        //函数注册窗口类型,这个函数有两个类型定义
        //RegisterClassA:  返回一个指向WNDCLASSA结构的指针
        //RegisterClassW:  返回一个指向WNDCLASSW结构的指针
        //如果用定义的UNICODE标识符编译了程序,程序将呼叫RegisterClassW,改程序可以在NT上执行,但是在98上就不能真正执行,
        //函数有一个进入点,但是会执行错误,返回一个0值,这就是为什么要进行判断的原因,
        //为了防止错误,就需要判断后退出程序。
    {
        MessageBox(NULL,TEXT("This program requires Windows NT!"),szAppName,MB_ICONERROR);
        return 0;
    }

     //生成窗口
    hwnd=CreateWindow(szAppName,   //窗口类名, 在匈牙利命名规则下以sz字符开头的标识符表示的是以'\0'结束的字符串
                      TEXT("The Hello Program"), //窗口的标题栏的提示
                      WS_OVERLAPPEDWINDOW, //窗口样式,标注样式窗口,具有标题栏,并且标题栏有缩小、放大和关闭按钮。
                                            //WS_OVERLAPPEDWINDOW,其实几个位旗标的组合值
                                            // #define WS_OVERLAPPEDWINDOW (WS_OVERLAPPED | \ 
                                            //                              WS_CAPTION | \
                                            //                              WS_SYSMENU | \
                                            //                              WS_THICKFRAME | \
                                            //                              WS_MINIMIZEBOX | \
                                            //                              WS_MAXIMIZEBOX )
                      CW_USEDEFAULT,  //初始化窗口的X轴起始位置,
                      CW_USEDEFAULT,  //初始化窗口的Y轴起始位置
                      CW_USEDEFAULT,  // initial x size 初始的X方向的大小
                      CW_USEDEFAULT,  // initial y size 初始的Y方向的大小
                      NULL, //父窗口的句柄,通常子窗口显示在父窗口的前面
                            //应用程序的窗口显示在桌面的上面,应用程序不必为呼叫CreateWindow函数获取桌面的句柄
                      NULL, //窗口的菜单句柄
                      hInstance, //程序的执行实例句柄
                      NULL //附加参数信息
                      );
      //在调用窗口产生函数createwindow函数后,系统会为窗口分配内存,用来保存createwindow函数制定的窗口信息和其他信息,
      //createwindow返回的句柄值是程序后面获取存储在内存信息的指针值。

     ShowWindow(hwnd,iCmdShow);  //显式窗口,两个参数,第一个参数是CreateWindow函数返回的窗口句柄
                                 //第二个参数,确定窗口如何显示在屏幕上,例如:最大化、最小化还是一般化
                                 //通常iCmdShow: SW_SHOWNORMAL 
                                 //               SW_SHOWMAXIMIZED
                                 //               SW_SHOWMINNOACTIVE   窗口仅显示在工作列上
                                 //如果icmdshow=sw_shownormal,则窗口的显示区域会被窗口类别中定义的背景画刷所覆盖。

     UpdateWindow(hwnd);  //更新窗口, 利用updatewindow函数会重画显示区域,这里是通过给窗口处理函数发送一个WM_PAINT消息完成这一过程

     //消息循环
     //windows为每一个应用程序维护一个消息队列,在发生输入事件后,windows将事件转化为一个消息并将消息放到消息队列中
     //应用程序通过消息循环取系统发给应用程序的消息,并对之做出响应
     //消息结构体MSG
     //
     //  typedef  struct tagMSG
     //         {
     //             HWND hwnd;   接受消息的窗口的句柄
     //             UINT message;  消息标识符,表示具体的消息信息
     //             WPARAM wParam;  32位的附加消息参数。根据消息的不同而不同
     //             LPARAM lParam;  32位的附加消息参数,其值与消息有关
     //             DWORD time; 消息放入消息队列的时间
     //             POINT pt; 消息放入消息队列时鼠标的坐标
     //         }
     //   MSG, *PMSG;
     //其中POINT也是一个结构体
     //  typedef struct tagPOINT
     //        {
     //           LONG x;
     //           LONG y;
     //         }
     //  POINT, *PPOINT;
     while(GetMessage(&msg,NULL,0,0))
         //getmessage函数取得windows系统发送给程序的消息,每次从消息队列里面取一个值
         //第二、第三、第四个参数设定为NULL,0,0 表示程序接收自己建立的所有窗口的所有消息
         //当从消息队列取回的MSG结构体的message子段位WM_QUIT消息是,GetMessage就返回一个 0 值
         //否则就返回非零值
         //在匈牙利命名法中:
         //                  WM_  开头的标识符表示的是消息
     {
         TranslateMessage(&msg); // 将消息结构体MSG回传给Windows,进行一些键盘转化
         DispatchMessage(&msg);  //将msg结构体回传给Windows。然后,Windows将该消息发送给适当的窗口处理程序,让其进行处理
                                 //即: Windows呼叫回调函数,回调函数处理完消息后,返回到Windows,此时
                                 // Windows还停留在DispatchMessage呼叫中,在结束DispatchMessage呼叫处理之后,Windows回到程序执行
                                 //实体,并进行下一轮的消息循环
     }
     return msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)
        //关于窗口处理函数的参数的意义
        //第一个参数:HWND hwnd, 表示的是接收消息的窗口的句柄,这个句柄值与CreateWindow函数返回的值相等
        //若用同一WNDCLASS建立多个窗口,则hwnd标识特定的某个窗体
        //第二个参数:UINT message, 为标识窗体的数值,最后两个参数都是32位的消息的附加信息参数
        //程序本身通常自己不呼叫窗口消息处理程序,而由系统呼叫窗口消息处理函数
        //通过SendMessage()函数可以实现程序自己呼叫窗口消息处理函数
        //Windows程序写作者使用switch和case结构处理消息; 窗口消息处理程序在处理消息时,必须回传一个0.
        //窗口消息处理程序不予处理的所有消息应该被传给名为DefWindowProc的Windows函数,而且从DefWindowProc传回的值必须由窗口消息处理程序传回
{
    HDC hdc;
    PAINTSTRUCT ps;
    RECT rect;

    switch (message)
    {
    //case WM_CREATE:

    case WM_PAINT:
        hdc=BeginPaint(hwnd,&ps);
        GetClientRect(hwnd,&rect);
        DrawText(hdc,TEXT("Hello win"),-1,&rect,DT_SINGLELINE | DT_CENTER | DT_VCENTER);
        EndPaint(hwnd,&ps);
        return 0; //消息处理完后必须返回0值到系统
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hwnd,message,wParam,lParam);  //这个函数的返回值必须由窗口消息处理程序返回到系统

}

执行上面的程序需要建立一个win32 应用程序工程,最好是个空的工程, 建议在VC 6.0中执行。


http://www.cnblogs.com/volcanol/archive/2011/06/11/2078237.html

相关文章:

  • 【CDOJ 1334】 郭大侠与Rabi-Ribi
  • 【CDOJ 1074】秋实大哥搞算术【栈计算表达式】
  • 【CDOJ 1329】卿学姐与魔法
  • C++ STL 之 BitSet
  • [CDOJ 1343] 卿学姐失恋了
  • 【CDOJ 1342】郭大侠与甲铁城 【离线树状数组】
  • 【CDOJ 1350】卿学姐失恋了Ⅱ
  • 【CDOJ】柱爷与咸鱼神功
  • 【CDOJ 1357】柱爷与最大区间和
  • 【CDOJ 1323】柱爷的下凡
  • 【CDOJ 1321】柱爷的恋爱
  • 【CDOJ 1355】柱爷与三叉戟不得不说的故事 【状压DP+子集枚举】
  • [NOIP2011DAY1P1]铺地毯
  • 【vijos 1116】【codevs 1038】一元三次方程求解
  • 标准C++中的string类的用法总结
  • 「前端」从UglifyJSPlugin强制开启css压缩探究webpack插件运行机制
  • 【挥舞JS】JS实现继承,封装一个extends方法
  • 【前端学习】-粗谈选择器
  • Angular 响应式表单 基础例子
  • echarts的各种常用效果展示
  • Git 使用集
  • hadoop入门学习教程--DKHadoop完整安装步骤
  • JavaScript 一些 DOM 的知识点
  • JavaScript中的对象个人分享
  • Java到底能干嘛?
  • js算法-归并排序(merge_sort)
  • magento 货币换算
  • MQ框架的比较
  • Sequelize 中文文档 v4 - Getting started - 入门
  • Spring Cloud Alibaba迁移指南(一):一行代码从 Hystrix 迁移到 Sentinel
  • spring学习第二天
  • Vue2 SSR 的优化之旅
  • 从setTimeout-setInterval看JS线程
  • 用Canvas画一棵二叉树
  • ​LeetCode解法汇总518. 零钱兑换 II
  • ### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLTr
  • ###51单片机学习(2)-----如何通过C语言运用延时函数设计LED流水灯
  • #FPGA(基础知识)
  • #我与Java虚拟机的故事#连载10: 如何在阿里、腾讯、百度、及字节跳动等公司面试中脱颖而出...
  • (0)Nginx 功能特性
  • (2015)JS ES6 必知的十个 特性
  • (floyd+补集) poj 3275
  • (二十三)Flask之高频面试点
  • (附源码)ssm高校志愿者服务系统 毕业设计 011648
  • (附源码)ssm基于web技术的医务志愿者管理系统 毕业设计 100910
  • (紀錄)[ASP.NET MVC][jQuery]-2 純手工打造屬於自己的 jQuery GridView (含完整程式碼下載)...
  • (六) ES6 新特性 —— 迭代器(iterator)
  • (四)docker:为mysql和java jar运行环境创建同一网络,容器互联
  • (原創) 如何讓IE7按第二次Ctrl + Tab時,回到原來的索引標籤? (Web) (IE) (OS) (Windows)...
  • **Java有哪些悲观锁的实现_乐观锁、悲观锁、Redis分布式锁和Zookeeper分布式锁的实现以及流程原理...
  • .net core控制台应用程序初识
  • .NET Core实战项目之CMS 第十二章 开发篇-Dapper封装CURD及仓储代码生成器实现
  • .Net Core与存储过程(一)
  • .NET 的程序集加载上下文
  • .pyc文件还原.py文件_Python什么情况下会生成pyc文件?