// IPEDIT.CPP #include "common.h" const TCHAR szClassEdit[] = "Edit"; const TCHAR szClassListBox[] = "ListBox"; const TCHAR szClassIpEdit[] = "IpEdit"; const TCHAR szClassIpList[] = "IpList"; WNDPROC lpfnEditOld; WNDPROC lpfnListBoxOld; int ibListBoxOld; int ibEditOld; static TCHAR szIpNone[32]; #define tIpValue 0 #define tIpRange 1 // Later #define tIpNone 2 ///////////////////////////////////////////////////////////////////////////// int ConvertIpAddrToString( IP_ADDRESS dwIp, // IN TCHAR szIp[]) // OUT { TCHAR *szTmp; szTmp = inet_ntoa (*(in_addr *)&dwIp); strcpy (szIp, szTmp); return (strlen(szIp)); } // ConvertIpAddrToString ///////////////////////////////////////////////////////////////////////////// IP_ADDRESS ConvertStringToIpAddr(const TCHAR szIp[]) { IP_ADDRESS dwIpAddress = 0; dwIpAddress = inet_addr (szIp); return dwIpAddress; } // ConvertStringToIpAddr ///////////////////////////////////////////////////////////////////////////// // IpListIpEdit_SetButtons() // // Attach buttons to both IpList and IpEdit controls // NOTE: move up and move down are optional // void IpListIpEdit_SetButtons( HWND hwndIpList, // Window handle of the IpList control HWND hwndIpEdit, // Window handle of the IpEdit control UINT wIdBtnMoveUp, // Control ID of the MoveUp button UINT wIdBtnMoveDown, // Control ID of the MoveDown button UINT wIdBtnAdd, // Control ID of the Add button UINT wIdBtnRemove) // Control ID of the Remove button { HWND hwndParent; // Parent of hwndIpList (ie, dialog) HWND hwndT; // Make sure hwndIpList is of class "IpList" AssertClassName(hwndIpList, szClassIpList); // Make sure hwndIpEdit is of class "IpEdit" AssertClassName(hwndIpEdit, szClassIpEdit); hwndParent = GetParent(hwndIpList); Assert(IsWindow(hwndParent)); if (wIdBtnMoveUp) { IpList_SetHwndBtnMoveUp(hwndIpList, HGetDlgItem(hwndParent, wIdBtnMoveUp)); } if (wIdBtnMoveDown) { IpList_SetHwndBtnMoveDown(hwndIpList, HGetDlgItem(hwndParent, wIdBtnMoveDown)); } IpList_SetHwndBtnRemove(hwndIpList, HGetDlgItem(hwndParent, wIdBtnRemove)); hwndT = HGetDlgItem(hwndParent, wIdBtnAdd); IpList_SetHwndBtnAdd(hwndIpList, hwndT); IpEdit_SetHwndBtnAdd(hwndIpEdit, hwndT); IpList_SetHwndIpEdit(hwndIpList, hwndIpEdit); } // IpListIpEdit_SetButtons ///////////////////////////////////////////////////////////////////////////// // IpListIpEdit_HandleButtonCommand() // // Handle the WM_COMMAND message generated by any of MoveUp, MoveDown, // Add or Remove buttons. // This function is only to save code in the dialogproc - nothing useful. // void IpListIpEdit_HandleButtonCommand(HWND hwndIpList, WPARAM wParam, LPARAM lParam) { HWND hwndT; if (HIWORD(wParam)) { // If there is a notification code, then the message is not // comming from a button. // Some Edit control send WM_COMMAND messages before the // WM_INITDIALOG, so by checking the notification code, we // avoid having hwndIpList to be NULL. return; } AssertClassName(hwndIpList, szClassIpList); Assert(IsWindow((HWND)lParam)); hwndT = IpList_GetHwndBtnAdd(hwndIpList); if (hwndT == (HWND)lParam || IpList_GetHwndBtnRemove(hwndIpList) == (HWND)lParam) { HWND hwndIpEdit = IpList_GetHwndIpEdit(hwndIpList); Assert(IsWindow(hwndIpEdit)); AssertClassName(hwndIpEdit, szClassIpEdit); if (hwndT == (HWND)lParam) { // Add button pressed IpList_AddAddress(hwndIpList, -1, IpEdit_GetAddress(hwndIpEdit)); IpEdit_ClearAddress(hwndIpEdit); SetFocus(hwndIpEdit); } else { // Remove button pressed IP_ADDRESS dwIpAddress; IpList_RemoveAddress(hwndIpList, -1, OUT &dwIpAddress); IpEdit_SetAddress(hwndIpEdit, dwIpAddress); } } else if (IpList_GetHwndBtnMoveUp(hwndIpList) == (HWND)lParam) { IpList_MoveUp(hwndIpList); } else if (IpList_GetHwndBtnMoveDown(hwndIpList) == (HWND)lParam) { IpList_MoveDown(hwndIpList); } } // IpListIpEdit_HandleButtonCommand void IpList_EnableWindow(HWND hwndIpList, BOOL fEnable) { // Make sure hwndIpList is of class "IpList" AssertClassName(hwndIpList, szClassIpList); } ///////////////////////////////////////////////////////////////////////////// // IpList_EnableButtons() // // Enable/disable IpList's buttons according to the listbox state. // static void IpList_EnableButtons(HWND hwndIpList) { // Make sure hwndIpList is of class "IpList" AssertClassName(hwndIpList, szClassIpList); const HWND hwndBtnUp = IpList_GetHwndBtnMoveUp(hwndIpList); const HWND hwndBtnDown = IpList_GetHwndBtnMoveDown(hwndIpList); const HWND hwndBtnRemove = IpList_GetHwndBtnRemove(hwndIpList); BOOL fIsEnabled = IsWindowEnabled(hwndIpList); LRESULT iCurSel = LSendMessage(hwndIpList, LB_GETCURSEL, 0, 0); if (hwndBtnUp) { Assert(IsWindow(hwndBtnUp)); Assert(LB_ERR < 0); EnableWindow(hwndBtnUp, fIsEnabled && (iCurSel > 0)); } // if if (hwndBtnDown) { Assert(IsWindow(hwndBtnDown)); if (iCurSel == LB_ERR) EnableWindow(hwndBtnDown, FALSE); else EnableWindow(hwndBtnDown, fIsEnabled && (iCurSel + 1 < LSendMessage(hwndIpList, LB_GETCOUNT, 0, 0))); } BOOL fListEmpty = IpList_GetFlags(hwndIpList) & IpList_mskfIsEmpty; if (hwndBtnRemove) { Assert(IsWindow(hwndBtnRemove)); EnableWindow(hwndBtnRemove, fIsEnabled && (!fListEmpty && iCurSel != LB_ERR)); } CallWindowProc(lpfnListBoxOld, hwndIpList, WM_ENABLE, fIsEnabled && !fListEmpty, 0); } // IpList_EnableButtons ///////////////////////////////////////////////////////////////////////////// LRESULT CALLBACK WndProcIpList(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { char szT[32]; int i; IP_ADDRESS * pdwIpAddr; LRESULT lResult; DWORD dwFlags; AssertSz1(IsWindow(hwnd), "WndProcIpList: Invalid window handle hwnd=0x%08X", hwnd); Assert(lpfnListBoxOld != NULL); dwFlags = IpList_GetFlags(hwnd); switch (uMsg) { case LB_SETSEL: // REVIEW: Get rid of this trace Trace0(1, "\nLB_SETSEL"); lResult = CallWindowProc(lpfnListBoxOld, hwnd, LB_SETSEL, wParam, lParam); IpList_EnableButtons(hwnd); return lResult; case LB_GETCOUNT: if (dwFlags & IpList_mskfIsEmpty) return 0; return CallWindowProc(lpfnListBoxOld, hwnd, LB_GETCOUNT, 0, 0); case LB_ADDSTRING: Assert(FALSE); break; case IpList_WM_ADDADDRESS: if (wParam == (WPARAM)-1) wParam = CallWindowProc(lpfnListBoxOld, hwnd, LB_GETCURSEL, 0, 0); // Fall Through // case IpList_WM_INSERTADDRESS: ConvertIpAddrToString(lParam, szT); Assert(lstrlen(szT) < sizeof(szT)); if (dwFlags & IpList_mskfIsEmpty) { CallWindowProc(lpfnListBoxOld, hwnd, LB_RESETCONTENT, 0, 0); dwFlags &= ~IpList_mskfIsEmpty; } // If nothing is selected, append the address at the end of the list Assert(LB_ERR == -1); lResult = CallWindowProc(lpfnListBoxOld, hwnd, LB_INSERTSTRING, wParam, (LPARAM)szT); Report(lResult >= 0); Report(tIpValue == CallWindowProc(lpfnListBoxOld, hwnd, LB_GETITEMDATA, lResult, 0)); IpList_SetFlags(hwnd, dwFlags | IpList_mskfIsDirty); IpList_EnableButtons(hwnd); return lResult; case IpList_WM_REMOVEADDRESS: if (wParam == (WPARAM)-1) wParam = CallWindowProc(lpfnListBoxOld, hwnd, LB_GETCURSEL, 0, 0); Assert(wParam != LB_ERR); if (lParam) { Assert(CallWindowProc(lpfnListBoxOld, hwnd, LB_GETTEXTLEN, wParam, 0) < sizeof(szT)); CallWindowProc(lpfnListBoxOld, hwnd, LB_GETTEXT, wParam, (LPARAM)szT); *(DWORD *)lParam = ConvertStringToIpAddr(szT); } lResult = CallWindowProc(lpfnListBoxOld, hwnd, LB_DELETESTRING, wParam, 0); Assert(lResult != LB_ERR); if (lResult == 0) { CchLoadString (IDS_IP_NONE, szIpNone, LENGTH(szIpNone)); CallWindowProc(lpfnListBoxOld, hwnd, LB_INSERTSTRING, 0, (LPARAM)szIpNone); CallWindowProc(lpfnListBoxOld, hwnd, LB_SETITEMDATA, 0, tIpNone); dwFlags |= IpList_mskfIsEmpty; } IpList_SetFlags(hwnd, dwFlags | IpList_mskfIsDirty); IpList_EnableButtons(hwnd); return lResult; case IpList_WM_MOVEADDRESS: Trace0(lParam ? mskTraceNone : mskTraceInfo, "\nINFO: Moving address by 0 element(s)"); if (wParam == (WPARAM)-1) wParam = CallWindowProc(lpfnListBoxOld, hwnd, LB_GETCURSEL, 0, 0); Assert(wParam != LB_ERR); lResult = CallWindowProc(lpfnListBoxOld, hwnd, LB_GETTEXT, wParam, (LPARAM)szT); Assert(tIpValue == CallWindowProc(lpfnListBoxOld, hwnd, LB_GETITEMDATA, wParam, 0)); Assert(lResult != LB_ERR); CallWindowProc(lpfnListBoxOld, hwnd, LB_DELETESTRING, wParam, 0); Assert(lResult != LB_ERR); lResult = CallWindowProc(lpfnListBoxOld, hwnd, LB_INSERTSTRING, wParam + lParam, (LPARAM)szT); AssertSz(lResult != LB_ERR, "Value out of range"); CallWindowProc(lpfnListBoxOld, hwnd, LB_SETCURSEL, lResult, 0); Assert(lResult != LB_ERR); IpList_SetFlags(hwnd, dwFlags | IpList_mskfIsDirty); IpList_EnableButtons(hwnd); return 0; case IpList_WM_SETLIST: // Prevent the window from repainting CallWindowProc(lpfnListBoxOld, hwnd, WM_SETREDRAW, FALSE, 0); // Flush the content of the previous list CallWindowProc(lpfnListBoxOld, hwnd, LB_RESETCONTENT, 0, 0); if (wParam) { AssertSz((IP_ADDRESS *)lParam != NULL, "Invalid memory address"); pdwIpAddr = ((IP_ADDRESS *)lParam) + wParam; // Insert the items from last to fisrt while (wParam) { pdwIpAddr--; ConvertIpAddrToString(*pdwIpAddr, szT); Assert(lstrlen(szT) < sizeof(szT)); CallWindowProc(lpfnListBoxOld, hwnd, LB_INSERTSTRING, 0, (LPARAM)szT); Assert(tIpValue == CallWindowProc(lpfnListBoxOld, hwnd, LB_GETITEMDATA, 0, 0)); wParam--; } // while dwFlags &= ~IpList_mskfIsEmpty; } else { CchLoadString (IDS_IP_NONE, szIpNone, LENGTH(szIpNone)); CallWindowProc(lpfnListBoxOld, hwnd, LB_INSERTSTRING, 0, (LPARAM)szIpNone); CallWindowProc(lpfnListBoxOld, hwnd, LB_SETITEMDATA, 0, tIpNone); dwFlags |= IpList_mskfIsEmpty; } CallWindowProc(lpfnListBoxOld, hwnd, WM_SETREDRAW, TRUE, 0); IpList_SetFlags(hwnd, dwFlags & ~IpList_mskfIsDirty); IpList_EnableButtons(hwnd); InvalidateRect(hwnd, NULL, TRUE); return 0; case IpList_WM_GETLIST: if (wParam != IpList_GetList_ALLOCATEMEMORY) { pdwIpAddr = (IP_ADDRESS *)lParam; Assert(pdwIpAddr); Assert(wParam == (WPARAM)LSendMessage(hwnd, LB_GETCOUNT, 0, 0)); Assert((int)wParam >= 0); } else { wParam = (WPARAM)LSendMessage(hwnd, LB_GETCOUNT, 0, 0); Assert((int)wParam >= 0); Assert(lParam); // Allocate the memory pdwIpAddr = (IP_ADDRESS *)Malloc(wParam * sizeof(IP_ADDRESS)); ReportFSz(pdwIpAddr != NULL, "IpList: Out of memory"); *(IP_ADDRESS **)lParam = pdwIpAddr; if (pdwIpAddr == NULL) return 0; } // Insert the items from last to fisrt for (i = 0; i < (int)wParam; i++) { Assert(CallWindowProc(lpfnListBoxOld, hwnd, LB_GETTEXTLEN, i, 0) < sizeof(szT)); CallWindowProc(lpfnListBoxOld, hwnd, LB_GETTEXT, i, (LPARAM)szT); Assert(tIpValue == CallWindowProc(lpfnListBoxOld, hwnd, LB_GETITEMDATA, i, 0)); *pdwIpAddr++ = ConvertStringToIpAddr(szT); } Assert((WPARAM)i == wParam); return wParam; case WM_MOUSEMOVE: if ((wParam & MK_LBUTTON) == 0) break; case WM_ENABLE: case WM_KEYDOWN: case WM_LBUTTONDOWN: CallWindowProc(lpfnListBoxOld, hwnd, uMsg, wParam, lParam); IpList_EnableButtons(hwnd); return 0; default: break; } // swtich return CallWindowProc(lpfnListBoxOld, hwnd, uMsg, wParam, lParam); } // WndProcIpList ///////////////////////////////////////////////////////////////////////////// LRESULT CALLBACK WndProcEditNumber(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { Assert(lpfnEditOld); switch (uMsg) { case WM_CHAR: if (wParam == VK_BACK) break; if(wParam >= '0' && wParam <= '9') { CallWindowProc(lpfnEditOld, hwnd, WM_CHAR, wParam, lParam); } if (wParam == ' ' || wParam == '.') (void)LSendMessage(GetParent(hwnd), UN_KEYDOWN, VK_RIGHT, (LPARAM)hwnd); // Eat the character return 0; case WM_KEYDOWN: if (0 != LSendMessage(GetParent(hwnd), UN_KEYDOWN, wParam, (LPARAM)hwnd)) return 0; break; } // switch return CallWindowProc(lpfnEditOld, hwnd, uMsg, wParam, lParam); } // WndProcEditNumber ///////////////////////////////////////////////////////////////////////////// void IpEdit_EnableAddButton(HWND hwndIpEdit, HWND hwndBtn) { AssertClassName(hwndIpEdit, szClassIpEdit); if (hwndBtn) { Assert(IsWindow(hwndBtn)); // Enable the button if the IpAddress is valid EnableWindow(hwndBtn, LSendMessage(hwndIpEdit, IpEdit_WM_ISADDRESSVALID, 0, 0)); } } // IpEdit_EnableAddButton ///////////////////////////////////////////////////////////////////////////// LRESULT CALLBACK WndProcIpEdit(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { IPEDITDATA * pData; TCHAR * pch; TCHAR szT[32]; int i; AssertSz1(IsWindow(hwnd), "WndProcIpEdit: Invalid window handle hwnd=0x%08X", hwnd); Assert(lpfnEditOld != NULL); pData = (IPEDITDATA *)GetWindowLongFrom(hwnd, ibIpEditData, szClassIpEdit); switch (uMsg) { case WM_SETTEXT: // Formats allowed: "n.n.n.n" ; n=="" or 0<=n<=255 // "" ; Clear the text Assert(pData); pch = (TCHAR *)lParam; for (i = 0; i < 4; i++) { int j = 0; if (pch != NULL) { while (*pch && *pch != _W'.') { Assert(*pch >= _W'0' && *pch <= _W'9'); Assert(j < 3); szT[j++] = *pch++; } if (*pch == _W'.') pch++; } szT[j] = 0; // Append a null terminator FSetWindowText(pData->rgChild[i].hwnd, szT); } // for IpEdit_EnableAddButton(hwnd, pData->hwndBtnAdd); return TRUE; case WM_GETTEXT: case WM_GETTEXTLENGTH: // Return the text in a format "n.n.n.n" where n=="" or 0<=n<=255 Assert(pData); pch = szT; for (i = 0; i < 4; i++) { if (i) *pch++ = _W'.'; CchGetWindowText(pData->rgChild[i].hwnd, pch, 2); while (*pch) pch++; } // for Assert(lstrlen(szT) < 16); if (lParam) { Assert(wParam >= 16); lstrcpy((char *)lParam, szT); } return lstrlen(szT); case IpEdit_WM_ISADDRESSVALID: Assert(lParam == 0); // Fall Throuth // case IpEdit_WM_GETADDRESS: Assert(wParam == 0); { DWORD dwIpAddress = 0; BOOL fIpFieldEmpty = FALSE; for (i = 3; i >= 0; i--) { dwIpAddress <<= 8; // Shift to the next byte CchGetWindowText(pData->rgChild[i].hwnd, szT, 4); if (!szT[0]) fIpFieldEmpty = TRUE; AssertSz(atol(szT) <= 255, "You are getting an invalid IP Address"); dwIpAddress |= atol(szT); } if (lParam) *(BOOL *)lParam = fIpFieldEmpty; if (uMsg == IpEdit_WM_GETADDRESS) return dwIpAddress; // Otherwise process message IpEdit_WM_ISADDRESSVALID if (fIpFieldEmpty && (pData->dwFlags & IpEdit_mskfEmptyFieldsValid) == 0) return (BOOL)FALSE; return (BOOL)(dwIpAddress || (pData->dwFlags & IpEdit_mskfZeroIpValid)); } case IpEdit_WM_SETADDRESS: Assert(wParam == 0); ConvertIpAddrToString(lParam, szT); Assert(lstrlen(szT) < LENGTH(szT)); FSetWindowText(hwnd, szT); return 0; case IpEdit_WM_CLEARADDRESS: FSetWindowText(hwnd, NULL); return 0; case WM_SETFOCUS: Assert(pData); SetFocus(pData->rgChild[0].hwnd); InvalidateRect(hwnd, NULL, TRUE); return 0; case WM_ENABLE: Assert(pData); IpEdit_EnableAddButton(hwnd, pData->hwndBtnAdd); // Fall Through // case WM_SETFONT: case EM_SETREADONLY: Assert(pData != NULL); // Forward the message to the children for (i = 0; i < 4; i++) LSendMessage(pData->rgChild[i].hwnd, uMsg, wParam, lParam); break; case WM_COMMAND: Assert(pData); if (pData->dwFlags & IpEdit_mskfNoNofity) return 0; switch(HIWORD(wParam)) { case EN_SETFOCUS: if (pData->dwFlags & IpEdit_mskfHasFocus) return 0; pData->dwFlags |= IpEdit_mskfHasFocus; break; case EN_KILLFOCUS: Assert(pData->dwFlags & IpEdit_mskfHasFocus); // The idea here is to find out who got the focus. If none of // the childs has the focus, then we obviously lost the focus, // so we need to send the notification message if (IsChild(hwnd, GetFocus())) return 0; // Otherwise, we lost the focus pData->dwFlags &= ~IpEdit_mskfHasFocus; break; case EN_CHANGE: // Validate the integer pData->dwFlags |= IpEdit_mskfNoNofity; gGI_dwFlags |= GI_mskfEmptyStringValid; if (!FGetCtrlDWordValue((HWND)lParam, OUT (DWORD *)&i, 0, 255)) { FSetWindowText((HWND)lParam, _W"255"); LSendMessage((HWND)lParam, EM_SETSEL, 0, -1); } pData->dwFlags &= ~IpEdit_mskfNoNofity; IpEdit_EnableAddButton(hwnd, pData->hwndBtnAdd); break; default: return 0; } // switch LSendMessage( GetParent(hwnd), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(hwnd), HIWORD(wParam)), (LPARAM)hwnd); return 0; case UN_KEYDOWN: { // UN_KEYDOWN: Notification message send by the child DWORD dwSelStart; DWORD dwSelEnd; Assert(pData); Assert(IsWindow((HWND)lParam)); switch (wParam) { case VK_HOME: SetFocus(pData->rgChild[0].hwnd); LSendMessage((HWND)lParam, EM_SETSEL, 0, 0); return TRUE; case VK_END: SetFocus(pData->rgChild[3].hwnd); LSendMessage((HWND)lParam, EM_SETSEL, 3, 3); return TRUE; } LSendMessage((HWND)lParam, EM_GETSEL, (WPARAM)&dwSelStart, (LPARAM)&dwSelEnd); if (dwSelStart != dwSelEnd) return 0; if ((wParam == VK_LEFT || wParam == VK_BACK) && dwSelStart == 0) { for (i = 0; i < 4; i++) if (pData->rgChild[i].hwnd == (HWND)lParam) break; Assert(i < 4); if (i) { SetFocus(pData->rgChild[--i].hwnd); if (wParam == VK_LEFT) LSendMessage(pData->rgChild[i].hwnd, EM_SETSEL, 0, -1); } return TRUE; } if (wParam == VK_RIGHT && (int)dwSelEnd == GetWindowTextLength((HWND)lParam)) { for (i = 0; i < 4; i++) if (pData->rgChild[i].hwnd == (HWND)lParam) break; Assert(i < 4); if (i < 3) { SetFocus(pData->rgChild[++i].hwnd); LSendMessage(pData->rgChild[i].hwnd, EM_SETSEL, 0, -1); } return TRUE; } return 0; } case WM_CREATE: { RECT rc; LONG id; Assert(pData == NULL); pData = (IPEDITDATA *)Malloc(sizeof(IPEDITDATA)); Report(pData); if (pData == NULL) return -1; // Attatch the allocated block to the window SetWindowLong(hwnd, ibIpEditData, (LONG)pData); pData->dwFlags = IpEdit_mskfEmptyFieldsValid; pData->hwndBtnAdd = NULL; GetClientRect(hwnd, &rc); Assert(rc.top == 0); Assert(rc.left == 0); int dxEdit = (rc.right - (4 * 3)) / 4; id = GetWindowLong(hwnd, GWL_ID); for (i = 0; i < 4; i++) { pData->rgChild[i].xPos = 2 + (dxEdit + 4) * i; pData->rgChild[i].hwnd = CreateWindow( szClassEdit, NULL, WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL, pData->rgChild[i].xPos, 1, dxEdit, rc.bottom, hwnd, (HMENU)id, hInstanceSave, NULL); LSendMessage(pData->rgChild[i].hwnd, EM_LIMITTEXT, 3, 0); SetWindowLong(pData->rgChild[i].hwnd, GWL_WNDPROC, (LONG)WndProcEditNumber); } //for Assert(((CREATESTRUCT *)lParam)->lpszName != NULL); Trace2(((CREATESTRUCT *)lParam)->lpszName[0] ? mskTraceAlways : mskTraceNone, "\nIpEdit: You should delete caption \"%s\" of control Id=%d.", ((CREATESTRUCT *)lParam)->lpszName, GetDlgCtrlID(hwnd)); } break; case WM_DESTROY: Assert(pData); Free(pData); break; case WM_PAINT: { HDC hdc; Assert(pData); // Let lpfnEditOld paint the control for us... CallWindowProc(lpfnEditOld, hwnd, WM_PAINT, 0, 0); // Then, add the dots hdc = GetDC(hwnd); SetBkMode(hdc, TRANSPARENT); SetTextColor(hdc, clrWindowText); SelectObject(hdc, (HGDIOBJ)(HFONT)LSendMessage(hwnd, WM_GETFONT, 0, 0)); for (i = 1; i < 4; i++) TextOut(hdc, pData->rgChild[i].xPos - 4, 1, _W".", 1); ReleaseDC(hwnd, hdc); } return 0; default: break; } // swtich return CallWindowProc(lpfnEditOld, hwnd, uMsg, wParam, lParam); } // WndProcIpEdit