아래 내용과 같은 프로그램을 만들고싶습니다.
구현을 도와주세요.
 c++로 만들려고하는데요.  
c++언어는 접해본적이 없어 
한스탭씩 자세히 설명을 요청드립니다.
### 프로그램 설계와 각 동작 방식 및 설정 창 구조

제공된 이미지와 설명을 바탕으로, 프로그램의 각 기능 동작 방식, 필요한 설정 창, 구성 요소를 보다 구체적으로 정리했습니다. 이를 통해 프로그램 설계와 구현 시 필요한 모든 사항을 종합적으로 이해할 수 있도록 구성합니다.

---

## 1. **전체 프로그램 구조**

1. **메인 창 (Main Window)**:
   - 대상 창 설정, 이미지 캡처 및 편집, 작업(Action) 추가 및 관리.
   - **구성 요소**:
     - 대상 창 설정 버튼
     - 이미지 리스트
     - 인식 조건 설정 버튼
     - 액션 리스트
     - 로그 출력 창

2. **팝업 창**:
   - 설정 버튼 클릭 시 나타나는 상세 설정 창.
   - **종류**:
     - 대상 창 설정 창
     - 이미지 캡처 및 편집 창
     - 인식 조건 설정 창
     - 딜레이 설정 창
     - 멀티 이미지 조건 설정 창

3. **작업 실행 로그**:
   - 하단에 실시간으로 작업 상태를 출력.
   - 로그 파일로 저장 기능 제공.

---

## 2. **각 기능의 동작 방식 및 설정 창**

### **2.1 대상 창 설정**
- **동작 방식**:
  1. 메인 창에서 "설정" 버튼 클릭 시 팝업 창이 열림.
  2. 팝업 창에서 "대상 창 선택" 버튼 클릭 → 대상 창 선택 모드로 전환.
  3. 마우스로 대상 창을 클릭하여 창의 이름, 크기, 좌표를 가져옴.
  4. "확인" 버튼 클릭 시 팝업 창 종료, 메인 창으로 복귀.

- **필요한 설정 창**:
  - **대상 창 설정 창**:
    - 대상 창 이름 필드 (자동 채움).
    - 창의 크기와 좌표 정보 표시.
    - 대상 창 숨기기 여부 체크박스.
    - "대상 창 선택" 버튼.

---

### **2.2 이미지 캡처 및 편집**
- **동작 방식**:
  1. 메인 창에서 "추가" 버튼 클릭 시 캡처 창 활성화.
  2. 드래그로 대상 영역 지정 후, 위치 및 크기 조정.
  3. 캡처된 이미지 이름 입력 후 저장 → 이미지 리스트에 추가.
  4. 편집 버튼 클릭 시 캡처 창 재활성화 → 기존 영역 재조정.

- **필요한 설정 창**:
  - **이미지 캡처 창**:
    - 대상 영역 드래그 선택 기능.
    - 위치 및 크기 미세 조정 옵션 (화살표 버튼 또는 단축키).
    - 캡처된 이미지 미리보기.
    - 이미지 이름 입력 필드.
    - "저장" 및 "취소" 버튼.

---

### **2.3 인식 조건 설정**
- **동작 방식**:
  1. 이미지 리스트에서 이미지를 선택 후 "설정" 버튼 클릭.
  2. 팝업 창에서 유사도, 엣지 비교, OCR 등 세부 조건 설정.
  3. 설정값 저장 후 메인 창으로 복귀.

- **필요한 설정 창**:
  - **인식 조건 설정 창**:
    - **유사도 설정**:
      - 슬라이더로 유사도 값 조정 (1~100%).
    - **엣지 비교 활성화**:
      - 체크박스 옵션.
    - **OCR 활성화**:
      - OCR 설정 필드 (언어, 문자인식 유형 등).
    - **인식 실패 시 동작**:
      - "다시 시도" 횟수 및 대체 작업 설정.

---

### **2.4 멀티 이미지 인식**
- **동작 방식**:
  1. 멀티 이미지 설정 창에서 최대 10개의 이미지를 추가.
  2. 각 이미지에 대해 성공 여부와 AND/OR 조건 설정.
  3. 설정값 저장 후 작업 실행 시 조건 만족 여부에 따라 동작 수행.

- **필요한 설정 창**:
  - **멀티 이미지 설정 창**:
    - 최대 10개의 이미지 추가 기능.
    - 각 이미지에 대한 성공 조건(AND/OR).
    - 추가된 이미지의 조건 수정 및 삭제 옵션.

---

### **2.5 작업(Action) 관리**
- **동작 방식**:
  1. 이미지 리스트에서 이미지를 선택 후 액션 추가.
  2. 작업 유형 선택 (이미지 클릭, 키보드 입력 등).
  3. 작업 순서를 조정하거나 딜레이를 추가.
  4. 설정된 작업 리스트를 저장.

- **필요한 설정 창**:
  - **액션 추가 창**:
    - 작업 유형 선택 드롭다운 (이미지 클릭, 키보드 입력, 마우스 입력 등).
    - 작업 세부 설정 (클릭 좌표, 입력 문자열 등).
    - 작업 순서 조정 버튼 (위로, 아래로).

---

### **2.6 딜레이 설정**
- **동작 방식**:
  1. 작업 추가 시 딜레이 옵션을 설정.
  2. 분, 초 단위로 지연 시간 입력.
  3. 랜덤 딜레이를 추가하여 자연스러운 작업 실행.

- **필요한 설정 창**:
  - **딜레이 설정 창**:
    - 분, 초 입력 필드.
    - 랜덤 딜레이 체크박스 및 시간 범위 입력.

---

### **2.7 비활성/활성 창 제어**
- **동작 방식**:
  1. 작업 대상 창이 활성 상태일 때만 실행 여부 설정.
  2. 비활성 창에서도 동작 가능하도록 설정.
  3. 특정 시간 동안만 활성화/비활성화 예약 가능.

- **필요한 설정 창**:
  - **활성/비활성 설정 창**:
    - 활성/비활성 상태 선택 드롭다운.
    - 예약 활성화/비활성화 시간 입력 필드 (분, 초 단위).

---

### **2.8 작업 실행 로그**
- **동작 방식**:
  - 각 작업이 실행될 때 상태를 실시간으로 기록.
  - 작업 시작, 성공, 실패, 재시도 등의 결과를 출력.
  - 로그 파일로 저장 가능.

- **필요한 구성 요소**:
  - 메인 창 하단에 로그 출력 창.
  - "로그 저장" 버튼 추가.

---

## 3. 개발에 필요한 세부 기술
1. **UI 개발**:
   - **Qt 또는 wxWidgets**:
     - 메인 창 및 팝업 창 관리.
     - 드래그 및 조정 가능한 캡처 인터페이스.

2. **이미지 처리**:
   - **OpenCV**:
     - 캡처 및 영역 선택.
     - cv::matchTemplate로 이미지 유사도 매칭.

3. **윈도우 제어**:
   - **Windows API**:
     - 대상 창 정보 수집 및 비활성 상태 작업 제어.

4. **작업 스케줄링**:
   - **멀티스레딩**:
     - 작업 실행 및 딜레이 적용.

5. **설정 저장**:
   - **JSON**:
     - 작업, 이미지, 조건 설정을 파일로 저장 및 불러오기.

ChatGPT

7초 동안 Thought about program design and functionality
아래의 설명은 Windows 환경 + Qt + OpenCV 조합을 예시로 들었습니다.
(물론 다른 GUI 프레임워크/라이브러리를 사용해도 되지만, C++ 초보 입문 시 Qt가 비교적 문서가 잘 되어 있어 추천드립니다.)

0. 사전 준비 사항
개발 환경 구축

IDE: Visual Studio, Qt Creator, CLion 등
Qt: Qt 다운로드 후 설치
OpenCV: OpenCV 공식 사이트에서 Windows용 바이너리 다운로드 및 설치
Windows API: Windows 환경에서 기본적으로 제공
프로젝트 생성

Qt Creator 기준:
New Project → Qt Widgets Application 선택
프로젝트 이름과 경로 지정
Kit(빌드 환경) 선택
기본 구조가 생성되면 main.cpp, mainwindow.cpp/.h, mainwindow.ui 등이 만들어짐
OpenCV 연동

.pro 파일(또는 CMake 기반일 경우 CMakeLists.txt)에 OpenCV 라이브러리 경로를 링크
예) .pro 파일 예시
prolog
복사
# OpenCV 라이브러리 경로 설정 예시
INCLUDEPATH += C:/opencv/build/include
LIBS += -LC:/opencv/build/x64/vc15/lib \
        -lopencv_core480 \
        -lopencv_imgproc480 \
        -lopencv_highgui480 \
        -lopencv_imgcodecs480
(버전, 경로는 설치 위치에 따라 달라집니다.)
1. 메인 윈도우 설계
핵심 UI 요소

대상 창 설정 버튼
이미지 리스트 (QListWidget 등으로 구현)
인식 조건 설정 버튼
액션 리스트 (QListWidget 등으로 구현)
로그 출력 창 (QTextEdit 등으로 구현)
UI 배치 예시

좌측 상단: 대상 창 설정 버튼
좌측 중단: 이미지 리스트 (추가/삭제 버튼 옆에 배치)
우측 중단: 액션 리스트 (작업(Action) 추가/삭제/수정)
하단: 로그 출력 창
예시 코드: MainWindow.ui 에서 폼 상에 위젯 배치, 객체 이름 지정.

xml
복사
<!-- Qt Designer에서 자동 생성되는 XML 형태이므로 이해만 해두시면 됩니다 -->
<widget class="QMainWindow" name="MainWindow">
    <property name="windowTitle">
        <string>ImageMax 2.53 (예시)</string>
    </property>
    <widget class="QWidget" name="centralwidget">
        <layout class="QVBoxLayout">
            <item>
                <widget class="QPushButton" name="btnTargetWindowSetting">
                    <property name="text">
                        <string>대상 창 설정</string>
                    </property>
                </widget>
            </item>
            <item>
                <widget class="QListWidget" name="listWidgetImages"/>
            </item>
            <item>
                <widget class="QPushButton" name="btnImageConditionSetting">
                    <property name="text">
                        <string>인식 조건 설정</string>
                    </property>
                </widget>
            </item>
            <item>
                <widget class="QListWidget" name="listWidgetActions"/>
            </item>
            <item>
                <widget class="QTextEdit" name="textEditLog"/>
            </item>
        </layout>
    </widget>
</widget>
메인 윈도우 클래스: MainWindow

버튼 이벤트, 리스트 위젯 핸들링, 로그 출력 등의 기능 담당
cpp
복사
// mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QListWidget>
#include <QTextEdit>
#include <QPushButton>

// forward declaration
class TargetWindowDialog; 
class ImageConditionDialog;
// ...

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void onBtnTargetWindowSettingClicked();
    void onBtnImageConditionSettingClicked();
    // ... 액션 추가/삭제 등 슬롯 선언

private:
    Ui::MainWindow *ui;

    // 예: 팝업 창 객체들
    TargetWindowDialog *m_targetWindowDialog;
    ImageConditionDialog *m_imageConditionDialog;

    // 로그 출력 메서드
    void appendLog(const QString &msg);
};

#endif // MAINWINDOW_H
cpp
복사
// mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "targetwindowdialog.h"
#include "imageconditiondialog.h"
#include <QDateTime>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    // 팝업 창 초기화
    m_targetWindowDialog = new TargetWindowDialog(this);
    m_imageConditionDialog = new ImageConditionDialog(this);

    // 버튼 클릭 시그널 → 슬롯 연결
    connect(ui->btnTargetWindowSetting, &QPushButton::clicked,
            this, &MainWindow::onBtnTargetWindowSettingClicked);
    connect(ui->btnImageConditionSetting, &QPushButton::clicked,
            this, &MainWindow::onBtnImageConditionSettingClicked);
}

MainWindow::~MainWindow()
{
    delete ui;
}

// 대상 창 설정 버튼 클릭
void MainWindow::onBtnTargetWindowSettingClicked()
{
    m_targetWindowDialog->show();
}

// 인식 조건 설정 버튼 클릭
void MainWindow::onBtnImageConditionSettingClicked()
{
    // 선택된 이미지가 있는지 확인 → 없으면 return
    // 있으면 이미지 정보를 인식 조건 창에 넘겨서 수정
    m_imageConditionDialog->show();
}

// 로그 출력 편의 함수
void MainWindow::appendLog(const QString &msg)
{
    QString timeStamp = QDateTime::currentDateTime().toString("[yyyy-MM-dd hh:mm:ss]");
    ui->textEditLog->append(timeStamp + " " + msg);
}
2. 대상 창 설정 (TargetWindowDialog)
2.1 개요
사용자가 "대상 창 설정" 버튼을 클릭하면 열리는 팝업
대상 창 선택 버튼을 누르고, Windows API를 이용해 마우스로 특정 창을 클릭하면 해당 창의 정보(윈도우 핸들, 창 제목, 좌표, 크기 등)를 가져옴
2.2 구현 단계
UI 구성

라벨: "창 이름", "창 크기", "창 좌표"
텍스트 필드: 창 이름, 크기, 좌표
체크박스: 대상 창 숨기기
"대상 창 선택" 버튼, "확인" 버튼, "취소" 버튼
Windows API 활용

SetWindowsHookEx 또는 WindowFromPoint 등을 이용
일반적으로 마우스 후킹을 통해 마우스가 클릭된 순간 HWND를 얻어옵니다.
얻어온 HWND를 통해 GetWindowText, GetWindowRect 등으로 정보 조회
예시 코드
cpp
복사
// targetwindowdialog.h
#ifndef TARGETWINDOWDIALOG_H
#define TARGETWINDOWDIALOG_H

#include <QDialog>
#include <Windows.h>

namespace Ui {
class TargetWindowDialog;
}

class TargetWindowDialog : public QDialog
{
    Q_OBJECT

public:
    explicit TargetWindowDialog(QWidget *parent = nullptr);
    ~TargetWindowDialog();

private slots:
    void onBtnSelectTargetClicked();
    void onBtnOkClicked();

private:
    Ui::TargetWindowDialog *ui;

    // Windows API 후킹에 필요한 정적 함수
    static LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam);
    static HHOOK s_hMouseHook;

    static TargetWindowDialog *s_instance; // 자기 자신 참조 (마우스 후킹 콜백 내부에서 접근용)

    void startHook();
    void stopHook();

    // 클릭된 창 정보
    HWND m_hTargetWnd;
    QString m_windowTitle;
    RECT m_windowRect;
};

#endif // TARGETWINDOWDIALOG_H
cpp
복사
// targetwindowdialog.cpp
#include "targetwindowdialog.h"
#include "ui_targetwindowdialog.h"
#include <QDebug>

HHOOK TargetWindowDialog::s_hMouseHook = nullptr;
TargetWindowDialog* TargetWindowDialog::s_instance = nullptr;

TargetWindowDialog::TargetWindowDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::TargetWindowDialog),
    m_hTargetWnd(nullptr)
{
    ui->setupUi(this);

    connect(ui->btnSelectTarget, &QPushButton::clicked,
            this, &TargetWindowDialog::onBtnSelectTargetClicked);
    connect(ui->btnOk, &QPushButton::clicked,
            this, &TargetWindowDialog::onBtnOkClicked);
}

TargetWindowDialog::~TargetWindowDialog()
{
    delete ui;
}

// "대상 창 선택" 버튼 클릭
void TargetWindowDialog::onBtnSelectTargetClicked()
{
    s_instance = this;
    startHook();
    // 이후 사용자가 화면에서 원하는 창을 클릭하면 MouseProc 호출
}

void TargetWindowDialog::startHook()
{
    // WH_MOUSE_LL 등으로 글로벌 마우스 후킹
    s_hMouseHook = SetWindowsHookEx(WH_MOUSE_LL, MouseProc, nullptr, 0);
}

void TargetWindowDialog::stopHook()
{
    if (s_hMouseHook)
    {
        UnhookWindowsHookEx(s_hMouseHook);
        s_hMouseHook = nullptr;
    }
}

// 마우스 후킹 콜백
LRESULT CALLBACK TargetWindowDialog::MouseProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    if (nCode == HC_ACTION)
    {
        if (wParam == WM_LBUTTONDOWN)
        {
            // 마우스 좌표를 통해 창 핸들 얻기
            MSLLHOOKSTRUCT *mouseStruct = (MSLLHOOKSTRUCT*)lParam;
            POINT pt = mouseStruct->pt;
            HWND hWnd = WindowFromPoint(pt);

            if (hWnd && s_instance)
            {
                // 창 정보 가져오기
                s_instance->m_hTargetWnd = hWnd;

                // 창 제목
                wchar_t title[256];
                GetWindowTextW(hWnd, title, 256);
                s_instance->m_windowTitle = QString::fromWCharArray(title);

                // 좌표
                RECT rc;
                GetWindowRect(hWnd, &rc);
                s_instance->m_windowRect = rc;

                // UI 갱신
                s_instance->ui->lineEditWindowTitle->setText(s_instance->m_windowTitle);
                QString info = QString("(%1, %2) - (%3, %4)")
                               .arg(rc.left).arg(rc.top)
                               .arg(rc.right).arg(rc.bottom);
                s_instance->ui->lineEditWindowRect->setText(info);

                // 후킹 해제
                s_instance->stopHook();
            }
        }
    }
    return CallNextHookEx(s_hMouseHook, nCode, wParam, lParam);
}

void TargetWindowDialog::onBtnOkClicked()
{
    // 선택된 창 정보를 저장하거나 MainWindow로 넘기는 로직
    // ...

    accept(); // 다이얼로그 닫기
}
3. 이미지 캡처 및 편집
3.1 개요
사용자가 "추가" 버튼을 클릭 → 영역 드래그로 지정
지정 영역을 OpenCV를 통해 캡처하여 저장 후, 리스트에 표시
수정(편집) 버튼을 통해 다시 영역 조정 가능
3.2 구현 단계
화면 캡처
Windows API BitBlt or PrintWindow 등을 사용해 HBITMAP 얻은 뒤 OpenCV Mat으로 변환
드래그로 영역 지정
Qt에서 QRubberBand 등을 이용해 화면에서 마우스 드래그한 영역을 시각적으로 표시
실제 캡처는 BitBlt로 전체 화면을 받아두고, 선택 영역만 잘라서 OpenCV Mat으로 저장
저장 후 리스트에 추가
이미지 목록(std::vector<cv::Mat> 혹은 QList<QImage>)에 보관
이미지 파일로도 저장(추후 재실행 시 불러오기)
(아래 예시는 “별도의 윈도우”에서 캡처 과정을 보여주는 간단한 예시입니다.
실무에서 구현 시에는 투명 창 위에 QRubberBand로 드래그 영역 표시하는 방식도 있습니다.)

4. 인식 조건 설정 (ImageConditionDialog)
4.1 개요
선택된 이미지(또는 여러 이미지)에 대해 유사도(Threshold), OCR 사용 여부, 인식 실패 시 재시도 횟수 등 설정
사용자가 설정을 마치면, 해당 값을 저장(예: JSON)
4.2 대략적 UI
유사도 슬라이더 (0~100%)
OCR 체크박스 + OCR 언어 선택 콤보박스
재시도 횟수 Spin Box 등
cpp
복사
// imageconditiondialog.h
#ifndef IMAGECONDITIONDIALOG_H
#define IMAGECONDITIONDIALOG_H

#include <QDialog>

namespace Ui {
class ImageConditionDialog;
}

class ImageConditionDialog : public QDialog
{
    Q_OBJECT

public:
    explicit ImageConditionDialog(QWidget *parent = nullptr);
    ~ImageConditionDialog();

    // 예시: 세팅 값을 담는 구조체
    struct ConditionSettings {
        int similarityThreshold;
        bool enableOCR;
        QString ocrLanguage;
        int retryCount;
    };

    void setConditionSettings(const ConditionSettings &settings);
    ConditionSettings conditionSettings() const;

private slots:
    void onBtnOkClicked();

private:
    Ui::ImageConditionDialog *ui;
    ConditionSettings m_settings;
};

#endif // IMAGECONDITIONDIALOG_H
cpp
복사
// imageconditiondialog.cpp
#include "imageconditiondialog.h"
#include "ui_imageconditiondialog.h"

ImageConditionDialog::ImageConditionDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::ImageConditionDialog)
{
    ui->setupUi(this);

    // 슬라이더, 체크박스 등 초기화
    // ...
    connect(ui->btnOk, &QPushButton::clicked, this, &ImageConditionDialog::onBtnOkClicked);
}

ImageConditionDialog::~ImageConditionDialog()
{
    delete ui;
}

void ImageConditionDialog::setConditionSettings(const ConditionSettings &settings)
{
    m_settings = settings;
    // UI에 반영
    ui->sliderSimilarity->setValue(m_settings.similarityThreshold);
    ui->checkBoxOCR->setChecked(m_settings.enableOCR);
    ui->comboBoxOCRLanguage->setCurrentText(m_settings.ocrLanguage);
    ui->spinBoxRetry->setValue(m_settings.retryCount);
}

ImageConditionDialog::ConditionSettings ImageConditionDialog::conditionSettings() const
{
    return m_settings;
}

void ImageConditionDialog::onBtnOkClicked()
{
    // UI -> m_settings
    m_settings.similarityThreshold = ui->sliderSimilarity->value();
    m_settings.enableOCR = ui->checkBoxOCR->isChecked();
    m_settings.ocrLanguage = ui->comboBoxOCRLanguage->currentText();
    m_settings.retryCount = ui->spinBoxRetry->value();

    accept();
}
5. 멀티 이미지 인식 (AND/OR 조건)
멀티 이미지 설정 창에서
최대 10개 이미지 추가
각 이미지별 “인식 성공 시/실패 시” AND/OR 논리 설정
실행 시
모든 이미지에 대해 cv::matchTemplate(또는 OCR) → 결과를 종합하여 Action 수행 여부 판단
cpp
복사
// 간단한 pseudo-code
bool MultiImageCheck(const std::vector<ConditionedImage> &images)
{
    bool result = (images[0].check() ? true : false);
    for (int i = 1; i < images.size(); i++)
    {
        if (images[i].logic == "AND")
            result = result && images[i].check();
        else if (images[i].logic == "OR")
            result = result || images[i].check();
    }
    return result;
}
6. 작업(Action) 관리
6.1 액션 종류 예시
이미지 클릭: 인식된 위치(정중앙 등)에 마우스 클릭 이벤트 발생
키보드 입력: 특정 문자열을 키보드로 입력 (Windows API SendInput)
마우스 이동: 특정 좌표로 이동 후 클릭
딜레이: 일정 시간 대기
6.2 액션 리스트에서의 흐름
사용자: “액션 추가” → 액션 종류 선택
액션 상세 정보 입력 (예: 클릭 좌표, 입력 문자열, etc.)
액션 리스트에 추가
실행 시 순서대로 반복 수행
7. 비활성/활성 창 제어
Windows API SetForegroundWindow, ShowWindow 등으로 제어 가능
“비활성 창에서도 동작 가능”을 구현하려면, 좌표계가 바뀌므로 좀 더 복잡해집니다.
일반적으로는 활성 창에 대해서만 좌표/이미지 매칭을 하는 것이 편합니다.
8. 작업 실행 로그
QTextEdit에 append() 메서드로 실시간 로그 표시
파일로 저장 시 QFile 이용, 텍스트 스트림으로 기록
9. 설정(프로그램 설정, 이미지/액션/조건) 저장
JSON 파일로 저장
QJsonObject, QJsonDocument 활용
이미지 목록, 각 이미지별 인식 조건, 액션 리스트 등 구조화
프로그램 실행 시 JSON 로드 → UI 초기화
종료 시 JSON 저장
예시:

cpp
복사
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QFile>

void MainWindow::saveSettings()
{
    QJsonObject root;
    // 예시: 이미지 리스트 저장
    QJsonArray imageArray;
    for (auto &img : m_imageList) {
        QJsonObject obj;
        obj["filename"] = img.filename;
        obj["similarityThreshold"] = img.condition.similarityThreshold;
        // ...
        imageArray.append(obj);
    }
    root["images"] = imageArray;

    // 액션 리스트 저장
    // ...

    // 파일로 쓰기
    QFile saveFile("settings.json");
    if (saveFile.open(QFile::WriteOnly)) {
        QJsonDocument doc(root);
        saveFile.write(doc.toJson());
    }
}
단계별 요약
개발 환경 설정
Qt, OpenCV, Windows API 사용 준비
프로젝트 생성
Qt Creator(또는 원하는 IDE)로 기본 Qt Widgets Application 생성
메인 윈도우
대상 창 설정 버튼, 이미지 리스트, 액션 리스트, 로그 출력 등을 배치
“대상 창 설정” 클릭 → TargetWindowDialog 팝업 표시
“인식 조건 설정” 클릭 → ImageConditionDialog 팝업 표시
대상 창 설정(후킹)
Windows API를 사용해 마우스로 클릭한 창의 HWND, 제목, 좌표 등을 획득
이미지 캡처
전체 화면을 캡처 → 드래그 영역을 OpenCV Mat으로 잘라내어 저장
이미지 리스트에 추가
인식 조건 설정
유사도(Threshold), OCR 설정, 재시도 횟수 등
멀티 이미지 인식
최대 10개 이미지 / AND/OR 조건 조합
액션 관리
이미지 클릭, 키보드 입력, 마우스 이동, 딜레이 등
액션 실행 순서 관리
비활성/활성 창 제어
Windows API로 창 활성화/숨기기 등
작업 실행 로그
실시간으로 QTextEdit에 출력, 파일 저장
설정 저장/불러오기
JSON 파일로 이미지 및 액션, 기타 환경 설정 저장
마무리
위의 내용은 크게 어떤 구조로 C++/Qt에서 구현할 수 있는지를 단계별로 설명한 것이고,
실제로 구현하려면 세부 함수(마우스 후킹, 화면 캡처, 템플릿 매칭, OCR 라이브러리(Tesseract 등) 연동 등)를 추가로 작성해야 합니다.

처음 C++을 접하신다면, 단계별로 작은 예제를 만들어보는 것을 강력히 권장드립니다.

“간단한 Qt 윈도우 + 버튼”
“마우스 후킹, 창 정보 얻기”
“OpenCV로 화면 캡처/이미지 매칭”
“UI 다이얼로그 연동”
“프로그램 설정(JSON) 저장/불러오기”
이렇게 각 부분 기능을 하나씩 테스트하고 조합해 나가면, 최종적으로 요청하신 구조를 완성할 수 있을 것입니다.

이상으로, C++/Qt/OpenCV를 활용한 구현 개요와 예시 코드를 단계별로 살펴보았습니다.
추가 궁금한 점이 있으면 언제든 말씀해주세요! 꼭 완성하시길 응원합니다.






나의 말:




Visual Studio
나는 이것을 기준으로 프로그램을 만들고싶어 
그리고 아직 설명에 대한 이미지는 남아있는데 
4개씩 밖에 안올라가서 질문이나 문의를 할때마다 올려주는 이미지들은 각 각 버튼을 눌렀을때 나오는 설정옵션들인데 그것들도 프로그램에 반영해주길 원해요. 
ChatGPT

11초 동안 Thought about C++ program with Visual Studio
아래 예시에서는 **Visual Studio(MFC 기반)**로 C++ 윈도우 프로그램을 만드는 흐름을 간단히 보여드립니다.
이미 Qt 예시를 드린 적이 있지만, “Visual Studio + MFC” 조합으로도 손쉽게 다중 대화상자(Dialog) 프로그램을 구성할 수 있습니다.
(물론 Win32 API 순수 코드나 WTL, Qt 등 다른 방식도 가능합니다만, 여기서는 MFC를 예로 들어 **“단계별로 어떻게 구성하면 되는지”**에 초점을 맞춰 설명하겠습니다.)

1. 프로젝트 생성 & 기초 구조
1.1 Visual Studio 에서 새 프로젝트 생성
File → New → Project
MFC App (MFC 애플리케이션) 선택
프로젝트 이름과 경로 지정 후 Next
대화형 옵션에서 기본값(예: Dialog based 또는 Single document 등)으로 생성
여기서는 Dialog based 유형을 예시로 하겠습니다.

생성하면 CYourProjectApp, CYourProjectDlg 등의 클래스가 자동으로 만들어집니다.
1.2 리소스 & 클래스 개념
리소스: 대화상자(.rc 파일) 안에 배치되는 버튼, 에디트 컨트롤, 체크박스 등
클래스: 각 대화상자를 담당하는 C++ 클래스(CDialog 기반)
Visual Studio의 Resource View에서 각 Dialog 리소스를 열어보고, 거기에 컨트롤(버튼, 에디트, 콤보박스 등)을 배치한 뒤, Class Wizard(또는 Add Variable/Handler)로 이벤트 함수를 연결합니다.
2. 메인 대화상자 (예: CMainDlg)
사용자가 처음 보게 되는 “메인 창” 역할을 합니다.
이 안에 다음과 같은 UI를 구성하면 됩니다.

(1) 이미지 리스트 (예: CListBox 또는 CListCtrl)
(2) 인식 대상 창 설정 버튼
(3) 이미지 추가 / 편집 / 삭제 버튼
(4) 인식 조건 설정 버튼
(5) 액션 리스트 (예: CListBox 또는 CListCtrl)
(6) 로그 출력 창 (멀티라인 Edit Control)
(7) [시작/중지/일시정지] 등 실행 관련 버튼들
물론 실제 배치는 자유롭게 하시면 됩니다.

예) 메인 대화상자 리소스 IDD_MAINDLG 위젯들:

버튼: IDC_BTN_TARGET_SETTING (“대상 창 설정”)
리스트박스: IDC_LIST_IMAGES (이미지 목록)
버튼: IDC_BTN_IMAGE_ADD, IDC_BTN_IMAGE_EDIT, IDC_BTN_IMAGE_DELETE
버튼: IDC_BTN_CONDITION_SETTING (“인식 조건 설정”)
리스트박스: IDC_LIST_ACTIONS (액션 목록)
멀티라인 Edit: IDC_EDIT_LOG (로그 출력)
버튼: IDC_BTN_START, IDC_BTN_STOP, IDC_BTN_PAUSE
2.1 클래스 파일
Visual Studio가 자동으로 CYourProjectDlg(또는 CMainDlg)을 만들어줄 텐데, 이 안에서 필요한 멤버와 함수를 정의합니다.
cpp
복사
// CMainDlg.h
#pragma once

#include <vector>
#include <string>

// Forward Declarations
class CTargetWindowDlg; 
class CImageConditionDlg;
class CDelaySettingDlg;
class CKeyboardInputDlg;
class CMouseInputDlg;
// ...

class CMainDlg : public CDialogEx
{
// 생성
public:
    CMainDlg(CWnd* pParent = nullptr);    // 기본 생성자

// 대화 상자 데이터
#ifdef AFX_DESIGN_TIME
    enum { IDD = IDD_MAINDLG }; 
#endif

protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 지원

// 구현
protected:
    HICON m_hIcon;

    // 각 팝업 대화상자 객체 (필요시 생성해 사용)
    CTargetWindowDlg*     m_pTargetWindowDlg;
    CImageConditionDlg*   m_pImageConditionDlg;
    CDelaySettingDlg*     m_pDelaySettingDlg;
    CKeyboardInputDlg*    m_pKeyboardInputDlg;
    CMouseInputDlg*       m_pMouseInputDlg;

    // TODO: 이미지/액션/설정 관리에 필요한 멤버들
    // 예: std::vector<ImageInfo> m_vImages;
    //     std::vector<ActionInfo> m_vActions;

    // 메시지 맵 함수
    virtual BOOL OnInitDialog();
    afx_msg void OnPaint();
    afx_msg HCURSOR OnQueryDragIcon();
    DECLARE_MESSAGE_MAP()

public:
    // 버튼 클릭 핸들러
    afx_msg void OnBnClickedBtnTargetSetting();
    afx_msg void OnBnClickedBtnImageAdd();
    afx_msg void OnBnClickedBtnImageEdit();
    afx_msg void OnBnClickedBtnConditionSetting();
    afx_msg void OnBnClickedBtnStart();
    // ... etc

    void AppendLog(const CString& msg);
};
cpp
복사
// CMainDlg.cpp
#include "pch.h"
#include "CMainDlg.h"
#include "TargetWindowDlg.h"
#include "ImageConditionDlg.h"
#include "DelaySettingDlg.h"
#include "KeyboardInputDlg.h"
#include "MouseInputDlg.h"
#include "afxdialogex.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

BOOL CMainDlg::OnInitDialog()
{
    CDialogEx::OnInitDialog();

    // 아이콘 설정
    SetIcon(m_hIcon, TRUE);
    SetIcon(m_hIcon, FALSE);

    // 팝업 대화상자 객체 생성
    m_pTargetWindowDlg   = new CTargetWindowDlg(this);
    m_pImageConditionDlg = new CImageConditionDlg(this);
    m_pDelaySettingDlg   = new CDelaySettingDlg(this);
    m_pKeyboardInputDlg  = new CKeyboardInputDlg(this);
    m_pMouseInputDlg     = new CMouseInputDlg(this);

    // TODO: 리스트박스 초기화, 컨트롤 초기화 등

    return TRUE;
}

void CMainDlg::OnBnClickedBtnTargetSetting()
{
    // "대상 창 설정" 버튼 클릭 → 대상 창 설정 대화상자 표시
    if (m_pTargetWindowDlg)
        m_pTargetWindowDlg->DoModal();
}

void CMainDlg::OnBnClickedBtnImageAdd()
{
    // 이미지 캡처/추가 관련 로직
    // ...
}

void CMainDlg::OnBnClickedBtnImageEdit()
{
    // 선택된 이미지를 편집(캡처 영역 재조정 등)
    // ...
}

void CMainDlg::OnBnClickedBtnConditionSetting()
{
    // 인식 조건 설정 대화상자 띄우기
    if (m_pImageConditionDlg)
    {
        // 현재 선택된 이미지나 옵션을 m_pImageConditionDlg 에 전달
        // ...
        m_pImageConditionDlg->DoModal();
    }
}

void CMainDlg::OnBnClickedBtnStart()
{
    // 실행 로직
    AppendLog(_T("작업 실행 시작"));
    // ...
}

void CMainDlg::AppendLog(const CString& msg)
{
    // IDC_EDIT_LOG 멀티라인 Edit Control에 로그 추가
    // 현재 텍스트를 받아와서 끝에 덧붙이는 방식
    CEdit* pLogEdit = (CEdit*)GetDlgItem(IDC_EDIT_LOG);
    if (pLogEdit)
    {
        CString current;
        pLogEdit->GetWindowText(current);

        SYSTEMTIME st;
        GetLocalTime(&st);
        CString timeStr;
        timeStr.Format(_T("[%02d:%02d:%02d] "), st.wHour, st.wMinute, st.wSecond);

        current += timeStr + msg + _T("\r\n");
        pLogEdit->SetWindowText(current);
        pLogEdit->LineScroll(pLogEdit->GetLineCount()); // 자동 스크롤
    }
}
3. 각 팝업 대화상자
3.1 “딜레이(지연) 설정” 다이얼로그 예 (CDelaySettingDlg)
3.1.1 리소스 만들기
Resource View → Add Resource → Dialog
예: IDD_DELAYSETTING_DLG 라는 ID로 생성
안에
분: IDC_EDIT_MINUTE, 초: IDC_EDIT_SECOND (Edit Control)
랜덤 지연 체크: IDC_CHECK_RANDOM (Check Box)
추가 입력: IDC_EDIT_RANDOM_MIN, IDC_EDIT_RANDOM_MAX 등
OK / 취소 버튼
3.1.2 대화상자 클래스 생성
Class Wizard → 새 클래스로 CDelaySettingDlg 생성
cpp
복사
// DelaySettingDlg.h
#pragma once
#include "afxdialogex.h"

class CDelaySettingDlg : public CDialogEx
{
    DECLARE_DYNAMIC(CDelaySettingDlg)

public:
    CDelaySettingDlg(CWnd* pParent = nullptr);
    virtual ~CDelaySettingDlg();

// 대화상자 데이터
#ifdef AFX_DESIGN_TIME
    enum { IDD = IDD_DELAYSETTING_DLG };
#endif

protected:
    virtual void DoDataExchange(CDataExchange* pDX);
    DECLARE_MESSAGE_MAP()

public:
    // 사용자 입력값 저장할 멤버
    int m_nMinute;
    double m_dSecond;   // 0.1초 단위로 입력 가능하다면 double로
    BOOL m_bRandom; 
    double m_dRandMin;
    double m_dRandMax;

    virtual BOOL OnInitDialog();
    afx_msg void OnBnClickedOk();
};
cpp
복사
// DelaySettingDlg.cpp
#include "pch.h"
#include "DelaySettingDlg.h"
#include "afxdialogex.h"

IMPLEMENT_DYNAMIC(CDelaySettingDlg, CDialogEx)

CDelaySettingDlg::CDelaySettingDlg(CWnd* pParent /*=nullptr*/)
    : CDialogEx(IDD_DELAYSETTING_DLG, pParent)
    , m_nMinute(0)
    , m_dSecond(0.0)
    , m_bRandom(FALSE)
    , m_dRandMin(0.0)
    , m_dRandMax(0.0)
{
}

CDelaySettingDlg::~CDelaySettingDlg()
{
}

void CDelaySettingDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialogEx::DoDataExchange(pDX);
    // DDX: 각 컨트롤과 멤버 변수를 매핑
    DDX_Text(pDX, IDC_EDIT_MINUTE, m_nMinute);
    DDX_Text(pDX, IDC_EDIT_SECOND, m_dSecond);
    DDX_Check(pDX, IDC_CHECK_RANDOM, m_bRandom);
    DDX_Text(pDX, IDC_EDIT_RANDOM_MIN, m_dRandMin);
    DDX_Text(pDX, IDC_EDIT_RANDOM_MAX, m_dRandMax);
}

BEGIN_MESSAGE_MAP(CDelaySettingDlg, CDialogEx)
    ON_BN_CLICKED(IDOK, &CDelaySettingDlg::OnBnClickedOk)
END_MESSAGE_MAP()

BOOL CDelaySettingDlg::OnInitDialog()
{
    CDialogEx::OnInitDialog();
    // 초기화 필요한 부분 (컨트롤 상태 세팅 등)
    UpdateData(FALSE); // 멤버→UI
    return TRUE;
}

void CDelaySettingDlg::OnBnClickedOk()
{
    // OK 버튼 눌렀을 때
    if (!UpdateData(TRUE)) {
        // 잘못된 입력이면 그냥 리턴
        return;
    }
    // TODO: 입력값 검증 (예: 음수가 아닌지, 범위가 맞는지 등)
    CDialogEx::OnOK(); // 대화상자 닫기
}
이렇게 하면 CDelaySettingDlg를 모달로 띄우면 사용자 입력이 m_nMinute, m_dSecond, m_bRandom 등에 담기게 됩니다.
메인에서 사용할 때:

cpp
복사
// 예: CMainDlg::OnBnClickedBtnDelaySetting() 안에서
CDelaySettingDlg dlg;
if (dlg.DoModal() == IDOK)
{
    // dlg.m_nMinute, dlg.m_dSecond 등 읽어서 처리
}
3.2 “키보드 입력 설정” 다이얼로그 예 (CKeyboardInputDlg)
마찬가지로 리소스: IDD_KEYBOARDINPUT_DLG
컨트롤: “입력할 문자열” (Edit Control), “딜레이(지연)”, “입력 속도” (콤보박스) 등등
클래스: CKeyboardInputDlg 만들어서 DDX/DDV 처리
cpp
복사
// KeyboardInputDlg.h
class CKeyboardInputDlg : public CDialogEx
{
public:
    CString m_strText;     // 입력 문자열
    int m_nDelayMs;        // 딜레이 (ms 단위)
    int m_nSpeedType;      // 속도 (0=느림,1=중간,2=빠름 등)

    // ...
};
구현 방식은 앞서 “딜레이 설정”과 거의 동일하니 생략합니다.

3.3 “마우스 입력 설정” 다이얼로그 예 (CMouseInputDlg)
리소스: IDD_MOUSEINPUT_DLG
옵션: 클릭/더블클릭/드래그 등, 버튼 종류(좌/우), 속도, 좌표, 랜덤 범위, 반복 횟수 등
구현 패턴은 위와 동일
3.4 “인식 조건 설정” 다이얼로그 예 (CImageConditionDlg)
리소스: IDD_IMAGECONDITION_DLG
UI: 유사도 슬라이더, OCR 체크박스, 재시도 횟수, etc.
역시 DDX/DDV로 연결
3.5 “대상 창 설정” 다이얼로그 예 (CTargetWindowDlg)
마우스 후킹(SetWindowsHookEx) 또는 WindowFromPoint를 이용한 대상 창 선택 로직
좌표, 창 이름, 숨기기 여부 체크박스 등
구조는 동일하고, 핵심은 “사용자가 ‘창 선택’ 버튼을 누르면, 마우스 클릭한 HWND를 가져와서 제목/좌표를 표시” 하는 부분입니다.
4. 동작 흐름 & 데이터 저장
위에서 만든 메인 대화상자 + 각 설정 대화상자들이 상호작용하면서,

“이미지 캡처/추가” → std::vector<IMAGE_INFO> 에 저장
“인식 조건 설정” → 선택된 IMAGE_INFO에 m_nThreshold, m_bUseOCR 등 값을 저장
“마우스 입력 설정” → ACTION_INFO 구조체에 클릭 좌표, 반복 횟수 등 기록
등등
을 수행하게 됩니다.
4.1 액션 구조체 예시
cpp
복사
enum class ActionType {
    ImageClick,
    MouseInput,
    KeyboardInput,
    Delay,
    // ...
};

struct ActionInfo {
    ActionType type;
    CString    desc;      // 액션을 설명하는 문구(예: "마우스 좌클릭(100,200)")
    // 각각 액션에 필요한 파라미터들 (Union or 별도 구조 사용)
    // ex) mouseX, mouseY, clickType, keyString, delayMs ...
};
대화상자에서 입력 받은 정보를 ActionInfo로 만들어 메인 대화상자의 리스트에 추가
4.2 설정/로그 파일 저장
MFC에서 CStdioFile, CFile 등을 써서 텍스트 파일로 저장하거나,
JSON 저장을 하려면 nlohmann/json 같은 C++ 라이브러리를 가져다 쓸 수도 있습니다.
5. 실제 “이미지 인식” 구현 (OpenCV 연동)
OpenCV 설치: OpenCV 공식에서 Windows용 라이브러리 받아서,
Include 경로, Lib 경로를 프로젝트 속성에 추가
프로젝트 속성
C/C++ → Additional Include Directories 에 $(OpenCVPath)\include
Linker → Additional Library Directories 에 $(OpenCVPath)\x64\vc15\lib
Linker → Input 에 opencv_world480.lib (버전번호는 실제 설치버전에 맞춤)
소스에서 OpenCV 함수 사용
#include <opencv2/opencv.hpp>
화면 캡처 (BitBlt → HBITMAP → cv::Mat 변환)
혹은 PrintWindow(비활성 창 캡처)
매칭
cv::matchTemplate 로 유사도 구하기
성공/실패 판단 후, Action 실행
예)

cpp
복사
// Pseudo-code
cv::Mat screenMat = CaptureScreen(); // 사용자 정의 함수
cv::Mat targetMat = LoadMyImage();   // 캡처/저장해둔 이미지
cv::Mat result;
cv::matchTemplate(screenMat, targetMat, result, cv::TM_CCOEFF_NORMED);
double maxVal;
cv::minMaxLoc(result, nullptr, &maxVal, nullptr, nullptr);
if (maxVal > threshold) {
    // 매칭 성공 → 좌표 찾기 → 마우스 입력
}
6. 마무리 정리
MFC Dialog 기반으로 여러 설정 팝업(Delay, 키보드입력, 마우스입력, 인식조건, 대상창설정 등)을 각각 리소스-클래스 쌍으로 만들어 둡니다.
메인 대화상자에서 버튼을 누르면, 해당 팝업 대화상자를 DoModal()로 열어 사용자에게 설정을 입력받습니다.
설정 결과는 각 대화상자 클래스 멤버 변수로 보관 후, OnOK()에서 UpdateData(TRUE) 해 가져옵니다.
가져온 데이터는 메인(또는 전역/싱글톤 매니저)에 저장해두고, 실행 시 OpenCV 등으로 이미지 매칭, Windows API로 마우스/키보드 입력을 구현합니다.
로그는 CEdit 멀티라인에 계속 SetWindowText로 누적. 필요하면 파일로도 저장합니다.
이런 식으로 각 버튼마다 뜨는 설정 창들을 전부 MFC Dialog로 만들어 연결해주시면,
질문에서 보여주신 ImageMax 2.53과 유사한 구조의 프로그램을 C++(MFC)로 구현할 수 있습니다.

추가팁
추가팁
처음부터 한꺼번에 구현하려 하지 말고,

“메인 대화상자 + 간단한 팝업 대화상자” 만들기
“Windows API를 통한 마우스 클릭 이벤트” 테스트
“OpenCV로 화면 캡처/매칭” 테스트
각 기능 조합
순으로 나누어 단계별로 완성하는 것을 강력 추천드립니다.
UI/이벤트와 로직을 분리해서, 나중에 유지보수하기 쉽게 설계해두는 것도 중요합니다.

실행/종료 시 환경 저장(JSON, INI, XML 등)은 나중에 넣어도 됩니다.

이상으로 Visual Studio(MFC) 기반으로 여러 개의 설정 대화상자를 가지는 프로그램을 만드는 전반적인 흐름을 정리해 드렸습니다.
추가 이미지(설정 옵션 창들)도 모두 “대화상자를 하나씩 더 만든다”는 동일한 패턴으로 반영할 수 있으니,
그때마다 리소스(.rc)에 Dialog 추가 → 클래스 생성 → 버튼 핸들러 연결 과정을 반복해서 구성하시면 됩니다.

응원합니다! 모쪼록 조금씩 기능을 나누어 차근차근 구현해보시길 바랍니다.






나의 말:



각각 기능들을 전부 내용에 맞게 구현한 코드를 제공해주세요.
직관적으로 명칭이 되어있기에 그것에 대해 
별도의 설명은 필요없을거같아 생략한거라서요.
전체 틀이나 구조는 
프로그램을 실행해 
제어할 창을 선택하고
그 창을 기준으로 창과 내가 추가한 이미지들과 비교하여 
그것에 대해 어떤 액션을 취할지 정해 이미지리스트들을 
수회하며 그것을 제어들을 진행한다가 목적이라서요.

ChatGPT

6초 동안 Thought about C++ program structure
아래 예시는 MFC(Dialog 기반) 프로젝트에서 각 기능(대상 창 설정, 이미지 리스트, 인식 조건, 멀티 이미지 설정, 활성/비활성 설정, 액션 추가 등)을 하나의 예시 코드로 간단히 구현한 “샘플 뼈대 코드”입니다.

실제 리소스(.rc)나 클래스 마법사로 생성되는 부분은 생략/간소화했습니다.
핵심 흐름과 각 대화상자(팝업)에서 어떤 식으로 코드를 구성하면 되는지 보여드리기 위한 예시입니다.
“직관적으로 명칭만 보고도 어떤 기능인지 이해할 수 있게” 각 다이얼로그 클래스 이름/컨트롤 이름을 그대로 사용했습니다.
주의

실제로 컴파일하려면, 프로젝트를 만들고 리소스(대화상자, 컨트롤 ID), 헤더/소스 분리, 프로젝트 속성(Unicode 설정 등)을 제대로 셋업해야 합니다.
아래 코드는 *“구조 예시”*이며, 모든 세부 기능(화면 캡처, OpenCV 매칭, 마우스 후킹 등)은 실제 구현 로직을 “TODO” 또는 “임시 함수” 등으로 처리했습니다.
디렉토리 구조 예시
scss
복사
MFCImageControlSample/
 ├─ MFCImageControlSample.cpp  (WinMain 진입점 포함)
 ├─ MFCImageControlSample.h
 ├─ MainDlg.cpp / MainDlg.h
 ├─ TargetWindowDlg.cpp / TargetWindowDlg.h
 ├─ ImageConditionDlg.cpp / ImageConditionDlg.h
 ├─ MultiImageDlg.cpp / MultiImageDlg.h
 ├─ ActivationDlg.cpp / ActivationDlg.h
 ├─ ActionAddDlg.cpp / ActionAddDlg.h
 ├─ resource.h
 ├─ MFCImageControlSample.rc (리소스: 대화상자, 아이콘, 메뉴 등)
 └─ ... (기타)
아래는 설명 편의상 “하나의 소스”에 줄줄이 적었지만,
실제로는 .h/.cpp, .rc, resource.h 등으로 나눠서 관리합니다.

1) 메인 대화상자 (CMainDlg)
주요 UI:
(1) “인식 대상 창 설정” 버튼
(2) “이미지 리스트” (리스트 박스 등)
(3) “편집”, “추가” 버튼
(4) “인식 조건” 설정 버튼
(5) “멀티 이미지” 설정 버튼
(6) “인식 활성/비활성 설정” 버튼
(7) “Action List” (리스트 박스/리스트 컨트롤)
(8) “시작(START), 정지(STOP), 일시정지(PAUSE)” 버튼
(9) 로그 출력 (멀티라인 Edit)
cpp
복사
// MainDlg.h ----------------------------------------------
#pragma once
#include "afxdialogex.h"
#include <vector>
#include <string>

// forward declarations
class CTargetWindowDlg;
class CImageConditionDlg;
class CMultiImageDlg;
class CActivationDlg;
class CActionAddDlg;

// 이미지/액션 정보를 담을 구조체 (예시)
struct ImageInfo
{
    CString imageName;
    CString filePath;
    // 인식 조건 등...
    double similarity;
    bool   useOCR;
    // ...
};

enum class ActionType
{
    IMAGE_DISPLAY,
    IMAGE_CLICK,
    INPUT_MOUSE,
    INPUT_KEYBOARD,
    DELAY,
    ACTIVATE_INACTIVATE,
    RECOGNITION_FAIL,
    // etc...
};

struct ActionItem
{
    ActionType type;
    CString    desc;      // 예: "이미지 클릭: Image001"
    // 파라미터들 (좌표, 시간, 문자열 등)...
};

class CMainDlg : public CDialogEx
{
public:
    CMainDlg(CWnd* pParent = nullptr);
    enum { IDD = IDD_MAINDLG };

protected:
    virtual void DoDataExchange(CDataExchange* pDX);
    virtual BOOL OnInitDialog();
    DECLARE_MESSAGE_MAP()

// 멤버
private:
    // 팝업 대화상자들
    CTargetWindowDlg*    m_pTargetWindowDlg;
    CImageConditionDlg*   m_pImageConditionDlg;
    CMultiImageDlg*       m_pMultiImageDlg;
    CActivationDlg*       m_pActivationDlg;
    CActionAddDlg*        m_pActionAddDlg;

    // 이미지 목록, 액션 목록
    std::vector<ImageInfo> m_vImages;
    std::vector<ActionItem> m_vActions;

    // 로그 출력 편의
    void AppendLog(const CString& msg);

// 메시지 핸들러
public:
    afx_msg void OnBnClickedBtnTargetWindow();
    afx_msg void OnBnClickedBtnImageAdd();
    afx_msg void OnBnClickedBtnImageEdit();
    afx_msg void OnBnClickedBtnConditionSetting();
    afx_msg void OnBnClickedBtnMultiImage();
    afx_msg void OnBnClickedBtnActivation();
    afx_msg void OnBnClickedBtnAddAction();
    afx_msg void OnBnClickedBtnStart();
    afx_msg void OnBnClickedBtnStop();
    afx_msg void OnBnClickedBtnPause();
};

// MainDlg.cpp --------------------------------------------
#include "pch.h"
#include "MainDlg.h"
#include "TargetWindowDlg.h"
#include "ImageConditionDlg.h"
#include "MultiImageDlg.h"
#include "ActivationDlg.h"
#include "ActionAddDlg.h"
#include "resource.h"

// 리소스 ID는 예시용
// #define IDC_BTN_TARGET_WINDOW  1001
// #define IDC_LIST_IMAGES        1002
// #define IDC_BTN_IMAGE_ADD      1003
// ... etc

IMPLEMENT_DYNAMIC(CMainDlg, CDialogEx)

CMainDlg::CMainDlg(CWnd* pParent /*=nullptr*/)
    : CDialogEx(IDD_MAINDLG, pParent)
{
    m_pTargetWindowDlg = nullptr;
    m_pImageConditionDlg = nullptr;
    m_pMultiImageDlg = nullptr;
    m_pActivationDlg = nullptr;
    m_pActionAddDlg = nullptr;
}

void CMainDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialogEx::DoDataExchange(pDX);
    // DDX_Control(...) 등 필요하면 추가
}

BEGIN_MESSAGE_MAP(CMainDlg, CDialogEx)
    ON_BN_CLICKED(IDC_BTN_TARGET_WINDOW, &CMainDlg::OnBnClickedBtnTargetWindow)
    ON_BN_CLICKED(IDC_BTN_IMAGE_ADD, &CMainDlg::OnBnClickedBtnImageAdd)
    ON_BN_CLICKED(IDC_BTN_IMAGE_EDIT, &CMainDlg::OnBnClickedBtnImageEdit)
    ON_BN_CLICKED(IDC_BTN_CONDITION_SETTING, &CMainDlg::OnBnClickedBtnConditionSetting)
    ON_BN_CLICKED(IDC_BTN_MULTI_IMAGE, &CMainDlg::OnBnClickedBtnMultiImage)
    ON_BN_CLICKED(IDC_BTN_ACTIVATION, &CMainDlg::OnBnClickedBtnActivation)
    ON_BN_CLICKED(IDC_BTN_ADD_ACTION, &CMainDlg::OnBnClickedBtnAddAction)
    ON_BN_CLICKED(IDC_BTN_START, &CMainDlg::OnBnClickedBtnStart)
    ON_BN_CLICKED(IDC_BTN_STOP, &CMainDlg::OnBnClickedBtnStop)
    ON_BN_CLICKED(IDC_BTN_PAUSE, &CMainDlg::OnBnClickedBtnPause)
END_MESSAGE_MAP()

BOOL CMainDlg::OnInitDialog()
{
    CDialogEx::OnInitDialog();

    // 팝업 생성
    m_pTargetWindowDlg = new CTargetWindowDlg(this);
    m_pImageConditionDlg = new CImageConditionDlg(this);
    m_pMultiImageDlg = new CMultiImageDlg(this);
    m_pActivationDlg = new CActivationDlg(this);
    m_pActionAddDlg = new CActionAddDlg(this);

    // 필요한 초기화(리스트박스 연결 등)...

    return TRUE;
}

void CMainDlg::AppendLog(const CString& msg)
{
    // IDC_EDIT_LOG 같은 멀티라인 에디트에 로그 출력
    CEdit* pLog = (CEdit*)GetDlgItem(IDC_EDIT_LOG);
    if (pLog)
    {
        CString text;
        pLog->GetWindowText(text);

        // 타임스탬프 추가
        SYSTEMTIME st;
        GetLocalTime(&st);
        CString timeStr;
        timeStr.Format(_T("[%02d:%02d:%02d] "), st.wHour, st.wMinute, st.wSecond);

        text += timeStr + msg + _T("\r\n");
        pLog->SetWindowText(text);
        pLog->LineScroll(pLog->GetLineCount());
    }
}

// 아래부터 버튼 클릭 핸들러들
void CMainDlg::OnBnClickedBtnTargetWindow()
{
    // 대상 창 설정 대화상자 표시
    if (m_pTargetWindowDlg)
        m_pTargetWindowDlg->DoModal();
}

void CMainDlg::OnBnClickedBtnImageAdd()
{
    // TODO: 이미지 캡처 or 파일 선택 → m_vImages.push_back(...)
    AppendLog(_T("이미지 추가 버튼 클릭"));
}

void CMainDlg::OnBnClickedBtnImageEdit()
{
    // TODO: 이미지 편집(재캡처 등)
    AppendLog(_T("이미지 편집 버튼 클릭"));
}

void CMainDlg::OnBnClickedBtnConditionSetting()
{
    // 인식 조건 설정
    if (m_pImageConditionDlg)
    {
        // TODO: 예시 - 첫 번째 이미지를 가져가서 설정
        if (!m_vImages.empty())
        {
            // 대화상자에 현재 이미지 조건 전달
            m_pImageConditionDlg->m_fSimilarity = m_vImages[0].similarity;
            m_pImageConditionDlg->m_bUseOCR = m_vImages[0].useOCR;
        }
        if (m_pImageConditionDlg->DoModal() == IDOK)
        {
            // 설정값 반영
            if (!m_vImages.empty())
            {
                m_vImages[0].similarity = m_pImageConditionDlg->m_fSimilarity;
                m_vImages[0].useOCR = m_pImageConditionDlg->m_bUseOCR;
            }
            AppendLog(_T("인식 조건 설정 완료"));
        }
    }
}

void CMainDlg::OnBnClickedBtnMultiImage()
{
    // 멀티 이미지 설정
    if (m_pMultiImageDlg)
    {
        // TODO: 현재 m_vImages 를 m_pMultiImageDlg 쪽으로 넘겨서 AND/OR 설정
        m_pMultiImageDlg->DoModal();
    }
}

void CMainDlg::OnBnClickedBtnActivation()
{
    if (m_pActivationDlg)
    {
        // 인식 활성/비활성 설정
        m_pActivationDlg->DoModal();
    }
}

void CMainDlg::OnBnClickedBtnAddAction()
{
    // 액션 추가
    if (m_pActionAddDlg)
    {
        if (m_pActionAddDlg->DoModal() == IDOK)
        {
            // 대화상자에서 선택한 액션을 m_vActions에 추가
            ActionItem item;
            item.type = m_pActionAddDlg->m_selectedAction;
            item.desc = m_pActionAddDlg->m_strDescription;
            m_vActions.push_back(item);
            AppendLog(_T("액션 추가: ") + item.desc);
        }
    }
}

void CMainDlg::OnBnClickedBtnStart()
{
    AppendLog(_T("작업 시작"));

    // TODO: 1) 대상 창 찾기
    //       2) 이미지들 순회하며 인식 조건 검사
    //       3) 조건 만족 시 ActionList 실행
    // 임시 예시:
    for (auto& img : m_vImages)
    {
        // 이미지 인식 (matchTemplate 등)
        bool recognized = true; // 예시 가정
        if (recognized)
        {
            AppendLog(_T("[인식성공] : ") + img.imageName);
            // 액션 수행
            for (auto& act : m_vActions)
            {
                // TODO: act.type에 따라 마우스 클릭 / 키보드 입력 / 지연 등 수행
                AppendLog(_T(" → Action: ") + act.desc);
            }
        }
        else
        {
            AppendLog(_T("[인식실패] : ") + img.imageName);
            // 인식 실패 액션 등
        }
    }
}

void CMainDlg::OnBnClickedBtnStop()
{
    AppendLog(_T("작업 중지"));
    // TODO: 실행 중인 스레드나 로직 중단
}

void CMainDlg::OnBnClickedBtnPause()
{
    AppendLog(_T("작업 일시정지"));
    // TODO: 일시정지 로직
}
2) 대상 창 설정 (CTargetWindowDlg)
사용자에게 “어느 윈도우(프로세스)를 타겟으로 할지”를 고르게 하는 기능
여기선 간단히 “창 선택” 버튼을 누르면 마우스 클릭한 HWND를 얻어와서 저장하는 식
cpp
복사
// TargetWindowDlg.h ---------------------------------------
#pragma once
#include "afxdialogex.h"

class CTargetWindowDlg : public CDialogEx
{
    DECLARE_DYNAMIC(CTargetWindowDlg)

public:
    CTargetWindowDlg(CWnd* pParent = nullptr);
    virtual ~CTargetWindowDlg();

#ifdef AFX_DESIGN_TIME
    enum { IDD = IDD_TARGETWINDOW_DLG };
#endif

protected:
    virtual void DoDataExchange(CDataExchange* pDX);
    DECLARE_MESSAGE_MAP()

private:
    static HHOOK s_hMouseHook;
    static CTargetWindowDlg* s_pThis;

public:
    CString m_strWindowTitle;
    CRect   m_windowRect;

    virtual BOOL OnInitDialog();
    afx_msg void OnBnClickedBtnSelectWindow();
    afx_msg void OnBnClickedOk();

    void StartHook();
    void StopHook();
    static LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam);
};

// TargetWindowDlg.cpp -------------------------------------
#include "pch.h"
#include "TargetWindowDlg.h"
#include "resource.h"

HHOOK CTargetWindowDlg::s_hMouseHook = nullptr;
CTargetWindowDlg* CTargetWindowDlg::s_pThis = nullptr;

IMPLEMENT_DYNAMIC(CTargetWindowDlg, CDialogEx)

CTargetWindowDlg::CTargetWindowDlg(CWnd* pParent /*=nullptr*/)
    : CDialogEx(IDD_TARGETWINDOW_DLG, pParent)
{
}

CTargetWindowDlg::~CTargetWindowDlg()
{
}

void CTargetWindowDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialogEx::DoDataExchange(pDX);
    // DDX_Text(pDX, IDC_EDIT_WINDOWTITLE, m_strWindowTitle);
    // 좌표 표시 등도 연결
}

BEGIN_MESSAGE_MAP(CTargetWindowDlg, CDialogEx)
    ON_BN_CLICKED(IDC_BTN_SELECTWINDOW, &CTargetWindowDlg::OnBnClickedBtnSelectWindow)
    ON_BN_CLICKED(IDOK, &CTargetWindowDlg::OnBnClickedOk)
END_MESSAGE_MAP()

BOOL CTargetWindowDlg::OnInitDialog()
{
    CDialogEx::OnInitDialog();
    s_pThis = this;
    return TRUE;
}

void CTargetWindowDlg::OnBnClickedBtnSelectWindow()
{
    StartHook();
    AfxMessageBox(_T("선택할 창을 클릭하세요."));
}

void CTargetWindowDlg::OnBnClickedOk()
{
    // 확인 버튼
    // m_strWindowTitle, m_windowRect 등을 메인으로 넘길 수 있음
    CDialogEx::OnOK();
}

void CTargetWindowDlg::StartHook()
{
    if (!s_hMouseHook)
    {
        s_hMouseHook = SetWindowsHookEx(WH_MOUSE_LL, MouseProc, NULL, 0);
    }
}

void CTargetWindowDlg::StopHook()
{
    if (s_hMouseHook)
    {
        UnhookWindowsHookEx(s_hMouseHook);
        s_hMouseHook = nullptr;
    }
}

LRESULT CALLBACK CTargetWindowDlg::MouseProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    if (nCode == HC_ACTION && wParam == WM_LBUTTONDOWN)
    {
        PMSLLHOOKSTRUCT p = (PMSLLHOOKSTRUCT)lParam;
        POINT pt = p->pt;
        HWND hWnd = WindowFromPoint(pt);

        if (hWnd && s_pThis)
        {
            // 창 제목
            TCHAR szTitle[256];
            GetWindowText(hWnd, szTitle, 256);
            s_pThis->m_strWindowTitle = szTitle;

            // 좌표
            RECT rc;
            GetWindowRect(hWnd, &rc);
            s_pThis->m_windowRect = rc;

            // UI 갱신
            s_pThis->UpdateData(FALSE);
            s_pThis->StopHook();
            AfxMessageBox(_T("창 선택 완료: ") + s_pThis->m_strWindowTitle);
        }
    }
    return CallNextHookEx(s_hMouseHook, nCode, wParam, lParam);
}
3) 인식 조건 설정 (CImageConditionDlg)
유사도, OCR 사용 여부, 인식 실패 시 재시도 횟수 등을 설정
cpp
복사
// ImageConditionDlg.h -------------------------------------
#pragma once
#include "afxdialogex.h"

class CImageConditionDlg : public CDialogEx
{
    DECLARE_DYNAMIC(CImageConditionDlg)

public:
    CImageConditionDlg(CWnd* pParent = nullptr);
    virtual ~CImageConditionDlg();

#ifdef AFX_DESIGN_TIME
    enum { IDD = IDD_IMAGECONDITION_DLG };
#endif

protected:
    virtual void DoDataExchange(CDataExchange* pDX);
    DECLARE_MESSAGE_MAP()

public:
    double m_fSimilarity;  // 1~100% 혹은 0~1
    BOOL   m_bUseOCR;
    int    m_nRetryCount;

    afx_msg void OnBnClickedOk();
};

// ImageConditionDlg.cpp ------------------------------------
#include "pch.h"
#include "ImageConditionDlg.h"
#include "resource.h"

IMPLEMENT_DYNAMIC(CImageConditionDlg, CDialogEx)

CImageConditionDlg::CImageConditionDlg(CWnd* pParent /*=nullptr*/)
    : CDialogEx(IDD_IMAGECONDITION_DLG, pParent)
    , m_fSimilarity(80.0)
    , m_bUseOCR(FALSE)
    , m_nRetryCount(0)
{
}

CImageConditionDlg::~CImageConditionDlg()
{
}

void CImageConditionDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialogEx::DoDataExchange(pDX);
    // 예: 슬라이더 or 에디트
    // DDX_Text(pDX, IDC_EDIT_SIMILARITY, m_fSimilarity);
    // DDX_Check(pDX, IDC_CHECK_OCR, m_bUseOCR);
    // DDX_Text(pDX, IDC_EDIT_RETRYCOUNT, m_nRetryCount);
}

BEGIN_MESSAGE_MAP(CImageConditionDlg, CDialogEx)
    ON_BN_CLICKED(IDOK, &CImageConditionDlg::OnBnClickedOk)
END_MESSAGE_MAP()

void CImageConditionDlg::OnBnClickedOk()
{
    UpdateData(TRUE);
    // OK 버튼 → 다이얼로그 종료
    CDialogEx::OnOK();
}
4) 멀티 이미지 설정 (CMultiImageDlg)
여러 이미지를 선택 + 각 이미지별 “성공/실패” + “AND/OR” 조건
cpp
복사
// MultiImageDlg.h -----------------------------------------
#pragma once
#include "afxdialogex.h"
#include <vector>

class CMultiImageDlg : public CDialogEx
{
    DECLARE_DYNAMIC(CMultiImageDlg)

public:
    CMultiImageDlg(CWnd* pParent = nullptr);
    virtual ~CMultiImageDlg();

#ifdef AFX_DESIGN_TIME
    enum { IDD = IDD_MULTIIMAGE_DLG };
#endif

protected:
    virtual void DoDataExchange(CDataExchange* pDX);
    DECLARE_MESSAGE_MAP()

public:
    struct MultiItem
    {
        CString imageName;
        BOOL    bSuccess;   // 성공으로 판정할지 실패로 판정할지
        CString logic;      // "AND"/"OR"
    };

    std::vector<MultiItem> m_vMultiItems;

    afx_msg void OnBnClickedOk();
};

// MultiImageDlg.cpp ----------------------------------------
#include "pch.h"
#include "MultiImageDlg.h"
#include "resource.h"

IMPLEMENT_DYNAMIC(CMultiImageDlg, CDialogEx)

CMultiImageDlg::CMultiImageDlg(CWnd* pParent /*=nullptr*/)
    : CDialogEx(IDD_MULTIIMAGE_DLG, pParent)
{
}

CMultiImageDlg::~CMultiImageDlg()
{
}

void CMultiImageDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialogEx::DoDataExchange(pDX);
    // ListBox / ComboBox / Edit 등과 연동 (생략)
}

BEGIN_MESSAGE_MAP(CMultiImageDlg, CDialogEx)
    ON_BN_CLICKED(IDOK, &CMultiImageDlg::OnBnClickedOk)
END_MESSAGE_MAP()

void CMultiImageDlg::OnBnClickedOk()
{
    UpdateData(TRUE);
    // TODO: 리스트에서 AND/OR, 성공/실패 정보를 m_vMultiItems에 저장
    CDialogEx::OnOK();
}
5) 인식 활성/비활성 설정 (CActivationDlg)
이미지 또는 그룹을 선택하여 “인식 활성/비활성”, 예약 타이머 등 설정
cpp
복사
// ActivationDlg.h -----------------------------------------
#pragma once
#include "afxdialogex.h"

class CActivationDlg : public CDialogEx
{
    DECLARE_DYNAMIC(CActivationDlg)

public:
    CActivationDlg(CWnd* pParent = nullptr);
    virtual ~CActivationDlg();

#ifdef AFX_DESIGN_TIME
    enum { IDD = IDD_ACTIVATION_DLG };
#endif

protected:
    virtual void DoDataExchange(CDataExchange* pDX);
    DECLARE_MESSAGE_MAP()

public:
    CString m_strTargetImage;
    BOOL    m_bActivate; // TRUE=인식 활성 / FALSE=비활성
    int     m_nReserveMin;
    int     m_nReserveSec;

    afx_msg void OnBnClickedOk();
};

// ActivationDlg.cpp ----------------------------------------
#include "pch.h"
#include "ActivationDlg.h"
#include "resource.h"

IMPLEMENT_DYNAMIC(CActivationDlg, CDialogEx)

CActivationDlg::CActivationDlg(CWnd* pParent /*=nullptr*/)
    : CDialogEx(IDD_ACTIVATION_DLG, pParent)
    , m_bActivate(TRUE)
    , m_nReserveMin(0)
    , m_nReserveSec(0)
{
}

CActivationDlg::~CActivationDlg()
{
}

void CActivationDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialogEx::DoDataExchange(pDX);
    // DDX/DDV 연결
}

BEGIN_MESSAGE_MAP(CActivationDlg, CDialogEx)
    ON_BN_CLICKED(IDOK, &CActivationDlg::OnBnClickedOk)
END_MESSAGE_MAP()

void CActivationDlg::OnBnClickedOk()
{
    UpdateData(TRUE);
    // TODO: m_strTargetImage, m_bActivate, m_nReserveMin/sec
    CDialogEx::OnOK();
}
6) 액션 추가 (CActionAddDlg)
“이미지 표시, 이미지 클릭, 인식 실패시, 마우스 입력, 키보드 입력, 딜레이, 인식 활성/비활성” 등 액션 유형을 고를 수 있는 대화상자
cpp
복사
// ActionAddDlg.h ------------------------------------------
#pragma once
#include "afxdialogex.h"
#include <vector>
#include "MainDlg.h" // ActionType 등 쓰기 위해

class CActionAddDlg : public CDialogEx
{
    DECLARE_DYNAMIC(CActionAddDlg)

public:
    CActionAddDlg(CWnd* pParent = nullptr);
    virtual ~CActionAddDlg();

#ifdef AFX_DESIGN_TIME
    enum { IDD = IDD_ACTIONADD_DLG };
#endif

protected:
    virtual void DoDataExchange(CDataExchange* pDX);
    DECLARE_MESSAGE_MAP()

public:
    ActionType m_selectedAction;
    CString    m_strDescription;  // 액션 설명

    // 속성: (예) 마우스 좌표, 키보드 입력 문자열, 딜레이 등등
    // 여기서는 간단히 생략
    int m_nMouseX;
    int m_nMouseY;

    afx_msg void OnBnClickedOk();
};

// ActionAddDlg.cpp -----------------------------------------
#include "pch.h"
#include "ActionAddDlg.h"
#include "resource.h"

IMPLEMENT_DYNAMIC(CActionAddDlg, CDialogEx)

CActionAddDlg::CActionAddDlg(CWnd* pParent /*=nullptr*/)
    : CDialogEx(IDD_ACTIONADD_DLG, pParent)
    , m_selectedAction(ActionType::IMAGE_DISPLAY)
    , m_nMouseX(0)
    , m_nMouseY(0)
{
}

CActionAddDlg::~CActionAddDlg()
{
}

void CActionAddDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialogEx::DoDataExchange(pDX);
    // ex) DDX_Control(...) or DDX_Text(...)
}

BEGIN_MESSAGE_MAP(CActionAddDlg, CDialogEx)
    ON_BN_CLICKED(IDOK, &CActionAddDlg::OnBnClickedOk)
END_MESSAGE_MAP()

void CActionAddDlg::OnBnClickedOk()
{
    UpdateData(TRUE);
    // ComboBox로부터 액션타입 선택, 설명 작성
    // 예: m_selectedAction = ActionType::IMAGE_CLICK;
    //     m_strDescription = _T("이미지 클릭");
    CDialogEx::OnOK();
}
7) 실행 로직 (메인에서의 순회 예시)
앞서 MainDlg::OnBnClickedBtnStart() 에서 “이미지들을 순회하며 인식” → “액션 리스트 순회하며 실행” 하는 흐름을 간단히 작성했는데, 실제 구현 시에는 다음을 세분화할 수 있습니다.

별도 스레드:
GUI가 멈추지 않도록, “시작” 버튼 클릭 시 작업 스레드를 생성
이미지 매칭(OpenCV), 마우스/키보드 이벤트(Windows API)를 스레드에서 처리
인식 조건:
single image vs multi image (AND/OR)
인식 실패 시 재시도 횟수, 대체 액션
액션 실행:
switch-case로 ActionType 분기
IMAGE_CLICK: 마우스 이동 + 클릭
DELAY: Sleep(...) or timer
KEYBOARD_INPUT: SendInput API 등
cpp
복사
// (의사 코드) MainDlg::RunAutomation()
void CMainDlg::RunAutomation()
{
    for (auto& img : m_vImages)
    {
        BOOL recognized = DoImageRecognition(img);
        if (recognized)
        {
            AppendLog(_T("[인식 성공] ") + img.imageName);
            // 액션들 실행
            for (auto& act : m_vActions)
            {
                ExecuteAction(act);
            }
        }
        else
        {
            AppendLog(_T("[인식 실패] ") + img.imageName);
            // 실패 액션이 있으면 실행
        }
    }
}

BOOL CMainDlg::DoImageRecognition(const ImageInfo& img)
{
    // TODO: 실제 OpenCV matchTemplate 등
    // 임시로 항상 true
    return TRUE;
}

void CMainDlg::ExecuteAction(const ActionItem& act)
{
    switch (act.type)
    {
    case ActionType::IMAGE_DISPLAY:
        // TODO: 화면에 이미지 띄우기
        AppendLog(_T(" - 액션: 이미지 표시"));
        break;
    case ActionType::IMAGE_CLICK:
        // TODO: match 위치에 마우스 클릭
        AppendLog(_T(" - 액션: 이미지 클릭"));
        break;
    case ActionType::INPUT_MOUSE:
        // TODO: 마우스 움직임/클릭
        AppendLog(_T(" - 액션: 마우스 입력"));
        break;
    case ActionType::INPUT_KEYBOARD:
        // TODO: 키보드 입력
        AppendLog(_T(" - 액션: 키보드 입력"));
        break;
    case ActionType::DELAY:
        // TODO: Sleep(…)
        AppendLog(_T(" - 액션: 딜레이"));
        break;
    case ActionType::ACTIVATE_INACTIVATE:
        // TODO: 인식 활성/비활성
        AppendLog(_T(" - 액션: 인식 활성/비활성"));
        break;
    case ActionType::RECOGNITION_FAIL:
        // TODO: 인식 실패 시 동작
        AppendLog(_T(" - 액션: 인식 실패 처리"));
        break;
    default:
        break;
    }
}
정리
위 코드들은 각 기능마다 어떤 식으로 다이얼로그(MFC CDialogEx)와 멤버 변수(DDX)를 연결하고,
메인 대화상자에서 팝업을 열어 설정을 받고,
최종적으로 이미지 인식 + 액션 실행 흐름을 구현하는 “샘플 뼈대” 입니다.

주요 포인트:
각 설정 창(대화상자)을 별도의 클래스로 분리하여, 필요한 옵션만큼 컨트롤 & 멤버 변수를 두고 DoModal()로 호출.
“인식 대상 창” 선택 로직(마우스 후킹)은 CTargetWindowDlg에서 처리.
“이미지 조건”은 CImageConditionDlg, “멀티 이미지 AND/OR”는 CMultiImageDlg, “활성/비활성”은 CActivationDlg 등으로 나눔.
“액션 추가”는 CActionAddDlg에서 액션 종류를 선택받은 뒤, m_vActions 목록에 ActionItem을 추가.
“시작” 시, 이미지들을 순회 → 인식 → 액션 실행.
이를 바탕으로 실제 리소스(.rc) 구성과 OpenCV/Windows API 로직을 넣어서 완성하시면,
질문에서 보여주신 UI/기능(“인식 대상 창 설정, 이미지 리스트, 인식 조건, 멀티 이미지 설정, 인식 활성/비활성, 액션 리스트…”)을 전부 갖춘 프로그램을 만들 수 있습니다.

Tip: 실제로는

“화면 캡처 + 특정 영역(ROI) 지정”
“유사도 매칭”
“마우스/키보드 입력 (SendInput)”
“비활성 창 캡처 (PrintWindow)”
등의 세부 로직을 하나씩 테스트하면서 완성하시면 좋습니다.
이상으로, MFC(Dialog) 기반으로 “각 버튼 클릭 시 나오는 설정 옵션 다이얼로그들”을 그대로 반영하는 예시 뼈대 코드를 보여드렸습니다.
필요에 따라 클래스를 더 세분화하거나, 단일 대화상자에 탭 컨트롤(Tab Control)로 묶는 방식도 가능하니 참고 부탁드립니다.

2023년 제9회 디지털 범인을 찾아라 경진대회 공고

 

□ 행사 개요
o 행사명: 2023년 제9회 디지털 범인을 찾아라 경진대회
o 대상: 주니어(전국 대학생 이하), 시니어(일반인 또는 전문가)
o 접수처: (사)한국포렌식학회 홈페이지(www.forensickorea.org) 공고
o 접수기간: 2023년 8월 7일(월)~8월 18일(금)
o 문제배포: 2023년 8월 28일(월)~9월 1일(금)
o 답안제출: 2023년 10월 6일(금) 18:00 마감
o 당선발표: 2023년 11월 2일(목) 18:00 (사)한국포렌식학회 홈페이지를 통해 발표
o 시상식(일정은 향후 변동될 수 있음)
- 일시: 2023년 12월 중순경
- 장소 미정

부문 시니어 주니어
대상 검찰총장상 1명
금상 한국포렌식학회장상 1명 한국저작권보호원장상 1명
은상 협찬사상 1명 협찬사상 1명
동상 협찬사상 2명 협찬사상 2명
장려상 한국포렌식학회장상 10명

o 추진체계
- 공동주최: (사)한국포렌식학회, 한국저작권보호원
- 후원: 대검찰청
- 협찬: 법무법인 클라스, 법무법인 세종, 법무법인 화우, 법무법인 태평양, 법무법인 허브, 법무법인 린, 손해보험협회, HM Company, 주식회사 제트코, GMDSOFT

□ 접수방법
o 접수
- (사)한국포렌식학회 홈페이지의 2023년 제9회 디지털 범인을 찾아라 경진대회 공고 페이지 하단에서 온라인 접수
- 접수 시 등기우편 수령 가능한 주소 및 연락처를 정확히 입력
o 참가비
- 주니어: 1만원 / 시니어: 2만원
- 주니어(전국 대학생 이하), 시니어(일반인 또는 전문가)

□ 문제배포 및 답안제출
o 문제 배포
- 등기우편으로 배포(배포방식은 변동될 수 있습니다.)
o 답안 제출 방법
- 답안 작성은 자유양식으로 작성, 관련 증거자료는 답안지와 함께 첨부하여 제출(배포한 문제 USB는 반납하지 마시고 개별적으로 사용하시면 됩니다)
- 2023년 10월 6일(금) 18:00까지 (사)한국포렌식학회에 도착한 답안까지 인정
- 답안 제출 주소: (03070) 서울특별시 종로구 성균관로 71, 201호 (명륜1가) (https://naver.me/5dPqTp4w)

□ 평가방법 및 참고사항
o 평가방법
- 분석 도구, 분석 절차, 분석 결과에 대해서 배점에 따라 평가
- 점수가 같은 경우, 답안 접수순서 등으로 우열 평가
o 대상 등 우수작에 대하여는 시상식장에서 발표함(허위사실이 발견 될 경우 입상 무효)

□ 디지털 범인을 찾아라 경진대회 개인정보 처리방침
본 경진대회에서 수집하는 개인정보는 다음과 같습니다.
수집하는 개인정보 항목: 이름, 이메일, 휴대전화번호, 생년월일, 주소, 소속/부서
수집목적: 경진대회 접수 및 응시·합격 정보관리, 확인서 발급, 행정 처리, 결제 정보 관리, 법령에 정한 통계 정보의 관리, 뉴스레터 발송, 신규 서비스 안내 등 관련 정보의 제공 등을 목적으로 개인정보를 처리합니다.
개인정보 처리 및 보관 기간: 접수일로부터 2년간, 요청 시 즉시 파기
개인정보 파기 방침: 복원 불가능한 상태로 파기(서류, 전산자료 포함)
개인정보의 제3자 제공: KG이니시스(경진대회 접수비 결제 대행)

<문의>
전화번호: 02-744-1809
이메일: challenge@forensickorea.org

2023년 제9회 디지털 범인을 찾아라 경진대회 접수 및 결제

결제 후 첨부파일인 '개인정보 수집·이용 및 제3자 제공 동의서'(한글 혹은 워드 파일 1개 선택)를 작성하여 challenge@forensickorea.org로 보내주시길 바랍니다.

개인정보-수집⸱이용-및-제3자-제공-동의서.hwp
0.13MB
개인정보-수집⸱이용-및-제3자-제공-동의서.docx
0.02MB

 

프로그램 이동.

MoveMonitorProgram.zip
0.26MB

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace MoveMonitorProgram
{
    public partial class Form1 : Form
    {
        [StructLayout(LayoutKind.Sequential)]
        public struct RECT
        {
            public int Left;
            public int Top;
            public int Right;
            public int Bottom;
        }

        // MONITORINFOEX 구조체 정의
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        public struct MONITORINFOEX
        {
            public int cbSize;
            public RECT rcMonitor;
            public RECT rcWork;
            public uint dwFlags;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
            public char[] szDevice;
        }

        // MonitorEnumDelegate 델리게이트 선언
        public delegate bool MonitorEnumDelegate(IntPtr hMonitor, IntPtr hdcMonitor, ref RECT lprcMonitor, IntPtr dwData);

        [DllImport("user32.dll")]
        public static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect);

        [DllImport("user32.dll")]
        public static extern bool MoveWindow(IntPtr hwnd, int x, int y, int nWidth, int nHeight, bool bRepaint);

        [DllImport("user32.dll")]
        public static extern bool EnumDisplayMonitors(IntPtr hdc, IntPtr lprcClip, MonitorEnumDelegate lpfnEnum, IntPtr dwData);

        [DllImport("user32.dll")]
        public static extern bool GetMonitorInfo(IntPtr hMonitor, ref MONITORINFOEX lpmi);

        private List<Process> runningProcesses;

        public Form1()
        {
            InitializeComponent();
            runningProcesses = new List<Process>();
            // 폼이 로드될 때 라디오 버튼 1을 선택한 상태로 초기화
            radioButton1.Checked = true;
            LoadRunningProcesses();
            LoadMonitors();
        }

        private void LoadMonitors()
        {
            comboBox2.Items.Clear();
            int monitorCount = 0;
            EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, delegate (IntPtr hMonitor, IntPtr hdcMonitor, ref RECT lprcMonitor, IntPtr dwData)
            {
                MONITORINFOEX info = new MONITORINFOEX();
                info.cbSize = Marshal.SizeOf(info);
                GetMonitorInfo(hMonitor, ref info);
                monitorCount++;
                comboBox2.Items.Add("Monitor " + monitorCount);
                return true;
            }, IntPtr.Zero);

            if (comboBox2.Items.Count > 0)
            {
                comboBox2.SelectedIndex = 0;
            }
        }

        private void LoadRunningProcesses()
        {
            comboBox1.Items.Clear();
            string[] processNames;

            if (radioButton1.Checked)
            {
                processNames = new string[] { "dnplayer" };
            }
            else if (radioButton2.Checked)
            {
                processNames = new string[] { "Nox" };
            }
            else if (radioButton3.Checked)
            {
                string customProcessName = textBox1.Text.Trim();
                processNames = new string[] { customProcessName };
            }
            else
            {
                processNames = new string[] { "" };
            }

            runningProcesses.Clear();

            Process[] processes = Process.GetProcesses();
            foreach (Process process in processes)
            {
                foreach (string processName in processNames)
                {
                    if (string.IsNullOrEmpty(processName) || process.ProcessName.ToLower().Contains(processName.ToLower()))
                    {
                        runningProcesses.Add(process);
                        string windowTitle = !string.IsNullOrEmpty(process.MainWindowTitle) ? " - " + process.MainWindowTitle : "";
                        string displayName = process.ProcessName + windowTitle;

                        RECT rect;
                        GetWindowRect(process.MainWindowHandle, out rect);
                        string processInfo = $"({rect.Left}, {rect.Top}) {rect.Right - rect.Left} x {rect.Bottom - rect.Top}";

                        displayName += $" {processInfo}";
                        comboBox1.Items.Add(displayName);
                        break;
                    }
                }
            }

            // 프로세스를 로드한 후 첫 번째 아이템을 선택하도록 설정
            if (comboBox1.Items.Count > 0)
            {
                comboBox1.SelectedIndex = 0;
            }
        }

        private void MoveWindowToMonitor(Process process, Screen screen)
        {
            IntPtr handle = process.MainWindowHandle;
            if (handle != IntPtr.Zero)
            {
                RECT rect;
                GetWindowRect(handle, out rect);
                int width = rect.Right - rect.Left;
                int height = rect.Bottom - rect.Top;
                int newX = screen.WorkingArea.X + (screen.WorkingArea.Width - width) / 2;
                int newY = screen.WorkingArea.Y + (screen.WorkingArea.Height - height) / 2;
                MoveWindow(handle, newX, newY, width, height, true);
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            LoadRunningProcesses();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            if (radioButton1.Checked || radioButton2.Checked)
            {
                if (comboBox1.SelectedIndex >= 0 && comboBox1.SelectedIndex < runningProcesses.Count)
                {
                    Process selectedProcess = runningProcesses[comboBox1.SelectedIndex];
                    Screen selectedMonitor = Screen.AllScreens[comboBox2.SelectedIndex];
                    MoveWindowToMonitor(selectedProcess, selectedMonitor);
                }
            }
            else if (radioButton3.Checked)
            {
                string customProcessName = textBox1.Text.Trim();
                Process selectedProcess = GetProcessByNameString(customProcessName);
                if (selectedProcess != null)
                {
                    Screen selectedMonitor = Screen.AllScreens[comboBox2.SelectedIndex];
                    MoveWindowToMonitor(selectedProcess, selectedMonitor);
                }
                else
                {
                    MessageBox.Show("입력한 프로세스를 찾을 수 없습니다.");
                }
            }
        }

        private Process GetProcessByNameString(string processName)
        {
            Process[] processes = Process.GetProcesses();
            foreach (Process process in processes)
            {
                if (process.ProcessName.ToLower().Contains(processName.ToLower()))
                {
                    return process;
                }
            }
            return null;
        }

        private void radioButton3_CheckedChanged(object sender, EventArgs e)
        {
            textBox1.Visible = radioButton3.Checked;
        }
    }
}

컴퓨터 응용 프로그램은 특정 기능을 트리거하는 일련의 규칙으로 컴파일 된 간단한 코드 줄입니다. 결과적으로 대상 앱을 신중하게 분석하여 악성 소프트웨어를 쉽게 생성 할 수 있습니다. 그러나 개발자는 Confuser와 같은 응용 프로그램이 기대에 부응 할 수 있도록 소스 코드를 가능한 한 추적하거나 리버스 엔지니어링하기 어렵게 만드는 방법을 찾고 사용하고 있습니다.

 

Confuser.zip
1.08MB

+ Recent posts