イネマルのプログラミング備忘録

趣味プログラマのメモ

Windows C++ 実行ファイルからバージョン情報を取得する

メモ

バージョンリソースが含まれるファイルからバージョン情報を取得する方法。
Windows API の GetFileVersionInfo を使って取得する。

実装

#include <windows.h>
#include <iostream>
#include <vector>
#include <string>

#pragma comment(lib, "version.lib")

// バージョン取得用の構造体
struct Version final {
    WORD minor;
    WORD major;
    WORD revision;
    WORD build;
};

// バージョン取得関数
bool GetFileVersion(const std::string& path, Version& version)
{
    DWORD infoHandle = 0;
    const auto size = GetFileVersionInfoSizeA(path.c_str(), &infoHandle);
    std::vector<char> buffer(size);
    if (buffer.empty()) {
        return false;
    }
    else if (GetFileVersionInfoA(path.c_str(), infoHandle, size, buffer.data()) == 0) {
        return false;
    }
    VS_FIXEDFILEINFO* pInfo;
    UINT verLen;
    if (VerQueryValueA(buffer.data(), "\\", reinterpret_cast<LPVOID*>(&pInfo), &verLen) == 0) {
        return false;
    }
    union {
        struct {
            DWORD fileVersionMS;
            DWORD fileVersionLS;
        };
        Version info;
    } ver = { pInfo->dwFileVersionMS, pInfo->dwFileVersionLS };
    version = ver.info;
    return true;
}

int main()
{
    const char* filePath = R"(C:\Windows\System32\notepad.exe)";

    // バージョン取得
    Version ver = {};
    if (GetFileVersion(filePath, ver)) {
        std::cout << "Major:" << ver.major << std::endl;
        std::cout << "Minor:" << ver.minor << std::endl;
        std::cout << "Build:" << ver.build << std::endl;
        std::cout << "Revision:" << ver.revision << std::endl;
    }
    else {
        std::cout << "エラー" << std::endl;
    }

    system("pause");
    return 0;
}

以上。

C++ 環境変数を含むパスをstd::stringに展開する方法

はじめに

パスに限らず、環境変数を含む文字列は、
WindowsAPIの ExpandEnvironmentStrings で展開できます。
std::string に展開する方法をメモ。

実装

#include <windows.h>
#include <iostream>
#include <string>

// 環境変数を含むパスを展開する
bool GetExpandEnvironmentPath(const std::string& path, std::string& dest)
{
    dest.resize(ExpandEnvironmentStringsA(path.c_str(), nullptr, 0));
    if (path.empty()) {
        return false;
    }
    ExpandEnvironmentStringsA(path.c_str(), &dest.front(), dest.size());
    return true;
}

int main()
{
    // 元のパス
    const char* basePath = "%tmp%\\data.bin";

    // 展開
    std::string resultPath;
    if (GetExpandEnvironmentPath(basePath, resultPath)) {
        std::cout << "展開前:" << basePath << std::endl;
        std::cout << "展開後:" << resultPath << std::endl;
    }
    else {
        std::cout << "失敗" << std::endl;
    }

    system("pause");
    return 0;
}

あとがき

今回は、stringに展開しましたが、
wstring なら ExpandEnvironmentStringsW で展開できます。

C++ で、コマンドの標準出力を取得する方法(CreateProcessなし)

はじめに

Windows でコマンドを実行する際、標準出力を取得する方法です。
コンソール画面が表示されても問題ない場合、
もしくは、CUIプログラムで使用できる方法です。

前提

system 関数だけでは、標準出力が簡単に取得できません。
Windows 環境では、APIの CreateProcess を使用する方法が良く使用されますが、popen を使用した方がシンプルに実装できます。

標準エラーの対応は、行っていませんが、
画面に表示したくない場合は、実行コマンド末尾に「 2>&1」を付けて標準出力にリダイレクトする等
適宜対応しましょう。

実装

#include <array>  // array
#include <cstdio>    // _popen
#include <iostream>  // cout
#include <memory>    // shared_ptr
#include <string>    // string

bool ExecCmd(const char* cmd, std::string& stdOut, int& exitCode) {
    std::shared_ptr<FILE> pipe(_popen(cmd, "r"), [&](FILE* p) {exitCode = _pclose(p); });
    if (!pipe) {
        return false;
    }
    std::array<char, 256> buf;
    while (!feof(pipe.get())) {
        if (fgets(buf.data(), buf.size(), pipe.get()) != nullptr) {
            stdOut += buf.data();
        }
    }
    return true;
}

int main(int argc, char** argv)
{
    // 適当なコマンドを用意する
    const char* cmd = "dir";
    std::string stdOut;
    int exitCode;
    if (ExecCmd(cmd, stdOut, exitCode)) {
        std::cout << stdOut << std::endl;
    }
    else {
        std::cout << "標準出力の取得に失敗しました。" << std::endl;
    }

    system("pause");
    return 0;
}

あとがき

Windows では、popen が使用できないと思い込んでいましたが、
_popen の名前で、使用できるので、実装して備忘録行き。

C++ コマンドライン引数をstd::vector<std::string>に展開する方法

ちょっとしたメモ

コマンドライン引数をstd::vector<std::string>に展開する方法です。
パース自体は、コンストラクタのみで実現できます。

実装

#include <iostream>
#include <vector>
#include <string>

int main(int argc, char** argv)
{
    using namespace std;
    vector<string> args(argv, argv + argc);
    for (const auto& arg : args) {
        cout << arg << endl;
    }
    return 0;
}

あとがき

エントリーポイントを int main(std::vector<std::string> args)
みたいに書けたら楽ですが、そういう横着はできません。

元ネタは、下記を参照。
stackoverflow.com

C / C++ の共用体(union)を初歩的に使う

入門時にイマイチ使いどころがわからんかった共用体

プログラミングを初めて超初期のころ入門書で出会った
”union” ”共用体” のキーワード
自分の使っていた入門書では
構造体の直後位に解説されていて
いったいどこで使うの?
といった感じで放置していたが
最近使う機会があったので、一応覚書

(自分なりの)結論

キャストとかを駆使せずに”データにアクセスする手段”
記述法だけ似ている構造体とは別物と考えて良し

目的

構造体にオペレーターやキャストを使用せずに配列のようにアクセスする

確認用のプログラム

Vector3型風の構造体pos{ x y z } の各要素に”[ ]”でアクセスする。
本来ならposは配列でないので”[ ]”でアクセスできないが
共用体の機能でアクセスを可能にする
※動作確認は、VC2013

#include	<iostream>
#include	<array>

int main()
{
	using namespace std;

	union testUnion{
		struct { float x, y, z; } pos;
		array<float, 3> v;	// もしくは、 float v[3];
	};
	testUnion uni;

	uni.v[0] = 100.0f;		// uni.pos.x = 100.0; と同じ
	uni.v[1] = 200.1f;		// uni.pos.y = 200.1; と同じ
	uni.v[2] = 300.2f;		// uni.pos.z = 300.2; と同じ

	// つまりこう書ける
	for (int id = 0; id < 3; ++id){
		uni.v[id] += 0.5f;
	}

	// 値表示
	cout << uni.pos.x << endl;
	cout << uni.pos.y << endl;
	cout << uni.pos.z << endl;

	// 入力待ち
	rewind(stdin), getchar();
	return 0;
}

あとがき

記事にするほどのネタでもないけど入門時の自分が見たら
少しくらいは助かる気がしたので備忘録行き