GetWindowThreadProcessId는 윈도우 핸들에 대한 프로세스 ID와 쓰레드 ID를 얻을수 있다.

윈도우와 관련된 쓰레드ID와 프로세스ID를 얻는다.

● 선언
Function GetWindowThreadProcessId Lib "user32" (ByVal hWnd As Long, lpdwProcessId As Long) As Long

● 인수
hWnd ━ 프로세스나 쓰레드 ID를 알고자 하는 윈도우 핸들
lpdwProcessId ━ 윈도우를 생성한 쓰레드를 포함하는 프로세스 ID 저장할 변수

● 반환

윈도우를 생성시킨 쓰레드 ID

다음은 예제 소스로, 현재 마우스 커서를 클릭 함으로, 클릭시 커서 아래에 있는 윈도우를 포함하는 프로세스의 ID(PID)를 얻어 프로세스를 종료시킨다

다음은 폼 모듈이다.
Option Explicit ' 모든 변수는 선언된 뒤에 사용할 수 있다.

Private Declare Function SetWindowsHookEx Lib "user32" Alias "SetWindowsHookExA" (ByVal idHook As Long, ByVal lpfn As Long, ByVal hmod As Long, ByVal dwThreadId As Long) As Long ' 훅체인에 훅프로시저를 선두에 끼워넣는다, 성공하면 훅프로시저의 핸들을 복귀한다.
Private Declare Function UnhookWindowsHookEx Lib "user32" (ByVal hHook As Long) As Long ' 훅체인에서 훅 핸들이 나타내는 훅 프로시저를 제거한다.
Private Const WH_MOUSE_LL = 14

Private hhkLowLevelMouse As Long  ' 훅 설치가 완료되는경우, 그 훅을 나타내는 핸들

Private Sub Form_Load()
    GetWindowThreadProcessId Me.hwnd, MyPid
    hhkLowLevelMouse = SetWindowsHookEx(WH_MOUSE_LL, AddressOf LowLevelMouseProc, App.hInstance, 0)
End Sub

Private Sub Form_Unload(Cancel As Integer)
    If hhkLowLevelMouse = 0 Then Exit Sub
    UnhookWindowsHookEx hhkLowLevelMouse ' 훅을 체인에서 훅 제거한다.
    hhkLowLevelMouse = 0
End Sub

' 다음은 일반 모듈 소스이다.
Option Explicit ' 모든 변수는 선언된 뒤에 사용할 수 있다.

Public TopWindow As Long

Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long) ' 메모리 내용을 복사한다.
Private Declare Function CallNextHookEx Lib "user32" (ByVal hHook As Long, ByVal nCode As Long, ByVal wParam As Long, lParam As Any) As Long ' 훅체인에서 다음 훅프로시저를 호출한다.
Private Declare Function WindowFromPoint Lib "user32" (ByVal xPoint As Long, ByVal yPoint As Long) As Long
Private Declare Function OpenProcess Lib "kernel32.dll" (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As Long
Private Declare Function TerminateProcess Lib "kernel32.dll" (ByVal hProcess As Long, ByVal uExitCode As Long) As Long
Private Declare Sub CloseHandle Lib "kernel32" (ByVal hPass As Long)
Public Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hwnd As Long, lpdwProcessId As Long) As Long
Private Const PROCESS_TERMINATE As Long = (&H1)
Private Const HC_ACTION = 0
Private Const HWND_TOPMOST = -1
Private Const HWND_NOTOPMOST = -2
Private Const SWP_NOMOVE = &H2
Private Const SWP_NOSIZE = &H1
Private Const WM_LBUTTONDOWN = &H201

Private Type POINTAPI
    X As Long
    Y As Long
End Type

Private Type MSLLHOOKSTRUCT
    pt          As POINTAPI
    mouseData   As Long
    flags       As Long
    time        As Long
    dwExtraInfo As Long
End Type

Public MyPid As Long ' 현재 프로세스의 PID
 
Public Function LowLevelMouseProc(ByVal nCode As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
    Dim p As MSLLHOOKSTRUCT
    Dim Windowhwnd As Long ' 마우스 위치의 윈도우 핸들
    Dim Pid As Long
   
    If (nCode = HC_ACTION) And wParam = WM_LBUTTONDOWN Then
        CopyMemory p, ByVal lParam, Len(p)
        Windowhwnd = WindowFromPoint(p.pt.X, p.pt.Y)
        GetWindowThreadProcessId Windowhwnd, Pid
        If Not Pid = MyPid Then KillByPid Pid ' 현재 프로세스 인경우는 제외
    End If
    LowLevelMouseProc = CallNextHookEx(0, nCode, wParam, ByVal lParam)
End Function

Public Sub KillByPid(ByVal Pid As Long) ' 프로세스 ID에 의해 프로세스를 강제 종료 시킨다
    Dim hwnd As Long
   
    hwnd = OpenProcess(PROCESS_TERMINATE, False, Pid)
    TerminateProcess hwnd, 0&
    CloseHandle hwnd
End Sub

CopyMemory는 지정한 메모리의 한 장소에서 지정한 메모리의 한 장소에 지정한 바이트 만큼을 복사한다.
CopyMemory는 RtlMoveMemory 함수를 호출하여 사용한다

메모리의 지정된 부분을 복사한다.
사용시 Type 데이타형 복사시에 Type데이타를 직접 사용하여도 된다, copymemory a,b,lenb(b)

● 선언
Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
Sub MoveMemory Lib "kernel32" Alias "RtlMoveMemory" (pDest As Any, pSource As Any, ByVal dwLength As Long)
Sub RtlMoveMemory Lib "kernel32" (hpvDest As Any, ByVal hpvSource As Long, ByVal cbCopy As Long)

● 인수

Destination ━ 복사받을 변수, 복사받을 메모리 위치가 기억된 주소 
Source ━ 복사할 변수, 복사할 멤모리 위치가 기억된 주소
Length ━ 복사할 바이트 수

VB의 배열은 SafeArray를 사용한다.
VB의 배열은 구조는
우선 배열은 SafeArray 구조를 가키리키는 메모리를 가리킨다(이것이 포인터)
즉 배열은 SafeArray 구조를 가리키는 포인터에 포인터가 된다.

다음 소스를 보면 VB에서의 배열의 구조와, SafeArray구조를 알수 있다.

다음은 소스의 초기 이미지이다.


초기 이미지에서 배열 내용 표시는 배열에 저장된 내용을 표시하는 것이고
배열 정보 표시 버튼은 배열 정보 즉 SafeArray구조를 메모리에서 복사하여 얻은 정보를 표시한다


다음은 소스이다
' VB에서 배열은 SafeArray이다.
' 배열->SafeArray 가리키는 포인터->SafeArray 구조->실제 데이타
Option Explicit ' 모든 변수는 선언된 뒤에 사용할 수 있다.

Private Declare Function VarPtrArray Lib "msvbvm60.dll" Alias "VarPtr" (Var() As Any) As Long ' 배열의 주소를 얻는다
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal length As Long) ' 메모리 복사

Private TestTable(5, 10) As Long

Private Type SAFEARRAYBOUND
    cElements As Long ' 차원 요소 갯수
    lLbound As Long ' ' 차원 하한값
End Type

Private Type SAFEARRAY
    cDims As Integer ' 테이블의 차원수
    fFeatures As Integer ' 배열 특징,자료형인지, 가변인지, 등
    cbElements As Long ' 배열의 요소의 크기는
    cLocks As Long ' 럭 카운터
    pvData As Long ' 배열의 실제 데이타 시작 위치
    rgsabound(1 To 2) As SAFEARRAYBOUND ' 각 배열 차원의 한계 정보
End Type

Private Sub Command1_Click() ' 테이블 표시
    Dim Row As Long
    Dim Col As Long

    Text1.Text = vbNullString
    For Row = 0 To UBound(TestTable, 1)
        For Col = 0 To UBound(TestTable, 2)
            Text1.Text = Text1.Text & Format(TestTable(Row, Col), "@@@")
        Next Col
        Text1.Text = Text1.Text & vbCrLf
    Next Row
End Sub

Private Sub Command2_Click() ' 테이블 정보 표시
    Dim Safe As SAFEARRAY
    Dim PointerArray As Long
    Dim PointerSafeArray As Long

    Text1.Text = ""
    PointerArray = VarPtrArray(TestTable) ' 배열 시작 주소를 얻는다.
    Text1.Text = Text1.Text & "배열의 시작 주소: " & Hex(PointerArray) & vbCrLf
    CopyMemory PointerSafeArray, ByVal PointerArray, Len(PointerSafeArray)
    Text1.Text = Text1.Text & "SafeArray 포인터 주소: " & Hex(PointerSafeArray) & vbCrLf
    CopyMemory Safe, ByVal PointerSafeArray, Len(Safe)
    Text1.Text = Text1.Text & "배열의 차원은: " & Safe.cDims & vbCrLf
    Text1.Text = Text1.Text & "배열의 특징은: " & Hex(Safe.fFeatures) & vbCrLf
    Text1.Text = Text1.Text & "배열 요소의 크기는: " & Hex(Safe.cbElements) & vbCrLf
    Text1.Text = Text1.Text & "배열 럭 카운터: " & Hex(Safe.cLocks) & vbCrLf
    Text1.Text = Text1.Text & "배열의 실제 데이타 위치는: " & Hex(Safe.pvData) & vbCrLf
    Text1.Text = Text1.Text & "첫번재 요수 주소: " & Hex(VarPtr(TestTable(0, 0))) & vbCrLf
    Text1.Text = Text1.Text & "첫번째 차원 요소 갯수: " & Safe.rgsabound(1).cElements & vbCrLf
    Text1.Text = Text1.Text & "첫번째 차원 요소 하한값: " & Safe.rgsabound(1).lLbound & vbCrLf
    Text1.Text = Text1.Text & "두번째 차원 요소 갯수: " & Safe.rgsabound(2).cElements & vbCrLf
    Text1.Text = Text1.Text & "두번째 차원 요소 하한값: " & Safe.rgsabound(2).lLbound & vbCrLf
End Sub

Private Sub Form_Load()
    Dim Row As Long
    Dim Col As Long

    For Row = 0 To UBound(TestTable, 1)
        For Col = 0 To UBound(TestTable, 2)
            TestTable(Row, Col) = Row * (UBound(TestTable, 2) + 1) + Col + 1
        Next Col
    Next Row
End Sub

SetWindowPos는 윈도우를 크기,위치,Z-위치(3차원 위치)를 지정할 수 있다.

윈도우의 Z위치, 2차원적인 위치, 크기를 설정한다.
● 선언
Function SetWindowPos Lib "user32" (ByVal hwnd As Long, ByVal hWndInsertAfter As Long, ByVal x As Long, ByVal y As Long, ByVal cx As Long, ByVal cy As Long, ByVal wFlags As Long) As Long

인수
hwnd ━ 위치 변경시킬 윈도우 핸들
hWndInsertAfterIMG SRC="_image\_Option.gif"> ━ Z 순서, 즉 윈도우의 앞뒤 상태를 지정한다. 윈도우 핸들을 지정해면, Z위치가 지정된 윈도우 핸들의 윈도우 뒤에 위치하게 된다.
x ━ 윈도우 왼쪽 상단 x축 위치
y ━ 윈도우 왼쪽 상단 y축 위치
cx ━ 윈도우의 폭
cy ━ 윈도우의 높이
wFlags ━ 크기나 위치를 어떤식으로 변경할지 나타내는 플래그

반환
성공 ━ 0이 아닌 값
실패 ━ 0

상수
hWndInsertAfter 사용, Z 순서(윈도우 앞뒤 순서)를 나타낸다.
상수 설명
HWND_BOTTOM 1 최하위
HWND_TOP 0 상위
HWND_NOTOPMOST -2 최상위가 아님
HWND_TOPMOST -1 최상위

wFlags 사용, 크기나 위치지 변경 플래그, or로 연결 가능

상수 설명
SWP_DRAWFRAME SWP_FRAMECHANGED
SWP_FRAMECHANGED &H20 새로운 윈도우(프레임) 스타일 적용을 알리도록 한다. WM_NCCALCSIZE 메시지를 강제적으로 보낸다
SWP_HIDEWINDOW &H80 윈도우를 보이지 않게 한다
SWP_NOACTIVATE &H10 윈도우를 활성화 시키지 않는다
SWP_NOCOPYBITS &H100 클라이언트 영역의 모든 요소를 무시
SWP_NOMOVE &H2 위치를 변경시키지 않는다.
SWP_NOOWNERZORDER &H200 윈도우의 Z위치를 변경하지 않는다
SWP_NOREDRAW &H8 윈도우를 새로 그리지 않는다
SWP_NOREPOSITION SWP_NOOWNERZORDER
SWP_NOSENDCHANGING &H400 WM_WINDOWPOSCHANGING 메시지를 받지 않도록 한다
SWP_NOSIZE &H1 크기를 변경시키지 않는다
SWP_NOZORDER &H4 Z 순서를 유지한다.
SWP_SHOWWINDOW &H40 윈도우를 보이게 한다

다음은 소스의 이미지이다.

상위의 옵션 버튼이 선택된 상태에서 마우스 왼쪽 버튼을 누르게 되면 현재 마우가 위치한 윈도우는 항상 최상위 윈도우가 된다
하위 옵션 버튼이 선택된 상태에서 마우스 왼쪽 버튼을 누르게 되면 현재 마우스가 위치한 윈도우는 Z-위치기 변화되는 일반 윈도우가 된다

다음은 소스이다.
' 폼 소스
' 최상위 윈도우
Option Explicit ' 모든 변수는 선언된 뒤에 사용할 수 있다.

Private Declare Function SetWindowsHookEx Lib "USER32" Alias "SetWindowsHookExA" (ByVal idHook As Long, ByVal lpfn As Long, ByVal hmod As Long, ByVal dwThreadId As Long) As Long ' 훅체인에 훅프로시저를 선두에 끼워넣는다, 성공하면 훅프로시저의 핸들을 복귀한다.
Private Declare Function UnhookWindowsHookEx Lib "USER32" (ByVal hHook As Long) As Long ' 훅체인에서 훅 핸들이 나타내는 훅 프로시저를 제거한다.
Private Const WH_MOUSE_LL = 14

Private hhkLowLevelMouse As Long  ' 훅 설치가 완료되는경우, 그 훅을 나타내는 핸들

Private Sub Form_Load()
    TopWindow = 0
    optTopWindow(TopWindow).Value = True
    hhkLowLevelMouse = SetWindowsHookEx(WH_MOUSE_LL, AddressOf LowLevelMouseProc, App.hInstance, 0)
End Sub

Private Sub Form_Unload(Cancel As Integer)
    If hhkLowLevelMouse = 0 Then Exit Sub
    UnhookWindowsHookEx hhkLowLevelMouse ' 훅을 체인에서 훅 제거한다.
    hhkLowLevelMouse = 0
End Sub

Private Sub optTopWindow_Click(Index As Integer)
    TopWindow = Index
End Sub

' 모듈 소스
Option Explicit ' 모든 변수는 선언된 뒤에 사용할 수 있다.

Public TopWindow As Long

Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long) ' 메모리 내용을 복사한다.
Private Declare Function CallNextHookEx Lib "USER32" (ByVal hHook As Long, ByVal nCode As Long, ByVal wParam As Long, lParam As Any) As Long ' 훅체인에서 다음 훅프로시저를 호출한다.
Private Declare Function WindowFromPoint Lib "USER32" (ByVal xPoint As Long, ByVal yPoint As Long) As Long
Private Declare Function SetWindowPos Lib "USER32" (ByVal hWnd As Long, ByVal hWndInsertAfter As Long, ByVal X As Long, ByVal Y As Long, ByVal cx As Long, ByVal cy As Long, ByVal wFlags As Long) As Long
Private Declare Function GetParent Lib "USER32" (ByVal hWnd As Long) As Long
Private Const HC_ACTION = 0
Private Const HWND_TOPMOST = -1
Private Const HWND_NOTOPMOST = -2
Private Const SWP_NOMOVE = &H2
Private Const SWP_NOSIZE = &H1
Private Const WM_LBUTTONDOWN = &H201

Private Type POINTAPI
    X As Long
    Y As Long
End Type

Private Type MSLLHOOKSTRUCT
    pt          As POINTAPI
    mouseData   As Long
    flags       As Long
    time        As Long
    dwExtraInfo As Long
End Type

Private p As MSLLHOOKSTRUCT

Public Function LowLevelMouseProc(ByVal nCode As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
    Dim Windowhwnd As Long ' 마우스 위치의 윈도우 핸들
    Dim CheckParenthwnd As Long ' 부모 윈도우를 찾기 위함
    Dim MousePoint As POINTAPI ' 마우스의 위치
   
    If (nCode = HC_ACTION) And wParam = WM_LBUTTONDOWN Then
        CopyMemory p, ByVal lParam, Len(p)
        Windowhwnd = WindowFromPoint(p.pt.X, p.pt.Y)
        CheckParenthwnd = Windowhwnd
        While Not CheckParenthwnd = 0
            Windowhwnd = CheckParenthwnd
            CheckParenthwnd = GetParent(Windowhwnd)
        Wend
        SetWindowPos Windowhwnd, IIf(TopWindow = 0, HWND_TOPMOST, HWND_NOTOPMOST), 0&, 0&, 0&, 0&, SWP_NOMOVE Or SWP_NOSIZE
    End If
    LowLevelMouseProc = CallNextHookEx(0, nCode, wParam, ByVal lParam)
End Function

훅에도 여러 종류가 있지만
전역  훅을 사에도
어떤 타입의 훅으 사용하느냐에 따라 훅 타입이 있으며
이들 훅은 각 타입에 따라 훅체인을 이룬다
훅체인은 각 훅 프로시저(훅으 처리하는 프로시저)를 고리처럼 묶어둔 것을 말한다. 훅 배열이라고 할가.

이 훅체인에 훅 프로시저를 추가하기 위해서는 용하기 SetWindowsHookEx를 사용하며
훅체인에서 훅 프로시저를 제거하기 위해선는 UnhookWindowsHookEx를 사용한다.

훅 타입에는 다음과 같은 타입이 있다.

WH_CALLWNDPROC 4 윈도우 프로시저 후킹
WH_CALLWNDPROCRET 12
WH_CBT 5
WH_DEBUG 9
WH_FOREGROUNDIDLE 11
WH_GETMESSAGE 3 GetMsgProc 메세지 큐에 메세지를 감시한다
WH_JOURNALPLAYBACK 1
WH_JOURNALRECORD 0
WH_KEYBOARD 2
WH_KEYBOARD_LL 13 LowLevelKeyboardProc 저수준 키보드 훅을 한다
WH_MOUSE 7 마우스 메세지
WH_MOUSE_LL 14 LowLevelMouseProc 마우스 저수준 훅을 한다
WH_MSGFILTER -1
WH_SHELL 10
WH_SYSMSGFILTER 6

훅체인에 훅 프로시저를 추가하여 메세지를 후킹한다
● 선언 Function SetWindowsHookEx Lib "user32" Alias "SetWindowsHookExA" (ByVal idHook As Long, ByVal lpfn As Long, ByVal hmod As Long, ByVal dwThreadId As Long) As Long

● 인수

idHook ━ 후킹할 메세지 타입
lpfn ━ 후킹 메세지가 전달될 프로시저 주소(AddressOf testHook) 하면 함수의(일반 모듈에 선언된) 주소를 얻을수 있다. 훅에 사용할 함수 형식은 함수명(ByVal nCode As Long, ByVal wParam As Long, ByVal lParam As Long) As Long, 각 인수의 값을 훅타입에 따라 의미가 다르다
hmod ━ 인스턴스 핸들로, lpfn에 전달된 프로시저를 포함한는 DLL의 인스턴스 핸들, 만약 DLL에 프로시적아 있다면 DLL의 인스턴스 핸들을 넘겨주면 되고, 그렇치 않다면 응용프로그램의 핸들(App.Instarance)을 넘겨주면 된다
dwThreadId ━ 쓰레드 식별자, 0이면 호출된 쓰레드를 나타낸다.

반환
성공 ━ 설치된 훅 핸들값
실패 ━ 0

다음은 마우스를 훅한 예이다



다음은 소스이다
' 폼 소스
' 저수준 마우스 전역 훅
Option Explicit ' 모든 변수는 선언된 뒤에 사용할 수 있다.

Private Declare Function SetWindowsHookEx Lib "user32" Alias "SetWindowsHookExA" (ByVal idHook As Long, ByVal lpfn As Long, ByVal hmod As Long, ByVal dwThreadId As Long) As Long ' 훅체인에 훅프로시저를 선두에 끼워넣는다, 성공하면 훅프로시저의 핸들을 복귀한다.
Private Declare Function UnhookWindowsHookEx Lib "user32" (ByVal hHook As Long) As Long ' 훅체인에서 훅 핸들이 나타내는 훅 프로시저를 제거한다.
Private Const WH_MOUSE_LL = 14

Private hhkLowLevelMouse As Long  ' 훅 설치가 완료되는경우, 그 훅을 나타내는 핸들

Private Sub Form_Load()
    Set WindowMessage = Label1
    hhkLowLevelMouse = SetWindowsHookEx(WH_MOUSE_LL, AddressOf LowLevelMouseProc, App.hInstance, 0)
End Sub

Private Sub Form_Unload(Cancel As Integer)
    If hhkLowLevelMouse = 0 Then Exit Sub
    UnhookWindowsHookEx hhkLowLevelMouse ' 훅을 체인에서 훅 제거한다.
    hhkLowLevelMouse = 0
End Sub

'모듈 소스
Option Explicit ' 모든 변수는 선언된 뒤에 사용할 수 있다.

Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long) ' 메모리 내용을 복사한다.
Private Declare Function CallNextHookEx Lib "user32" (ByVal hHook As Long, ByVal nCode As Long, ByVal wParam As Long, lParam As Any) As Long ' 훅체인에서 다음 훅프로시저를 호출한다.

Private Const HC_ACTION = 0 ' 메세지의 일반적인 상태

Private Const WM_LBUTTONDOWN = &H201
Private Const WM_LBUTTONUP = &H202
Private Const WM_MBUTTONDOWN = &H207
Private Const WM_MBUTTONUP = &H208
Private Const WM_MOUSEMOVE = &H200
Private Const WM_MOUSEWHEEL = &H20A
Public Const WM_RBUTTONDOWN = &H204
Private Const WM_RBUTTONUP = &H205

Private Type POINTAPI
    x As Long
    y As Long
End Type

Private Type MSLLHOOKSTRUCT
    pt          As POINTAPI
    mouseData   As Long
    flags       As Long
    time        As Long
    dwExtraInfo As Long
End Type

Public WindowMessage As Label

Dim p As MSLLHOOKSTRUCT

' 키보드 훅 프로시저
Public Function LowLevelMouseProc(ByVal nCode As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
    If (nCode = HC_ACTION) Then
        Select Case wParam
            Case WM_MOUSEMOVE
                WindowMessage.Caption = "마우스가 움직였읍니다"
            Case WM_LBUTTONDOWN
                WindowMessage.Caption = "마우스가 왼쪽 버튼이 눌렸읍니다."
            Case WM_LBUTTONUP
                WindowMessage.Caption = "마우스가 왼쪽 버튼이 눌렸다 놓였읍니다."
            Case WM_RBUTTONDOWN
                WindowMessage.Caption = "마우스가 오른쪽 버튼이 눌렸읍니다."
            Case WM_RBUTTONUP
                WindowMessage.Caption = "마우스가 오른쪽 버튼이 눌렸다 놓였읍니다."
            Case WM_MOUSEWHEEL
                WindowMessage.Caption = "마우스가 휠이 움직였읍니다."
            Case WM_MBUTTONDOWN
                WindowMessage.Caption = "마우스가 가운데 버튼이 눌렸읍니다."
            Case WM_MBUTTONUP
                WindowMessage.Caption = "마우스가 가운데 버튼이 눌렸다 놓였읍니다."
            Case Else
                Debug.Print Hex(wParam)
        End Select
    End If
    CopyMemory p, ByVal lParam, Len(p)
    WindowMessage.Caption = WindowMessage.Caption & " " & p.pt.x & " " & p.pt.y
    LowLevelMouseProc = CallNextHookEx(0, nCode, wParam, ByVal lParam) ' 훅체인에 다음 훅 프로시저를 호출한다.
End Function


CallNextHookEx는 훅체인에서 다음 훅 프로시저를 호출하게 된다

ODBC설정중 시스템 DSN은 레지스트리를 설정하므로서 ODBC 시스템 DSN을 설정할 수 있다.

레지스트리의 HKEY_LOCAL_MACHINE\SOFTWARE\ODBC\ODBC.INI 키값에 설정하면 된다.

ODBC\ODBC.INI 키 아래 ODBC Data Sources에서 문자 값을 새성할 시스템 DSN의 이름을 설정하면 된다.
ODBC\ODBC.INI 키 아래 시스템 DSN과 같은 이름의 키를 생성하고 그 안에 여러 값들을  설정들 하면된다.

실제로 ODBC 관리자(시작-제어판-관리 도구-데이터 원본 (ODBC))로 시스템 DSN을 생성해 보고, 위 키값을 참조하면 설저 값에 어떤 값을 설정할지 알수 있다. vb6에서 레지스트 함수로는 설정할 수 없으므로, API를 사용하면 설정 가능하다.
먼저 비교할 이미지의 크기는 같다는 조건이며, 하나의 픽셀도 다르다면 다른것으로 간주한다.

GetDIBits는 비트맵에 대한 정보,데이타(각 픽셀 데이타)를 지정된 방식에 따라 구성하여 반환한다.

비트맵을 버퍼에 복사한다
● 선언
Function GetDIBits Lib "gdi32" (ByVal aHDC As Long, ByVal hBitmap As Long, ByVal nStartScan As Long, ByVal nNumScans As Long, lpBits As Any, lpBI As BITMAPINFO, ByVal wUsage As Long) As Long

● 인수

aHDC ━ DC 핸들
hBitmap ━ 비트맵 핸들
nStartScan ━ 시작할 스캔 라인
nNumScans ━ 스캔할 라인수
lpBits ━ 비트맵의 각 비트 데이터가 저장될 버퍼
lpBI ━ 비트맵 형식
wUsage ━ 어떤 칼라를 사용할지를 나타낸다

● 반환
성공 ━ lpBits이 Null이 아니면 스캔 라인수, Null이면 0이 아닌 값
실패 ━ 0

상수
wUsage 사용, 어떤 칼라 사용하는지

상수 설명
DIB_RGB_COLORS 0 RGB 색 사용
DIB_PAL_COLORS 1 팔레트의 인덱스 사용(16-bit인덱스)

소스의 간략한 설명이다.
우선 메모리의 비트맵과 DC를 생성하고, 메모리 비트맵에 비교할 첫번째 이미지를 복사하고, 두번째 이미지를 메모리 비트맵과 XOR를 하여서 복하나다. 같은 픽셀의 같은 칼러의 픽셀은 검은색(&H00000000)이 되 버린다. GetDIBs를 사용해서 비트 데이터를 복하한후에 비트 데이터가 다 0이라면 두 이미가 같다는 것이고 0이 아닌 값이 있다면 다른것으로 간주한다.

소스 초기 이미지이다


이미지 비교 버튼을 누르면 두 이미지를 비교한다.


소스이다.
Option Explicit
Option Base 1

Private Declare Function CreateCompatibleDC Lib "gdi32" (ByVal hdc As Long) As Long
Private Declare Function SelectObject Lib "gdi32" (ByVal hdc As Long, ByVal hObject As Long) As Long
Private Declare Function DeleteDC Lib "gdi32" (ByVal hdc As Long) As Long
Private Declare Function BitBlt Lib "gdi32" (ByVal hDCDest As Long, ByVal XDest As Long, ByVal YDest As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hdcSrc As Long, ByVal XSrc As Long, ByVal YSrc As Long, ByVal dwRop As Long) As Long
Private Declare Function CreateCompatibleBitmap Lib "gdi32" (ByVal hdc As Long, ByVal nWidth As Long, ByVal nHeight As Long) As Long
Private Declare Function OleCreatePictureIndirect Lib "olepro32.dll" (PicDesc As Any, RefIID As GUID, ByVal fPictureOwnsHandle As Long, IPic As IPicture) As Long ' 생성된 비트맵을 VB에서 사용가능하도록, 이미지 개체로 만든다.
Private Declare Function CLSIDFromString Lib "ole32" (ByVal str As Long, id As GUID) As Long
Private Declare Function GetObject Lib "gdi32" Alias "GetObjectA" (ByVal hObject As Long, ByVal nCount As Long, lpObject As Any) As Long
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (ByVal Destination As Long, ByVal Source As Long, ByVal length As Long)
Private Declare Function GetDIBits Lib "gdi32" (ByVal aHDC As Long, ByVal hBitmap As Long, ByVal nStartScan As Long, ByVal nNumScans As Long, lpBits As Any, lpBI As BITMAPINFOHEADER, ByVal wUsage As Long) As Long

Private Type GUID ' 이미지 개체 클래스 ID를 저장할 구조체
    Data1 As Long
    Data2 As Integer
    Data3 As Integer
    Data4(7) As Byte
End Type

Private Type PicBmp ' VB에서 이미지 개체를 생성하기 위한 정보 구조체
    Size As Long
    Type As Long
    BitmapHwnd As Long
    hPal As Long
    Reserved As Long
End Type

Private Type BITMAP
    bmType As Long
    bmWidth As Long
    bmHeight As Long
    bmWidthBytes As Long
    bmPlanes As Integer
    bmBitsPixel As Integer
    bmBits As Long
End Type

Private Type BITMAPINFOHEADER
    biSize          As Long
    biWidth         As Long
    biHeight        As Long
    biPlanes        As Integer
    biBitCount      As Integer
    biCompression   As Long
    biSizeImage     As Long
    biXPelsPerMeter As Long
    biYPelsPerMeter As Long
    biClrUsed       As Long
    biClrImportant  As Long
End Type

Private Const SRCCOPY = &HCC0020
Private Const SRCINVERT = &H660046
Private Const BI_RGB = 0
Private Const DIB_RGB_COLORS = 0

Public Function CreateImageDC(sPic As StdPicture) As Long ' 이미지에 대한 DC 생성
    Dim hdcPicture As Long ' 메모리 DC

   hdcPicture = CreateCompatibleDC(ByVal 0&)
   SelectObject hdcPicture, sPic.Handle
   CreateImageDC = hdcPicture
End Function

Private Function CheckBitmapBlack(ByVal ImageHandle As Long, ByVal ImageDC As Long) As Boolean ' 비트맵이 검은색인지를 판별한다. 검은색일경우 True 반환
    Dim BITMAPINFO As BITMAP
    Dim BitmapInfoHeafer As BITMAPINFOHEADER
    Dim CheckData() As Byte
   
    GetObject ImageHandle, LenB(BITMAPINFO), BITMAPINFO
    With BitmapInfoHeafer
        .biSize = LenB(BitmapInfoHeafer)
        .biWidth = BITMAPINFO.bmWidth
        .biHeight = BITMAPINFO.bmHeight
        .biPlanes = 1
        .biBitCount = BITMAPINFO.bmBitsPixel
        .biCompression = BI_RGB
    End With
    GetDIBits ImageDC, ImageHandle, 0, BITMAPINFO.bmHeight, ByVal 0&, BitmapInfoHeafer, DIB_RGB_COLORS ' 이미지의 비트 데이터 크기를얻는다
    ReDim CheckData(BitmapInfoHeafer.biSizeImage)
    GetDIBits ImageDC, ImageHandle, 0, BITMAPINFO.bmHeight, ByVal VarPtr(CheckData(1)), BitmapInfoHeafer, DIB_RGB_COLORS  ' 이미지의 비트 데이터 크기를얻는다
    CheckBitmapBlack = Len(Replace(CheckData, vbNullChar, "")) = 0
End Function

Private Sub cmdImageCompare1_Click() ' 같은 이미지 비교
    Dim Sorhdc As Long ' 소스 DC
    Dim Deshdc As Long ' 대상 DC(메모리)
    Dim DesBitmap As Long ' 대상 비트맵(메모리)
    Dim SizeX As Long ' 첫번째 이미지 박스 비트맵 크기 : X축 픽셀수
    Dim SizeY As Long ' 첫번째 이미지 박스 비트맵 크기 : Y축 픽셀수
    Dim Pic As PicBmp ' 비트맵을 OLE 개체로 생성하기 위한 비트맵 정보
    Dim IID_IDispatch As GUID ' VB에서 사용할 이미지 개체의 GUID
    Dim IPic As IPictureDisp ' VB에서 사용할 이미지 개체

    SizeX = CLng(Me.ScaleX(imgSource11.Picture.Width, vbHimetric, vbPixels)) ' 이미지박스 이지미의 픽셀 크기
    SizeY = CLng(Me.ScaleY(imgSource11.Picture.Height, vbHimetric, vbPixels))

    Sorhdc = CreateImageDC(imgSource11.Picture) ' 이미지에 대한 DC를 생성한다
    Deshdc = CreateCompatibleDC(ByVal 0&) ' 메모리 DC와 메모리 비트맵을 생성한다.
    DesBitmap = CreateCompatibleBitmap(picXor1.hdc, SizeX, SizeY)
    SelectObject Deshdc, DesBitmap ' 메모리 DC와 메모리 비트맵을 연결한다
    BitBlt Deshdc, 0, 0, SizeX, SizeY, Sorhdc, 0, 0, SRCCOPY ' 이미지 박스 이미지를 메모리 비트맵에 복사
    DeleteDC Sorhdc
    Sorhdc = CreateImageDC(imgSource12.Picture) ' 이미지에 대한 DC를 생성한다
    BitBlt Deshdc, 0, 0, SizeX, SizeY, Sorhdc, 0, 0, SRCINVERT ' 이미지 박스 이미지와 메모리 비트맵에 이미지를 XOR로 하여 복사한다
    DeleteDC Sorhdc
   
    labResult1.Caption = IIf(CheckBitmapBlack(DesBitmap, Deshdc), "같음", "다름") ' 두 이미지를 XOR 한 결과가 검은색이면 True를 반환한다
   
    DeleteDC Deshdc

    ' 메모리의 비트맵을 VB에서 사용하는 이미지 개체로 변환
    Call CLSIDFromString(StrPtr("{00020400-0000-0000-C000-000000000046}"), IID_IDispatch) ' vb에서 사용되는 GUID를 구조체에 저장한다.
    With Pic
        .Size = Len(Pic)
        .Type = vbPicTypeBitmap
        .BitmapHwnd = DesBitmap
        .hPal = 0&
    End With
    Call OleCreatePictureIndirect(Pic, IID_IDispatch, 1, IPic)
    Set picXor1.Picture = IPic ' 생성된 이미지 개체 반환
End Sub

Private Sub cmdImageCompare2_Click() ' 다른 이미지 비교
    Dim Sorhdc As Long ' 소스 DC
    Dim Deshdc As Long ' 대상 DC(메모리)
    Dim DesBitmap As Long ' 대상 비트맵(메모리)
    Dim SizeX As Long ' 첫번째 이미지 박스 비트맵 크기 : X축 픽셀수
    Dim SizeY As Long ' 첫번째 이미지 박스 비트맵 크기 : Y축 픽셀수
    Dim Pic As PicBmp ' 비트맵을 OLE 개체로 생성하기 위한 비트맵 정보
    Dim IID_IDispatch As GUID ' VB에서 사용할 이미지 개체의 GUID
    Dim IPic As IPictureDisp ' VB에서 사용할 이미지 개체

    SizeX = CLng(Me.ScaleX(imgSource21.Picture.Width, vbHimetric, vbPixels)) ' 이미지박스 이지미의 픽셀 크기
    SizeY = CLng(Me.ScaleY(imgSource21.Picture.Height, vbHimetric, vbPixels))

    Sorhdc = CreateImageDC(imgSource21.Picture) ' 이미지에 대한 DC를 생성한다
    Deshdc = CreateCompatibleDC(ByVal 0&) ' 메모리 DC와 메모리 비트맵을 생성한다.
    DesBitmap = CreateCompatibleBitmap(picXor2.hdc, SizeX, SizeY)
    SelectObject Deshdc, DesBitmap ' 메모리 DC와 메모리 비트맵을 연결한다
    BitBlt Deshdc, 0, 0, SizeX, SizeY, Sorhdc, 0, 0, SRCCOPY ' 이미지 박스 이미지를 메모리 비트맵에 복사
    DeleteDC Sorhdc
    Sorhdc = CreateImageDC(imgSource22.Picture) ' 이미지에 대한 DC를 생성한다
    BitBlt Deshdc, 0, 0, SizeX, SizeY, Sorhdc, 0, 0, SRCINVERT ' 이미지 박스 이미지와 메모리 비트맵에 이미지를 XOR로 하여 복사한다
    DeleteDC Sorhdc
   
    labResult2.Caption = IIf(CheckBitmapBlack(DesBitmap, Deshdc), "같음", "다름")
   
    DeleteDC Deshdc

    ' 메모리의 비트맵을 VB에서 사용하는 이미지 개체로 변환
    Call CLSIDFromString(StrPtr("{00020400-0000-0000-C000-000000000046}"), IID_IDispatch) ' vb에서 사용되는 GUID를 구조체에 저장한다.
    With Pic
        .Size = Len(Pic)
        .Type = vbPicTypeBitmap
        .BitmapHwnd = DesBitmap
        .hPal = 0&
    End With
    Call OleCreatePictureIndirect(Pic, IID_IDispatch, 1, IPic)
    Set picXor2.Picture = IPic ' 생성된 이미지 개체 반환
End Sub

OleCreatePictureIndirect은 메모리의 이미지 개체를 활용하여 지정된 ID의 OLE 개체를 생성한다.

이미지 개체를 생성한다.
● 선언
Function OleCreatePictureIndirect Lib "olepro32.dll" (PicDesc As PICTDESC, RefIID As GUID, ByVal fPictureOwnsHandle As Long, IPic As IPicture) As Long

인수
PicDesc ━ 이미지 개체 생성시 필요한 정보가 담긴 구조체, PICTDESC 구조체 사용
RefIID ━ 어떤 개체를 만들건지를 나타내는 식별자(16바이트), GUID 구조체 사용
fPictureOwnsHandle ━ True(1)이면 생성된 이미지 개체가 파괴(소멸)될때 주어진 이미지 핸들도 같이 파괴됩니다. False
(0)이면 생성된 이미지 개체가 파괴되도 주어진 이미지 핸들은 파괴되지 않습니다.
IPic ━ 생성한 이미지 개체를 참조(저장)시킬 개체변수(VB에서 사용)

반환
성공 ━ S_OK
실패 ━ E_NOINTERFACE, E_UNEXPECTED

다은은 초기 이미지이다.

두개의 이미지 박스에 이미지가 있다 이 두 개체의 이미지를 합성 하기 위해서, 이미지 합성 버튼을 누르면, 두 이미지 개체의 메모리 DC를 우선 생성하여 이미지와 연결시켜 놓과, 메모리 DC아 메모리 비트맵을 생성하여 메모리 DC에 메모리 비트맵을 할당한다.
합성하기 전의 준비 작업은 끝낱다.
우선 첫번째 이미의 DC을 메모리 DC에 복사하고, 다음 두번째 이미지 DC를 메모리 DC의 적당한 위치에 복사한다. 그러면
두 이미지는 합성된 상태이다. 이 이미지를 OleCreatePictureIndirect을 사용하여 메모리 비트맵을 VB에서 사용할수 있는 이미지 개체를 생성하여 픽처박스에 설정하면 끝니다.

다음은 결과 이미지이다.



다음은 소스이다.
' 두 이미지 박스의 이미지 합성
' 두 이미지 DC를 메모리 DC에 복사 후, 메모리 DC에서 이미지 개체를 얻고, 그 개체를 픽처박스의 이미지로 설정
Option Explicit

Private Declare Function CreateCompatibleDC Lib "gdi32" (ByVal hdc As Long) As Long
Private Declare Function SelectObject Lib "gdi32" (ByVal hdc As Long, ByVal hObject As Long) As Long
Private Declare Function DeleteDC Lib "gdi32" (ByVal hdc As Long) As Long
Private Declare Function BitBlt Lib "gdi32" (ByVal hDCDest As Long, ByVal XDest As Long, ByVal YDest As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hdcSrc As Long, ByVal XSrc As Long, ByVal YSrc As Long, ByVal dwRop As Long) As Long
Private Declare Function CreateCompatibleBitmap Lib "gdi32" (ByVal hdc As Long, ByVal nWidth As Long, ByVal nHeight As Long) As Long
Private Declare Function OleCreatePictureIndirect Lib "olepro32.dll" (PicDesc As Any, RefIID As GUID, ByVal fPictureOwnsHandle As Long, IPic As IPicture) As Long ' 생성된 비트맵을 VB에서 사용가능하도록, 이미지 개체로 만든다.
Private Declare Function CLSIDFromString Lib "ole32" (ByVal str As Long, id As GUID) As Long
Private Type GUID ' 이미지 개체 클래스 ID를 저장할 구조체
    Data1 As Long
    Data2 As Integer
    Data3 As Integer
    Data4(7) As Byte
End Type

Private Type PicBmp ' VB에서 이미지 개체를 생성하기 위한 정보 구조체
    Size As Long
    Type As Long
    BitmapHwnd As Long
    hPal As Long
    Reserved As Long
End Type

Private Const SRCCOPY = &HCC0020

Public Function CreateImageDC(sPic As StdPicture) As Long ' 이미지에 대한 DC 생성
    Dim hdcPicture As Long ' 메모리 DC

   hdcPicture = CreateCompatibleDC(ByVal 0&)
   SelectObject hdcPicture, sPic.Handle
   CreateImageDC = hdcPicture
End Function

Private Sub cmdImageCopy_Click() ' 이미지 복사
    Dim Sorhdc As Long ' 소스 DC
    Dim Sorhdc2 As Long ' 소스 DC
    Dim Deshdc As Long ' 대상 DC(메모리)
    Dim DesBitmap As Long ' 대상 비트맵(메모리)
    Dim SizeX As Long ' 첫번째 이미지 박스 비트맵 크기 : X축 픽셀수
    Dim SizeY As Long ' 첫번째 이미지 박스 비트맵 크기 : Y축 픽셀수
    Dim SizeX2 As Long ' 두번째 이미지 박스의 비트맵 크기 : X축 픽셀수
    Dim SizeY2 As Long ' 두번째 이미지 박스의 비트맵 크기 : Y축 픽셀수
    Dim Pic As PicBmp ' 비트맵을 OLE 개체로 생성하기 위한 비트맵 정보
    Dim IID_IDispatch As GUID ' VB에서 사용할 이미지 개체의 GUID
    Dim IPic As IPictureDisp ' VB에서 사용할 이미지 개체
   
    SizeX = CLng(Me.ScaleX(Image1.Picture.Width, vbHimetric, vbPixels)) ' 첫번째 이미지박스 이지미 픽셀 크기
    SizeY = CLng(Me.ScaleY(Image1.Picture.Height, vbHimetric, vbPixels))
    SizeX2 = CLng(Me.ScaleX(Image2.Picture.Width, vbHimetric, vbPixels)) ' 두번째 이미지박스 이지미 픽셀 크기
    SizeY2 = CLng(Me.ScaleY(Image2.Picture.Height, vbHimetric, vbPixels))
   
    Sorhdc = CreateImageDC(Image1.Picture) ' 이미지에 대한 DC를 생성한다
    Sorhdc2 = CreateImageDC(Image2.Picture) ' 이미지에 대한 DC를 생성한다
    Deshdc = CreateCompatibleDC(ByVal 0&) ' 메모리 DC와 메모리 비트맵을 생성한다.
    DesBitmap = CreateCompatibleBitmap(Sorhdc, SizeX, SizeY)
    SelectObject Deshdc, DesBitmap
   
    BitBlt Deshdc, 0, 0, SizeX, SizeY, Sorhdc, 0, 0, SRCCOPY ' 첫번째 이미지 박스 이미지를 메모리 비트맵에 복사
   
    BitBlt Deshdc, (SizeX - SizeX2) / 2, (SizeY - SizeY2) / 2, SizeX2, SizeY2, Sorhdc2, 0, 0, SRCCOPY ' 두번째 이미지 박스 이미지를 메모리 비트맵에 복사하므로 이미지 합성
   
    DeleteDC Sorhdc
    DeleteDC Sorhdc2
    DeleteDC Deshdc
   
    ' 메모리의 비트맵을 VB에서 사용하는 이미지 개체로 변환
    Call CLSIDFromString(StrPtr("{00020400-0000-0000-C000-000000000046}"), IID_IDispatch) ' vb에서 사용되는 GUID를 구조체에 저장한다.
    With Pic
        .Size = Len(Pic)
        .Type = vbPicTypeBitmap
        .BitmapHwnd = DesBitmap
        .hPal = 0&
    End With
    Call OleCreatePictureIndirect(Pic, IID_IDispatch, 1, IPic)
    Set Picture1.Picture = IPic ' 생성된 이미지 개체 반환
End Sub


이전 예하고 크게 달라진 부분은 없다. 단지 두번째 이미지박스의 이미지에 대한 DC를 생성하여 그 이미지를 첫번째 이미지를 복사한 메모리 DC에 복사하는 코드만 다를뿐이다.

이전 예제를 보면, picture에 개체를 설정한 것이 아니어서, 이미지 복사후 창이 가려졌다 다시 보여지면 복사한 이미가 창에 보이지 않는다. 이것을 해결하기 위해, 메모리 비트맵을 생성하고 메모리에 이미지를 복사한 후에, 비트맵을 OleCreatePictureIndirect 사용하여 VB에서 사용한 개체로 변환 후에 픽처박스에 Picture 속성에 이미지 개체를 설정한 예이다.

지정된 DC와 호완되는 비트맵을 만든다.

● 선언
Function CreateCompatibleBitmap Lib "gdi32" (ByVal hdc As Long, ByVal nWidth As Long, ByVal nHeight As Long) As Long

● 인수

hDC ━ 호완되는 DC 핸들, 즉 어떤 장치의 DC이냐
nWidth ━ 생성할 비트맵의 폭,픽셀
nHeight ━ 생성할 비트맵의 높이,픽셀

● 반환

성공 ━ 생성한 비트맵의 핸들
실패 ━ Null

다음은 초기 이미지다


이미지 복사 버튼을 누르게 되면, 이미지에 대한 메모리 DC를 생성하고 그 DC를 이미지와 연결하고, 다시 메모리 DC와 메모리 비트맵을 생성하고, DC에 비트맵을 연결하고, 이미지를 DC 메모리 DC에 복사한 후에 메모리의 비트맵을 VB에서 사용가능한 이미지 개체로 변환 후에 그 이미지 개체를 픽처박스의 Picture 속성에 할당한다.



소스이다.

' 이미지 DC를 메모리 DC에 복사 후, 메모리 DC에서 이미지 개체를 얻고, 그 개체를 픽처박스의 이미지로 설정
Option Explicit

Private Declare Function CreateCompatibleDC Lib "gdi32" (ByVal hdc As Long) As Long
Private Declare Function SelectObject Lib "gdi32" (ByVal hdc As Long, ByVal hObject As Long) As Long
Private Declare Function DeleteDC Lib "gdi32" (ByVal hdc As Long) As Long
Private Declare Function BitBlt Lib "gdi32" (ByVal hDCDest As Long, ByVal XDest As Long, ByVal YDest As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hdcSrc As Long, ByVal XSrc As Long, ByVal YSrc As Long, ByVal dwRop As Long) As Long
Private Declare Function CreateCompatibleBitmap Lib "gdi32" (ByVal hdc As Long, ByVal nWidth As Long, ByVal nHeight As Long) As Long
Private Declare Function OleCreatePictureIndirect Lib "olepro32.dll" (PicDesc As Any, RefIID As GUID, ByVal fPictureOwnsHandle As Long, IPic As IPicture) As Long ' 생성된 비트맵을 VB에서 사용가능하도록, 이미지 개체로 만든다.
Private Declare Function CLSIDFromString Lib "ole32" (ByVal str As Long, id As GUID) As Long
Private Type GUID ' 이미지 개체 클래스 ID를 저장할 구조체
    Data1 As Long
    Data2 As Integer
    Data3 As Integer
    Data4(7) As Byte
End Type

Private Type PicBmp ' VB에서 이미지 개체를 생성하기 위한 정보 구조체
    Size As Long
    Type As Long
    BitmapHwnd As Long
    hPal As Long
    Reserved As Long
End Type

Private Const SRCCOPY = &HCC0020

Public Function CreateImageDC(sPic As StdPicture) As Long ' 이미지에 대한 DC 생성
    Dim hdcPicture As Long ' 메모리 DC

   hdcPicture = CreateCompatibleDC(ByVal 0&)
   SelectObject hdcPicture, sPic.Handle
   CreateImageDC = hdcPicture
End Function

Private Sub cmdImageCopy_Click() ' 이미지 복사
    Dim Sorhdc As Long ' 소스 DC
    Dim Deshdc As Long ' 대상 DC(메모리)
    Dim DesBitmap As Long ' 대상 비트맵(메모리)
    Dim SizeX As Long ' 비트맵 크기 : X축 픽셀수
    Dim SizeY As Long ' 비트맵 크기 : Y축 픽셀수
    Dim Pic As PicBmp ' 비트맵을 OLE 개체로 생성하기 위한 비트맵 정보
    Dim IID_IDispatch As GUID ' VB에서 사용할 이미지 개체의 GUID
    Dim IPic As IPictureDisp ' VB에서 사용할 이미지 개체
   
    SizeX = CLng(Me.ScaleX(Image1.Picture.Width, vbHimetric, vbPixels)) ' 이지미 픽셀 크기
    SizeY = CLng(Me.ScaleY(Image1.Picture.Height, vbHimetric, vbPixels))
    Sorhdc = CreateImageDC(Image1.Picture) ' 이미지에 대한 DC를 생성한다
    Deshdc = CreateCompatibleDC(ByVal 0&)
    DesBitmap = CreateCompatibleBitmap(Sorhdc, SizeX, SizeY)
    SelectObject Deshdc, DesBitmap
    BitBlt Deshdc, 0, 0, SizeX, SizeY, Sorhdc, 0, 0, SRCCOPY ' 이미지 박스 이미지를 메모리 비트맵에 복사
    DeleteDC Sorhdc
    DeleteDC Deshdc
   
    ' 메모리의 비트맵을 VB에서 사용하는 이미지 개체로 변환
    Call CLSIDFromString(StrPtr("{00020400-0000-0000-C000-000000000046}"), IID_IDispatch) ' vb에서 사용되는 GUID를 구조체에 저장한다.
    With Pic
        .Size = Len(Pic)
        .Type = vbPicTypeBitmap
        .BitmapHwnd = DesBitmap
        .hPal = 0&
    End With
    Call OleCreatePictureIndirect(Pic, IID_IDispatch, 1, IPic)
    Set Picture1.Picture = IPic ' 생성된 이미지 개체 반환
End Sub

픽처박스는 윈도우의 핸들이 있기 때문에 DC를 생성할 수 있다.
그렇치만 이미지박스 같은 경우는 윈도우가 아니기 때문에 DC를 생성할 수 없다.
그러면 이미지 박스의 DC를 어떻게 얻을가.

이미지 박스의 DC는 생성할 수 없으니가, 이미지 박스가 포함하는 이미지는 핸들을 가지고 있으므로
메모리의 DC를 생성하고, 이 DC와 메모리를 연결하면 DC를 사용하여 이미지 박스 안의 이미지를 컨트롤할 수 있다.
메모리의 DC를 생성하기 위해서는 CreateCompatibleDC를 사용하면 DC를 생성할 수 있다.

* DC(Device Context)는 구조체를 말하는 것이며, hDC가 이 구조체를 핸들링 할 수 있는 번호 즉 DC의 핸들이다.

지정된 DC와 호완되는 DC를 메모리에 만든다.
생성한 DC는 DeleteDC로 제거한다.

● 선언

Function CreateCompatibleDC Lib "gdi32" (ByVal hdc As Long) As Long

● 인수
hDC ━ 호완되는 DC 핸들, 즉 어떤 장치의 DC이냐, Null이면 현재 화면과 호완되는 메모리 DC를 생성한다

● 반환
성공 ━ 지정된 DC와 호완되는 메모리에 생성한 DC 핸들
실패 ━ Null

다음은 소스의 초기 이미지이다.


다은 이미지의 DC를 생성해서 그 DC와 이미지를 연결하고, 생성한 DC에서 픽처박스의 DC에 복하는 결과이다. 이미지 복사를 버튼을 클릭하면 픽처박스에 복사된다


소스이다
Option Explicit

Private Declare Function CreateCompatibleDC Lib "gdi32" (ByVal hdc As Long) As Long
Private Declare Function SelectObject Lib "gdi32" (ByVal hdc As Long, ByVal hObject As Long) As Long
Private Declare Function DeleteDC Lib "gdi32" (ByVal hdc As Long) As Long
Private Declare Function BitBlt Lib "gdi32" (ByVal hdcDest As Long, ByVal XDest As Long, ByVal YDest As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hdcSrc As Long, ByVal xSrc As Long, ByVal ySrc As Long, ByVal dwRop As Long) As Long
Private Const SRCCOPY = &HCC0020

Public Function CreateImageDC(sPic As StdPicture) As Long ' 이미지에 대한 DC 생성
    Dim hdcPicture As Long ' 메모리 DC

   hdcPicture = CreateCompatibleDC(ByVal 0&) ' 메모리 DC 생성
   SelectObject hdcPicture, sPic.Handle ' 생성한 DC와 이미지 연결
   CreateImageDC = hdcPicture
End Function

Private Sub cmdImageCopy_Click() ' 이미지 복사
    Dim hdc As Long

    Picture1.ScaleMode = vbPixels
    hdc = CreateImageDC(Image1.Picture) ' 이미지에 대한 DC를 생성한다
    With Picture1
        BitBlt .hdc, 0, 0, .ScaleWidth, .ScaleHeight, hdc, 0, 0, SRCCOPY ' 이미지 복사
    End With
    DeleteDC hdc ' 이미지에 대한 DC 삭제
End Sub

ChangeDisplaySettings는 현재 디스플레이 장치의 해상도를 변경한다.

● 선언
Function ChangeDisplaySettings Lib "User32" Alias "ChangeDisplaySettingsA" (ByVal lpDevMode As Long, ByVal dwflags As Long) As Long

● 인수

lpDevMode ━ 변경하고자 하는 정보가 저장된 구조체, DEVMODE 구조 사용, 아래 표와 같은 상수를 dmFields에 지정할 수 있다
dwflags ━ 그래픽 모드의 변경 방법

다은은 소스의 폼 이미지이다.


바꾸고 싶은 해상도 모드에서 더블 클릭하면 지정행의 해상도 모드로 변경된다
원래대로 버튼은 초기 이미지로 변경한다

다음은 소스이다
Option Explicit

Private Declare Function EnumDisplayDevices Lib "user32" Alias "EnumDisplayDevicesA" (Unused As Any, ByVal iDevNum As Long, lpDisplayDevice As DISPLAY_DEVICE, ByVal dwFlags As Long) As Boolean
Private Declare Function EnumDisplaySettings Lib "user32" Alias "EnumDisplaySettingsA" (ByVal lpszDeviceName As String, ByVal iModeNum As Long, lpDevMode As DEVMODE) As Boolean
Private Declare Function ChangeDisplaySettings Lib "user32" Alias "ChangeDisplaySettingsA" (lpDevMode As Any, ByVal dwFlags As Long) As Long
Private Const CCDEVICENAME = 32
Private Const CCFORMNAME = 32
Private Const ENUM_CURRENT_SETTINGS = -1
Private Const DISPLAY_DEVICE_ACTIVE As Long = &H1
Private Type DISPLAY_DEVICE
    cb           As Long
    DeviceName   As String * 32
    DeviceString As String * 128
    StateFlags   As Long
    DeviceID     As String * 128
    DeviceKey    As String * 128
End Type
Private Type DEVMODE
    dmDeviceName    As String * CCDEVICENAME
    dmSpecVersion   As Integer
    dmDriverVersion As Integer
    dmSize As Integer
    dmDriverExtra As Integer
    dmFields As Long
    dmOrientation As Integer
    dmPaperSize As Integer
    dmPaperLength As Integer
    dmPaperWidth As Integer
    dmScale As Integer
    dmCopies As Integer
    dmDefaultSource As Integer
    dmPrintQuality As Integer
    dmColor As Integer
    dmDuplex As Integer
    dmYResolution As Integer
    dmTTOption As Integer
    dmCollate As Integer
    dmFormName As String * CCFORMNAME
    dmUnusedPadding As Integer
    dmBitsPerPel As Integer
    dmPelsWidth As Long
    dmPelsHeight As Long
    dmDisplayFlags As Long
    dmDisplayFrequency As Long
End Type

Private DisplayDeviceInfo As DISPLAY_DEVICE ' 디스플레이 다바이스 정보 구조체
Private CurrentDisplayInfo As DEVMODE ' 초기 디스플레이 설정 정보 구조체
   
Private Sub Command1_Click() ' 원래대로
    ChangeDisplaySettings CurrentDisplayInfo, 0
End Sub

Private Sub Form_Load()
    Dim DisplayInfo As DEVMODE ' 디스플레이 설정 정보 구조체
    Dim DNum As Long ' 디바이스 인덱스 번호, 디스플레이 모드 인덱스 번호
    Dim Ret As Long ' 반환값
   
    Me.AutoRedraw = True
    mfgMode.SelectionMode = flexSelectionByRow
    Ret = -1 ' 활성중인 디스플레이 장치의 정보를 얻는다.
    DNum = 0
    DisplayDeviceInfo.cb = Len(DisplayDeviceInfo)
    Do
        If Not Ret = -1 Then
            If (DisplayDeviceInfo.StateFlags And DISPLAY_DEVICE_ACTIVE) = DISPLAY_DEVICE_ACTIVE Then
                Exit Do
            End If
            DNum = DNum + 1
        End If
        DisplayDeviceInfo.cb = Len(DisplayDeviceInfo)
        Ret = EnumDisplayDevices(ByVal 0&, DNum, DisplayDeviceInfo, ByVal 0&)
    Loop While Not Ret = 0
   
    Ret = -1 ' 활성중인 디스플레이 장치에 대한 디스플레이 모드를 구한다.
    DNum = 0
    With mfgMode
        .Rows = 2
        .Cols = 5
        .FixedCols = 0
        .FixedRows = 1
       
        .Row = 0
        .Col = 0
        .FixedAlignment(.Col) = flexAlignCenterCenter
        .ColAlignment(.Col) = flexAlignCenterCenter
        .Text = "모드 인덱스 번호"
        .ColWidth(.Col) = Me.TextWidth(.Text) + 100
        .Col = 1
        .FixedAlignment(.Col) = flexAlignCenterCenter
        .ColAlignment(.Col) = flexAlignCenterCenter
        .Text = "수평 해상도"
        .ColWidth(.Col) = Me.TextWidth(.Text) + 100
        .Col = 2
        .FixedAlignment(.Col) = flexAlignCenterCenter
        .ColAlignment(.Col) = flexAlignCenterCenter
        .Text = "수직 해상도"
        .ColWidth(.Col) = Me.TextWidth(.Text) + 100
        .Col = 3
        .FixedAlignment(.Col) = flexAlignCenterCenter
        .ColAlignment(.Col) = flexAlignCenterCenter
        .Text = "색상 비트수"
        .ColWidth(.Col) = Me.TextWidth(.Text) + 100
        .Col = 4
        .FixedAlignment(.Col) = flexAlignCenterCenter
        .ColAlignment(.Col) = flexAlignCenterCenter
        .Text = "모니터 재생율"
        .ColWidth(.Col) = Me.TextWidth(.Text) + 100
        Do
            If Not Ret = -1 Then
                .Rows = DNum + 2
                .Row = .Rows - 1
                .Col = 0
                .Text = DNum
                .Col = 1
                .Text = Format(DisplayInfo.dmPelsWidth, "@@@@")
                .Col = 2
                .Text = Format(DisplayInfo.dmPelsHeight, "@@@@")
                .Col = 3
                .Text = DisplayInfo.dmBitsPerPel
                .Col = 4
                .Text = DisplayInfo.dmDisplayFrequency & "Hz"
                DNum = DNum + 1
            End If
            Ret = EnumDisplaySettings(DisplayDeviceInfo.DeviceName, DNum, DisplayInfo)
        Loop While Not Ret = 0
        Ret = EnumDisplaySettings(DisplayDeviceInfo.DeviceName, ENUM_CURRENT_SETTINGS, CurrentDisplayInfo)
    End With
End Sub

Private Function GetVBStringFromAPIString(ByVal Str As String) As String ' API에서 사용한 문자열이 담겨있는 버퍼에 유용한 문자열만 취한다.
    GetVBStringFromAPIString = Left$(Str, InStr(1, Str, vbNullChar) - 1)
End Function

Private Sub mfgMode_DblClick() ' 디스플레이 설정 변경
    Dim DisplayInfo As DEVMODE ' 디스플레이 설정 정보 구조체
    Dim Ret As Long
   
    With mfgMode
        If Not .RowSel = .FixedRows - 1 Then ' 고정 행이 아닐때
            .Col = 0
            Ret = EnumDisplaySettings(DisplayDeviceInfo.DeviceName, CLng(.Text), DisplayInfo)
            Debug.Print DisplayInfo.dmPelsWidth, DisplayInfo.dmPelsHeight, DisplayInfo.dmBitsPerPel, DisplayInfo.dmDisplayFrequency
            ChangeDisplaySettings DisplayInfo, 0
        End If
    End With
End Sub

EnumDisplaySettings은 지정된 디스플레이 장치에 대한 지정한 모드 인덱스 번호에 해당하는 정보를 얻는다

● 선언
Function EnumDisplaySettings Lib "user32" Alias "EnumDisplaySettingsA" (ByVal lpszDeviceName As String, ByVal iModeNum As Long, lpDevMode As DEVMODE) As Boolean

인수
lpszDeviceName ━ 디스플레이 장치를 지정한다. DISPLAY_DEVICE.DeviceName의 값이나 Null이다, Null이면 현재 디스플레이 장치가 선택된다
iModeNum ━ 정보 타입을 나타내며, 그래픽 해상도 모드 인덱스 번호나, 지정된 상수 값을 지정할 수 있다. 인덱스 번호는 0부터 시작하며 1씩 증가한다
lpDevMode ━ 정보를 저장할 구조체

● 반환

성공 ━ 0이 아닌 값
실패 ━ 0, iModeNum이 디스플레이 디바이스 인덱스 번호보다 클때

● 상수
iModeNum 사용, 정보 타입
상수 설명
ENUM_CURRENT_SETTINGS -1 현재 설정 모드 정보
ENUM_REGISTRY_SETTINGS -2 현재 레지스트리에 저장된 설정 정보

    다음은 소스의 결과 이미지 입니다.



    소스입니다.
    Option Explicit

    Private Declare Function EnumDisplayDevices Lib "user32" Alias "EnumDisplayDevicesA" (Unused As Any, ByVal iDevNum As Long, lpDisplayDevice As DISPLAY_DEVICE, ByVal dwFlags As Long) As Boolean
    Private Declare Function EnumDisplaySettings Lib "user32" Alias "EnumDisplaySettingsA" (ByVal lpszDeviceName As String, ByVal iModeNum As Long, lpDevMode As DEVMODE) As Boolean
    Private Const CCDEVICENAME = 32
    Private Const CCFORMNAME = 32
    Private Const DISPLAY_DEVICE_ACTIVE As Long = &H1
    Private Type DISPLAY_DEVICE
        cb           As Long
        DeviceName   As String * 32
        DeviceString As String * 128
        StateFlags   As Long
        DeviceID     As String * 128
        DeviceKey    As String * 128
    End Type
    Private Type DEVMODE
        dmDeviceName    As String * CCDEVICENAME
        dmSpecVersion   As Integer
        dmDriverVersion As Integer
        dmSize As Integer
        dmDriverExtra As Integer
        dmFields As Long
        dmOrientation As Integer
        dmPaperSize As Integer
        dmPaperLength As Integer
        dmPaperWidth As Integer
        dmScale As Integer
        dmCopies As Integer
        dmDefaultSource As Integer
        dmPrintQuality As Integer
        dmColor As Integer
        dmDuplex As Integer
        dmYResolution As Integer
        dmTTOption As Integer
        dmCollate As Integer
        dmFormName As String * CCFORMNAME
        dmUnusedPadding As Integer
        dmBitsPerPel As Integer
        dmPelsWidth As Long
        dmPelsHeight As Long
        dmDisplayFlags As Long
        dmDisplayFrequency As Long
    End Type

    Private Sub Form_Load()
        Dim DisplayDeviceInfo As DISPLAY_DEVICE ' 디스플레이 다바이스 정보 구조체
        Dim DisplayInfo As DEVMODE ' 디스플레이 설정 정보 구조체
        Dim DNum As Long ' 디바이스 인덱스 번호, 디스플레이 모드 인덱스 번호
        Dim Ret As Long ' 반환값
       
        Me.AutoRedraw = True
        Ret = -1 ' 활성중인 디스플레이 장치의 정보를 얻는다.
        DNum = 0
        DisplayDeviceInfo.cb = Len(DisplayDeviceInfo)
        Do
            If Not Ret = -1 Then
                If (DisplayDeviceInfo.StateFlags And DISPLAY_DEVICE_ACTIVE) = DISPLAY_DEVICE_ACTIVE Then
                    Exit Do
                End If
                DNum = DNum + 1
            End If
            DisplayDeviceInfo.cb = Len(DisplayDeviceInfo)
            Ret = EnumDisplayDevices(ByVal 0&, DNum, DisplayDeviceInfo, ByVal 0&)
        Loop While Not Ret = 0
       
        Ret = -1 ' 활성중인 디스플레이 장치에 대한 디스플레이 모드를 구한다.
        DNum = 0
        With mfgMode
            .Rows = 2
            .Cols = 5
            .FixedCols = 0
            .FixedRows = 1
           
            .Row = 0
            .Col = 0
            .FixedAlignment(.Col) = flexAlignCenterCenter
            .ColAlignment(.Col) = flexAlignCenterCenter
            .Text = "모드 인덱스 번호"
            .ColWidth(.Col) = Me.TextWidth(.Text) + 100
            .Col = 1
            .FixedAlignment(.Col) = flexAlignCenterCenter
            .ColAlignment(.Col) = flexAlignCenterCenter
            .Text = "수평 해상도"
            .ColWidth(.Col) = Me.TextWidth(.Text) + 100
            .Col = 2
            .FixedAlignment(.Col) = flexAlignCenterCenter
            .ColAlignment(.Col) = flexAlignCenterCenter
            .Text = "수직 해상도"
            .ColWidth(.Col) = Me.TextWidth(.Text) + 100
            .Col = 3
            .FixedAlignment(.Col) = flexAlignCenterCenter
            .ColAlignment(.Col) = flexAlignCenterCenter
            .Text = "색상 비트수"
            .ColWidth(.Col) = Me.TextWidth(.Text) + 100
            .Col = 4
            .FixedAlignment(.Col) = flexAlignCenterCenter
            .ColAlignment(.Col) = flexAlignCenterCenter
            .Text = "모니터 재생율"
            .ColWidth(.Col) = Me.TextWidth(.Text) + 100
            Do
                If Not Ret = -1 Then
                    .Rows = DNum + 2
                    .Row = .Rows - 1
                    .Col = 0
                    .Text = DNum
                    .Col = 1
                    .Text = Format(DisplayInfo.dmPelsWidth, "@@@@")
                    .Col = 2
                    .Text = Format(DisplayInfo.dmPelsHeight, "@@@@")
                    .Col = 3
                    .Text = DisplayInfo.dmBitsPerPel
                    .Col = 4
                    .Text = DisplayInfo.dmDisplayFrequency & "Hz"
                    DNum = DNum + 1
                End If
                Ret = EnumDisplaySettings(DisplayDeviceInfo.DeviceName, DNum, DisplayInfo)
            Loop While Not Ret = 0
        End With
    End Sub

    Private Function GetVBStringFromAPIString(ByVal Str As String) As String ' API에서 사용한 문자열이 담겨있는 버퍼에 유용한 문자열만 취한다.
        GetVBStringFromAPIString = Left$(Str, InStr(1, Str, vbNullChar) - 1)
    End Function

    EnumDisplayDevices는 디스플레이 장치에 대한 정보를 얻습니다.

    지정한 디스플레이 장치 인덱스 번호에 해당하는 디스플레이 장치의 정보를 얻는다
    ● 선언
    Function EnumDisplayDevices Lib "user32" Alias "EnumDisplayDevicesA" (Unused As Any, ByVal iDevNum As Long, lpDisplayDevice As DISPLAY_DEVICE, ByVal dwFlags As Long) As Boolean

    ● 인수

    Unused ━ 디바이스 명, Null이면 iDevNum에 해당하는 디바이스 정보를 얻는다
    iDevNum ━ 디스플레이 디바이스의 인덱스 번호, 디스플레이 디바이스 인덱스 번호는 0번 부터 시작하여 1씩 증가하여 부여된다
    lpDisplayDevice ━ 디스플레이 디바이스 정보가 담길 구조체
    dwFlags

    ● 반환

    성공 ━ 0이 아닌 값
    실패 ━ 0, iDevNum이 디스플레이 디바이스

    소스의 실행 이미지 입니다.


    소스입니다.
    Option Explicit

    Private Declare Function EnumDisplayDevices Lib "user32" Alias "EnumDisplayDevicesA" (Unused As Any, ByVal iDevNum As Long, lpDisplayDevice As DISPLAY_DEVICE, ByVal dwFlags As Long) As Boolean
    Private Type DISPLAY_DEVICE
        cb           As Long
        DeviceName   As String * 32
        DeviceString As String * 128
        StateFlags   As Long
        DeviceID     As String * 128
        DeviceKey    As String * 128
    End Type
    Private Const DISPLAY_DEVICE_ACTIVE As Long = &H1
    Private Const DISPLAY_DEVICE_PRIMARY_DEVICE As Long = &H4

    Private Sub Form_Load()
        Dim Info As DISPLAY_DEVICE ' 다바이스 정보 구조체
        Dim DNum As Long ' 디바이스 인덱스 번호
        Dim Ret As Long ' 반환값
       
        Me.AutoRedraw = True
        Ret = -1
        Info.cb = Len(Info)
        While Not Ret = 0
            If Not Ret = -1 Then
                Me.Print "디스플레이 장치 번호:" & DNum
                Me.Print "디스플레이 아답터명:" & GetVBStringFromAPIString(Info.DeviceString)
                Me.Print "디스플레이 ID:" & GetVBStringFromAPIString(Info.DeviceName)
                Me.Print "현재 활성중인 디스플레이:" & IIf((Info.StateFlags And DISPLAY_DEVICE_ACTIVE) = DISPLAY_DEVICE_ACTIVE, True, False)
                Me.Print "첫번째 디스플레이 장치:" & IIf((Info.StateFlags And DISPLAY_DEVICE_PRIMARY_DEVICE) = DISPLAY_DEVICE_PRIMARY_DEVICE, True, False)
                DNum = DNum + 1
            End If
            Info.cb = Len(Info)
            Ret = EnumDisplayDevices(ByVal 0&, DNum, Info, ByVal 0&)
        Wend
    End Sub

    Private Function GetVBStringFromAPIString(ByVal Str As String) As String
        GetVBStringFromAPIString = Left$(Str, InStr(1, Str, vbNullChar) - 1)
    End Function

    vb6에서 메세지를 얻기 위해서는 후킹을 사용해야 한다.
    그중 가장 간단한 방법이 서브 클래싱을 사용한다.
    서브 클래싱은, 지정된 윈도우에 할당된 원래의 윈도우 프로시저(윈도우에 전달되는 모든 메시지를 처리하는 윈도우에 할당된 프로시저를 말한다)를 사용자가 지정한 프로시저로 바꾸어 주면 된다. 그러면 윈도우에 전달된 메세지는 사용자가 지정한 프로시저로 통과 되므로 윈도우에 전달되는 윈도우 메세지를 확인할수 있다.

    윈도우 프로시저 형식은 다음과 같다.
    프로시저명 (ByVal hWnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
    인수
    hWnd ━ 윈도우 핸들
    Msg ━ 전달된 윈도우 메세지
    wParam ━ 메세지와 같이 전달된 wParam값
    lParam ━ 메세지와 같이 전달된 lParam값
    이다.

    다음은 마우스의 휠 버튼이 움직이게 되면 해당 윈도우에 WM_MOUSEWHEEL 윈도우 메세지가 전달된다.
    WM_MOUSEWHEEL와 함께 같이 전달되는 wParam과 lPram값은 다음과 같다.
    ● 선언
    WM_MOUSEWHEEL = &H20A
     
    wparam
    상위 워드 : 휠이 얼마나 움직였는지를 나타낸다. 이값은 120으로 나누어 나머지가 0인 값이다. 나누어진 값이 양수면 휠이 앞쪽으로 회전된것이다. 음수이면 뒤쪽으로 회전된것이다.
    하위 워드 : 어떤 키가 눌렸는지를 나타낸다.

    lparam
    상위 워드는 화면을 상대로한 X좌표
    하위 워드는 화면을 상대로한 Y좌표

    다음은 소스이다. 폼에 수직이나 수평 스크롤바를 하나 올린다.

    폼 모듈
    ' 서브클래싱을 사용한 휠버튼 후킹
    Option Explicit

    Private Sub Form_Activate()
        vscFeelTest.SetFocus
    End Sub

    Private Sub Form_Load()
        vscFeelTest.Max = 100
        Call WheelHook(vscFeelTest.hWnd, ObjPtr(vscFeelTest))
    End Sub

    Private Sub Form_Unload(Cancel As Integer)
      Call WheelUnHook(vscFeelTest.hWnd)
    End Sub

    Public Sub MouseWheel(ByVal MoveValue As Long)
        Dim lngMove As Long
       
        MoveValue = MoveValue * -1
        With vscFeelTest
            lngMove = .Value + MoveValue
            If lngMove > .Max Then lngMove = .Max
            If lngMove < .Min Then lngMove = .Min
            .Value = lngMove
        End With
    End Sub

    일반 모듈
    Option Explicit

    Private Declare Function GetProp Lib "user32.dll" Alias "GetPropA" (ByVal hWnd As Long, ByVal lpString As String) As Long
    Private Declare Function SetProp Lib "user32.dll" Alias "SetPropA" (ByVal hWnd As Long, ByVal lpString As String, ByVal hData As Long) As Boolean
    Private Declare Function SetWindowLong Lib "user32.dll" Alias "SetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
    Private Declare Function RemoveProp Lib "user32.dll" Alias "RemovePropA" (ByVal hWnd As Long, ByVal lpString As String) As Long
    Private Declare Function CallWindowProc Lib "user32.dll" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, ByVal hWnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
    Private Const StrPrevWndProc As String = "PrevWndProc"
    Private Const StrObj As String = "Obj"
    Private Const GWL_WNDPROC = -4
    Private Const WHEEL_DELTA = 120
    Private Const WM_MOUSEWHEEL = &H20A

    Private Function WindowProc(ByVal Lwnd As Long, ByVal Lmsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
        Dim Rotation As Integer ' 회전 방향과 회전수
        Dim lngObj As Long
        Dim obj As Object

        Select Case Lmsg ' 메세지는
            Case WM_MOUSEWHEEL
                CopyMemory VarPtr(Rotation), VarPtr(wParam) + 2, 2 ' 회전수를 얻는다
                Rotation = Rotation / WHEEL_DELTA
                If Not Rotation = 0 Then ' 휠 버튼이 돌아가다면
                    lngObj = GetProp(Lwnd, StrObj)
                    CopyMemory VarPtr(obj), VarPtr(lngObj), 4
                    obj.Parent.MouseWheel Rotation
                    lngObj = 0
                    CopyMemory VarPtr(obj), VarPtr(lngObj), 4
                End If
        End Select
        WindowProc = CallWindowProc(GetProp(Lwnd, "PrevWndProc"), Lwnd, Lmsg, wParam, lParam)
    End Function

    Public Sub WheelHook(ByVal hWnd As Long, ByVal obj As Long) ' 서브 클래싱 시작
        SetProp hWnd, StrPrevWndProc, SetWindowLong(hWnd, GWL_WNDPROC, AddressOf WindowProc) ' 개체의 윈도우 프로시저 이전 핸들
        SetProp hWnd, StrObj, obj ' 개체 주소
    End Sub

    Public Sub WheelUnHook(ByVal hWnd As Long) ' 서브 클래싱 이전 상태로
        SetWindowLong hWnd, GWL_WNDPROC, RemoveProp(hWnd, StrPrevWndProc)
        RemoveProp hWnd, StrObj
    End Sub

    LoadCursorFromFile을 사용하면 커서 파일 *.cur이나 *.ani 파일에서 마우스 커서를 로드하고, 그 마우스 커서의 핸들을 얻는다.
    마우스 커서로 지정한 것은 아니고, 메모리로 마우스 커서 이미지를 로드하고 그 핸들을 얻는 것이다.

    지정된 파일로부터(*.cur,*.ani) 마우스 커서를 로드한다

    ● 선언
    Function LoadCursorFromFile Lib "user32" Alias "LoadCursorFromFileA" (ByVal lpFileName As String) As Long

    ● 인수

    lpFileName ━ 마우스 커서 파일명(*.cur,*.ani)

    ● 반환

    성공 ━ 로드한 커서의 핸들
    실패 ━ Null


    다음은 소스이다.
    ' 애니메이션 커서(*.ani) 파일 사용하기
    Option Explicit

    DefLng A-Z ' a에서 z로 시작하는 변수를 Long 형으로 한다

    Private PreviousCoursorHandle  ' 이전 커서 핸들
    Private AniCursorHandle  ' 에니메이션 커서 핸들

    Private Declare Function SetCapture Lib "user32" (ByVal hwnd As Long) As Long
    Private Declare Function ReleaseCapture Lib "user32" () As Long
    Private Declare Function LoadCursorFromFile Lib "user32" Alias "LoadCursorFromFileA" (ByVal lpFileName As String) As Long
    Private Declare Function SetCursor Lib "user32" (ByVal hCursor As Long) As Long

    Private Sub cmdEndAni_Click() ' 원래 커서로 복귀
        Call ReleaseCapture ' 캡처 중지
        Call SetCursor(PreviousCoursorHandle) ' 원래 커서로
        cmdEndAni.Enabled = False
        cmdStartAni.Enabled = True
    End Sub

    Private Sub cmdEndAni_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
        cmdEndAni_Click
    End Sub

    Private Sub cmdStartAni_Click() ' 에니메이션 커서 지정
        Call SetCapture(cmdEndAni.hwnd) ' 폼을 벋어나도 마우스 커서 움직임을 얻도록 한다, 따라서 폼을 벋어나도 마우스 커서가 변경되지 않도록 한다
        PreviousCoursorHandle = SetCursor(AniCursorHandle) ' 커서를 로드한 커서로 지정한다
        cmdEndAni.Enabled = True
        cmdStartAni.Enabled = False
    End Sub

    Private Sub Form_Load()
        AniCursorHandle = LoadCursorFromFile(App.Path & "\macwait.ani")
        cmdEndAni.Enabled = False
    End Sub

    MakeSureDirectoryPathExists는 주어진 절대 경로에 포함된 모든 디렉토리를 검사하여 그 디렉토리가 존재하면 생성하지 않고, 존재 하지 않는 디렉토리라면 생성합니다.
    c:\a\b\c\d 라는 절대 경로를 인수로 주었을때 c:\a와 c:\a\b 디렉토리는 존재한다고 하면
    c:\a\b\c와 c:\a\b\c\d 디렉토리를 생성합니다.

    주어진 절대 경로에 포함된 디렉토리중 존재하지 않는 경로는 모두 생성한다

    ● 선언
    Private Declare Function MakeSureDirectoryPathExists Lib "imagehlp.dll" (ByVal DirPath As String) As Boolean

    ● 인수

    DirPath ━ 절대 경로, 마지막 문자는 \로 끝나야 한다.

    ● 반환

    성공 ━ True
    실패 ━ False

    waveOutSetVolume은 마스터 볼륨이 아닌 웨이브의 볼륨을 조정한다.
    따라서 웨이브 볼륨이 아닌 다른 장치들의 볼륨은 그대로이다.

    웨이브 출력의 볼륨을 조정한다
    ● 선언
    Private Declare Function waveOutSetVolume Lib "winmm.dll" (ByVal uDeviceID As Long, ByVal dwVolume As Long) As Long

    ● 인수

    uDeviceID ━ waveform-audio 장치 핸들
    dwVolume ━ 하위워드는 왼쪽 체널 볼륨, 상위워드는 오른쪽 채널 볼륨, 각 채널의 볼륨 값은 0에서 &hffff까지이다.

    ● 반환

    성공 ━ MMSYSERR_NOERROR
    실패 ━ 에러 원인

    소스 이미지이다.


    라인과 커맨드 버튼 컨트롤을 이용해서 슬라이더를 만들었다. 슬라이더바를 움직이면 웨이브 출력 장치의 볼륨을 조정한다.

    다음은 소스이다.
    ' 마스터 볼륨이 아니라 웨이브 볼륨을 조정한다.
    ' 공식은 이렇다. 슬라이더 위치:볼륨값 = 슬라이더 최대 위치:볼륨 최대값
    Option Explicit

    Private Const MMSYSERR_NOERROR = 0
    Private Const MAXVOLUME = &HFFFF&
    Private Declare Function waveOutGetVolume Lib "winmm.dll" (ByVal uDeviceID As Long, lpdwVolume As Long) As Long
    Private Declare Function waveOutGetNumDevs Lib "winmm.dll" () As Long
    Private Declare Function waveOutSetVolume Lib "winmm.dll" (ByVal uDeviceID As Long, ByVal dwVolume As Long) As Long
    Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (ByVal Destination As Any, ByVal Source As Any, ByVal Length As Long)
    Private CenterSliderPos As Long '  슬라이더의 중심 위치
    Private FirstSliderPos As Long ' 슬라이더 처음 위치
    Private LastSliderPos As Long ' 슬라이더 마지막 위치
    Private FirstLinePos As Long ' 라인의 처음 위치
    Private LastLinePos As Long ' 라인의 마지막 위치
    Private MaxSlider As Long ' 슬라이더의 최대값
    Private InitSlider As Long ' 슬라이더 처음 위치
    Private MaxLine As Long ' 라인의 최대값
    Private PrePos As Long

    Private Sub cmdSlider_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single) ' 슬라이드 드래그 처리
        If Not Button = vbLeftButton Then Exit Sub
        PrePos = Y
    End Sub

    Private Sub cmdSlider_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
        Dim Pos As Long
        If Not Button = vbLeftButton Then Exit Sub
        Pos = cmdSlider.Top + Y - PrePos
        If Pos < FirstSliderPos Then Pos = FirstSliderPos
        If Pos > LastSliderPos Then Pos = LastSliderPos
        cmdSlider.Top = Pos
        SetVolume (GetSliderVolume)
    End Sub

    Private Sub Form_Load()
        If Not HasWaveOutDevice Then ' 웨이브 출력 장치 존재 유무 확인
            MsgBox "사운드 카드가 없거나, 웹이브 출력장치가 없는 사운드 카으입니다."
            Unload Me
            Exit Sub
        End If
        CenterSliderPos = cmdSlider.Height / 2
        FirstLinePos = linLimit.Y1
        LastLinePos = linLimit.Y2
        FirstSliderPos = FirstLinePos - CenterSliderPos
        LastSliderPos = LastLinePos - CenterSliderPos

        linLimit.X1 = Me.Width / 2 ' 컨트롤 위치
        linLimit.X2 = linLimit.X1
        cmdSlider.Left = linLimit.X1 - cmdSlider.Width / 2
        cmdSlider.Top = FirstSliderPos
       
        MaxSlider = linLimit.Y2 - linLimit.Y1
        MaxLine = linLimit.Y2 - linLimit.Y1
        InitSlider = cmdSlider.Height / 2 - linLimit.Y1
        SetSliderVolume (GetVolume)
    End Sub

    Private Function HasWaveOutDevice() As Boolean ' 시스템에 웨이브 출력 장치의 유무 검사
        Dim HowNumDevice As Long ' 웨이브 출력 장치 갯수
        Dim DevCount As Long ' 웨이브 출력 장치 카운트
        Dim UseCount As Long ' 사용할 수 있는 웨이브 출력 장치 카운트
        Dim DummyVolume As Long ' 의미없는 볼륨값
       
        HasWaveOutDevice = False
        HowNumDevice = waveOutGetNumDevs() ' 웨이브 출력 장치 갯수를 얻는다.
        HasWaveOutDevice = HowNumDevice
        If HasWaveOutDevice Then ' 웨이브 출력 장치를 가지고 있다면
            For DevCount = 0 To HowNumDevice - 1
                If MMSYSERR_NOERROR = waveOutGetVolume(DevCount, DummyVolume) Then
                    UseCount = UseCount + 1
                End If
            Next DevCount
            HasWaveOutDevice = UseCount
        End If
    End Function

    Private Function GetVolume() As Long ' 웨이브 출력 장치의 불륨을 얻는다.
        Call waveOutGetVolume(0, GetVolume)
        GetVolume = GetVolume And MAXVOLUME
    End Function
    Private Sub SetVolume(ByVal Volume As Long) '웨이브 출력 장치의 볼륨을 지정한다.
        CopyMemory VarPtr(Volume) + 2, VarPtr(Volume), 2
        Call waveOutSetVolume(0, Volume)
    End Sub

    Private Function GetSliderVolume() As Long ' 슬라이더 값을 읽는다. 볼륨 값을 반환
        Dim SliderPos As Long ' 라인에서 슬라이더 위치
       
        SliderPos = cmdSlider.Top + InitSlider
        GetSliderVolume = SliderPos * MAXVOLUME / MaxLine
    End Function

    Private Sub SetSliderVolume(ByVal Volume As Long) ' 슬라이더 값을 설정한다.
        cmdSlider.Top = (Volume * MaxLine / MAXVOLUME) - InitSlider
    End Sub

    단 조건이 있다. 사운드 카드에 하나의 웨이브 출력 장치만 존재해야만 사운드 카드 수와, 웨이브 출력 장치 수가 일치한다.
    만약 그렇치 않고 사운드 카드에 2개 이상의 웨이브 출력 장치가 있다면 이 방법으로는 사운드 카드수는 알 수 없다.

    웨이브 출력 장치의 갯수를 구한다
    ● 선언
      Private Declare Function waveOutGetNumDevs Lib "winmm.dll" () As Long
     
    반환
    성공 ━ 웨이브 출력 장치 갯수
    실패 ━ 0으로 웨이브 출력 장치가 없거나, 에러 발생

    다은 이미지 처럼 사운드 카드가 2개 가 설치된 시스템이라 할때

    이 시스템이에서 waveOutGetNumDevs을 호출한 이미지이다.

    툴팁 박스에 보면 결과가 2로 나와있는 거처럼 사운드 카드의 갯수를 알수 있다.
    위에도 말했지만, 사운드 카드에 단 하나의 웨이브 출력 장치가 존재하는 사운드 카드 일때이다.
    웨이브 출력 장치가 2개 이상이라면 다른 방법을 사용하여야 한다.

    어떤 윈도에 키 값을 보낼때 사용하는 메시지는
    키가 눌릴때 WM_KEYDOWN, 키가 눌렸다 때어질때 WM_KEYUP을 윈도우에 보내야 한다.

    윈두우 메세지는 다음과 같은 상수로 정의되어 있다.
    WM_KEYDOWN = &H100
    WM_KEYUP = &H101

    위 두 메시지를 키가 눌림을 보내기 위해서는 Sendmessage를 사용하는 것이 아니라 PostMessage 사용하여 메세지를 보내야 한다.

    PostMessage는 다음과 같다.
    지정된 윈도우에 윈도우 메세지를 보낸다.(해당 윈도우의 메세지 큐에 보내며 보낸 메세지는 메세지 큐에 저장된다)

    ● 선언 
       Function PostMessage Lib "user32" Alias "PostMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long

    ● 인수

    hwnd ━ 메세지를 전달할 윈도우의 핸들, HWND_BROADCAST 지정하면 시스템의 모든 상위 레벨 윈도우에 전해진다.
    wMsg ━ 전달될 메세지, 윈도우 메세지
    wParam ━ 메세지와 전달될 wParam 값
    lParam ━ 메세지와 전달될 lParam 값

    ● 반환
    성공 ━ 0이 아닌 값
    실패 ━ 0

    WM_KEYDOWY과 WM_KEYUP 메세지 보낼때
    wParam 값은 눌린 키의 가상 키값을 보내야 한다.
    가상키 상수의 정의는 다음과 같다.

    VK_LBUTTON &h1 마우스 왼쪽 버튼
    VK_RBUTTON &h2 마우스 오른쪽 버튼
    VK_CANCEL &h03 콘트롤+브레이크
    VK_MBUTTON &h04 마우스 중간 버튼
    VK_XBUTTON1 &h05 마우스 첫번째 X 버튼
    VK_XBUTTON2 &h06 마우스 두뻔째 X 버튼
    VK_BACK &h08 백스페이스 키
    VK_TAB &h09 탭키
    VK_CLEAR &h0C Clear 키
    VK_RETURN &h0D 엔터 키
    VK_SHIFT &h10 쉬프트 키(양쪽)
    VK_CONTROL &h11 콘트롤 키(양쪽)
    VK_MENU &h12 알크(양쪽)
    VK_PAUSE &h13 파우스 키
    VK_CAPITAL &h14 캡스 럭 키
    VK_KANA &h15 IME 카나가다 모드
    VK_HANGUEL &h15 IME 한글 모드
    VK_HANGUL &h15 IME 한글 모드
    VK_JUNJA &h17 IME 전자 모드
    VK_FINAL &h18 IME final mode
    VK_HANJA &h19 IME 한자 모드
    VK_KANJI &h19 IME Kanji mode
    VK_ESCAPE &h1B 이스케이프 키
    VK_CONVERT &h1C IME convert
    VK_NONCONVERT &h1D IME nonconvert
    VK_ACCEPT &h1E IME accept
    VK_MODECHANGE &h1F IME mode change request
    VK_SPACE &h20 스페이스바
    VK_PRIOR &h21 페이지 업 키
    VK_NEXT &h22 페이지 다운 키
    VK_END &h23 엔드 키
    VK_HOME &h24 홈 키
    VK_LEFT &h25 왼쪽 방향 화살표키
    VK_UP &h26 윗위쪽 방향 화살표키
    VK_RIGHT &h27 오른쪽 방향 화살표키
    VK_DOWN &h28 아래쪽 방향 화살표키
    VK_SELECT &h29 SELECT key
    VK_PRINT &h2A 프린트 키
    VK_EXECUTE &h2B EXECUTE key
    VK_SNAPSHOT &h2C 프린트 스크린 키
    VK_INSERT &h2D 인서트 키
    VK_DELETE &h2E 딜리트 키
    VK_HELP &h2F HELP key
    &h30-&h39 숫자 0-9키
    &h41-&h5A 알파벳 A-Z키
    VK_LWIN &h5B 왼쪽 윈도우 키
    VK_RWIN &h5C 오른쪽 윈도우 키
    VK_APPS &h5D Applications key (Natural keyboard)
    VK_SLEEP &h5F Computer Sleep key
    VK_NUMPAD0 &h60 키패드 0 키
    VK_NUMPAD1 &h61 키패드 1 키
    VK_NUMPAD2 &h62 키패드 2 키
    VK_NUMPAD3 &h63 키패드 3 키
    VK_NUMPAD4 &h64 키패드 4 키
    VK_NUMPAD5 &h65 키패드 5 키
    VK_NUMPAD6 &h66 키패드 6 키
    VK_NUMPAD7 &h67 키패드 7 키
    VK_NUMPAD8 &h68 키패드 8 키
    VK_NUMPAD9 &h69 키패드 9 키
    VK_MULTIPLY &h6A 키패드 곱셈 키
    VK_ADD &h6B 키패드 덧셈 키
    VK_SEPARATOR &h6C 키패드 엔터 키
    VK_SUBTRACT &h6D 키패드 빼기 키
    VK_DECIMAL &h6E 키패드 소수점 키
    VK_DIVIDE &h6F 키패드 나누기 키
    VK_F1 &h70 펑션 F1 키
    VK_F2 &h71 펑션 F2 키
    VK_F3 &h72 펑션 F3 키
    VK_F4 &h73 펑션 F4 키
    VK_F5 &h74 펑션 F5 키
    VK_F6 &h75 펑션 F6 키
    VK_F7 &h76 펑션 F7 키
    VK_F8 &h77 펑션 F8 키
    VK_F9 &h78 펑션 F9 키
    VK_F10 &h79 펑션 F10 키
    VK_F11 &h7A 펑션 F11 키
    VK_F12 &h7B 펑션 F12 키
    VK_F13 &h7C 펑션 F13 키
    VK_F14 &h7D 펑션 F14 키
    VK_F15 &h7E 펑션 F15 키
    VK_F16 &h7F 펑션 F16 키
    VK_F17 &h80 펑션 F17 키
    VK_F18 &h81 펑션 F18 키
    VK_F19 &h82 펑션 F19 키
    VK_F20 &h83 펑션 F20 키
    VK_F21 &h84 펑션 F21 키
    VK_F22 &h85 펑션 F22 키
    VK_F23 &h86 펑션 F23 키
    VK_F24 &h87 펑션 F24 키
    VK_NUMLOCK &h90 눔럭 키
    VK_SCROLL &h91 스크롤럭 키
    &h92-&h96 OEM specific
    VK_LSHIFT &hA0 왼쪽 쉬프트 키
    VK_RSHIFT &hA1 오른쪽 쉬프트 키
    VK_LCONTROL &hA2 왼쪽 컨트롤 키
    VK_RCONTROL &hA3 오른쪽 컨트롤 키
    VK_LMENU &hA4 왼쪽 알트 키
    VK_RMENU &hA5 오른쪽 알트 키
    VK_BROWSER_BACK &hA6 Browser Back key
    VK_BROWSER_FORWARD &hA7 Browser Forward key
    VK_BROWSER_REFRESH &hA8 Browser Refresh key
    VK_BROWSER_STOP &hA9 Browser Stop key
    VK_BROWSER_SEARCH &hAA Browser Search key
    VK_BROWSER_FAVORITES &hAB Browser Favorites key
    VK_BROWSER_HOME &hAC Browser Start and Home key
    VK_VOLUME_MUTE &hAD Volume Mute key
    VK_VOLUME_DOWN &hAE Volume Down key
    VK_VOLUME_UP &hAF Volume Up key
    VK_MEDIA_NEXT_TRACK &hB0 Next Track key
    VK_MEDIA_PREV_TRACK &hB1 Previous Track key
    VK_MEDIA_STOP &hB2 Stop Media key
    VK_MEDIA_PLAY_PAUSE &hB3 Play/Pause Media key
    VK_LAUNCH_MAIL &hB4 Start Mail key
    VK_LAUNCH_MEDIA_SELECT &hB5 Select Media key
    VK_LAUNCH_APP1 &hB6 Start Application 1 key
    VK_LAUNCH_APP2 &hB7 Start Application 2 key
    VK_OEM_1 &hBA ';:' 키
    VK_OEM_PLUS &hBB '+' 키
    VK_OEM_COMMA &hBC ',' 키
    VK_OEM_MINUS &hBD '-' 키
    VK_OEM_PERIOD &hBE '.' 키
    VK_OEM_2 &hBF '/?' 키
    VK_OEM_3 &hC0 '`~' 키
    VK_OEM_4 &hDB '[{' 키
    VK_OEM_5 &hDC '\|' 키
    VK_OEM_6 &hDD ']}' 키
    VK_OEM_7 &hDE '작은따옴표.큰따옴표' 키
    VK_OEM_8 &hDF
    &hE1 OEM specific
    VK_OEM_102 &hE2 Either the angle bracket key or the backslash key on the RT 102-key keyboard
    &hE3-&hE4 OEM specific
    VK_PROCESSKEY &hE5 IME PROCESS key
    &hE6 OEM specific
    VK_PACKET &hE7 Used to pass Unicode characters as if they were keystrokes. The VK_PACKET key is the low word of a 32-bit Virtual Key value used for non-keyboard input methods. For more information, see Remark in KEYBDINPUT, SendInput, WM_KEYDOWN, and WM_KEYUP
    &hE9-&hF5 OEM specific
    VK_ATTN &hF6 Attn key
    VK_CRSEL &hF7 CrSel key
    VK_EXSEL &hF8 ExSel key
    VK_EREOF &hF9 Erase EOF key
    VK_PLAY &hFA Play key
    VK_ZOOM &hFB Zoom key
    VK_NONAME &hFC Reserved
    VK_PA1 &hFD PA1 key
    VK_OEM_CLEAR &hFE Clear key

    lParam 값은 아래 형식을 맞추어 보내야 한다.

    0~15 반복 카운트
    16~23 스캔코드
    24 오른쪽 Alt,Ctrl,키패드등 101키에만 있는 확장키가 눌러졌을 경우 1이 돤다
    25~28 미사용
    29 Alt키가 눌러졌으면 1
    30 메시지가 보내지기 전에 키가 눌려져 있었으면 1
    31 키가 놓아지면 1, 눌러지면 0

    하위 비트가 0번 비트이다. 즉 1이라는 숫자가 저장될때 1이 지정되는 비트(최하위 비트)가 0번 비트이다.
    가상키 값에 대한 스캔코드 값을 구하기 위해서는 MapVirtualKey함수를 사용하면 된다.

    다음의 명령버튼에 엔터 키를 보내는 소스이다.
    Option Explicit
    Private Const VK_RETURN = &HD
    Private Const MAPVK_VK_TO_VSC = 0
    Private Const WM_KEYDOWN = &H100
    Private Const WM_KEYUP = &H101
    Private Declare Function MapVirtualKey Lib "user32" Alias "MapVirtualKeyA" (ByVal wCode As Long, ByVal wMapType As Long) As Long
    Private Declare Function PostMessage Lib "user32.dll" Alias "PostMessageA" (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long

    Private Sub Command1_Click()
        Debug.Print "테스트"
    End Sub

    Private Sub Command2_Click()
        Dim VirtualKey As Long
        Dim ScanKey As Long
        
        Command1.SetFocus
        VirtualKey = VK_RETURN
        ScanKey = MapVirtualKey(VK_RETURN, MAPVK_VK_TO_VSC) * 2 ^ 16 + 1 ' 스캔 코드 값이 저장될 위치에 들어가도록 하기 위해서고, 1은 반복 카운터이다.
        PostMessage Command1.hWnd, WM_KEYDOWN, VirtualKey, ByVal ScanKey
        PostMessage Command1.hWnd, WM_KEYUP, VirtualKey, ByVal ScanKey Or &HC0000000 ' 키가 떼어졌을때, 31과 30 번째 비트를 설정하기 위해 비트 연산을 하였음
    End Sub

    vb에서 두정수의 논리 연산자를 사용하여 연산을 하게 되면 비트 연산을 하게 되다.

    파일 시스템과 볼륨에 대한 정보를 얻는다

    ● 선언
    privat declare Function GetVolumeInformation Lib "kernel32.dll" Alias "GetVolumeInformationA" (ByVal lpRootPathName As String, ByVal lpVolumeNameBuffer As String, ByVal nVolumeNameSize As Long, lpVolumeSerialNumber As Long, lpMaximumComponentLength As Long, lpFileSystemFlags As Long, ByVal lpFileSystemNameBuffer As String, ByVal nFileSystemNameSize As Long) As Long

    ● 인수
    lpRootPathName ━ 알고자 하는 볼륨의 루트 디렉토리, Null이면 현재 작업 디렉토리 지정된다. 네트워크 드라이브면 UNC로 지정에서 공유까지만 지정
    lpVolumeNameBuffer ━ 볼륨의 이름을 받을 버퍼
    nVolumeNameSize ━ lpVolumeNameBuffer 버퍼 길이
    lpVolumeSerialNumber ━ 시리얼 번호
    lpMaximumComponentLength ━ 파일명의 최대 길이
    lpFileSystemFlags ━ 파일 플래그
    lpFileSystemNameBuffer ━ 파일 시스템명
    nFileSystemNameSize ━ lpFileSystemNameBuffer 버퍼 길이

    ● 반환

    성공 ━ 0이 아닌 값
    실패 ━ 0

    GetVolumeInformation 사용해서 드라이브의 정보를 얻을수 있다.

    다음은 소스의 결과 이미지 이다.

    다은은 소스이다
    Option Explicit

    Private Declare Function GetLogicalDriveStrings Lib "kernel32" Alias "GetLogicalDriveStringsA" (ByVal nBufferLength As Long, ByVal lpBuffer As String) As Long
    Private Declare Function GetDriveType Lib "kernel32" Alias "GetDriveTypeA" (ByVal nDrive As String) As Long
    Private Declare Function GetVolumeInformation Lib "kernel32.dll" Alias "GetVolumeInformationA" (ByVal lpRootPathName As String, ByVal lpVolumeNameBuffer As String, ByVal nVolumeNameSize As Long, lpVolumeSerialNumber As Long, lpMaximumComponentLength As Long, lpFileSystemFlags As Long, ByVal lpFileSystemNameBuffer As String, ByVal nFileSystemNameSize As Long) As Long

    Private Const MAXBUFFER = &HFF
    Private Const DRIVE_FIXED = 3

    Private Sub cobDrive_Click() ' 드라이브 선택
        If cobDrive.Tag = CStr(cobDrive.ListIndex) Then Exit Sub
        labResult.Caption = GetDriveInfo(cobDrive.List(cobDrive.ListIndex))
        cobDrive.Tag = cobDrive.ListIndex
    End Sub

    Private Sub Form_Load()
        Dim Buffer As String ' 전체 드라이브명
        Dim DriveName() As String ' 각 드라이브명
        Dim ForCounter As Long ' For 카운터
        Dim NumberDrive As Long ' 드라이브 수
       
        Buffer = Space(MAXBUFFER) ' 콤보박스에 하드드라이브명을 지정한다.
        GetLogicalDriveStrings MAXBUFFER, Buffer
        DriveName = Split(Buffer, vbNullChar)
        NumberDrive = UBound(DriveName)
        For ForCounter = 0 To NumberDrive
            If GetDriveType(DriveName(ForCounter)) = DRIVE_FIXED Then
                cobDrive.AddItem Left(DriveName(ForCounter), InStrRev(DriveName(ForCounter), "\") - 1)
            End If
        Next ForCounter
        cobDrive.ListIndex = 0
    End Sub

    Private Function GetDriveInfo(ByVal DriveName As String) As String ' 드라이브 정보를 얻는다.
        Dim VolumeName As String  ' 볼륨명
        Dim SerialNumber As Long  ' 시리얼 번호
        Dim SerialNumberString As String ' 문자열로 변경한 시리얼 번호
        Dim MaxFileNameLength As Long  ' 최대 파일명 길이
        Dim sysflags As Long    ' 파일 시스템과 관련된 플래그
        Dim FileSystemName As String ' 파일 시스템명

        DriveName = DriveName & "\"
        VolumeName = Space(MAXBUFFER)
        FileSystemName = Space(256)
        Call GetVolumeInformation(DriveName, VolumeName, MAXBUFFER, SerialNumber, MaxFileNameLength, 0&, FileSystemName, MAXBUFFER)
        SerialNumberString = Trim(Hex(SerialNumber))
        SerialNumberString = String(8 - Len(SerialNumberString), "0") & SerialNumberString
        SerialNumberString = Left(SerialNumberString, 4) & "-" & Right(SerialNumberString, 4)
        GetDriveInfo = "Volume Name: " & GetString(VolumeName)
        GetDriveInfo = GetDriveInfo & vbCrLf & "Serial Number: " & SerialNumberString
        GetDriveInfo = GetDriveInfo & vbCrLf & "Max File Name Length: " & MaxFileNameLength
        GetDriveInfo = GetDriveInfo & vbCrLf & "File System: " & GetString(FileSystemName)
    End Function

    Private Function GetString(ByVal APIStr As String) As String ' 버퍼로 사용된 문자열에서 유효한 문자열만 반환
        GetString = Left$(APIStr, InStr(APIStr, vbNullChar) - 1)
    End Function

    드라이브 정보를 얻기 위해 FileSystemObject의 GetDrive를 사용해도
    드라이브의 정보를 얻을수 있다.
    FileSystemObject를 사용하기 위해서는  Microsoft Scripting Runtime 참조하면 된다.

    확장자와 연결된 프로그램의 경로를 얻는다.

    ● 선언
    Declare Function FindExecutable Lib "shell32.dll" Alias "FindExecutableA" (ByVal lpFile As String, ByVal lpDirectory As String, ByVal lpResult As String) As Long


    ● 인수
    lpFile ━ 관련 파일명
    lpDirectory ━ 기본 폴더 또는 Null
    lpResult ━ 결과를 저장할 버퍼


    ● 반환
    성공 ━ 32보다 큰값
    실패 ━ 32보다 작은값


    소스의 초기 이미지


    소스의 결과 이미지


    ' 확장자에 대한 실행파일 찾기
    Option Explicit

    Private Declare Function FindExecutable Lib "shell32.dll" Alias "FindExecutableA" (ByVal lpFile As String, ByVal lpDirectory As String, ByVal lpResult As String) As Long
    Private Declare Function GetTempFileName Lib "kernel32" Alias "GetTempFileNameA" (ByVal lpszPath As String, ByVal lpPrefixString As String, ByVal wUnique As Long, ByVal lpTempFileName As String) As Long
    Private Declare Function GetTempPath Lib "kernel32" Alias "GetTempPathA" (ByVal nBufferLength As Long, ByVal lpBuffer As String) As Long

    Private Sub Form_Load() ' 메모리에 로드될때 발생
        txtExtension.Text = "txt"
    End Sub

    Private Sub txtExtension_KeyDown(KeyCode As Integer, Shift As Integer) ' 키가 눌린 경우 발생
        If Not KeyCode = vbKeyReturn Then Exit Sub
        labReult.Caption = GetConnectPath(txtExtension.Text) ' 확자자와 연결된 프로그램 경로를 얻는다.
    End Sub

    Public Function GetConnectPath(ByVal Extension As String) As String ' 확장자에 연결된 프로그램 절대 경로를 얻는다
        Const MAX_PATH As Long = 260 ' 버퍼 길이
       
        Dim TempPath As String ' 임시 파일 경로
        Dim TempFileName As String ' 임시 파일명
        Dim ChangeFileName As String ' 변경한 파일명
        Dim ConnectProgramPath As String ' 연결된 프로그램 파일명
        Dim nRet As Long

        If InStr(1, Extension, ".") > 0 Then Extension = Mid$(Extension, InStr(1, Extension, ".") + 1)
        TempPath = Space$(MAX_PATH) ' 임시 파일 경로
        If GetTempPath(MAX_PATH, TempPath) Then ' 임시 파일 경로를 얻는다
            TempPath = GetString(TempPath)
            TempFileName = String$(MAX_PATH, 0) ' 임시 파일명
            If GetTempFileName(TempPath, "~", 0, TempFileName) Then
                TempFileName = GetString(TempFileName)
                ChangeFileName = Left$(TempFileName, InStrRev(TempFileName, ".")) & Extension
                Name TempFileName As ChangeFileName ' 임시 파일명을 변경한다
                ConnectProgramPath = Space$(MAX_PATH) ' 확장자와 연결된 프로그램 경로
                Call FindExecutable(ChangeFileName, vbNullString, ConnectProgramPath)
                GetConnectPath = GetString(ConnectProgramPath)
                Kill ChangeFileName
            End If
        End If
    End Function

    Private Function GetString(ByVal APIStr As String) As String ' 버퍼로 사용된 문자열에서 유효한 문자열만 반환
        GetString = Left$(APIStr, InStr(APIStr, vbNullChar) - 1)
    End Function

    + Recent posts