お茶漬けびより

学んだことを整理する場所です。主に、C++, Unreal Engine 4 (UE4) を扱います。たまに趣味や雑記も。現在(2017/7/1より)無職です。

Visual Studio で 動的ライブラリ(DLL)を作成する

f:id:pickles-ochazuke:20170705153246j:plain

自分へのメモ書き(上の画像に意味はないです)。

ライブラリを自分で作ったことがなかったので、ちょいと作ってみることに。
Visual Studio は 2017 です。あと C++ で書きます。

DLL プロジェクトの作成

まず、いつも通り Visual Studio を起動して、新しいプロジェクトを作成します。
選ぶプロジェクトの種類ですが、Win32 コンソールアプリケーション を選びます。
で、名前はてきとうで構いません。
簡単で打ち間違いにくい名前にするといいかもしれません。ここでは、DLLTest にしました。

f:id:pickles-ochazuke:20170704231404p:plain

OK > 次へ > と進み、アプリケーションの種類では、DLL を選びます。
他は、何もいじらなくてOKです。完了を押します。

f:id:pickles-ochazuke:20170704231409p:plain

するとプロジェクトが作成され、ソリューションエクスプローラソースファイルの下には、dllmain.cpp, DLLTest.cpp, stdafx.cpp があり、ヘッダーファイルには、stdafx.h, targetver.h があります。

f:id:pickles-ochazuke:20170704231751p:plain

それぞれ開くと、中にコメントが書かれているので、読んでおくといいかもしれません。まぁ触るのは、DLLTest.cpp だけなのですが。

DLL の実装

では、ライブラリを作っていきます。まず、ヘッダーファイルにファイルを追加します。ここは、いつも通り新しいクラスを追加するときと同じように作れば良いです。ファイル名も同じように自由です。ここでは、DLLTest.hにしました。
そして、先頭に以下を書きます。

#ifdef DLLTEST_EXPORTS
#define DLLTEST_API __declspec(dllexport)
#else
#define DLLTEST_API __declspec(dllimport)
#endif

ここで注意して欲しいのは、ifdef DLLTEST_EXPORTS です。これは、プロジェクト名によって異なりますので注意です。新しいプロジェクトを DLL として作成したときに、Visual Studio 側が勝手に定義してくれます。その名前は、プロジェクト名(全て大文字)_EXPORTS となります。今回、プロジェクト名は、DLLTest だったので、DLLTEST_EXPORTS となるわけです。

あとは、この下にいつものようにクラスを書いていきます。今回は、以下のように書いてみました。

namespace DLLTest {
    class HelloWorld {
    public:
        int huga;
        DLLTEST_API HelloWorld();
        static DLLTEST_API void hello();
        DLLTEST_API void world();
    private:
        int hoge = 0;
    };
}

各関数の手前に、DLLTEST_API があります。先頭行で書いた define です。DLLTEST_EXPORTS が定義されているので中身は、__declspec(dllexport) となるはずです。__declspec(dllexport)__declspec(dllimport) の違いは、ちゃんと調べていないですが、DLL を作成するときは、dllexport で、 DLL を利用するときは、dllimport だと判断しています。

変数には付けなくても問題ないようです(ここら辺のことは、どこを見たら分かるんですかね……)。

次に、DLLTest.cpp に書いていきます。全文を載せています。

#include "stdafx.h"
#include "DLLTest.h"
#include "iostream"

namespace DLLTest {
    HelloWorld::HelloWorld()
        : hoge(32)
    {  
        std::cout << "HelloWorld " << hoge << std::endl;
    }

    void HelloWorld::HelloWorld::hello()
    {
        std::cout << "Hello";
        return;
    }

    void HelloWorld::HelloWorld::world()
    {
        std::cout << "World" << std::endl;
        return;
    }
}

こちらは、とくに変わらずいつも通りに書いてしまえば問題ありません。気をつけるのは、stdafx.h は一番最初にインクルードする必要があることです。原因はよく分かっていないですが、他のインクルードの後だと、ビルドが通りませんでした。

ビルドをして成功したら、DLLTest のフォルダ下にある Debug フォルダに DLLTest.dll があると思います。

おわり

以上で DLL の作成は完了です。まだまだ何となくで作っていますが、少しずつ理解を深めていこうと思います。そのときに分かったことがあれば、また記事にしたいですね。何か参考になりそうな資料を教えていただければ幸いです。

次回は、作成した DLL を使ってみたいと思います。

pickles-ochazuke.hatenablog.com

以下を参考にしました。

https://msdn.microsoft.com/ja-jp/library/ms235636.aspx
https://msdn.microsoft.com/ja-jp/library/3y1sfaz2.aspx

抽象基本クラス(Abstract Base Class : ABC)の復習

ファクトリーメソッドを復習しようと思ったら、抽象基本クラスの復習をする羽目になったので、 調べて学んだことをメモ書きします。

抽象クラスとは

概念的なもの。このクラスから実態を持つことは出来ないが、抽象クラス型へのポインタと参照は使用できる。だそうです。

抽象クラス (C++)

実態を持たないので、継承しないと意味がありません。なので、抽象基本クラスとも言うそうです(自分が読んだ本には、抽象基本クラスと書いてあった)。今後、抽象基本クラスは ABC(Abstract Base Class) と表記します。

じゃあ、どういうものが ABC かというと、純粋仮想メンバ関数を持つクラスのことを ABC といいます。純粋仮想メンバ関数というのは、以下の様なものです。

virtual void Method() = 0;

純粋仮想メンバは、継承先で必ずオーバーライドしないといけません。つまり継承先で必ず実装しないといけません。ですが、ABC 側で実装をしてはいけないという意味ではありません。Method().cpp ファイルで実装するとコンパイルは通り、呼ぶことが可能です。

ABC は実態を持つことは出来ませんが、ABC 型へのポインタを作ることが可能です。
このポインタを使って、ABC を継承した様々なクラスのインスタンスへのポインタを入れることが出来ます。つまり、以下のように書きます。

IBase *instance = new Derived();

IBase は、ABC です。Derived は、 IBase を継承したクラスです。
instance->method() のように使えば、継承したインスタンスを呼ぶことが出来ます。上記は、Derived のみですが、他に IBase を継承したクラスがあれば、同じように使うことが出来ます。

コンストラクタ、デストラクタ

コンストラクタを仮想化することは出来ませんが、デストラクタは可能です。 つまり以下のように書けます。

virtual ~IBase();

これは、ABC で必ず必要になります。ABC は、実態を持つことはないので、必ず継承されて使われます。しかし、書かなくてもコンパイルは通ります。このときコンパイラは、デフォルトデストラクタを作成しますが、この状態で ABC を継承した Derived 型のポインタを delete すると、大変なことが起こります。コードで示すと以下のようになります(コンパイルは通らないかもしれません……)。

// 抽象基本クラス
class IBase
{
public:
    virtual void Method() = 0;
};
// 抽象基本クラスを継承したクラス
class Derived : public IBase
{
public:
    ~Derived(){};
    void Method() {};
};

int main()
{
    IBase *instance = new Derived();
    instance->Method();
    delete instance;
    
    return 0;
}

このとき、delete で呼ばれるデストラクタは、IBase のデフォルトデストラクタです。継承先のデストラクタは呼ばれません。どうしてかは詳しく説明できないですが、IBase のデストラクタが実態を持っていることが原因であることは分かると思います。 ですので、抽象基本クラスを作るときは、必ずデストラクタに virtual を付けるように気を付けましょう。

キーワード:override

デストラクタには virtual を付けるように気をつけましょうと言いましたが、このミスをコンパイル時に見つける方法があります。それが、override 指定子です。
こいつを付けることで、コンパイラに「今からオーバーライドするよ」と教えることが出来ます。つまり以下のようになります。

// 抽象基本クラスを継承したクラス
class Derived : public IBase
{
public:
    ~Derived() override {};
    void Method() {};
};

~Derived() の後に override を付けることで、コンパイル時にエラーが発生するようになります(Visual Studio 2017 だとコンパイルする前に赤い波線が出ました)。
もちろんデストラクタだけでなく、メソッドにも付けることが可能です。オーバーライドをするときには、必ず override を付けるようにしましょう。

override 指定子

キーワード:final

今度は、逆にオーバーライドをして欲しくないときの対処法です。override と同じようにメソッドの後ろに final を付けます。以下のようになります。

virtual int value() final { return value_; };

このように書くことで、継承先がオーバーライドをしようとすると、コンパイラはエラーを発します。処理が今後変わることがないときは、 final を付けるようにしましょう。

final 指定子

キーワード:delete

最後に delete を紹介します。これは、delete 演算子ではありません。何と言うのかちょっと調べただけでは分からないですが、このキーワードを使うことで、メソッドやコンストラクタ、デストラクタを削除することが出来ます。 以下のように書きます。

public:
IBase() = delete;

public 側で書くことをお勧めします。これはコンパイラが delete であるかどうかよりもアクセスの可否を先に見るためです。private で delete されたメソッドをユーザが使おうとすると メソッドが private であることだけをエラーとして吐くそうです。また他にも利点があるのですが、ここでのメインの話ではないので省きます。詳細は、Effective Modern C++ の項目 11 を見てください。

Effective Modern C++ ―C++11/14プログラムを進化させる42項目

Effective Modern C++ ―C++11/14プログラムを進化させる42項目

おわり

以上、抽象基本クラスの復習でした。 C++11 から使えるようになったキーワードを混ぜることで、安全に継承をすることが出来るようになりそうです。

ここで記載したコードは、以下に上げています。

github.com

最後に、参考にした(復習する羽目になった)書籍を貼っておきます。

C++のためのAPIデザイン

C++のためのAPIデザイン

DirectX 11 を始めました # 0

自分の調べた内容を整理するために、書き起こしてみました。
なるべく DirectX 初心者でも分かるように書きたいと思います。

今回は、DirectX や別のライブラリを紹介していきます。

DirectX って?

Microsoft が開発したゲームやマルチメディア処理用のグラフィックス API です。
PC でゲームをする人は、初めてゲームをするときに入れたことがあるかもしれません。開発者は、これを開発のために使います。
この DirectX には、WindowsWindows 7Windows 8.1, Windows 10 のように大きなバージョンごとに名前が少し変わります。
有名なところだと、DirectX 9.0, DirectX 11.0, DirectX 12 です。名前が変わるきっかけは、大きな機能が追加、変更されたりすると変わります。

Windows のバージョンによって、使える DirectX が決まります。 だいたい以下のようになっています。

Windows の対応している DirectX のバージョンは、Wikipedia や 公式サイトが詳しいです。

https://support.microsoft.com/ja-jp/help/179113/how-to-install-the-latest-version-of-directx Microsoft DirectX - Wikipedia

いくつか種類はありますが、要は DirectX を使えば、ゲームを作れるよということです。
ここでは、DirectX 11.0 を使いますがこれは、DirectX 11 がそこそこ新しくて、そこそこ学びやすいからです。 DirectX 12 は、新しいのですが難しくいきなり触るのは危険だからです。

本当に DirectX でいいの?

先に DirectX を使えばゲームを作れると書きましたが、これは現実的ではありません。
というのも、DirectX というのは、開発者がコンピュータグラフィックス(CG)を扱うための手段としては低レベルな API だからです。
ゲームを作るというのが目的であれば、DirectX を直接触る必要なく作ることが出来る API(ライブラリ)があります。

例えば、DXライブラリというゲームライブラリがありますが、これは DirectX の機能をゲームのために簡単に扱えるようにしたライブラリです。
他にも、openFrameworks や、SFMLCinderSiv3D などがあります。ライブラリとは少し異なりますが、Cocos2d-x というフレームワークもあります。

自分が使いたい言語(C や C++Java など)、環境(PC やスマフォ)、目的は何なのか。だいたいこの 3 つを決めた上で、先に挙げたようなライブラリを選択することをお勧めします(特に目的が大事です)。
各ライブラリの URL は面倒なので省きますが、検索すると別のライブラリの名前も出ることがあるので、一度自分の目的を明確にしてから調べてみるといいでしょう。

実は、先に挙げたライブラリ、自分はどれも使ったことがありません・・・・・・。ゲームを作ろうと DirectX 9.0 を少し触ったことがあり、簡単なゲームを一つ作りましたが、かなり時間がかかりました(だらだらやっていたせいもありますが)。

調べた感じだと、
Cocos2d-xDXライブラリ や openFrameWorksDirectX や OpenGL
の順番で低レベルな API になっていくと思います。

じゃあ DirectX を使うメリットは何なのかといいますと、CG が描画される仕組みをより深く知ることができ、より自由で高速なプログラムを作ることが出来ます。ですが、自由で高速なプログラムなんて初心者には無理です。それなら初心者は、ライブラリで十分です。

ライブラリで十分なのですが、CG が描画される仕組みをより深く知ることができるのは、かなりのメリットだと思います。なので、DirectX を触ることは無駄ではないです。自分のしたいことと自由に使える時間と相談して、選んでいくといいと思います。

また、少しずつ低レベルな API に変えていくという方法もあると思います。
例えば、Cocos2d-x → DXライブラリ → DirectX というように使っている API に慣れたらさらに低レベルな API に切り替えて新しい(または同じ)ゲームを作っていくという方法です。
これは、ゲームで例えると自分があまり苦戦せずに倒せる敵でレベル上げをしていく感じです。逆にいきなり DirectX を触るのは、強敵や高経験値を持っているけど運要素が強い敵でレベル上げをしていくようなものです。

さいごに

長くなりましたが、なんとなく DirectX を触ってみようと考えていた方の参考になればいいなと思って書きました。

最後に、目的がゲームを作ることで、プログラミングなんてしたくないって方には、ゲームエンジンをおすすめします。
有名どころですと、UnityUnrealEngine があります。他にも XenkoDefold , Playground など様々なゲームエンジンが無料で扱えます。 どれも同じなわけではなくそれぞれに特徴があるので、これもまた自分の作りたいものに合わせて選ぶといいでしょう。あとは、使い方の情報の多さも大事です。

今回はこの辺でおしまいにします。

Re:ゼロから始めるポインタ入門 #2

ポインタの基礎からちょっとずつ難しくしていこうかと思っていたのですが、 それだと書きづらいので、もう好きなように書いていくことにしました。すみません……。

はじめに

今回からは、配列とポインタを関数の引数にするときの様々な方法を見ていきます。

仮引数が配列の関数に、配列を渡す

引数として渡す変数は以下のような配列です。

int array[ARRAY_LENGTH]; /* ARRAY_LENGTH は、定数 3 */

まず、実引数として配列を渡す方法を見ていきます。 以下のような関数を使います。

void ArrayPrint(int array[ARRAY_LENGTH]);

これを使う場合、以下のように配列を渡します。

#include <stdio.h>
#define ARRAY_LENGTH (3)

void ArrayPrint(int array[ARRAY_LENGTH]);

int main() {
    printf("Re:ゼロから始めるポインタ入門 #2\n");

    int array[ARRAY_LENGTH];
    int i;
    for (i=0; i<ARRAY_LENGTH; i++) {
        array[i] = i * i;
    }
    ArrayPrint(array);
    return 0;
}

void ArrayPrint(int array[ARRAY_LENGTH])
{
    int i;
    for (i=0; i<ARRAY_LENGTH; i++) {
        printf("array[%d]: %d\n", i, array[i]);
    }
    return;
}

初期化した array を引数として、関数 ArrayPrint() に渡して、ArrayPrint 内で printf を使って、array の中を出力しています。 結果は、以下のようになります。

Re:ゼロから始めるポインタ入門 #2
array[0]: 0
array[1]: 1
array[2]: 4

関数の引数と同じ配列を渡しているので、とくに何も考えずに使うことが出来ると思います。

追記

指摘を頂きました。
仮引数が要素数固定の配列でも、要素数が異なる配列を 渡すことが出来る。といった指摘です。 では、試してみましょう。

main 関数内で定義している array[ARRAY_LENGTH]array[3] にして、ARRAY_LENGTH の値を 10 にします。これでコンパイルをすると仮引数と実引数の要素数が異なるのですが、普通に通ってしまいます。実行すると以下のようになりました。

Re:ゼロから始めるポインタ入門 #2 指摘
array[0]: 0
array[1]: 1
array[2]: 4
array[3]: 0
array[4]: 0
array[5]: 0
array[6]: 1873092421
array[7]: 32708
array[8]: 0
array[9]: 0

思ってたよりは値がおとなしいですが、array[6]array[7] は、デタラメな値が入っていますね。上記のように、関数の仮引数に配列の要素数を指定してもコンパイルエラーを起こすことは出来ないようです。これは、仮引数に配列を指定しても、ポインタを指定したのと同じだからです。ただ、関数を以下のようにすれば一応、同じ配列の要素数ではないとコンパイルエラーを起こすことが出来ます。

void ArrayPrint(int (*array)[ARRAY_LENGTH])

関数を呼び出す時は、ArrayPrint(&array) のようにします。これは、多次元配列なので、以下のように書くことも出来ます。

void ArrayPrint(int array[][ARRAY_LENGTH])
void ArrayPrint(int array[2][ARRAY_LENGTH])

3つ書き方を示しましたが、どれも同じ意味です。こうすると今度は、行の指定を無視できるので、 結局、要素数の異なる配列を渡すことが出来てしまいます。
結論としては、関数の仮引数に配列を指定するのを止めて、次に説明するポインタとその要素数を指定する方法か、仮引数に配列を明記しつつ、内部の処理で配列の要素数が仕様で決めたものと異なる場合は、さっさとシステムを殺すように作るというのが解決策かなと思います(どなたか詳しい方いれば、ご指摘お願いします)。

追記は以上です。ご指摘ありがとうございました。 追記内容のコードは、以下に置いてあります。 github.com

仮引数がポインタの関数に、配列を渡す

次は、関数の仮引数が、ポインタの場合です。以下のような関数に配列を渡します。

void PointsPrint(int* points, size_t point_num) {
    int i;
    for (i=0; i<point_num; i++) {
        printf("points(%p): %d\n", points+i, *(points+i));        
    }
}

point_num には、引数の長さを渡します。 記事が長くなってしまうので、全文は避けますが、先ほど全文を載せたソースコードに、上記の関数 PointsPrint() を使います。渡す実引数は、先ほどと同じ array[ARRAY_LENGTH] です。
渡し方は、以下のようになります。

PointsPrint(array, ARRAY_LENGTH);

実行すると、以下のようになります。

Re:ゼロから始めるポインタ入門 #2
points(0x7ffff1384160): 0
points(0x7ffff1384164): 1
points(0x7ffff1384168): 4

array と書くことで、配列の先頭( array[0] )を渡すことになります。 配列は、同じ型のポインタが並んでいるみたいなものなので、関数 PointsPrint() のように ポインタとして扱えます。
ただ、配列のサイズが分からなくなってしまうので、引数として配列のサイズを受け取る必要があります。 逆に言えば、ポインタとして扱えば、どんなサイズの配列でも扱うことが出来るようになります。

今回は、以上です。 コードの全文は、以下に置いてあります。

github.com

C# と .Net Framework で 他の Windows アプリを操作するアプリ

簡単にですが作りました。

経緯

最近、C# を学び始めて .Net Framework を使うようになったのですが、これがけっこう便利で楽しくて、 ふと、WindowsAPI なら ハンドル取ってこれそうだなと思い、調べるとすでにやってる人がいたので、 マネをしてみました。

目的

アプリ一つ一つに割り当てられているハンドル(ウィンドウハンドル)を取得して、 外部のアプリからハンドル先に値を送ることでアプリを操作することが目的です。
まだ可能かどうかは分かりませんが、ゲームのテストや同じ操作の繰り返しをプログラムで 制御できればいいなと思って作りました。

こちらを参考にしたので、試したい方はこちらを見た方が早いかと思います。

普通、アプリだと複数のウィンドウやボタンがあるので、操作をするのは難しいのですが、 自分の場合は、ゲームの操作が目的だったので、ハンドルを取ってしまえばあとはコントローラの キーにあたる値を送ることで操作することが可能です。

操作するためのキーの値はこちらを見ればいいかと
Virtual-Key Codes (Windows)

環境

環境は以下です。

以下は、今回使う言語やフレームワーク、その他出てくる技術です。
もし読んでて分からないところがあれば、以下のキーワードを加えて検索するといいかも……?

GUI の作成

WPFGUI を作ります。 重要な部分ではないので飛ばします。以下のように作りました。

f:id:pickles-ochazuke:20170326231753p:plain

各ボタンの処理

Get Handle というボタンは、隣にある Process Name の入力内容を元に関係するアプリを探して、ウィンドウハンドルを取得します。Process Name は、タスクマネージャを開き、詳細タブに並んでいる .exe を除いたアプリの名前を入力します。

ボタン入力時の処理は以下です。

private void GetHandleButton_Click(object sender, RoutedEventArgs e)
{
    process = System.Diagnostics.Process.GetProcessesByName(ProcessName.Text);
    foreach (System.Diagnostics.Process ps in process) {
        LogBox.Text += ps.Id + ps.MainWindowTitle + ps.MainWindowHandle + "\n";
    }
}

GetProcessesByName でアプリのハンドルを取得します。 取得したら、内容をテキストボックスに出力します(これは確認用)。

Button というボタンは、Message の内容をハンドル先に送ります。つまり、Message に操作用の値を書き、 Button を押すことでアプリを操作します。が、これはまだ未実装です。現状は、予め用意した値を送るようにしています。

IntPtr hWnd = process[0].MainWindowHandle; 
LogBox.Text += SendMessage(hWnd, 0x0100, 0x20, 0x00);

SendMessage() で、どう操作したいかを指示しています。その後、確認用にテキストボックスに出力しています。 SendMessage(ハンドル, イベント, 内容1, 内容2) のように指定します。
イベントは、キーが押されたキーが離されたマウスのボタンがクリックされたなどです。内容1,2はイベントに合わせて指定します。何もない場合は 0x00 でいいと思います。 上記だと、hWnd に、スペースキー押されたという情報を送ります。 SendMessage は、.Net FrameworkAPI ではなく、Win32APIAPI です。これは、以下のように宣言することで使えるようになります。

[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, uint Msg, uint wParam, uint lParam);

DllImport という属性(Attribute)を使うことで、外部のライブラリを使えるようにしています。 ここら辺は、以下を参照してください。(正直、自分はちゃんと理解出来ていません……)

@IT:.NET TIPS Win32 APIやDLL関数を呼び出すには? - C#
キーワード:Attribute, Reflection, Annotation

これだけのコードで、別のアプリにプログラムで好きなキー入力を行うことができます。

コード

XAML 側のコードがないので、これだけでは動かないのですが、一応全文載せておきます。

using System;
using System.Windows;

namespace GetHandle
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        System.Diagnostics.Process[] process;

        public MainWindow()
        {
            InitializeComponent();
        }

        private void GetHandleButton_Click(object sender, RoutedEventArgs e)
        {
            process = System.Diagnostics.Process.GetProcessesByName(ProcessName.Text);
            foreach (System.Diagnostics.Process ps in process) {
                LogBox.Text += ps.Id + ps.MainWindowTitle + ps.MainWindowHandle + "\n";

            }
        }

        private void MessageButton_Click(object sender, RoutedEventArgs e)
        {
            IntPtr hWnd = process[0].MainWindowHandle; 
            LogBox.Text += SendMessage(hWnd, 0x0100, 0x20, 0x00);
        }

        [System.Runtime.InteropServices.DllImport("user32.dll")]
        public static extern int SendMessage(IntPtr hWnd, uint Msg, uint wParam, uint lParam);
    }
}

デモ