输入对话框,可以象函数一样方便调用,VC++
可以放在头文件中,不需要资源文件,在程序中随时象子程序一样调用。
运行效果如图:
相关程序:
对话框的调用:
调用时输入对话框的提示和默认字符串,返回输入字符串指针,用完后注意delete,因为字符串为了长度可变,是在函数中根据输入分配的空间。
wchar_t* p = CXH::InputStrW(m_hInst, hWnd, _T("Password"), _T("DefaultPassword")); //传入默认值 PRINT(_T("\r\n Input= %s"),p); delete p; //xgz 之前 delete 失败是因为new空间少了字符串末尾0
对话框的实现:
需要注意的:
1. 参数传递中,传地址的不同方式
2. 用窗口的用户数据保存指针,避免使用静态变量
3. 给字符串分配空间一定要增加字符串末尾的0,这个问题自己的程序比较容易调试,但是和其他不完全清楚的API函数搞在一起就烦人了。
//xh.h 头文件
class CXH { public: CXH(void); //传入default字符串,传出字符串指针,调用程序用完要delete这个指针 static wchar_t* InputStrW(HINSTANCE hInst, HWND hWndOwner, LPWSTR lpszTitle, LPWSTR lpszDefault); //字符串空间分配要增加末尾的0,否则delete会失败 static LRESULT CALLBACK InputStrWProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); //XGZ 用来测试控件是否会 distroy static LONG OldEditProc; static LRESULT CALLBACK NewEditProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); };
//xh.cpp 文件 //XGZ:在回调函数中为字符串分配的内存空间,一定要注意字符串末尾的0 wchar_t* CXH::InputStrW(HINSTANCE hInst, HWND hWndOwner, LPWSTR lpszTitle, LPWSTR lpszDefault) { LRESULT ret; int nchar; HGLOBAL hgbl; LPDLGTEMPLATE lpdt; LPWORD lpw; hgbl = GlobalAlloc(GMEM_ZEROINIT, 1024); lpdt = (LPDLGTEMPLATE)GlobalLock(hgbl); lpdt = (LPDLGTEMPLATE)hgbl; lpdt->style = WS_POPUP | WS_BORDER | WS_THICKFRAME | WS_CAPTION | WS_SYSMENU; //DS_MODALFRAME lpdt->cdit = 0; // number of controls lpdt->x = 100; lpdt->y = 50; lpdt->cx = 150; lpdt->cy = 60; lpw = (LPWORD)(lpdt + 1); *lpw++ = 0; // no menu *lpw++ = 0; // predefined dialog box class (by default) wcscpy((wchar_t*)lpw, lpszTitle); //对话框的Title nchar = wcslen((wchar_t*)lpw) + 1; lpw += nchar; GlobalUnlock(hgbl); //XGZ这样传地址是合理的,因为InputStrW函数传入的指针,实际上是一个指针的拷贝,和指针的指针是不同的 //这里把程序中拷贝的指针的地址传给DialogBoxIndirectParam,是可以在子程序中修改并传回一个新的空间,原空间在Main主程序分配,这里无需处理 ret = DialogBoxIndirectParam(hInst, (LPDLGTEMPLATE)hgbl, hWndOwner, (DLGPROC)InputStrWProc, (LPARAM)&lpszDefault); //XGZ 若为了便于理解,可以用以下写法,其实是一样的。 //wchar_t* p = NULL; //p = (wchar_t*)lpszDefault; //ret = DialogBoxIndirectParam(hInst, (LPDLGTEMPLATE)hgbl, hWndOwner, (DLGPROC)InputStrWProc, (LPARAM)&p); //0传出另一个指针 //return (wchar_t*)p; GlobalFree(hgbl); if (ret == IDOK) { return (wchar_t*)lpszDefault; } else { return NULL; } } LRESULT CALLBACK CXH::InputStrWProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { // ID的作用域是窗口,读取函数都要带 HWND参数的,只有对话框用,定义在这里就OK了 #define IDC_EDIT1 1000 #define IDC_BNOK 1001 #define IDC_BNCANCEL 1002 RECT rt; //没必要初始化,因为使用从赋值开始 wchar_t** p;// = &str; //指向字符串指针地址的指针,用来返回不定长字符串 HINSTANCE hInst; static HWND hWndEdit1; static HWND hWndBNOK; static HWND hWndBNCANCEL; if (message == WM_INITDIALOG) { int tt = 100; tt = GetWindowLong(hWnd, GWL_USERDATA); //测试: tt==0 原来是没有用的 if ((wchar_t **)lParam != NULL) //内容必须是一个有效的指针,用它传出新的字符串 { p = (wchar_t**)lParam; //*p 不能是NULL, **p可以是NULL } else { EndDialog(hWnd, IDCANCEL); //取消并传出cancel return TRUE; } //WM_INITDIALOG 消息会传入参数,把这个参数保存在窗口用户数据处,这个空间时调用函数分配好的 SetWindowLong(hWnd, GWL_USERDATA, reinterpret_cast<LPARAM>(p)); hInst = (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE); GetClientRect(hWnd, &rt); //主窗口 hWndEdit1 = CreateWindow(_T("edit"), NULL, WS_CHILD | WS_BORDER | WS_VISIBLE | ES_MULTILINE | WS_VSCROLL,// | ES_READONLY rt.left + 10, rt.top + 10, rt.right - rt.left - 20, rt.bottom - rt.top - 70, hWnd, (HMENU)IDC_EDIT1, hInst, NULL); //这里只是为了测试对话框销毁时,控件是否被销毁 OldEditProc = SetWindowLong(hWndEdit1, GWL_WNDPROC, (LONG)NewEditProc);
hWndBNOK = CreateWindow(_T("button"), _T("OK"), WS_CHILD | WS_VISIBLE | BS_FLAT, rt.right - 220, rt.bottom - 40, 80, 25, hWnd, (HMENU)IDOK, hInst, NULL); hWndBNCANCEL = CreateWindow(_T("button"), _T("CANCEL"), WS_CHILD | WS_VISIBLE | BS_FLAT, rt.right - 120, rt.bottom - 40, 80, 25, hWnd, (HMENU)IDCANCEL, hInst, NULL); SetDlgItemText(hWnd, IDC_EDIT1, (LPWSTR)((wchar_t*)(*p))); //这里的Defualt串 return TRUE; } else { //其他消息把之前保存的参数取出 p = reinterpret_cast<wchar_t **>(GetWindowLong(hWnd, GWL_USERDATA)); switch (message) { case WM_INITDIALOG: // 移到外面了,且已经return true,因为else的缘故 return TRUE; case WM_SIZE: GetClientRect(hWnd, &rt); //主窗口 MoveWindow(hWndEdit1, rt.left + 10, rt.top + 10, rt.right - rt.left - 20, rt.bottom - rt.top - 70, TRUE); MoveWindow(hWndBNOK, rt.right - 220, rt.bottom - 40, 80, 25, TRUE); MoveWindow(hWndBNCANCEL, rt.right - 120, rt.bottom - 40, 80, 25, TRUE); break; case WM_PAINT: break; case WM_COMMAND: if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) //这里其实应该把IDCANCEL分出来,以后改吧 { //XGZ 这里要注意加上末尾的0,否则delete 指针会失败 int len= GetWindowTextLength(GetDlgItem(hWnd, IDC_EDIT1))+1; wchar_t* wcp = new wchar_t[len]; //调用函数要释放 *p = wcp; //传回新分配的地址空间,实际不是传,而是修改了传入的这个指针的所指 GetDlgItemText(hWnd, IDC_EDIT1, wcp, len); EndDialog(hWnd, LOWORD(wParam)); //传出ID return TRUE; } break; } return FALSE; } } long CXH::OldEditProc = 0; //只是用来测试 LRESULT CALLBACK CXH::NewEditProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { static int i = 0; switch (message) { case WM_CREATE: //测试:不会执行,因为这个消息是建立自身的前提 i++; break; case WM_DESTROY: //测试:会执行,说明控件随父窗口销毁而销毁 i++; break; case WM_KEYDOWN: i++; break; default: //SendMessage(GetParent(hWnd), message, wParam, lParam); //测试:不要乱发,最好改为Notify 通知,携带自己的HWND break; } return CallWindowProc((WNDPROC)OldEditProc, hWnd, message, wParam, lParam); }