ネット上で公開されているランチャーを模擬したソースコード。
会社でフリーソフトウエア禁止のため自作。
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();
}