Win32API学习笔记第三章
来源:互联网 发布:如何在淘宝上发布宝贝 编辑:程序博客网 时间:2024/06/09 08:36
这次记录的是鼠标与键盘的消息和部分相应API的使用如与标准(本人学的是Win程序设计第五版)有偏差,或哪里有不妥,欢迎大家给予斧正!
一:键盘(初阶)
Windows有8种不同的消息来传递不同的键盘事件,但是其中的大部分是我们一般不会去处理的,比如一些常用到系统功能的几件,一般情况下我们是不用去刻意去监视这些消息的(除非你要搞事情2333),通常情况下这些消息都会传给DefWindowProc(默认的窗口过程)
每一个键盘的KeyUp和Down都只有一个窗口过程可以接收,此时接受待定的键盘事件的窗口具有输入焦点(Focus)
消息:
一:WM_SETFCOUS和WM_KELLFCOUS
我们通过名字可以知道这两个消息就是在获得焦点和失去焦点的时候产生的,
二:击键消息:
WM_KEYDOWN&UP和WM_SYSKEYDOWN&UP
Down和UP一般是成对出现的
其中这两种击键消息一个是系统的一个是非系统的(SYS)SYS的一般主要是与Alt的组合击键产生的,一般我们是不会去管SYSKEYDOWN和SYSKEYUP的都是由经默认的窗口过程来处理。
在这四个击键消息产生时wParam参数和lParam参数分别是:
wParam:虚拟键码,也就是大概不能被显示出来的字符所代表的如回车,Backspace等,但是却有相应的标识符与其对应
lParam:这个参数的32个位分为了六个几件消息域
大概是:0~15是重复计数用的,16~26是OEM扫描码,24是扩展键标志,31是转换状态,30是键的先前状态(是否是释放或是被按下的),29是环境代码(系统击键和非系统击键例如对于KEYDOWN这一位是0对于SYSKEYDOWN这一位是1)
三:字符消息:
还记得之前的,那个在消息循环里面出现的TranslateMessage函数吗?没错,这个玩意就是将击键消息转换为字符的(才编码表中存在的字符或者说是击键消息(组合)产生了一个字符)此时这个消息将放入击键消息的下一个消息
下面说说4类字符消息:
WM_CHAR和WM_SYSCHAR,以及相应的死字符(DEADCHAR)
此时和击键消息不同的是wParam变成了相应的ANSI(UNICODE)编码
四:插入符:
还记得刚开始的那两个关于焦点的消息嘛?没错,对于插入符,配上这两个信息就可以模拟出控制台的效果了:
当收到WM_SETFOCUS时就可以运行CreateCaret了这个时候就会创建一个和这个窗口有关的插入符,并且依次调用SetCaretPos和ShowCaret来设置其位置并显示出来
当收到WM_KILLFOCUS时就可以运行HideCaret和DestoryCaret来隐藏并销毁这个插入符
二:鼠标
其实鼠标在早期的Windows设计时是被当作一个非必需品设计的,因为在现在的窗口操作中每个按钮其实都对应了一个等价的键盘操作,但是随着时代的进步,现在的Windows是需要鼠标的。
现在言归正传,对于鼠标:
消息:
WM_?BUTTONDOWN&UP&DBLCLK
WM_NC?BUTTONDOWN&UP&DBLCLK
其中的?可以是L,M,R(左,中,右)
带有NC的消息则时在非客户区产生的鼠标击键消息
故名思意这个就是在单击和双击产生的三个消息
WM_MOUSEMOVE&NC~
这个就是鼠标移过客户区时产生的消息,但是并不是每一个像素都产生这个消息,这个消息的产生次数依赖于计算机的处理速度
对于上面客户区产生的这几个消息,参数lParam的高低位就是产生这个消息时鼠标对应客户区的像素坐标,wParam则指示了鼠标键及Shift和Ctrl建的状态
而下面非客户区的消息,参数lParam则是相对于屏幕的坐标,wParam则是指明了移动或单击鼠标键的非客户区的位置,它一般值与HT(命中测试)开头的宏定义相对
WM_NCHITTEST
这个消息时21个鼠标消息的最后一个消息他就是“非客户区命中测试”
这个消息是一个比较特殊但是在Windows中是非常普遍的消息,Windows一般是把这个消息传给DefWindowProc并且这个消息的除法优于所有客户区以及非客户区的鼠标消息,其中lParam时相对于屏幕的坐标,wParam没有用处
然而,当DefWinodwProc处理完以后其返回值将保留到wParam里面这个值有一下内容:
HTCLIENT客户区
HTNOWHERE不在窗口中
HTTRANSPARENT窗口由另一个窗口覆盖
HTERROR使DefWindowProc产生蜂鸣声
结合上面所说的这个消息后的鼠标消息的wParam的值我们可以知道,这四个消息的值可以加上任意之前非客户区的鼠标消息如果我们在自己的Proc中捕获了这人个消息,那么后果就是鼠标将失效
这个消息之所以特殊是因为这个消息在被捕获并执行之后会接着产生一个鼠标的消息并且wParam的值会按照之前四个参数的鼠标的所处情况而定
下面这个是我对前面记个笔记的一个总结的小程序,虽然有一些错误,但是大概功能都是有的,主要是模拟了一个简易的控制台窗口,这个为我后面自己写一个8086汇编的简易的编译器打下了一个基础
代码质量不高,忘打手轻喷,并给予斧正,谢谢!
#include "stdafx.h"#include "Win32ProjectWithAASM.h"#define MAX_LOADSTRING 100// 全局变量: HINSTANCE hInst; // 当前实例WCHAR szTitle[MAX_LOADSTRING]; // 标题栏文本WCHAR szWindowClass[MAX_LOADSTRING]; // 主窗口类名// 此代码模块中包含的函数的前向声明: ATOM MyRegisterClass(HINSTANCE hInstance);BOOL InitInstance(HINSTANCE, int);LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow){ UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); // TODO: 在此放置代码。 // 初始化全局字符串 LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); LoadStringW(hInstance, IDC_WIN32PROJECTWITHAASM, szWindowClass, MAX_LOADSTRING); MyRegisterClass(hInstance); // 执行应用程序初始化: if (!InitInstance (hInstance, nCmdShow)) { return FALSE; } HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_WIN32PROJECTWITHAASM)); MSG msg; // 主消息循环: while (GetMessage(&msg, nullptr, 0, 0)) { if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } return (int) msg.wParam;}//// 函数: MyRegisterClass()//// 目的: 注册窗口类。//ATOM MyRegisterClass(HINSTANCE hInstance){ WNDCLASSEXW wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WIN32PROJECTWITHAASM)); wcex.hCursor = LoadCursor(nullptr, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_WIN32PROJECTWITHAASM); wcex.lpszClassName = szWindowClass; wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL)); return RegisterClassExW(&wcex);}//// 函数: InitInstance(HINSTANCE, int)//// 目的: 保存实例句柄并创建主窗口//// 注释: //// 在此函数中,我们在全局变量中保存实例句柄并// 创建和显示主程序窗口。//BOOL InitInstance(HINSTANCE hInstance, int nCmdShow){ hInst = hInstance; // 将实例句柄存储在全局变量中 HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr); if (!hWnd) { return FALSE; } ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE;}////页面内存调度函数,参数对应为:缓冲区指针,段长度指针,缓冲区最长宽度,缓冲区最长长度//inline bool BufferAlloc(TCHAR **&bfBuffer, UINT32 *&UsefulLen, UINT32 cxBufferMax, UINT32 cyBufferMax){ if (UsefulLen == nullptr) { UsefulLen = (UINT32 *)malloc(sizeof(UINT32)*cyBufferMax); memset(UsefulLen, 0, cyBufferMax * sizeof(UINT32)); } else if (cyBufferMax > (_msize(UsefulLen) / sizeof(UINT32))) { UINT32 tempSize = _msize(UsefulLen) / sizeof(UINT32); UsefulLen = (UINT32 *)realloc(UsefulLen, cyBufferMax * sizeof(UINT32)); memset(UsefulLen + tempSize, 0, (cyBufferMax - tempSize)* sizeof(UINT32)); } if (bfBuffer == nullptr) { bfBuffer = (TCHAR **)malloc(sizeof(PTCH)*cyBufferMax); for (UINT32 i = 0; i < cyBufferMax; ++i) { *(bfBuffer + i) = (TCHAR *)malloc(sizeof(TCHAR)*cxBufferMax); wmemset(*(bfBuffer + i), ' ', cxBufferMax); } } else { UINT32 temSize = _msize(*(bfBuffer)) / sizeof(TCHAR); UINT32 temSize2 = _msize(bfBuffer) / sizeof(bfBuffer); if (temSize < cxBufferMax) { for (UINT32 i = 0; i < temSize2; ++i) { *(bfBuffer + i) = (PTCH)realloc(*(bfBuffer + i), sizeof(TCHAR)*cxBufferMax); wmemset(*(bfBuffer + i) + temSize, ' ', cxBufferMax - temSize); } } if (temSize2 < cyBufferMax) { bfBuffer = (TCHAR **)realloc(bfBuffer, cyBufferMax * sizeof(PTCH)); for (UINT32 i = temSize2; i < cyBufferMax; ++i) { *(bfBuffer + i) = (PTCH)malloc(sizeof(TCHAR)*cxBufferMax); wmemset(*(bfBuffer + i), ' ', cxBufferMax); } } } return 0;}UINT32 LastLine(UINT32 *UsefulLen){ UINT32 i = _msize(UsefulLen); for (i /= sizeof(UINT32), --i; !UsefulLen[i]; --i); return i;}//// 函数: WndProc(HWND, UINT, WPARAM, LPARAM)//// 目的: 处理主窗口的消息。//// WM_COMMAND - 处理应用程序菜单`// WM_PAINT - 绘制主窗口// WM_DESTROY - 发送退出消息并返回////LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam){ static DWORD dwCharSet = DEFAULT_CHARSET; static UINT32 cxClient, cyClient, cxCaret = 0, cyCaret = 0, cxChar, cyChar, cxBuffer, cyBuffer, cxMaxAdrr, cyMaxAdrr, cyBufferMax, cxBufferMax, xC, yC, cxCaretB, cyCaretB; static UINT32 *UsefulLen = nullptr; int iSVPos, iSHPos; HDC hdc; static TCHAR **bfBuffer = nullptr; static SCROLLINFO si; TEXTMETRIC tm; switch (message) { case WM_COMMAND: { int wmId = LOWORD(wParam); // 分析菜单选择: switch (wmId) { case IDM_ABOUT: DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About); break; case IDM_EXIT: DestroyWindow(hWnd); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } } break; case WM_LBUTTONDOWN: { UINT32 tempY, tempX; tempY = HIWORD(lParam) / cyChar; tempX = LOWORD(lParam) / cxChar; if (tempX > UsefulLen[tempY]) cxCaret = UsefulLen[tempY]; if (tempY<= cyCaretB) cyCaret = tempY; HideCaret(hWnd); InvalidateRect(hWnd, NULL, true); SetCaretPos(cxCaret*cxChar, cyCaret*cyChar); ShowCaret(hWnd); } break; case WM_CREATE: { hdc = GetDC(hWnd); SelectObject(hdc, CreateFont(0, 0, 0, 0, 0, 0, 0, 0, dwCharSet, 0, 0, 0, FIXED_PITCH, NULL)); GetTextMetrics(hdc, &tm); cxChar = tm.tmAveCharWidth; cyChar = tm.tmHeight; DeleteObject(SelectObject(hdc, GetStockObject(SYSTEM_FONT))); ReleaseDC(hWnd, hdc); } break; case WM_VSCROLL: { si.cbSize = sizeof(si); si.fMask = SIF_ALL; GetScrollInfo(hWnd, SB_VERT, &si); iSVPos = si.nPos; switch (LOWORD(wParam)) { case SB_TOP: si.nPos = si.nMin; break; case SB_BOTTOM: si.nPos = si.nMax; break; case SB_LINEUP: si.nPos -= 1; break; case SB_LINEDOWN: si.nPos += 1; break; case SB_PAGEUP: si.nPos -= si.nPage; break; case SB_PAGEDOWN: si.nPos += si.nPage; break; case SB_THUMBTRACK: si.nPos = si.nTrackPos; break; default: break; } si.fMask = SIF_POS; SetScrollInfo(hWnd, SB_VERT, &si, true); if (si.nPos != iSVPos) { ScrollWindow(hWnd, 0, cyChar*(iSVPos - si.nPos), NULL, NULL); UpdateWindow(hWnd); } return 0; } case WM_SIZE: { cxClient = LOWORD(lParam); cyClient = HIWORD(lParam); cxBuffer = max(1, cxClient / cxChar); cyBuffer = max(1, cyClient / cyChar); cyBufferMax = cyBuffer > cyBufferMax ? cyBuffer : cyBufferMax; cyBufferMax = cyCaret > cyBufferMax ? cyCaret : cyBufferMax; cxBufferMax = cxBuffer > cxBufferMax ? cxBuffer : cxBufferMax; cxBufferMax = cxCaret > cxBufferMax ? cxCaret : cxBufferMax; BufferAlloc(bfBuffer, UsefulLen, cxBufferMax, cyBufferMax); if ((signed)(cyCaretB + 1 - cyBuffer) > 0) yC = cyCaretB + 1 - cyBuffer; if ((signed)(cxCaretB + 1 - cxBuffer) > 0) xC = cxCaretB + 1 - cxBuffer; si.cbSize = sizeof(si); si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS; si.nMax = cyCaretB; si.nMin = 0; si.nPage = cyBuffer; SetScrollInfo(hWnd, SB_VERT, &si, true); InvalidateRect(hWnd, NULL, true); return 0; } case WM_PAINT: { PAINTSTRUCT ps; hdc = BeginPaint(hWnd, &ps); // TODO: 在此处添加使用 hdc 的任何绘图代码... si.cbSize = sizeof(si); si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS; si.nMax = cyCaretB; si.nMin = 0; si.nPos = cyCaretB; si.nPage = cyBuffer; SelectObject(hdc, CreateFont(0, 0, 0, 0, 0, 0, 0, 0, dwCharSet, 0, 0, 0, FIXED_PITCH, nullptr)); for (UINT32 y = 0; y < cyBuffer; ++y) TextOut(hdc, 0, y * cyChar, *(bfBuffer + y + yC) + xC, cxBuffer); DeleteObject(SelectObject(hdc, GetStockObject(SYSTEM_FONT))); SetScrollInfo(hWnd, SB_VERT, &si, true); EndPaint(hWnd, &ps); return 0; } case WM_SETFOCUS: { CreateCaret(hWnd, NULL, cxChar, cyChar); SetCaretPos(cxCaret*cxChar, cyCaret*cyChar); ShowCaret(hWnd); return 0; } case WM_KILLFOCUS: { HideCaret(hWnd); DestroyCaret(); return 0; } case WM_KEYDOWN: { switch (wParam) { case VK_HOME: cxCaret = 0; xC = 0; break; case VK_END: if (cxCaretB <= cxBuffer) cxCaret = UsefulLen[cyCaret + yC]; else { cxCaret = cxBuffer - 1; xC = cxCaretB - cxBuffer; } break; case VK_LEFT: if (cxCaret == 0) if (xC != 0) --xC; if (cxCaret != 0) --cxCaret; break; case VK_RIGHT: ++xC; if (cxCaret < cxBuffer&&cxCaret < UsefulLen[cyCaret + yC]) { ++cxCaret; if(xC != 1) --xC; } else if(cxCaret + xC >= UsefulLen[cyCaret + yC]) --xC; break; case VK_UP: if (cyCaret == 0) if (yC != 0) --yC; if (cyCaret != 0) --cyCaret; break; case VK_DOWN: ++yC; if (cyCaret < cyBuffer - 1) { --yC; if (cyCaret < LastLine(UsefulLen)) ++cyCaret; } else if (cyCaret + yC >= cyBufferMax) --yC; break; case VK_DELETE: { if (cxCaret > 0) { memmove(*(bfBuffer + cyCaret + yC) + cxCaret + xC, *(bfBuffer + cyCaret + yC) + cxCaret + xC + 1, cxBufferMax - cxCaret - xC); if (UsefulLen[cyCaret + yC] != 0) --UsefulLen[cyCaret + yC]; } else if (xC != 0) { if (xC > cxBuffer) { xC -= cxBuffer; cxCaret = cxBuffer - 1; } else { cxCaret = xC; xC = 0; } memmove(*(bfBuffer + cyCaret + yC) + cxCaret + xC, *(bfBuffer + cyCaret + yC) + cxCaret + xC + 1, cxBufferMax - cxCaret - xC); if (UsefulLen[cyCaret + yC] != 0) --UsefulLen[cyCaret + yC]; } else { if (UsefulLen[cyCaret + yC]) { memmove(*(bfBuffer + cyCaret + yC) + cxCaret + xC, *(bfBuffer + cyCaret + yC) + cxCaret + xC + 1, cxBufferMax - cxCaret - xC); if (UsefulLen[cyCaret + yC] != 0) --UsefulLen[cyCaret + yC]; } else { if (cyCaret != 0) { xC = 0; --cyCaret; --cyCaretB; cxCaret = cxCaretB = UsefulLen[cyCaret + yC]; if (cxCaretB > cxBuffer - 1) { xC = cxCaretB - cxBuffer; cxCaret = cxBuffer - 1; } } else if (yC != 0) { if (yC > cyBuffer) { yC -= cyBuffer; cyCaret = cyBuffer - 1; } else { cyCaretB = cyCaret = yC - 1; yC = 0; cxCaret = UsefulLen[cyCaret]; } } } } si.cbSize = sizeof(si); si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS; si.nMax = cyCaretB; si.nPos = cyCaretB; si.nMin = 0; si.nPage = cyBuffer; SetScrollInfo(hWnd, SB_VERT, &si, true); HideCaret(hWnd); InvalidateRect(hWnd, NULL, true); ShowCaret(hWnd); } break; default: break; } InvalidateRect(hWnd, NULL, true); SetCaretPos(cxCaret*cxChar, cyCaret*cyChar); return 0; } case WM_CHAR: { for (int i = 0; i < (int)LOWORD(lParam); ++i) { switch (wParam) { case '\b': if (cxCaret > 0) --cxCaret; SendMessage(hWnd, WM_KEYDOWN, VK_DELETE, 1); break; case '\t': for (; cxCaret % 8;) SendMessage(hWnd, WM_CHAR, ' ', 1); break; case '\n': /*if (cyCaret > cyCaretB) cyCaretB = cyCaret; if (cyCaret >= cyBuffer - 1) ++cyCaretB; else { ++cyCaret; ++cyCaretB; } if (cyCaretB >= cyBufferMax) { cyBufferMax = cyCaretB + 1; BufferAlloc(bfBuffer, UsefulLen, cxBufferMax, cyBufferMax); } if ((signed)(cyCaretB + 1 - cyBuffer) > 0) yC = cyCaretB + 1 - cyBuffer; if ((signed)(cxCaretB + 1 - cxBuffer) > 0) xC = cxCaretB + 1 - cxBuffer; InvalidateRect(hWnd, NULL, true);*/ case '\r': cxCaret = 0; cxCaretB = 0; xC = 0; if (cyCaret > cyCaretB) cyCaretB = cyCaret; if (cyCaret >= cyBuffer - 1) ++cyCaretB; else { ++cyCaret; ++cyCaretB; } if (cyCaretB >= cyBufferMax) { cyBufferMax = cyCaretB + 1; BufferAlloc(bfBuffer, UsefulLen, cxBufferMax, cyBufferMax); } if ((signed)(cyCaretB + 1 - cyBuffer) > 0) yC = cyCaretB + 1 - cyBuffer; if ((signed)(cxCaretB + 1 - cxBuffer) > 0) xC = cxCaretB + 1 - cxBuffer; InvalidateRect(hWnd, NULL, true); break; default: if ((signed)(cyCaretB + 1 - cyBuffer) > 0) yC = cyCaretB + 1 - cyBuffer; if ((signed)(cxCaretB + 1 - cxBuffer) > 0) xC = cxCaretB + 1 - cxBuffer; if (cxCaret > cxCaretB) cxCaretB = cxCaret; if (cxCaret >= cxBuffer - 1) ++cxCaretB; else { ++cxCaret; cxCaretB = cxCaret; } HideCaret(hWnd); if (cxCaretB + 1 > cxBufferMax) { cxBufferMax = cxCaretB + 1; BufferAlloc(bfBuffer, UsefulLen, cxBufferMax, cyBufferMax); } *(*(bfBuffer + cyCaret + yC) + cxCaret - 1 + xC) = (TCHAR)wParam; ++UsefulLen[cyCaret + yC]; InvalidateRect(hWnd, NULL, true); ShowCaret(hWnd); break; } si.cbSize = sizeof(si); si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS; si.nMax = cyCaretB; si.nPos = cyCaretB; si.nMin = 0; si.nPage = cyBuffer; SetScrollInfo(hWnd, SB_VERT, &si, true); } SetCaretPos(cxCaret*cxChar, cyCaret*cyChar); return 0; } case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0;}// “关于”框的消息处理程序。INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam){ UNREFERENCED_PARAMETER(lParam); switch (message) { case WM_INITDIALOG: return (INT_PTR)TRUE; case WM_COMMAND: if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) { EndDialog(hDlg, LOWORD(wParam)); return (INT_PTR)TRUE; } break; } return (INT_PTR)FALSE;}
这个程序其中包括了之前没有记录的拖动条的API
- Win32API学习笔记第三章
- Win32API学习笔记第二章
- Win32API学习笔记第四章
- Win32API学习笔记第八章
- Win32API学习笔记第五章(一)
- Win32API学习笔记第五章(二)
- Win32API学习笔记第五章(三)
- Win32API学习笔记第六章(一)
- Win32API学习笔记第六章(二)
- Win32API学习笔记第六章(三)
- Win32API学习笔记第六章(四)
- Win32API学习笔记第七章(一)
- Win32API学习笔记第七章(二)
- Win32API学习笔记第七章(三)
- Win32API学习笔记第一章
- Win32API多线程程序设计学习笔记
- 第三章学习笔记
- 学习笔记 第三章
- Caffe:python版本Faster R-CNN测试
- C/C++ BMP(24位真彩色)图像处理(2)------图像の截取
- 游戏sdk接入教程:360渠道SDK接入
- 将博客搬至51CTO
- “==”和equals()的区别
- Win32API学习笔记第三章
- centos7下mysql操作
- window.applicationCache事件,介绍
- 剪切板操作
- 优秀班组长分享:生产车间管理技巧
- 40个Java多线程问题总结
- SOCKET.IO,理解SOCKET.IO
- grails处理json数据
- 数据库设计--数据字典