WinApi c++ MDI관련 질문
글쓴이: 익명 사용자 / 작성시간: 수, 2021/05/05 - 9:36오후
Visual studio 8.0, 32bit에서 Winapi관련 프로그램을 작성하다가 안되는 것이 있어서 여쭤봅니다.
MDI --> Child윈도우를 열고 그 안에 Listview를 만들었습니다.
Listview안에 있는 내용을 주기적(1ms)으로 갱신하도록 하였습니다. 카운트하는 값을 표시했습니다.
그런데, 1줄 업데이트만 할 경우 2400정도에서, 그리고 2줄을 업데이트할 경우 1400정도에서 멈추고 동작하지 않습니다.
올린 소스는 가상모드로 동작한 결과이나, 이전에 일반 모드로 메시지를 날려 업데이트한 경우도 마찬가지였습니다.
MDI를 사용하지 않고 SDI에서 Listview를 올려서 한 경우에는 아무 문제없었습니다..
이유가 무엇인지 알 수 있을까요?
#include <windows.h> #include <commctrl.h> #include "wchar.h" #include "CommCtrl.h" LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM); LRESULT CALLBACK MDIChildProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam); HINSTANCE g_hInst; //Instance Handle HWND hWndMain; //Main frame window handle LPCTSTR lpszClass=TEXT("Measure"); HWND hListTrace; HWND hMDIClient; static int te4=0; wchar_t temp[200]; int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance ,LPSTR lpszCmdParam,int nCmdShow) { HWND hWnd; MSG Message; WNDCLASS WndClass; g_hInst=hInstance; WndClass.cbClsExtra = 0; WndClass.cbWndExtra = 0; WndClass.hbrBackground = (HBRUSH)COLOR_APPWORKSPACE+1;//(COLOR_WINDOW+1); WndClass.hCursor = LoadCursor(NULL,IDC_ARROW); WndClass.hIcon = LoadIcon(NULL,IDI_APPLICATION); WndClass.hInstance = hInstance; WndClass.lpfnWndProc = WndProc; WndClass.lpszClassName = lpszClass; WndClass.lpszMenuName = NULL; WndClass.style = CS_HREDRAW | CS_VREDRAW; RegisterClass(&WndClass); hWnd = CreateWindow(lpszClass, lpszClass, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL,(HMENU)NULL, hInstance, NULL); ShowWindow(hWnd,nCmdShow); hWndMain = hWnd; //UpdateWindow(hWnd); // For MDI Child window class enroll. WndClass.hbrBackground = (HBRUSH)COLOR_APPWORKSPACE+1;//(COLOR_WINDOW+1); WndClass.lpszClassName = L"MDIExamChild"; WndClass.lpfnWndProc = MDIChildProc; WndClass.hIcon = LoadIcon(NULL,IDI_ASTERISK); WndClass.lpszMenuName = NULL; WndClass.cbWndExtra = sizeof(DWORD); // Extra memory. RegisterClass(&WndClass); while (GetMessage(&Message,NULL,0,0)) { if (!TranslateMDISysAccel(hMDIClient, &Message)) { TranslateMessage(&Message); DispatchMessage(&Message); } } return (int)Message.wParam; } #define IDM_WINDOWCHILD 41000 int temp4; LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam) { CLIENTCREATESTRUCT ccs; MDICREATESTRUCT mcs; static HANDLE hTimer; RECT rcClient; int temp4; switch(iMessage) { case WM_CREATE: InitCommonControls(); GetClientRect (hWnd, &rcClient); ccs.hWindowMenu = NULL; ccs.idFirstChild = IDM_WINDOWCHILD; hMDIClient = CreateWindow(TEXT("MDICLIENT"), NULL, WS_CHILD | WS_VSCROLL | WS_HSCROLL | WS_CLIPCHILDREN , 0, 0, 1000, 1000, hWnd,(HMENU)NULL, g_hInst, (LPSTR)&ccs); ShowWindow(hMDIClient, SW_SHOW); /**/ hTimer = (HANDLE)SetTimer(hWnd, 1, 1000, NULL); mcs.szClass = TEXT("MDIExamChild"); mcs.szTitle = TEXT("Child"); mcs.hOwner = g_hInst; mcs.x = mcs.y = CW_USEDEFAULT; mcs.cx = mcs.cy = CW_USEDEFAULT; mcs.style = MDIS_ALLCHILDSTYLES; mcs.lParam = 0; SendMessage(hMDIClient, WM_MDICREATE, 0, (LPARAM)(LPMDICREATESTRUCT)&mcs); break; case WM_TIMER: KillTimer(hWnd, 1); mcs.szClass = TEXT("MDIExamChild"); mcs.szTitle = TEXT("Child"); mcs.hOwner = g_hInst; mcs.x = mcs.y = CW_USEDEFAULT; mcs.cx = mcs.cy = CW_USEDEFAULT; mcs.style = MDIS_ALLCHILDSTYLES; mcs.lParam = 0; SendMessage(hMDIClient, WM_MDICREATE, 0, (LPARAM)(LPMDICREATESTRUCT)&mcs); break; case WM_DESTROY: PostQuitMessage(0); return 0; } return(DefFrameProc(hWnd,hMDIClient,iMessage,wParam,lParam)); //return(DefWindowProc(hWnd,iMessage,wParam,lParam)); } HWND hTrace; LRESULT CALLBACK MDIChildProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam) { LVITEM LI; LVCOLUMN COL; PAINTSTRUCT ps; HDC hdc; TCHAR str[128]; RECT rcClient; // The parent window's client area. time_t mytime; HANDLE hTimer; HFONT hFont=CreateFont(16, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, TEXT("Consolas")); switch (iMessage) { case WM_CREATE: //InitCommonControls(); hTrace = hWnd; GetClientRect (hWnd, &rcClient); SetWindowLong(hWnd, 0, 1); SetWindowPos(hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE); hTimer = (HANDLE)SetTimer(hWnd, 1, 1, NULL); hListTrace = CreateWindow(WC_LISTVIEW, NULL, WS_CHILD | WS_VISIBLE | WS_BORDER | LVS_REPORT | LVS_SHOWSELALWAYS | LVS_OWNERDATA, 0, 0, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top, hWnd, (HMENU)5000, g_hInst, NULL); SendMessage(hListTrace, WM_SETFONT, (WPARAM)hFont, TRUE); SetFocus(hListTrace); SetWindowPos(hListTrace, NULL, 0, 0, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top, SWP_NOZORDER); SetWindowLongPtr(hWnd,0,0); SetWindowPos(hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE); ListView_SetExtendedListViewStyle( hListTrace, LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES ); COL.mask=LVCF_FMT | LVCF_WIDTH | LVCF_TEXT |LVCF_SUBITEM; COL.fmt=LVCFMT_LEFT; COL.cx=100; COL.pszText=TEXT("Time"); // COL.iSubItem=0; ListView_InsertColumn(hListTrace,0,&COL); ListView_SetItemCount(hListTrace,2); return 0; case WM_NOTIFY: LPNMHDR hdr; NMLVDISPINFO *ndi; hdr=(LPNMHDR)lParam; ndi=(NMLVDISPINFO *)lParam; TCHAR tmp[300]; if (hdr->hwndFrom == hListTrace) { switch (hdr->code) { case LVN_GETDISPINFO: if (ndi->item.iSubItem == 0) { if ((ndi->item.mask & LVIF_TEXT) == LVIF_TEXT) { wsprintf(tmp, L"%d", te4); lstrcpy(ndi->item.pszText, tmp); } } else { switch (ndi->item.iSubItem) { case 1: wsprintf(tmp, L"%d", ndi->item.iItem); break;/* case 2: wsprintf(tmp, L"%d", ndi->item.iItem); break; case 3: wsprintf(tmp, L"%s", ndi->item.iItem); break; case 4: wsprintf(tmp, L"%d", ndi->item.iItem); break; case 5: wsprintf(tmp, L"%d", ndi->item.iItem); break;*/ } lstrcpy(ndi->item.pszText, tmp); } return 0; case LVN_ODCACHEHINT: return 0; case LVN_ODFINDITEM: return -1; } } case WM_SIZE: GetClientRect (hWnd, &rcClient); SetWindowPos(hListTrace, NULL, 0, 0, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top, SWP_NOZORDER); return 0; case WM_PAINT: hdc=BeginPaint(hWnd, &ps); wsprintf(str,L"Child window"); TextOut(hdc,0,0,str,lstrlen(str)); EndPaint(hWnd, &ps); return 0; case WM_TIMER: ListView_SetItemCount(hListTrace,2); te4++; break; } return(DefMDIChildProc(hWnd, iMessage, wParam, lParam)); }
Forums:
제목
킬타이머 하고 다시 셋타이머 해야하는거 아닌가요?
그리고 winapi 어디서 쓰는데 있나요?
winapi + mfc + com 망한지 아주 오래된걸로 아는데요
제목
윈도우용 프로그램 개발일은 거의 없고
있어도 거의다 c# 일 겁니다
winapi 공부 괜히 쓸데없는 시간낭비일 가능성이 클듯합니다
물론 c# 에서 지원안하는 필요한 라이브러리가 있다면
불러올 dll 을 winapi 활용해서 만들수도 있는데
보통은 이미 개발된 라이브러리로 만들거나, 없다고 새로
만드는게 아니라 못만드는 걸로 합니다
저기다가 c++ 에다가 mfc 에다가 com 에다가 stl 에다가
c++ 최신에다가.. qt 까지 해버리면... 흠....
다른 예로 누군가 윈도우 95와 아레아한글 열심히 공부해서
사무직 취직한다면 프리젠테이션 만들수 있겠습니까?
SetTimer는 처음에 Child윈도우를 열기위해
SetTimer는 처음에 Child윈도우를 열기위해 사용하고 Kill했으며,
Child윈도우에서 다시 SetTimer해서 동작하게 했습니다.
C#은 아직 접해본 적이 없습니다. 그렇게 편한 것이군요.
질문에 대한 답글을 원했었는데, 조금 다른 답이 올라온 것 같아 C#을 검색하는데, 시간을 보내야겠군요.
현재 다니고 있는 회사에서 사용 가능한 라이센스 내에서 응용할만한 프로그램을 만들고자, Winapi는 사용하고 있습니다. C#은 라이센스 없이도 사용 가능한가요?
제목
차일드에서 settimer 가 안맞는거 같은데요? 메인프레임이나 kill 한데서 하는게 맞는거 같은데.. 저도 뭐 확인할 방법이 없어서 확실친 않습니다.
c# 은 .net 이라는걸(자바가상머신비슷한개념) 개발하는데 최적화된 언어인데요 c# 을 지원하는 ide (visual studio) 가 조건에 따라 유료입니다.
대충 프리랜서나 영세기업은 무료고, 사원수 좀 되는 기업은 유료입니다
다른 ide 중에 mono develop 이라고 있는데 무료긴 한데, 쓰기는 싫으실 수 있습니다
멈추고 동작하지 않는다는 것이, 타이머 이벤트 발생이
멈추고 동작하지 않는다는 것이, 타이머 이벤트 발생이 멈추는 것인가요 아니면 타이머 이벤트는 계속 발생하는데 리스트뷰 갱신만 멈추는 것인가요? 로그를 남겨보면 확인될테고, 현상을 정확히 규명할수록 해결책에 가까워지겠죠.
그리고 좀 다른 얘기지만, SetTimer()에 1ms를 줘도 실제로는 10 ~ 20ms마다 한 번 정도 발생할것입니다. https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-settimer 을 보면, 아무리 작은 값을 줘도 특정 최소값(0x0A == 10ms인듯)으로 설정된다고 나오네요.
정말 1ms 수준의 주기가 필요하다면 멀티미디어 타이머( https://docs.microsoft.com/ko-kr/windows/win32/multimedia/about-multimedia-timers )를 쓰셔야 할 겁니다. 윈도우의 메시지룹에 WM_TIMER를 끼워넣는 SetTimer() 방식이 아닌, 멀티미디어 타이머 자체스레드에서 콜백함수를 호출해주는 식으로 동작한다고 합니다. 저도 써본적은 없고, 아마 아래 페이지가 참고/출발점이 될 듯 하네요.
https://dh8607.tistory.com/185
답변 감사합니다.
답변 감사합니다.
타이머 동작을 정상적으로 이뤄졌습니다. 리스트뷰의 업데이트만 멈추는 것으로 보이다가, 결국에는 뻗어버렸습니다.
Child내에 있는 폰트 [HFONT hFont=CreateFont(16, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, TEXT("Consolas"));]를 전역 쪽으로 옮기니, 해당 문제가 사라졌습니다. 잘 이해는 안되지만, 일단은 이제 업데이트는 잘 됩니다.
알려주신 SetTime관련 자료는 잘 읽어보도록 하겠습니다.
원하는 업데이트 주기가 100ms이상이고, 정확도는 떨어져도 괜찮아서, 멀티미디어 타이머까지는 필요 없을 것 같습니다.
기존코드에서는 MDIChildProc() 호출시마다
기존코드에서는 MDIChildProc() 호출시마다 CreateFont()가 반복적으로 실행됐겠네요. WM_CREATE 처리할때 만든 한 개만 리스트뷰에 쓰고, 그 외의 수많은 호출시엔 쓰지도 않을 폰트오브젝트가 계속 만들어지며 새나가다가 감당 안되면 멈춘 것인가 봅니다.
SDI에서 이런 문제가 생기지 않은건, 아마 반복생성 문제를 부지불식중에 운좋게 피해가신 것 같고요.
댓글 달기