LoginSignup
0
1

More than 5 years have passed since last update.

C++ Builder XE4 > 起動中のソフトウェアのうち、同じフォルダに含まれる実行ファイルと関連があるものだけ見つける > v0.1:EnumWindows実装, v0.2: ビルトイン?EnumWindows()使用

Last updated at Posted at 2018-10-24
動作環境
C++ Builder XE4

処理概要

  • 前提
    • 実行ファイルが特定のフォルダに用意されている
        • C:\Work\Project1.exe
        • C:\Work\Project2.exe
    • 各ソフトは実行ファイル名の文字列がキャプションに含まれる
      • 例: 「Project1 (v0.1.0) - This software is ...」
  • 仕様
    • 起動中のソフトウェアのうち、同じフォルダに含まれる実行ファイルと関連があるものだけ見つける
      • Project1からProject2の実行を見つける

関連

実装 > v0.1 > EnumetareWindows()の再帰処理

Unit1.h
//---------------------------------------------------------------------------

#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <System.Classes.hpp>
#include <Vcl.Controls.hpp>
#include <Vcl.StdCtrls.hpp>
#include <Vcl.Forms.hpp>
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published:    // IDE で管理されるコンポーネント
    TButton *B_listup;
    TMemo *Memo1;
    void __fastcall B_listupClick(TObject *Sender);
private:    // ユーザー宣言
    void __fastcall listFileNamesFromFolder(TStringList *dstPtr);
    void __fastcall listSoftCaption(TStringList *dstPtr);
    void EnumetareWindows(HWND hWndParent, int depth, TStringList *dstPtr);
    void __fastcall removeNonTargetSoft(TStringList *fileNameSL, TStringList *softCapsSL);
public:     // ユーザー宣言
    __fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif
Unit1.cpp
//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include <IOUtils.hpp>
#include <memory>
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::B_listupClick(TObject *Sender)
{
    // 1. ファイルリストアップ
    std::unique_ptr<TStringList> fileNames(new TStringList);
    listFileNamesFromFolder(fileNames.get());

    // 2. 起動ソフトのキャプションをリストアップ
    std::unique_ptr<TStringList> softCaps(new TStringList);
    listSoftCaption(softCaps.get());

    // 3. フィルター
    removeNonTargetSoft(fileNames.get(), softCaps.get());

    // 4. 結果の表示
    Memo1->Lines->Clear();
    for(int idx=0; idx < softCaps->Count; idx++) {
        Memo1->Lines->Add(softCaps->Strings[idx]);
    }
}
//---------------------------------------------------------------------------
void __fastcall TForm1::listFileNamesFromFolder(TStringList *dstPtr)
{
    // .exeのあるフォルダ内の.exeファイルをリストアップする

    if (dstPtr == NULL) {
        return; // error
    }

    String curDir = GetCurrentDir();

    TStringDynArray fileList;
    TSearchOption option = TSearchOption::soTopDirectoryOnly;

    fileList = TDirectory::GetFiles(curDir, L"*.exe", option);

    for(int idx=0; idx<fileList.Length; idx++) {
        String filnam = ExtractFileName(fileList[idx]);
        //Memo1->Lines->Add(filnam);
        dstPtr->Add(filnam);
    }
}

void __fastcall TForm1::listSoftCaption(TStringList *dstPtr)
{
    // (階層1で)起動中のソフトキャプションをリストアップする

    if (dstPtr == NULL) {
        return; // error
    }

    HWND hWndParent = GetDesktopWindow();
    EnumetareWindows(hWndParent, /*depth=*/0, dstPtr); // 0: 探索開始階層(トップ)
}

static const int kMaxDepth = 1; // 起動ソフトをチェックする最大階層

void TForm1::EnumetareWindows(HWND hWndParent, int depth, TStringList *dstPtr)
{
    if (dstPtr == NULL) {
        return; // error
    }

    wchar_t szBuf[512] = {0};
    GetWindowText(hWndParent, szBuf, 512);

    if (szBuf[0] != 0x00) {
        if (depth <= kMaxDepth) {
            String msg = IntToStr(depth) + L":" + String(szBuf);
            dstPtr->Add(String(szBuf));
            //Memo1->Lines->Add(msg);

        }
    }

    HWND hWndChild = FindWindowEx(hWndParent, NULL, NULL, NULL);
    if (hWndChild == NULL) {
        return;
    }

    do {
        EnumetareWindows(hWndChild, (depth+1), dstPtr);
    } while ( (hWndChild = FindWindowEx(hWndParent, hWndChild, NULL, NULL)) != NULL );
}

void __fastcall TForm1::removeNonTargetSoft(TStringList *fileNameSL, TStringList *softCapsSL)
{
    // 1. 非対象ソフトの確認
    for(int si=0; si < softCapsSL->Count; si++){ // si: software index
        int cnt=0;
        for(int fi=0; fi < fileNameSL->Count; fi++) { // fi: filename index
            String filnam = fileNameSL->Strings[fi];
            // .exeの削除
            filnam = StringReplace(filnam, L".exe", L"", TReplaceFlags()<<rfReplaceAll);

            //
            if (softCapsSL->Strings[si].Pos(filnam) > 0) { // 実行ファイル名がキャプションに含まれる
                cnt++;
            }
        }
        if (cnt == 0) {
            softCapsSL->Strings[si] = L""; // Delete()で削除すると位置が変わるため、ここではゼロ長さとする
        }
    }

    // 2. 非対象ソフトのリストからの削除
    //    (ずれを考慮して、後ろから消す)
    for(int si = (softCapsSL->Count - 1); si >= 0; si--) { // si: software index
        if (softCapsSL->Strings[si].Length() == 0) {
            softCapsSL->Delete(si);
        }
    }

}

結果

  1. Project2を起動する
  2. Project1を起動する
  3. Project1上のボタンを押す

2018-10-24_09h26_24.png

備考

  • Proejct1(自分)が2つ見つかる
    • 自分の情報は別の方法で取得できるので使わない方法を検討する
  • Project2.exeとProject27182818_napier.exeが同居すると探索に失敗する
    • 将来のソフトウェアアップデート時の名前付けを考慮しておくこと
      • XXX.exe
      • XXX2.exe
        • XXX.exeとXXX2.exeが同時に起動している場合に問題となる
  • .exeのショートカットファイルにも対応すると便利かもしれない
    • v0.1では未対応

改善点

「FindWindowEx()を使えば、もっとシンプルになるかもしれない」というのが人と話していく中で出てきた。

実装 > v0.2 > EnumWindows()使用

参考

実装

Unit1.h
 //---------------------------------------------------------------------------

#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <System.Classes.hpp>
#include <Vcl.Controls.hpp>
#include <Vcl.StdCtrls.hpp>
#include <Vcl.Forms.hpp>
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published:    // IDE で管理されるコンポーネント
    TButton *B_listup;
    TMemo *Memo1;
    void __fastcall B_listupClick(TObject *Sender);
private:    // ユーザー宣言
    void __fastcall listFileNamesFromFolder(TStringList *dstPtr);
    String __fastcall findWindowCaption_hasStringOf(String partStr);
public:     // ユーザー宣言
    __fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif
Unit1.cpp
//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include <IOUtils.hpp>
#include <memory>
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::B_listupClick(TObject *Sender)
{
    // 1. ファイルリストアップ
    std::unique_ptr<TStringList> fileNames(new TStringList);
    listFileNamesFromFolder(fileNames.get());

    // 2. 関係ソフトの探索と表示
    Memo1->Lines->Clear();
    for(int idx=0; idx<fileNames->Count; idx++) {
        String acap = findWindowCaption_hasStringOf(fileNames->Strings[idx]);
        Memo1->Lines->Add(acap);
    }
}
//---------------------------------------------------------------------------
void __fastcall TForm1::listFileNamesFromFolder(TStringList *dstPtr)
{
    // .exeのあるフォルダ内の.exeファイルをリストアップする

    if (dstPtr == NULL) {
        return; // error
    }

    String curDir = GetCurrentDir();

    TStringDynArray fileList;
    TSearchOption option = TSearchOption::soTopDirectoryOnly;

    fileList = TDirectory::GetFiles(curDir, L"*.exe", option);

    for(int idx=0; idx<fileList.Length; idx++) {
        String filnam = ExtractFileName(fileList[idx]);
        //Memo1->Lines->Add(filnam);
        dstPtr->Add(filnam);
    }
}

static String s_partStr; // 探索用文字列
static bool FindWindowWithExpr(HWND hWnd, LPARAM lParam)
{
    TCHAR szText[256] = {0};
    GetWindowText(hWnd, szText, 255);

    if (String(szText).Pos(s_partStr) > 0) {
        *((HWND*) lParam) = hWnd;
        return false;
    }

    return true;
}

String __fastcall TForm1::findWindowCaption_hasStringOf(String partStr)
{
    // 戻り値: ウィンドウのキャプション

    HWND hWnd = NULL; // root から探索

    // .exeの削除した文字列で検索 (例: Project1)
    s_partStr = StringReplace(partStr, L".exe", L"", TReplaceFlags()<<rfReplaceAll);
    EnumWindows((WNDENUMPROC)&FindWindowWithExpr, (LPARAM)&hWnd); // top-level windowsのみ探索
    if (hWnd == NULL) {
        return L"";
    }

    // ウィンドウキャプションの取得
    wchar_t winCap[256] = {0};
    GetWindowText(hWnd, winCap, 256);
    return String(winCap);
}

結果

  1. Project2を起動する
  2. Project1を起動する
  3. Project1上のボタンを押す

2018-10-24_13h17_18.png

備考

static String s_partStr; // 探索用文字列
は多重処理時には別の方法が必要になる。

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