0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

自作のwindowsランチャー メモ

Last updated at Posted at 2025-02-25

ネット上で公開されているランチャーを模擬したソースコード。
会社でフリーソフトウエア禁止のため自作。

Launcher.cpp
// Launcher.cpp : アプリケーションのエントリ ポイントを定義します。
//

#include "framework.h"
#include "Launcher.h"
#include <vector>
#include <string>
#include <fstream>
#include <sstream>
#include <shellapi.h>
#include <windows.h>
#include <shlobj.h>  // SHGetKnownFolderPath
#include <iostream>

#define MAX_LOADSTRING 100
#define HOTKEY_ID 1  // ホットキーのID


// グローバル変数:
HINSTANCE hInst;                                // 現在のインターフェイス
WCHAR szTitle[MAX_LOADSTRING];                  // タイトル バーのテキスト
WCHAR szWindowClass[MAX_LOADSTRING];            // メイン ウィンドウ クラス名
WNDPROC origEditProc = NULL;
WNDPROC origEditProc2 = NULL;

HWND hWnd;
HWND edittext;
HWND hListBox;  // 候補表示用のリストボックス
int candidateCount = 0;  // 候補数
int selIndex;
std::string fname = "C:\\launcher\\launcherconfig.ini";
std::wstring Ckey[500];
std::wstring Cvalue[500];
int keynum;//ショートカットキーナンバー


// このコード モジュールに含まれる関数の宣言を転送します:
ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK SubclassProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK SubclassProc2(HWND, UINT, WPARAM, LPARAM);

void LoadCandidatesFromFile(const std::string& filename);


bool WaitForSystemReady(int maxWaitTime = 10000, int interval = 500) {
    int elapsedTime = 0;
    while (elapsedTime < maxWaitTime) {
        PWSTR path = nullptr;
        if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_Desktop, 0, NULL, &path))) {
            CoTaskMemFree(path);  // メモリ解放
            return true;  // システム準備完了
        }
        Sleep(interval);
        elapsedTime += interval;
    }
    return false;  // タイムアウト
}

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    if (WaitForSystemReady()) {
    }
    else {
        MessageBoxW(nullptr, L"ファイルを開けませんでした", L"エラー", MB_OK);
    }


    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    // TODO: ここにコードを挿入してください。
    LoadCandidatesFromFile(fname);

    // グローバル文字列を初期化する
    LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadStringW(hInstance, IDC_LAUNCHER, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);

    // アプリケーション初期化の実行:
    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }

    HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_LAUNCHER));

    MSG msg;

    // メイン メッセージ ループ:
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return (int) msg.wParam;
}



//
//  関数: MyRegisterClass()
//
//  目的: ウィンドウ クラスを登録します。
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEXW wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WndProc;
    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;
    wcex.hInstance      = hInstance;
    wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_LAUNCHER));
    wcex.hCursor        = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = MAKEINTRESOURCEW(IDC_LAUNCHER);
    wcex.lpszClassName  = szWindowClass;
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    return RegisterClassExW(&wcex);
}

//
//   関数: InitInstance(HINSTANCE, int)
//
//   目的: インスタンス ハンドルを保存して、メイン ウィンドウを作成します
//
//   コメント:
//
//        この関数で、グローバル変数でインスタンス ハンドルを保存し、
//        メイン プログラム ウィンドウを作成および表示します。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   hInst = hInstance; // グローバル変数にインスタンス ハンドルを格納する

   hWnd = CreateWindowW(szWindowClass, szTitle, WS_POPUP | WS_BORDER,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
   
   if (!hWnd)
   {
      return FALSE;
   }
   SetWindowPos(hWnd, HWND_TOP, 0, 0, 175, 25, SWP_NOZORDER);
   SetWindowLong(hWnd, GWL_STYLE, WS_POPUP);

   edittext = CreateWindowA("EDIT","",WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT | ES_AUTOHSCROLL,0, 0, 175, 25,hWnd, (HMENU)2, NULL, NULL);
   hListBox = CreateWindowW(L"LISTBOX", NULL, WS_CHILD | WS_VISIBLE | LBS_HASSTRINGS | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_HSCROLL ,0, 30, 175, 75,hWnd, (HMENU)4, NULL, NULL);

   // 元々のプロシージャを保存
   origEditProc = (WNDPROC)GetWindowLongPtr(hListBox, GWLP_WNDPROC);
   //// サブクラス化: 新しいプロシージャを設定
   SetWindowLongPtr(hListBox, GWLP_WNDPROC, (LONG_PTR)SubclassProc);

   origEditProc2 = (WNDPROC)GetWindowLongPtr(edittext, GWLP_WNDPROC);
   // サブクラス化: 新しいプロシージャを設定
   SetWindowLongPtr(edittext, GWLP_WNDPROC, (LONG_PTR)SubclassProc2);

   if(keynum == 1) RegisterHotKey(hWnd, HOTKEY_ID, MOD_CONTROL | MOD_ALT, 0x54);
   if(keynum == 2) RegisterHotKey(hWnd, HOTKEY_ID, 0, VK_NONCONVERT);

   ShowWindow(hListBox, SW_HIDE);
   ShowWindow(edittext, SW_SHOW);
   ShowWindow(hWnd, SW_HIDE);
   SetFocus(edittext);  // テキストボックスへフォーカスを移動
   UpdateWindow(hWnd);

   return TRUE;
}

//
//  関数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  目的: メイン ウィンドウのメッセージを処理します。
//
//  WM_COMMAND  - アプリケーション メニューの処理
//  WM_PAINT    - メイン ウィンドウを描画する
//  WM_DESTROY  - 中止メッセージを表示して戻る
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {

    case WM_HOTKEY:
        if (!IsWindowVisible(hWnd)) {
            SetMenu(hWnd, nullptr);  // メニューを削除
            SetWindowLong(hWnd, GWL_EXSTYLE, WS_EX_TOOLWINDOW);  // ツールウィンドウに変更
            LoadCandidatesFromFile(fname);
            SetForegroundWindow(hWnd);
            SetActiveWindow(hWnd);
            ShowWindow(hWnd, SW_SHOW);
            SendMessageW(hListBox, LB_RESETCONTENT, 0, 0);
            SetWindowText(edittext, L"");
            SetFocus(edittext); 
        }
        else {
            ShowWindow(hWnd, SW_HIDE);  // ウィンドウを非表示にする
        }
        return 0;

    case WM_COMMAND:
    {
        int wmId = LOWORD(wParam);
        switch (wmId)
        {
        case 2: // テキストボックスが入力された
            if (HIWORD(wParam) == EN_CHANGE) {
                    wchar_t inputText[256];
                    GetWindowTextW(edittext, inputText, 256);

                    // リストボックスを更新
                    SendMessageW(hListBox, LB_RESETCONTENT, 0, 0);
                    candidateCount = 0;
                    for (int i = 0; i < 100; i++) {
                        if (Ckey[i].empty()) break;

                        // 入力した文字列で始まるかチェック
                        if (wcslen(inputText) > 0 && Ckey[i].find(inputText) == 0) {
                            SendMessageW(hListBox, LB_ADDSTRING, 0, (LPARAM)Ckey[i].c_str());
                            candidateCount++;
                        }
                    }

                    // 候補があればリストボックスを表示、なければ隠す
                    if (candidateCount > 0) {
                        ShowWindow(hListBox, SW_SHOW);
                        SetFocus(hListBox);  // 🔹リストボックスへフォーカスを移動
                        SendMessageW(hListBox, LB_SETCURSEL, 0, 0);  // 最初の候補を選択  
                        SetWindowPos(hWnd, HWND_TOP, 0, 0, 175, 110, SWP_NOZORDER);
                    }
                    else {
                        SendMessageW(hListBox, LB_RESETCONTENT, 0, 0);
                        ShowWindow(hListBox, SW_HIDE);
                        SetFocus(edittext);
                        SetWindowPos(hWnd, HWND_TOP, 0, 0, 175, 25, SWP_NOZORDER);
                    }
            }
            break;

        case 4: // リストボックスが選択された
            if (HIWORD(wParam) == LBN_SELCHANGE) {
                selIndex = (int)SendMessageW(hListBox, LB_GETCURSEL, 0, 0);
                if (selIndex != LB_ERR) {
                    wchar_t selectedText[256];
                    SendMessageW(hListBox, LB_GETTEXT, selIndex, (LPARAM)selectedText);
                }
            }
            break;

        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
        break;
    }
        
    case WM_KEYDOWN:
    {
        HWND focusedWnd = GetFocus();  // 現在フォーカスがあるウィンドウを取得

         if (focusedWnd == hListBox) {  // リストボックスがアクティブ
             if (!(wParam == VK_DOWN || wParam == VK_UP || wParam == VK_RETURN)) {
                 BYTE keyboardState[256];
                 GetKeyboardState(keyboardState); // 現在のキーボード状態を取得

                 WCHAR unicodeChar;
                 UINT scanCode = MapVirtualKey((UINT)wParam, MAPVK_VK_TO_VSC);

                 // 仮想キーコードを Unicode に変換
                 if (ToUnicode((UINT)wParam, scanCode, keyboardState, &unicodeChar, 1, 0) == 1) {
                     // 取得した文字をテキストボックスに送信
                     SendMessage(edittext, WM_CHAR, (WPARAM)unicodeChar, 0);
                 }
                 //SetFocus(edittext);
             }
             if (wParam == VK_RETURN) {
                 selIndex = (int)SendMessageW(hListBox, LB_GETCURSEL, 0, 0);
                 if (selIndex != LB_ERR) {
                     wchar_t selectedText[256];
                     SendMessageW(hListBox, LB_GETTEXT, selIndex, (LPARAM)selectedText);

                     // 選択されたテキストと一致する実行パスを探す
                     for (int i = 0; i < 100; i++) {
                         if (Ckey[i] == selectedText) {
                             ShellExecuteW(NULL, L"open", Cvalue[i].c_str(), NULL, NULL, SW_SHOWNORMAL);
     
                             ShowWindow(hWnd, SW_HIDE);  // ウィンドウを非表示にする
                             break;
                         }
                     }
                 }
             }
        }
        break;
    }
    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            // TODO: HDC を使用する描画コードをここに追加してください...
            EndPaint(hWnd, &ps);
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return DefWindowProc(hWnd, message, wParam, lParam);
}

LRESULT CALLBACK SubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    HWND parentWnd = GetParent(hwnd);  // 親ウィンドウのハンドルを取得

    if (msg == WM_KEYDOWN) {
        // まず、テキストボックスの標準の動作を実行
        LRESULT result = CallWindowProc(origEditProc, hwnd, msg, wParam, lParam);

        // 次に、親ウィンドウにキーイベントを同期的に送信
        SendMessage(parentWnd, WM_KEYDOWN, wParam, lParam);

        return result;  // 標準の処理結果を返す
    }

    // 他のメッセージは通常処理
    return CallWindowProc(origEditProc, hwnd, msg, wParam, lParam);
}

LRESULT CALLBACK SubclassProc2(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    HWND parentWnd = GetParent(hwnd);  // 親ウィンドウのハンドルを取得

    if (msg == WM_KEYDOWN) {
        // まず、標準のテキストボックスの動作を実行
        LRESULT result = CallWindowProc(origEditProc2, hwnd, msg, wParam, lParam);

        // 次に、親ウィンドウにキーイベントを送る(同期的に処理)
        SendMessage(parentWnd, WM_KEYDOWN, wParam, lParam);

        return result;
    }

    // 他のメッセージは通常処理
    return CallWindowProc(origEditProc2, hwnd, msg, wParam, lParam);
}


// 変換関数: UTF-8 → wstring
std::wstring Utf8ToWstring(const std::string& utf8Str) {
    int size_needed = MultiByteToWideChar(CP_UTF8, 0, utf8Str.c_str(), -1, NULL, 0);
    if (size_needed <= 0) return L"";

    std::wstring wstr(size_needed - 1, 0); // -1 で末尾のNULLを除外
    MultiByteToWideChar(CP_UTF8, 0, utf8Str.c_str(), -1, &wstr[0], size_needed);
    return wstr;
}

// UTF-8 BOM (Byte Order Mark) の削除
std::string RemoveUTF8BOM(const std::string& str) {
    if (str.size() >= 3 &&
        (unsigned char)str[0] == 0xEF &&
        (unsigned char)str[1] == 0xBB &&
        (unsigned char)str[2] == 0xBF) {
        return str.substr(3);
    }
    return str;
}

void LoadCandidatesFromFile(const std::string& filename) {
    int count = 0;

    // ファイルをバイナリモードで開く(BOMの影響を受けないように)
    std::ifstream file(filename, std::ios::binary);
    if (!file) {
        MessageBoxW(nullptr, L"ファイルを開けませんでした", L"エラー", MB_OK);
        return;
    }

    std::string line;
    while (std::getline(file, line)) {
        // UTF-8 BOMがあれば削除
        if (count == 0) {
            line = RemoveUTF8BOM(line);
        }

        // 空行チェック
        if (line.empty()) continue;

        if (!line.empty() && line.back() == '\r') {
            line.pop_back();
        }

        // 先頭が "#" の場合はスキップ(コメント行)
        if (line[0] == '#') continue;

        // コロン `:` の位置を探す
        size_t pos = line.find(':');
        if (pos == std::string::npos) continue; // `:` がなければ無効データとしてスキップ

        // コマンド名と実行パスを分割
        std::string key = line.substr(0, pos);
        std::string value = line.substr(pos + 1);

        // 空のデータを無視
        if (key.empty() || value.empty()) continue;
        if (key._Equal("shortkey")) {
            keynum = atoi(value.c_str());
            continue;
        }
        // UTF-8 → wstring に変換して格納
        Ckey[count] = Utf8ToWstring(key);
        Cvalue[count] = Utf8ToWstring(value);

        count++;

        // 配列の上限を超えないようにする
        if (count >= 500) break;
    }
    file.close();
}
0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?