• 최초 작성일: 2002-04-24
  • 최종 수정일: 2026-09-30
  • 조회수: 9 회
  • 작성자: 권현욱 (엑셀러)
  • 강의 제목: 유저폼 캡션 없애기

들어가기 전에

오래 전 엑셀 파일로 진행하던 강의를 웹 글로 복원한 것이라, "버튼을 눌러보세요"처럼 엑셀에서 직접 조작하라는 표현이 남아 있을 수 있습니다. 결과는 본문의 그림이나 표로 확인하실 수 있으며, 현재와 맞지 않는 내용은 보완하되 전반적인 스타일은 당시의 갬성(?)을 유지했습니다.

이번 질문은 유저폼을 하나 만들어 화면의 특정 위치에 표시하는 것까지는 했는데, 유저폼을 이동하지 못하도록 캡션 바를 마우스로 잡을 수 없게 만들고 싶다는 내용입니다.

권현욱(엑셀러)
저자: 권현욱(엑셀러), 아이엑셀러 대표

필자는 Excel 컨설턴트, 작가, 그리고 크리에이터입니다. 현재 Microsoft Excel MVP이며, 『챗GPT+엑셀 업무자동화 정석』을 비롯한 10여 권의 도서를 집필했습니다. Excel 자동화 및 생산성 향상 분야에서 25년 넘는 경력을 보유하고 있습니다.

권현욱(엑셀러) 님의 최신 포스트:
  • 최신 글을 불러오는 중...


26년 경력 Microsoft MVP 권현욱(엑셀러) 대표 직강 1강 무료

🎁 파워툴스 마스터클래스 · 엑셀 바이브 코딩

AI에게 말로 지시해서 '나만의 엑셀 도구'를 만들고 리본에 장착합니다.

  • 코딩 지식 없이 시작 · 코드는 AI가 작성
  • 엑셀 리본에 내 탭과 버튼을 만들고 XLAM으로 공유
  • 1강 무료 수강 가능
수강 신청하기 강의 소개 보기

유저폼 캡션 없애기

핵심 요약: API로 유저폼 캡션 없애기

FindWindow로 유저폼의 핸들을 찾고 스타일에서 WS_CAPTION을 제거한 뒤 MoveWindow로 크기를 조정하면 캡션이 없는 유저폼이 됩니다.

  • 1단계: 일반 모듈에 API 함수와 상수, 사용자 정의 변수(MYRECT)를 선언합니다.
  • 2단계: FindWindow와 GetWindowLong으로 핸들과 현재 스타일을 얻습니다.
  • 3단계: 스타일에서 WS_CAPTION을 빼거나 더한 뒤 MoveWindow로 크기를 조정합니다.

Step 1: 캡션 없는 유저폼

엑셀이나 유저폼을 아주 넓~게, 즉 캡션을 없애고 사용하는 방법에 대해 질문을 하시는 분이 아주 가끔씩 계십니다.

워크시트 상의 경우(즉 엑셀 어플리케이션 상)라면 '보기-전체 화면' 메뉴를 선택하면 됩니다만 그렇더라도 남게 되는 메뉴바 마저도 없앤다거나 유저폼 상단의 캡션을 없애고자 할 경우에는 API 함수의 도움을 받으면 가능합니다.

버튼을 누르면 유저폼의 캡션이 사라졌다가 다시 나타납니다.

만약 질문이 단순히 "유저폼의 캡션을 없애려면 어떻게 해요?"였다면 실용성이 별로 없는 고민이라 여겨 깊이 고민하지 않았을 수도 있습니다. 하지만 구체적인 상황을 들어 질문해 주시면 한 번 더 방법을 찾아보게 됩니다. 질문은 가급적 상세하게 해 주시는 것이 답변하기에도 수월합니다.

Step 2: 유저폼 만들기

(1) VB Editor에서 유저폼을 하나 삽입하고 그림과 같이 레이블과 커맨드 버튼을 그립니다. 커맨드 버튼의 이름은 각각 cmdOK, cmdClose로 지정하였습니다.

레이블 하나와 확인, 닫기 버튼이 배치된 유저폼
아이엑셀러

(2) 유저폼의 바탕을 더블 클릭하고 우측 상단의 콤보 상자에서 Initialize 이벤트를 선택한 다음 아래와 같은 코드를 작성합니다.

코드 창 우측 상단 콤보 상자에서 Initialize 이벤트를 선택하는 화면
아이엑셀러
Private Sub UserForm_Initialize()
    Me.Label1.Caption = "확인 버튼을 클릭하면 캡션이 제거됩니다."
End Sub

Step 3: 확인 버튼 코드

(3) Shift + F7 키를 누르면 유저폼으로 돌아가는데 '확인' 버튼을 더블 클릭하고 코드를 작성합니다.

Private Sub cmdOK_Click()
    Dim lngOld As Long
    Dim lngNew As Long
    Dim MyRct As MYRECT
    #If VBA7 Then
        Dim lngHwnd As LongPtr
    #Else
        Dim lngHwnd As Long
    #End If
    Dim lngX As Long
    Dim lngY As Long
    Dim lngStart As Long
    Dim lngFlags As Long
    Dim strMsg As String
    Static blnOK As Boolean
    lngHwnd = FindWindow(vbNullString, Me.Caption)
    lngOld = GetWindowLong(lngHwnd, GWL_STYLE)
    blnOK = Not blnOK
    ' 변수 선언 단계에서부터 예사롭지가 않지요? ^^
    ' MYRECT라는 것은 사용자 정의 변수라는 것입니다. 예전 강좌에서 설명드린 기억이
    ' 나는데 사용자 정의 변수는 "변수 종합 선물세트" 입니다.

    ' 그럼 이 사용자 정의 변수가 어디 있는지 궁금하시지요? 조금만 더 기다리세요.
    ' 사용자 정의 변수는 일반 모듈 시트에 선언을 해 주었는데 뒤에서 설명드립니다.

    If blnOK Then
    ' blnOK 변수는 캡션을 보여 주었다가 사라지게 하였다가 번갈아가며 설정하기 위한
    ' 역할을 수행합니다.

        lngNew = lngOld And Not WS_CAPTION
    Else
        lngNew = lngOld Or WS_CAPTION
    End If

    lngOld = SetWindowLong(lngHwnd, GWL_STYLE, lngNew)
    ' 현재 유저폼의 스타일 값을 lngOld 변수에 담아둡니다.

    GetWindowRect lngHwnd, MyRct
    ' 현재 유저폼의 좌표값을 얻어 냅니다.

    lngX = MyRct.Right - MyRct.Left
    ' 유저폼의 폭을 계산하는 과정입니다. 유저폼의 폭은 변동이 없으므로 이 과정은
    ' 의미가 없습니다.

    If blnOK Then
    ' 캡션의 상태에 따라 유저폼의 크기를 조절하고 레이블에 표시할 텍스트를 달리합니다.

        lngY = MyRct.Bottom - MyRct.Top - GetSystemMetrics(SM_CYCAPTION)
        strMsg = strMsg & "캡션을 제거하였습니다." & vbCr
        strMsg = strMsg & "확인 버튼을 누르면 다시 표시합니다."
    Else
        lngY = MyRct.Bottom - MyRct.Top + GetSystemMetrics(SM_CYCAPTION)
        strMsg = strMsg & "캡션을 표시하였습니다." & vbCr
        strMsg = strMsg & "확인 버튼을 누르면 제거됩니다."
    End If
    MoveWindow lngHwnd, MyRct.Left, MyRct.Top, lngX, lngY, True
    Me.Label1.Caption = strMsg
End Sub

MYRECT는 사용자 정의 변수(변수 종합 선물세트)로, 유저폼의 좌표를 담기 위한 것입니다. 이것은 일반 모듈의 맨 앞부분에 선언하며 뒤에서 설명합니다. blnOK 변수는 캡션을 제거했다가 다시 표시하는 동작을 번갈아 하기 위한 스위치이고, WS_CAPTION 속성을 제거하거나 다시 더한 뒤 캡션 높이만큼 유저폼의 크기를 조정합니다.

Step 4: API 함수 선언하기

(4) 일반 모듈 시트를 한장 삽입하고('삽입-모듈') 다음과 같은 코드를 작성합니다. 실은 코드라기보다… API 함수를 사용하기 위한 함수 및 상수를 선언하는 것입니다.

Type MYRECT
    Left As Long
    Top As Long
    Right As Long
    Bottom As Long
End Type

#If VBA7 Then
    Declare PtrSafe Function GetWindowRect Lib "user32" _
        (ByVal hWnd As LongPtr, lpRect As MYRECT) As Long
    Declare PtrSafe Function FindWindow Lib "user32" Alias "FindWindowA" _
        (ByVal lpClassName As String, ByVal lpWindowName As String) As LongPtr
    Declare PtrSafe Function GetWindowLong Lib "user32" Alias "GetWindowLongA" _
        (ByVal hWnd As LongPtr, ByVal nIndex As Long) As Long
    Declare PtrSafe Function SetWindowLong Lib "user32" Alias "SetWindowLongA" _
        (ByVal hWnd As LongPtr, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
    Declare PtrSafe Function GetSystemMetrics Lib "user32" _
        (ByVal nIndex As Long) As Long
    Declare PtrSafe Function MoveWindow Lib "user32" _
        (ByVal hWnd As LongPtr, ByVal x As Long, ByVal y As Long, _
        ByVal nWidth As Long, ByVal hHeight As Long, ByVal bRepaint As Long) As Long
    Declare PtrSafe Function SetWindowPos Lib "user32" (ByVal hWnd As LongPtr, _
        ByVal hWndInsertAfter As LongPtr, ByVal x As Long, ByVal y As Long, _
        ByVal cx As Long, ByVal cy As Long, ByVal wFlags As Long) As Long
#Else
    Declare Function GetWindowRect Lib "user32" _
        (ByVal hWnd As Long, lpRect As MYRECT) As Long
    Declare Function FindWindow Lib "user32" Alias "FindWindowA" _
        (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
    Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" _
        (ByVal hWnd As Long, ByVal nIndex As Long) As Long
    Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" _
        (ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
    Declare Function GetSystemMetrics Lib "user32" _
        (ByVal nIndex As Long) As Long
    Declare Function MoveWindow Lib "user32" _
        (ByVal hWnd As Long, ByVal x As Long, ByVal y As Long, _
        ByVal nWidth As Long, ByVal hHeight As Long, ByVal bRepaint As Long) As Long
    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
#End If

Public Const GWL_STYLE = (-16)
Public Const WS_CAPTION = &HC00000
Public Const SM_CYCAPTION = 4

각 API 함수의 역할은 다음과 같습니다. GetWindowRect는 윈도우의 좌표를 얻고, FindWindow는 윈도우 캡션으로 윈도우 핸들을 찾습니다. GetWindowLong과 SetWindowLong은 윈도우의 속성을 읽고 변경하며, GetSystemMetrics는 캡션의 높이를 측정합니다. MoveWindow는 윈도우의 위치와 크기를 지정합니다.

참고: 이 많은 API 함수를 모두 외울 필요는 없습니다. Win32 API 도움말이나 검색을 통해 필요한 선언문을 찾아 복사해서 쓰면 됩니다. 64비트 엑셀에서는 Declare 문에 PtrSafe 키워드와 LongPtr 형식이 필요하므로 위 코드는 조건부 컴파일로 두 환경 모두 대응하도록 선언하였습니다.

VBA(VB도 마찬가지!)에서 API 함수를 컨트롤 하는 것은 좀 복잡합니다. 처음 접하는 분은 '뭐 이런 것이 다 있나!' 싶기도 할 것입니다만, 엑셀이 얼마나 확장성이 있는 것인지만 알고 넘어가셔도 소기의 목적은 달성하신 것입니다.

고참님들은 엑셀의 행/열 머리글만 남고 다른 것은 모두 없어지도록 하는 방법에 대해 연구해 보세요. 오늘의 숙제입니다. ^^ 질문이 들어올 경우에 한해서만 Exceller도 고민을 해 볼 작정입니다.

다음 시간에 또…

정리 — 유저폼 캡션 제거 핵심

구분 사용한 코드 역할
핸들 찾기 FindWindow(vbNullString, Me.Caption) 유저폼 윈도우 핸들
스타일 얻기 GetWindowLong(hwnd, GWL_STYLE) 현재 스타일 값
캡션 제거 lngOld And Not WS_CAPTION 스타일에서 캡션 제외
스타일 적용 SetWindowLong 변경한 스타일 설정
크기 조정 MoveWindow, GetSystemMetrics 캡션 높이만큼 크기 변경

자주 묻는 질문 (FAQ)

Q1. 유저폼의 캡션을 없앨 수 있나요?

유저폼 자체 속성으로는 없앨 수 없지만 API 함수로 윈도우 스타일에서 WS_CAPTION을 제거하면 가능합니다.

Q2. MYRECT는 무엇인가요?

유저폼의 좌표(Left, Top, Right, Bottom)를 담기 위한 사용자 정의 변수 형식이며 일반 모듈의 맨 앞부분에 선언해야 합니다.

Q3. 64비트 엑셀에서도 동작하나요?

Declare 문에 PtrSafe와 LongPtr 형식을 사용해야 하며 예제 코드는 조건부 컴파일로 두 환경을 모두 지원하도록 선언하였습니다.

마치며

API 함수를 다루는 것은 조금 복잡하지만 엑셀이 얼마나 확장성 있는지 알아 두는 것만으로도 충분히 의미가 있습니다.