Tutorial 27: Tooltip Control
이번 장에서는, 툴팁 컨트롤에 대해서 설명한다.
메인 소스 | 헤더 파일 | 리소스 | 실행 결과 |
|
툴팁 컨트롤은 작은 사각형태의 윈도우로서, 마우스 포인터가 지정 영역에 위치했을때 화면에 표시된다. 툴팁 윈도우는 문자열도 표시할 수 있다. 이렇게 본다면, 툴팁컨트롤은 상태바와 같은 역할을 하는것 같지만, 유저가 마우스를 클릭하거나 다른 장소에 마우스 포인터를 이동시키면 사라진다는 점이 다르다. 이 툴팁은 대부분 어플리케이션에서 사용하는 툴바 버튼에서 사용하므로, 이미 본적이 있을 것이다. 이러한 툴팁은 툴바 컨트롤에 의해 제공되는 편리한 기능중 하나이다. 만약 다른 윈도우나 컨트롤에서 툴팁을 사용하고 싶다면, 자신만의 독립적인 툴팁컨트롤을 생성할 필요가 있다.
그럼, 이제 툴팁이 어떤 것인지 알았으니, 사용법과 만드는 방법을 설명한다.
- CreateWindowEx 함수로 툴팁컨트롤을 생성
- 마우스의 이동을 감시하는 영역을 정의한다
- 툴팁컨트롤의 영역을 지정한다
- 지정한 영역에서 마우스 메세지와 툴팁컨트롤을 연결 시킨다
그럼 자세히 알아보자.
●툴팁의 생성
툴팁컨트롤은 공통컨트롤중 하나이므로, MASM에서 프로그램이 comctl32.dll과 묵시적으로 링크되게 하기 위해서 InitCommonControls 함수를 호출할 필요가 있다. 이 후, CreateWindowEx 함수를 호출해서 툴팁컨트롤을 생성한다. 전형적인 코드는 다음과 같다.
.data TooltipClassName db "Tooltips_class32", 0 .code ..... invoke InitCommonControls invoke CreateWindowEx, NULL, addr TooltipClassName, NULL, TIS_ALWAYSTIP, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL윈도우 스타일이 TIS_ALWAYSTIP 인것을 주의하기 바란다. 이 스타일을 지정함으로써, 마우스 포인터가 지정한영역에 위치하면, 윈도우 고유의 처리를 하지 않고 툴팁이 표시되게 되는 것이다. 간단히, 이 플래그를 사용하면, 툴팁컨트롤을 등록한 영역에 마우스가 위치하게되면, 윈도우가 비활성화 상태라도 툴팁 윈도우가 표시된다.
CreateWindowEx 함수에서 이외에도 주의 깊게 봐야할 것은,WS_POPUP과WS_EX_TOOLWINDOW를 지정 하지 않아도 된다는 것이다. 왜냐하면, 툴팁컨트롤의 윈도우 프로시저에서 자동으로 붙여주기 때문이다. 또한, 좌표값이나 폭, 높이도 지정할 필요가 없다. 툴팁컨트롤은 표시될 때에 자동으로 조절해서 표시되기 때문이다. 그래서, 4개의 인수는CW_USEDEFAULT로 충분하다. 나머지의 인수에서 특별히 주의할 것은 없다.
●툴팁영역의 지정
이 상태에서 툴팁컨트롤은 생성되었지만, 화면에 표시되지 않는다. 미리 얘기했듯이, 어느영역에 마우스가 위치했을때에 툴팁윈도우가 표시될 것이다.이런 영역을「툴팁영역」이라 부르기로 한다. 툴팁영역은 윈도우의 사각형태의 영역으로서, 툴팁컨트롤은 마우스 포인터를 감시하고 있다. 만약에 마우스 포인터가 툴팁영역에 위치하게 되면, 툴팁윈도우가 표시된다. 그 사각영역은 클라이언트전체 영역일 수도 있고 특정 영역일 수도 있다.
그래서, 툴팁영역에는 2가지 타입이 있다. 하나는 윈도우 전체를 사용하는 것이고, 다른 하나는 윈도우의 작업영역중 특정 영역을 사용하는 것이다.윈도우영역 전체를 툴팁영역으로 지정하는 것은, 보통 버튼이나 에디트 컨트롤등과 같은 컨트롤에서 사용할 때이다. 그런 경우, 툴팁영역의 좌표값, 크기를 지정할 필요는 없다. 윈도우의 작업영역 전체라고 인식되기 때문이다.
차일드 윈도우를 사용하지 않고, 작업영역을 몇가지로 나눠서,작업영역의 특정 구역을 툴팁영역으로 지정해서 사용하려면, 툴팁영역의 좌표값과 크기를 지정해야 한다.툴팁영역을 지정할 때 필요한 TOOLINFO 구조체는 다음과 같이 정의되어 있다.
TOOLINFO STRUCT cbSize DWORD ? uFlags DWORD ? hWnd DWORD ? uId DWORD ? rect RECT <> hInst DWORD ? lpszText DWORD ? lParam LPARAM ? TOOLINFO ENDS
멤버 설명 cbSize TOOLINFO 구조체의 크기로,반드시 설정해야 한다. Windows는 이 값을 체크하지 않기 때문에 제대로 설정하지 않으면 알수 없는 에러가 발생 하게 된다. uFlags 툴팁영역의 속성을 결정하는 플래그. 다음의 플래그를 조합해서 사용한다.
- TTF_IDISHWND (= "ID is hWnd")
이 플래그에 의해, 윈도우 영역 전체를 툴팁영역으로 사용하는 것을 의미한다(위에서 설명한 처음의 방법). 이 플래그를 지정했다면,반드시 이 구조체의uId멤버에 윈도우 핸들을 지정해야만 한다. 이 플래그를 사용하지 않는다면, 두번째 방법으로 사용하는 것을 의미하고, 즉 클라이언트 영역의 일부영역을 툴팁영역으로 사용한다는 의미이다. 이 경우,rect구조체에 툴팁영역을 설정해야 한다.- TTF_CENTERTIP
보통, 툴팁윈도우는 마우스 포인터의 우측하단에 표시되지만, 이 플래그를 지정하게되면, 툴팁윈도우는 마우스 포인터의 위치에 상관없이 툴팁영역의 하단에 표시된다.- TTF_RTLREADING
아랍어나 히브리어 시스템을 사용하는 경우가 아니면 이 플래그는 무시해도 된다. 이는 문자열의 방향을 오른쪽에서 왼쪽으로 정렬되게 한다. 다른 시스템에서는 작동하지 않는다.- TTF_SUBCLASS
이 플래그에 의해, 툴팁컨트롤이 툴팁영역 윈도우를 서브 클래싱한다는 것을 의미한다. 그 결과로, 윈도우에 보내지는 마우스 메세지를 중간에서 가로챌 수 있다. 이 플래그는 유용하지만, 이를 사용할려면, 툴팁컨트롤에 마우스 메세지를 릴레이 한다는 설정을 해줘야 사용이 가능하다.hWnd 툴팁영역을 포함한 윈도우 핸들. TTF_IDISHWND 플래그를 지정하면, Windows는 uId를 윈도우 핸들로 사용하게 되므로 이 필드는 무시된다. 그러나,다음의 경우에 해당된다면 설정해줄 필요가 있다.
- TTF_IDISHWND 플래그를 사용하지 않을 때(즉, 특정영역을 툴팁영역으로 사용할 때)
- lpszText에 LPSTR_TEXTCALLBACK을 지정할 때. 이것은, 툴팁컨트롤이 표시될 때 어떤 문자열을 표시할지를 결정한다. 동적으로 문자열을 갱신하는 툴팁의 기법중 하나로서, 실행시에 문자열을 변경하고 싶다면, lpszText에 LPSTR_TEXTCALLBACK을 지정하면 된다. 툴팁컨트롤은 hWnd 필드의 윈도우에 TTN_NEEDTEXT 통지 메세지를 보내게 된다.
uId 이 필드는 2가지 의미가 있어서, uFlags 멤버에 TTF_IDISHWND의 설정여부에 따라서 용도가 바뀌게 된다.
- TTF_IDISHWND 플래그가 지정되지 않다면 어플리케이션에서 정의되는 툴ID가 된다. 즉, 클라이언트 영역의 일부만 사용하는 것으로서,한개의 클라이언트영역에서, 얼마든지의 여려개의 툴팁영역을(겹치지 않도록 ) 정의할 수 있다. 툴팁컨트롤은 그들을 구별해야 하지만, 같은 윈도우에 있으므로, hWnd 멤버의 윈도우 핸들만으로는 구별이 불가능하다.
따라서, 어플리케이션에서 사용하는 ID가 필요하다. 참고로 이런ID는 당연히 유일해야한다.- TTF_IDISHWND 플래그가 지정되어 있다면, 툴팁영역으로 사용하는 클라이언트영역을 가지는 윈도우의 핸들이 된다. 다만, hWnd 필드가 이미 있는데, 왜 또다시 존재하는지 의문이 들것이다.
lpszText 멤버에 LPSTR_TEXTCALLBACK이 지정되어 있으면, hWnd 멤버가 이미 설정되어있다는 것이다. 그리고, 툴팁문자열을 표시하는 윈도우와 툴팁영역을 포함한 윈도우는 같은곳에 있지 않을 수도 있는 것이다.
rect 툴팁영역의 영역을 지정하는RECT구조체. 이는,hWnd멤버로 지정된 윈도우의 작업영역의 좌상단의 좌표값으로 결정된다. 즉,작업영역의 일부를 툴팁영역으로 만들고 싶다면, 이 구조체를 설정해서 사용하면 된다.
툴팁컨트롤은,TTF_IDISHWND플래그가 설정되어 있다면(즉, 작업영역전체를 툴팁영역으로 사용하는 경우), 이 필드는 무시한다.hInst 툴팁문자열로 사용되는 문자열 리소스를 보유하고 있는 인스턴스 핸들. lpszText멤버에 문자열 리소스 ID를 지정했다면, 툴팁문자열은 문자열 리소스로부터 얻은 문자열을 사용하게 된다. 이는 혼동스럽게 할 수도 있다. 먼저,lpszText멤버에 대한 설명을 읽어 보는것이 도움될 것이다. 툴팁컨트롤은,lpszText에 문자열 리소스 ID가 설정되어 있지 않으면 이 필드를 무시하게 되어 있다. lpszText 이 필드에는 몇가지 의미가 있다.
- LPSTR_TEXTCALLBACK을 지정하면, 툴팁컨트롤은 툴팁윈도우가 문자열을 표시할 수 있도록,hWnd로 지정된 윈도우에TTN_NEEDTEXT 통지 메세지를 전송한다. 이 방법으로 동적으로 툴팁 문자열을 변경할 수 있다.
- 문자열 리소스 ID를 지정했을 경우, 툴팁윈도우에 문자열을 표시할 필요가 있을때, 툴팁컨트롤은 hInst멤버로 지정된 인스턴스의 문자열 테이블에서 문자열을 찾아낸다. 문자열 ID는 16 비트크기므로, 툴팁컨트롤은 상위워드가 0 인지를 검사한다.
이식성을 생각하면 이런 방법은 편리하다. 문자열 리소스는 리소스 스크립트에서 정의되므로, 소스코드를 수정할 필요가 없다. 프로그램에서 버그가 발생할 소지가 없고, 문자열 테이블과 툴팁 텍스트를 수정할 수 있다.- LPSTR_TEXTCALLBACK도 아니고, 상위 워드가 0 도 아니면, 문자열의 포인터라고 인식한다. 가장 간단하지만, 유연성이 부족한 방법이다.
툴팁 컨트롤을 동작시키기 전에 이TOOLINFO구조체에 유효한 값을 설정 해 둘 필요가 있다. 이 구조체에 의해, 어떠한 툴팁을 생성하는지를 지정할 수 있다.
●tooltip 컨트롤에 대한 tool의 등록
TOOLINFO구조체를 설정하면, 툴팁 컨트롤에 알릴 필요성이 있다. 툴팁 컨트롤은 어떤 툴에도 적용할 수 있으므로, 툴팁 컨트롤을 여러 개 실행할 필요는 없다. 툴팁 컨트롤에 툴을 등록하기 위해서는,TTM_ADDTOOL메세지를 툴팁 컨트롤에 전송한다. wParam는 사용하지 않지만,lParam에TOOLINFO구조체의 포인터를 지정해야 한다.
.data? ti TOOLINFO <> ....... .code ....... [fill the TOOLINFO structure] ....... invoke SendMessage, hwndTooltip, TTM_ADDTOOL, NULL, addr ti툴팁 컨트롤이 성공적으로 등록되면, SendMessage함수는 TRUE를 반환하고, 실패한다면 FALSE를 반환한다. 등록을 제거하려면 TTM_DELTOOL메세지를 전송하면 된다.
●툴팁 컨트롤에 마우스 메세지의 릴레이
지금까지의 과정이 완료되면, 툴팁 컨트롤은 어느 영역에서 마우스 메세지를 감시하면 되는지, 또 툴팁 윈도우에 어떤 문자열을 표시하는지를 알고 있다. 이후 작업은, 그 동작의 핵심이 되는 것이다. 툴팁 컨트롤은, 툴 영역에 마우스 포인터가 위치하는 시간을 측정 해서, 어떤 지정된 시간 동안 마우스 포인터가 위치하고 있으면, 툴팁 윈도우를 표시한다는 동작을 하게된다.
툴에 의해 설정된 영역이 다른 윈도우였다면, 툴팁 컨트롤은 어떻게 마우스 메세지를 얻는것이 좋을까.
이는 2가지의 방법이 있다.그 두가지 방법은 툴 영역을 포함한 윈도우와 협력하는 방법과 협력하지 않는 방법이다.
툴을 포함한 윈도우는,TTM_RELAYEVENT메세지를 툴팁 컨트롤에 전송해서 메세지를 릴레이 해야 한다. 이 메세지의lParam에는 툴팁 컨트롤이 릴레이 해야 할 MSG구조체의 포인터를 지정해야 한다. 툴팁 컨트롤은 아래의 마우스 메세지만을 처리하게 되어 있다.
- WM_LBUTTONDOWN
- WM_MOUSEMOVE
- WM_LBUTTONUP
- WM_RBUTTONDOWN
- WM_MBUTTONDOWN
- WM_RBUTTONUP
- WM_MBUTTONUP
이외의 메세지는 모두 무시된다. 그래서, 윈도우 프로시저에는 다음과 같은 switch문을 포함하게 된다.
WndProc proc hWnd:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD ....... .if uMsg==WM_CREATE ............. .elseif uMsg==WM_LBUTTONDOWN || uMsg==WM_MOUSEMOVE || uMsg==WM_LBUTTONUP || uMsg==WM_RBUTTONDOWN || uMsg==WM_MBUTTONDOWN || uMsg==WM_RBUTTONUP || uMsg==WM_MBUTTONUP invoke SendMessage, hwndTooltip, TTM_RELAYEVENT, NULL, addr msg ..........- TOOLINFO구조체의 uFlags멤버에TTF_SUBCLASS플래그를 지정할 수 있다. 이 플래그를 지정하면, 툴팁 컨트롤이 툴을 포함한 윈도우를 서브 클래스할 수가 있게 된다.
그래서, 윈도우간에 협력하지 않고서도 마우스 메세지를 처리할 수 있게 된다. 이 방법은 비교적 간단해서,TTF_SUBCLASS플래그를 지정하면 그만이다. 그러면 툴팁 컨트롤은 모든 메세지를 다루게 된다.지금까지의 설명으로 툴팁 컨트롤은 정상적으로 동작하게 될 것이다. 별도로, 편리한 메세지 릴레이 방법을 몇가지 설명한다.
- TTM_ACTIVATE
툴팁 컨트롤의 enable/disable을 동적으로 조작하고 싶다면, 이 메세지를 사용한다. wParam에 TRUE를 지정하면, 툴팁은 enable 상태가 되고, FALSE를 지정하면 disable 상태가 된다. 추가로 툴팁 컨트롤을 생성했을 때는 enable 상태이다.- TTM_GETTOOLINFO, TTM_SETTOOLINFO
TOOLINFO 구조체의 값을 취득, 변경하고 싶다면 이 메세지를 사용한다. 그 때는 올바른 uId와 hWnd를 지정해야 한다. rect 구조체의 멤버를 변경하고 싶으면,TTM_NEWTOOLRECT메세지를, 툴팁 문자열을 바꾸고 싶다면TTM_UPDATETIPTEXT를 사용한다.- TTM_SETDELAYTIME
이 메세지로서, 툴팁 텍스트를 표시할 때까지의 시간을 지정할 수 있다.
|
아래의 코드는 버튼이 두개 달린 단순한 대화상자 프로그램이다. 클라이언트 영역은, 좌상, 우상, 좌하, 우하, 라는 4구역으로 분할되어 있다. 각각, 독립적인 툴팁 문자열이 있고, 툴로서 기술되고 있다. 2개의 버튼도 툴팁 문자열이 설정되어 있다.
.386 .model flat, stdcall option casemap:none include \masm32\include\windows.inc include \masm32\include\kernel32.inc include \masm32\include\user32.inc include \masm32\include\comctl32.inc includelib \masm32\lib\comctl32.lib includelib \masm32\lib\user32.lib includelib \masm32\lib\kernel32.lib DlgProc proto :DWORD, :DWORD, :DWORD, :DWORD EnumChild proto :DWORD, :DWORD SetDlgToolArea proto :DWORD, :DWORD, :DWORD, :DWORD, :DWORD .const IDD_MAINDIALOG equ 101 .data ToolTipsClassName db "Tooltips_class32", 0 MainDialogText1 db "This is the upper left area of the dialog", 0 MainDialogText2 db "This is the upper right area of the dialog", 0 MainDialogText3 db "This is the lower left area of the dialog", 0 MainDialogText4 db "This is the lower right area of the dialog", 0 .data? hwndTool dd ? hInstance dd ? .code start: invoke GetModuleHandle, NULL mov hInstance, eax invoke DialogBoxParam, hInstance, IDD_MAINDIALOG, NULL, addr DlgProc, NULL invoke ExitProcess, eax DlgProc proc hDlg:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD LOCAL ti:TOOLINFO LOCAL id:DWORD LOCAL rect:RECT .if uMsg==WM_INITDIALOG invoke InitCommonControls invoke CreateWindowEx, NULL, ADDR ToolTipsClassName, NULL,\ TTS_ALWAYSTIP, CW_USEDEFAULT,\ CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL,\ hInstance, NULL mov hwndTool, eax mov id, 0 mov ti.cbSize, sizeof TOOLINFO mov ti.uFlags, TTF_SUBCLASS push hDlg pop ti.hWnd invoke GetWindowRect, hDlg, addr rect invoke SetDlgToolArea, hDlg, addr ti, addr MainDialogText1, id, addr rect inc id invoke SetDlgToolArea, hDlg, addr ti, addr MainDialogText2, id, addr rect inc id invoke SetDlgToolArea, hDlg, addr ti, addr MainDialogText3, id, addr rect inc id invoke SetDlgToolArea, hDlg, addr ti, addr MainDialogText4, id, addr rect invoke EnumChildWindows, hDlg, addr EnumChild, addr ti .elseif uMsg==WM_CLOSE invoke EndDialog, hDlg, NULL .else mov eax, FALSE ret .endif mov eax, TRUE ret DlgProc endp EnumChild proc uses edi hwndChild:DWORD, lParam:DWORD LOCAL buffer[256]:BYTE mov edi, lParam assume edi:ptr TOOLINFO push hwndChild pop [edi]. uId or [edi]. uFlags, TTF_IDISHWND invoke GetWindowText, hwndChild, addr buffer, 255 lea eax, buffer mov [edi]. lpszText, eax invoke SendMessage, hwndTool, TTM_ADDTOOL, NULL, edi assume edi:nothing ret EnumChild endp SetDlgToolArea proc uses edi esi hDlg:DWORD, lpti:DWORD, lpText:DWORD, id:DWORD, lprect:DWORD mov edi, lpti mov esi, lprect assume esi:ptr RECT assume edi:ptr TOOLINFO .if id==0 mov [edi]. rect.left, 0 mov [edi]. rect.top, 0 mov eax,[esi]. right sub eax,[esi]. left shr eax, 1 mov [edi]. rect.right, eax mov eax,[esi]. bottom sub eax,[esi]. top shr eax, 1 mov [edi]. rect.bottom, eax .elseif id==1 mov eax,[esi]. right sub eax,[esi]. left shr eax, 1 inc eax mov [edi]. rect.left, eax mov [edi]. rect.top, 0 mov eax,[esi]. right sub eax,[esi]. left mov [edi]. rect.right, eax mov eax,[esi]. bottom sub eax,[esi]. top mov [edi]. rect.bottom, eax .elseif id==2 mov [edi]. rect.left, 0 mov eax,[esi]. bottom sub eax,[esi]. top shr eax, 1 inc eax mov [edi]. rect.top, eax mov eax,[esi]. right sub eax,[esi]. left shr eax, 1 mov [edi]. rect.right, eax mov eax,[esi]. bottom sub eax,[esi]. top mov [edi]. rect.bottom, eax .else mov eax,[esi]. right sub eax,[esi]. left shr eax, 1 inc eax mov [edi]. rect.left, eax mov eax,[esi]. bottom sub eax,[esi]. top shr eax, 1 inc eax mov [edi]. rect.top, eax mov eax,[esi]. right sub eax,[esi]. left mov [edi]. rect.right, eax mov eax,[esi]. bottom sub eax,[esi]. top mov [edi]. rect.bottom, eax .endif push lpText pop [edi]. lpszText invoke SendMessage, hwndTool, TTM_ADDTOOL, NULL, lpti assume edi:nothing assume esi:nothing ret SetDlgToolArea endp end start
|
메인 다이얼로그를 생성한 후, CreateWindowEx 함수로 툴팁 컨트롤을 생성한다.
invoke InitCommonControls invoke CreateWindowEx, \ NULL, \ ADDR ToolTipsClassName, \ NULL, \ TTS_ALWAYSTIP, \ CW_USEDEFAULT, \ CW_USEDEFAULT, \ CW_USEDEFAULT, \ CW_USEDEFAULT, \ NULL, \ NULL, \ hInstance, \ NULL mov hwndTool, eax그 후, 다이얼로그 박스의 4구역에 대한 툴을 정의한다.
mov id, 0 ; used as the tool ID mov ti.cbSize, sizeof TOOLINFO mov ti.uFlags, TTF_SUBCLASS ; tell the tooltip control to subclass the dialog window. push hDlg pop ti.hWnd ; handle to the window that contains the tool invoke GetWindowRect, hDlg, addr rect ; obtain the dimension of the client area invoke SetDlgToolArea, hDlg, addr ti, addr MainDialogText1, id, addr rectTOOLINFO구조체를 초기화한다. 여기에서는, 작업영역을 4개의 툴에 분할하고 싶기 때문에, 작업영역을 파악할 필요가 있다. 그렇기 때문에,GetWindowRect함수를 호출한다. 다만, 툴팁 컨트롤에 마우스 메세지를 릴레이 하고 싶지 않기 때문에,TIF_SUBCLASS플래그를 지정하고 있다.
SetDlgToolArea함수는 툴의 사각영역을 계산해서, 툴을 툴팁 컨트롤에 등록한다. 복잡한 계산은 하고 싶지 않기 때문에, 균등하게 4개의 영역을 나누는 것으로 한다. 그리고,TOOLINFO구조체의 포인터를 lParam로 지정해서,TTM_ADDTOOL메세지를 툴팁 컨트롤에 전송한다.
invoke SendMessage, hwndTool, TTM_ADDTOOL, NULL, lpti이로서, 4개의 툴이 등록된다. 이제 다음은 버튼 작업을 해보자. 버튼은 ID에 의해 관리할 수도 있지만, 다루기 힘들어지므로, EnumChildWindows API를 호출해서 다이얼로그 박스상의 컨트롤을 모두 열거한 후, 툴팁 컨트롤에 등록하기로 하자. EnumChildWindows함수는 다음과 같다.
EnumChildWindows proto hWnd:DWORD, lpEnumFunc:DWORD, lParam:DWORDhWnd는 부모윈도우의 핸들로, lpEnumFunc는 모든 컨트롤에 대해서 호출하는 EnumChildProc함수의 포인터다. lParam는 어플리케이션을 유일하게 정의하는 값으로,EnumChildProc함수에서 전달받는다. EnumChildProc함수는 다음과 같다.
EnumChildProc proto hwndChild:DWORD, lParam:DWORDhwndChild는 EnumChildWindows함수에 의해서 열거된 윈도우 핸들로서, lParam는EnumChildWindows함수에 전달된 lParam와 같다. 이번 예제에서는, 다음과 같이 EnumChildWIndows함수를 호출하고 있다.
invoke EnumChildWindows, hDlg, addr EnumChild, addr ti다이얼로그 박스의 차일드 컨트롤을 EnumChild함수로 툴팁 컨트롤로 등록하고 싶기 때문에, lParam에 TOOLINFO구조체의 포인터를 전달했다. 이 방법을 사용하지 않으면 ,ti를 전역변수로 정의해야 하며, 그렇지 않으면 버그가 발생할 소지가 매우 높다.
EnumChildWindows함수를 호출하면, Windows는 대화상자의 모든 컨트롤에 대해서,EnumChild함수를 호출하게 되어 있다. 만약에, 대화상자에 2개의 아이템이 있다면, EnumChild함수는 2번 호출 되게 된다.
EnumChild 함수는 TOOLINFO 구조체와 관련이 있는 멤버를 인수로 해서, 툴팁 컨트롤에 상응한 툴을 등록한다.
EnumChild proc uses edi hwndChild:DWORD, lParam:DWORD LOCAL buffer[256]:BYTE mov edi, lParam assume edi:ptr TOOLINFO push hwndChild pop [edi]. uId ; we use the whole client area of the control as the tool or [edi]. uFlags, TTF_IDISHWND invoke GetWindowText, hwndChild, addr buffer, 255 lea eax, buffer ; use the window text as the tooltip text mov [edi]. lpszText, eax invoke SendMessage, hwndTool, TTM_ADDTOOL, NULL, edi assume edi:nothing ret EnumChild endp여기에서는, 작업영역 전체를 처리하는 방식을 사용하고 있는것도 유의하기 바란다. 그래서,uID에 툴을 포함 하는 윈도우의 핸들을 설정 할 필요가 있고, 또, uFlags멤버에TTF_IDISHWND를 지정해야 한다.