ChatGPTの書いたプログラム　簡易版EXCEL

## 初めに

### 要求
```
簡単なエクセルのプログラムを書いて

# 要求
Widows上で動作
C言語を使いWINDOWSAPIを呼び出して作成
対応する形式はCSVのみ

# その他
関数、マクロ、字体変更など複雑な機能は不要
GCCでコンパイル
```

### GPTの書いたコード
<details>
<summary>詳細</summary>

```csv_reader.c
#include <windows.h>
#include <commdlg.h>
#include <stdio.h>
#include <stdlib.h>

#define IDC_LISTBOX 1001

// グローバル変数
HWND hListBox;

// 関数プロトタイプ
void OpenCSVFile(HWND hwnd);
void LoadCSVToListBox(const char* filename);
void ShowErrorMessage(const char* message);

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    switch (uMsg) {
        case WM_CREATE:
        {
            // リストボックスを作成
            hListBox = CreateWindowEx(0, "LISTBOX", NULL,
                                      WS_CHILD | WS_VISIBLE | LBS_STANDARD,
                                      10, 10, 400, 300,
                                      hwnd, (HMENU)IDC_LISTBOX, GetModuleHandle(NULL), NULL);

            // 「ファイルを開く」ボタンを作成
            CreateWindow("BUTTON", "Open CSV", WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,
                         420, 10, 100, 30, hwnd, (HMENU)1, GetModuleHandle(NULL), NULL);
            break;
        }

        case WM_COMMAND:
            if (LOWORD(wParam) == 1) {
                // 「Open CSV」ボタンがクリックされたとき
                OpenCSVFile(hwnd);
            }
            break;

        case WM_DESTROY:
            PostQuitMessage(0);
            break;

        default:
            return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
    return 0;
}

// 「ファイルを開く」ダイアログ
void OpenCSVFile(HWND hwnd) {
    OPENFILENAME ofn;
    char szFile[260] = "";

    ZeroMemory(&ofn, sizeof(ofn));
    ofn.lStructSize = sizeof(ofn);
    ofn.hwndOwner = hwnd;
    ofn.lpstrFile = szFile;
    ofn.nMaxFile = sizeof(szFile);
    ofn.lpstrFilter = "CSV Files\0*.CSV\0All Files\0*.*\0";
    ofn.nFilterIndex = 1;
    ofn.lpstrFileTitle = NULL;
    ofn.nMaxFileTitle = 0;
    ofn.lpstrInitialDir = NULL;
    ofn.lpstrTitle = "Open CSV File";
    ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_OVERWRITEPROMPT;

    if (GetOpenFileName(&ofn)) {
        LoadCSVToListBox(ofn.lpstrFile);
    }
}

// CSVファイルをリストボックスに読み込む
void LoadCSVToListBox(const char* filename) {
    FILE* file = fopen(filename, "r");
    if (file == NULL) {
        ShowErrorMessage("File not found or cannot be opened.");
        return;
    }

    char line[1024];
    while (fgets(line, sizeof(line), file)) {
        // 改行を削除
        line[strcspn(line, "\n")] = 0;
        SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)line);
    }
    fclose(file);
}

// エラーメッセージを表示
void ShowErrorMessage(const char* message) {
    MessageBox(NULL, message, "Error", MB_ICONERROR | MB_OK);
}

int main() {
    // ウィンドウクラスの登録
    const char CLASS_NAME[] = "CSVReaderWindowClass";

    WNDCLASS wc = {0};
    wc.lpfnWndProc = WindowProc;
    wc.hInstance = GetModuleHandle(NULL);
    wc.lpszClassName = CLASS_NAME;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);

    if (!RegisterClass(&wc)) {
        ShowErrorMessage("Window class registration failed.");
        return 1;
    }

    // ウィンドウの作成
    HWND hwnd = CreateWindowEx(0, CLASS_NAME, "CSV Reader",
                               WS_OVERLAPPEDWINDOW,
                               CW_USEDEFAULT, CW_USEDEFAULT, 540, 400,
                               NULL, NULL, GetModuleHandle(NULL), NULL);

    if (hwnd == NULL) {
        ShowErrorMessage("Window creation failed.");
        return 1;
    }

    ShowWindow(hwnd, SW_SHOW);
    UpdateWindow(hwnd);

    // メッセージループ
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return 0;
}

```
</details>

### 動作結果

#### 開いたところ
- 背景が描画されていない
- マス（セル）が１つしかない

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4054974/d7001cee-7d23-494f-b537-fe59043d9adf.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4054974/e08b11a7-f02c-4e7e-8b94-d10e309a7803.png)

#### CSVを開いてみる
- 1マスの中に全て読み込まれテキストエディタのようになってしまう
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4054974/a4ade6f9-7271-43d8-9bcf-1b753278e8d9.png)

## 改善要求
### １回目
```
背景が描画されていなくて後ろの画面が透けている
マス（セル）が１つしかない
1マスの中に全て読み込まれテキストエディタのようになってしまう
```
#### GPT回答(抜粋)
```
なるほど、リストボックスに直接テキストを追加してしまったため、1つのセルにすべてのデータが詰め込まれた形になり、さらに背景が表示されていないのですね。

これを改善するために、次の変更を行います：

    背景描画:
    背景を描画するために、ウィンドウに対して明示的に背景を設定します。WM_ERASEBKGNDメッセージを処理して背景を描画するコードを追加します。

    マス（セル）の改善:
    リストボックスを使うのではなく、セルごとに描画するアプローチに変更します。CSVファイルの内容を格子状に表示する方法に変更し、各セルごとにテキストを描画します。
```

回答はそれらしいが改善されたコードを実行しても改善していない
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4054974/703deb0c-9e13-4f87-b097-d2b0ac18e3ac.png)

### ２回目
```
改善していない

１　表示するセルは5×5
２　背景は緑で塗りつぶす
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4054974/1c6f0d0a-eb3c-4e72-8e36-22d34d5de30f.png)

### ３回目
```
セルとセルの間に線が欲しい。
テキストボックスのように見えてしまう
```
何となく惜しいが今回はこれで良いとしましょう
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4054974/a77d8627-d600-41fb-b694-ba462c863a49.png)

<details>
<summary>最終的なコード</summary>

```csv_reader.c
#include <windows.h>
#include <commdlg.h>
#include <stdio.h>

#define MAX_CELLS 25
#define CELL_WIDTH 100
#define CELL_HEIGHT 30

char csvData[MAX_CELLS][128];  // 最大25セル、各セル128文字まで
int numCells = 0;

// CSV読み込み関数
void LoadCSV(const char* filename) {
    FILE* file = fopen(filename, "r");
    if (!file) return;

    char line[1024];
    numCells = 0;
    while (fgets(line, sizeof(line), file) && numCells < MAX_CELLS) {
        char* token = strtok(line, ",\n");
        while (token && numCells < MAX_CELLS) {
            strncpy(csvData[numCells++], token, 127);
            token = strtok(NULL, ",\n");
        }
    }
    fclose(file);
}

// ファイルを開くダイアログ
void OpenCSVFile(HWND hwnd) {
    OPENFILENAME ofn;
    char szFile[260] = "";

    ZeroMemory(&ofn, sizeof(ofn));
    ofn.lStructSize = sizeof(ofn);
    ofn.hwndOwner = hwnd;
    ofn.lpstrFile = szFile;
    ofn.nMaxFile = sizeof(szFile);
    ofn.lpstrFilter = "CSV Files\0*.csv\0All Files\0*.*\0";
    ofn.Flags = OFN_FILEMUSTEXIST;

    if (GetOpenFileName(&ofn)) {
        LoadCSV(szFile);
        InvalidateRect(hwnd, NULL, TRUE); // 再描画
    }
}

// 描画処理
void DrawGrid(HDC hdc) {
    // 背景緑
    HBRUSH greenBrush = CreateSolidBrush(RGB(0, 255, 0));
    RECT clientRect;
    GetClientRect(WindowFromDC(hdc), &clientRect);
    FillRect(hdc, &clientRect, greenBrush);
    DeleteObject(greenBrush);

    // ペンの設定（白線）
    HPEN hPen = CreatePen(PS_SOLID, 1, RGB(255, 255, 255));
    HPEN oldPen = SelectObject(hdc, hPen);

    const int rows = 5;
    const int cols = 5;
    const int offsetX = 10;
    const int offsetY = 10;

    // 縦線（列+1本）
    for (int col = 0; col <= cols; ++col) {
        int x = offsetX + col * CELL_WIDTH;
        MoveToEx(hdc, x, offsetY, NULL);
        LineTo(hdc, x, offsetY + CELL_HEIGHT * rows);
    }

    // 横線（行+1本）
    for (int row = 0; row <= rows; ++row) {
        int y = offsetY + row * CELL_HEIGHT;
        MoveToEx(hdc, offsetX, y, NULL);
        LineTo(hdc, offsetX + CELL_WIDTH * cols, y);
    }

    // テキスト描画
    for (int row = 0; row < rows; ++row) {
        for (int col = 0; col < cols; ++col) {
            int index = row * cols + col;
            if (index < numCells) {
                RECT cell = {
                    offsetX + col * CELL_WIDTH,
                    offsetY + row * CELL_HEIGHT,
                    offsetX + (col + 1) * CELL_WIDTH,
                    offsetY + (row + 1) * CELL_HEIGHT
                };
                DrawTextA(hdc, csvData[index], -1, &cell,
                          DT_CENTER | DT_VCENTER | DT_SINGLELINE);
            }
        }
    }

    SelectObject(hdc, oldPen);
    DeleteObject(hPen);
}


LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    switch (msg) {
        case WM_CREATE:
            CreateWindow("BUTTON", "Open CSV", WS_VISIBLE | WS_CHILD,
                         10, 170, 100, 30, hwnd, (HMENU)1,
                         ((LPCREATESTRUCT)lParam)->hInstance, NULL);
            break;

        case WM_COMMAND:
            if (LOWORD(wParam) == 1) {
                OpenCSVFile(hwnd);
            }
            break;

        case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hwnd, &ps);
            DrawGrid(hdc);
            EndPaint(hwnd, &ps);
            break;
        }

        case WM_DESTROY:
            PostQuitMessage(0);
            break;
    }

    return DefWindowProc(hwnd, msg, wParam, lParam);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine, int nCmdShow) {
    const char CLASS_NAME[] = "CSVGridWindow";

    WNDCLASS wc = {0};
    wc.lpfnWndProc = WndProc;
    wc.hInstance = hInstance;
    wc.lpszClassName = CLASS_NAME;
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);

    RegisterClass(&wc);

    HWND hwnd = CreateWindowEx(0, CLASS_NAME, "CSV Viewer",
                               WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME & ~WS_MAXIMIZEBOX,
                               CW_USEDEFAULT, CW_USEDEFAULT,
                               550, 400, NULL, NULL, hInstance, NULL);

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);

    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return 0;
}
```
</details>

## プログラムの構造を解説して貰う

### 質問
```
プログラムの流れ図と詳しい説明を書いて
```

### 回答
```
以下に、今回の「CSVを読み込み、GUIで5×5に表示するCプログラム（Windows API使用）」の **プログラムの流れ図（フローチャート）**と、詳しい処理内容の説明をまとめます。
🧭 全体の流れ（概要）

    アプリ起動（WinMain）

    ウィンドウ生成（WndProcのWM_CREATE）

    「Open CSV」ボタン表示

    ボタンが押されるとCSV読み込み

    読み込んだ内容をセル状に描画（5×5）

    背景は緑、線でセル区切り、テキスト表示

🗂 プログラム構成（関数と役割）
関数名	役割
WinMain()	アプリケーションのエントリーポイント。ウィンドウ作成、メッセージループ
WndProc()	ウィンドウのメッセージ処理（WM_CREATE, WM_PAINT, WM_COMMANDなど）
OpenCSVFile()	ファイル選択ダイアログを表示し、CSVファイルを選ばせる
LoadCSV()	CSVファイルを読み込んで csvData[][] に格納
DrawGrid()	背景を緑で塗りつぶし、格子線＋CSVデータを描画
📊 フローチャート（簡略図）

┌────────────────────────────┐
│          WinMain()         │
└──────────┬─────────────────┘
           │
           ▼
┌────────────────────────────┐
│ RegisterClass / CreateWindow │
└──────────┬─────────────────┘
           │
           ▼
     ┌────────────┐
     │  WndProc() │ ←─┐
     └────┬───────┘   │ メッセージ
          │WM_CREATE  │
          ▼           │
┌────────────────────────────┐
│ ボタン作成（Open CSV）     │
└──────────┬─────────────────┘
           │
           ▼ [ボタンクリック]
     ┌────────────┐
     │ WM_COMMAND │
     └────┬───────┘
          ▼
┌────────────────────────────┐
│ OpenCSVFile()              │
│  └→ GetOpenFileName()      │
│  └→ LoadCSV()              │
│      └→ csvData[]に格納    │
└──────────┬─────────────────┘
           ▼
┌────────────────────────────┐
│ InvalidateRect → WM_PAINT  │
└──────────┬─────────────────┘
           ▼
     ┌────────────┐
     │ WM_PAINT   │
     └────┬───────┘
          ▼
┌────────────────────────────┐
│ DrawGrid()                 │
│  ├→ 背景を緑で塗る         │
│  ├→ 格子状に白線を描く     │
│  └→ CSVの文字を中央表示   │
└────────────────────────────┘

🔍 各処理の詳細説明
1. WinMain()

    RegisterClass() でウィンドウクラス登録

    CreateWindowEx() でウィンドウ生成

    ShowWindow() と UpdateWindow() で表示

    GetMessage() ループでイベント待機

2. WndProc()
WM_CREATE

    「Open CSV」ボタン作成 (CreateWindow)

WM_COMMAND

    ボタンIDが1なら OpenCSVFile() を呼び出す

WM_PAINT

    DrawGrid() を呼び出して描画処理

3. OpenCSVFile()

    GetOpenFileName() を使ってファイル選択ダイアログを表示

    選択されたCSVファイルを LoadCSV() へ渡す

4. LoadCSV()

    CSVファイルを開き、1行ずつ fgets() で読む

    各行を strtok() でカンマ分割し、csvData[25][128] に格納

5. DrawGrid()

    FillRect() でウィンドウ全体を緑塗りつぶし

    MoveToEx() ＋ LineTo() を使って格子線を描画

        縦線：6本（5列+1）

        横線：6本（5行+1）

    DrawTextA() を用いてセル内に中央揃えでテキスト描画

💡 今後の拡張も可能

    セルの選択やクリック反応

    CSV保存

    文字色やフォント変更

    任意サイズのテーブル対応

必要であれば、**図の画像化（PNG）**も可能です。希望されますか？
```

## 考察
全くプログラミングの知識がない状態で完全にGPTに丸投げするのは少し心許無いですが、GPTに書かせたプログラムを自分でも理解して手直ししながら`協力して`開発するのは現実的にありではないかと感じました。