WinApi c++ MDI관련 질문

익명 사용자의 이미지

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));
}
이름의 이미지

킬타이머 하고 다시 셋타이머 해야하는거 아닌가요?

그리고 winapi 어디서 쓰는데 있나요?
winapi + mfc + com 망한지 아주 오래된걸로 아는데요

이름의 이미지

윈도우용 프로그램 개발일은 거의 없고
있어도 거의다 c# 일 겁니다

winapi 공부 괜히 쓸데없는 시간낭비일 가능성이 클듯합니다

물론 c# 에서 지원안하는 필요한 라이브러리가 있다면
불러올 dll 을 winapi 활용해서 만들수도 있는데
보통은 이미 개발된 라이브러리로 만들거나, 없다고 새로
만드는게 아니라 못만드는 걸로 합니다

저기다가 c++ 에다가 mfc 에다가 com 에다가 stl 에다가
c++ 최신에다가.. qt 까지 해버리면... 흠....

다른 예로 누군가 윈도우 95와 아레아한글 열심히 공부해서
사무직 취직한다면 프리젠테이션 만들수 있겠습니까?

글쓴이의 이미지

SetTimer는 처음에 Child윈도우를 열기위해 사용하고 Kill했으며,
Child윈도우에서 다시 SetTimer해서 동작하게 했습니다.

C#은 아직 접해본 적이 없습니다. 그렇게 편한 것이군요.
질문에 대한 답글을 원했었는데, 조금 다른 답이 올라온 것 같아 C#을 검색하는데, 시간을 보내야겠군요.

현재 다니고 있는 회사에서 사용 가능한 라이센스 내에서 응용할만한 프로그램을 만들고자, Winapi는 사용하고 있습니다. C#은 라이센스 없이도 사용 가능한가요?

이름의 이미지

차일드에서 settimer 가 안맞는거 같은데요? 메인프레임이나 kill 한데서 하는게 맞는거 같은데.. 저도 뭐 확인할 방법이 없어서 확실친 않습니다.
c# 은 .net 이라는걸(자바가상머신비슷한개념) 개발하는데 최적화된 언어인데요 c# 을 지원하는 ide (visual studio) 가 조건에 따라 유료입니다.
대충 프리랜서나 영세기업은 무료고, 사원수 좀 되는 기업은 유료입니다
다른 ide 중에 mono develop 이라고 있는데 무료긴 한데, 쓰기는 싫으실 수 있습니다

chanik의 이미지

멈추고 동작하지 않는다는 것이, 타이머 이벤트 발생이 멈추는 것인가요 아니면 타이머 이벤트는 계속 발생하는데 리스트뷰 갱신만 멈추는 것인가요? 로그를 남겨보면 확인될테고, 현상을 정확히 규명할수록 해결책에 가까워지겠죠.

그리고 좀 다른 얘기지만, SetTimer()에 1ms를 줘도 실제로는 10 ~ 20ms마다 한 번 정도 발생할것입니다. https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-settimer 을 보면, 아무리 작은 값을 줘도 특정 최소값(0x0A == 10ms인듯)으로 설정된다고 나오네요.

Quote:
uElapse

Type: UINT

The time-out value, in milliseconds.

If uElapse is less than USER_TIMER_MINIMUM (0x0000000A), the timeout is set to USER_TIMER_MINIMUM. If uElapse is greater than USER_TIMER_MAXIMUM (0x7FFFFFFF), the timeout is set to USER_TIMER_MAXIMUM.

정말 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이상이고, 정확도는 떨어져도 괜찮아서, 멀티미디어 타이머까지는 필요 없을 것 같습니다.

chanik의 이미지


기존코드에서는 MDIChildProc() 호출시마다 CreateFont()가 반복적으로 실행됐겠네요. WM_CREATE 처리할때 만든 한 개만 리스트뷰에 쓰고, 그 외의 수많은 호출시엔 쓰지도 않을 폰트오브젝트가 계속 만들어지며 새나가다가 감당 안되면 멈춘 것인가 봅니다.

SDI에서 이런 문제가 생기지 않은건, 아마 반복생성 문제를 부지불식중에 운좋게 피해가신 것 같고요.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.