Category Archives: 未分類

C/C++ で作った DLL を C# で使う

Wednesday, August 8, 2012

この記事の目的は「アンマネージド DLL に親しむ」です。

  1. C/C++ で DLL を作る
  2. C# で DLL 関数を呼び出す
  3. 文字列を扱う

C/C++ で DLL を作る

C で書かれた次の資産 arithmetic.c を,C# のプログラムから使いたいとします。

// arithmetic.c

int add(int a, int b)
{
    return a + b;
}

これを EXE ではなく DLL としてコンパイルすれば,C# のプログラムから DLL を読み込むことで,add 関数を呼び出すことができます。ただし,正しく DLL 化するには少々の細工が必要です。実際に,正しく細工を施した次のコードを DLL としてコンパイルしてみてください。

// arithmetic.c

__declspec(dllexport) int __stdcall add(int a, int b);

__declspec(dllexport) int __stdcall add(int a, int b)
{
    return a + b;
}

※ VC++ で DLL としてコンパイルするには,[表示] – [(プロジェクト名) のプロパティ] から,[構成プロパティ] – [全般] – [構成の種類] を [ダイナミックライブラリ (.dll)] に設定してください。

※ BCC で DLL としてコンパイルするには,-WD オプションを指定してください。

C++ としてコンパイルする場合には,extern "C" を付加する必要があります。関数のオーバーロードの関係で,関数名がめちゃくちゃに変えられてしまうのを防ぐためです。

// arithmetic.cpp

extern "C" __declspec(dllexport) int __stdcall add(int a, int b);

extern "C" __declspec(dllexport) int __stdcall add(int a, int b)
{
    return a + b;
}

__declspec(dllexport) は,関数のエクスポートに必要なキーワードです。関数のエクスポートとは,関数を他のプログラムから利用できるように公開することです。

__stdcall は呼び出し規約といい,関数の呼び出し方を決定するために必要なキーワードです。よく使われる呼び出し規約には __stdcall 以外に __cdecl などがあります。__stdcall を修飾する関数はプロトタイプ宣言が必要とのことです。

__stdcall (MSDN)
http://msdn.microsoft.com/ja-jp/library/zxk0tw93.aspx

C でも C++ でも通用する書き方として,次のようなマクロ定義をしたコードをお勧めします。ここで __cplusplus は,C++ モードでコンパイラが自動的に定義するマクロ定数です。

// arithmetic.c または arithmetic.cpp

#ifdef __cplusplus
#define DLLEXPORT extern "C" __declspec(dllexport)
#else
#define DLLEXPORT __declspec(dllexport)
#endif

DLLEXPORT int __stdcall add(int a, int b);

DLLEXPORT int __stdcall add(int a, int b)
{
    return a + b;
}

C# で DLL 関数を呼び出す

さて,一方の DLL を呼び出す側ですが,C# のコードは次のようにします。

// Program.cs

using System;
using System.Runtime.InteropServices;

class Program
{
    [DllImport("arithmetic.dll")]
    static extern int add(int a, int b);

    static void Main()
    {
        Console.WriteLine("2 + 3 = {0}", add(2, 3));
        Console.WriteLine("4 + 5 = {0}", add(4, 5));
    }
}

arithmetic.dll 内にある add() を関数を読み込むために,次の 2 行からなる宣言を行っています。

    [DllImport("arithmetic.dll")]
    static extern int add(int a, int b);

DllImport 属性 (System.Runtime.InteropServices 名前空間) は,関数が指定したアンマネージド DLL から読み込まれることを示します。続く修飾子 static extern は決まり文句です。戻り値,関数名,引数リストは,型が C と C# に共通である限り,元の関数プロトタイプをそのまま写せば OK です。

さて,このプログラムをビルドして,生成された実行ファイルと同じディレクトリに DLL ファイルを置けば準備完了です。プログラムをコマンドプロンプトで走らせてみてください。

cdllsample.png

文字列を扱う

文字列を受け渡しする次の mystrcpy() 関数を DLL 化します。

// strutils.c

#include <string.h>

#ifdef __cplusplus
#define DLLEXPORT extern "C" __declspec(dllexport)
#else
#define DLLEXPORT __declspec(dllexport)
#endif

DLLEXPORT void __stdcall mystrcpy(char *dest, const char *src);

DLLEXPORT void __stdcall mystrcpy(char *dest, const char *src)
{
    strcpy(dest, src);
}

これを C# から呼び出すためには,次のようにします。

// Program.cs

using System;
using System.Runtime.InteropServices;
using System.Text;

class Program
{
    [DllImport("strutils.dll")]
    static extern void mystrcpy(StringBuilder dest, string src);

    static void Main()
    {
       StringBuilder dest= new StringBuilder(1024);
       string src = "this is a test.";
       mystrcpy(dest, src);
       Console.WriteLine(dest.ToString());
    }
}

cdllsample2.png

char *System.Text.StringBuilderconst char *string に対応付けられます。StringBuilder を使用する際は,new StringBuilder(1024) のように,十分な領域を確保する必要があります (*1)。

C の型 C# の型
const char * string
char * System.Text.StringBuilder

関数宣言の頭に unsafe を指定すれば,ポインタをポインタのまま残すことも可能です。

その他の型の対応については,次のサイトが参考になります。

.NET TIPS Win32 APIやDLL関数を呼び出すには? – @IT
http://www.atmarkit.co.jp/fdotnet/dotnettips/024w32api/w32api.html

(*1) コメントでご指摘いただき,new StringBuilder()new StringBuilder(1024) に訂正,その旨補足しました (2012-08-20)。

[C++] new による多次元配列の動的作成

Saturday, January 14, 2012

2 次元配列を作ろうとして double **arr = new double[size_x][size_y]; とか書いたらコンパイラに怒られたのでメモ。

1 次元配列

double *array = new double[size_x];

delete[] array;

2 次元配列

ダメな例

double **array = new double[size_x][size_y];  // これはダメ

new により確保できる配列は 1 次元までなので,次のように 1 次元ずつ確保していく必要があります。delete による解放も同様に 1 次元ずつ行います。

正しい例

double **array = new double*[size_x];
for (int i = 0; i < size_x; i++) {
    array[i] = new double[size_y];
}

for (int i = 0; i < size_x; i++) {
    delete[] array[i];
}
delete[] array;

3 次元配列

3 次元以上の配列でも同じ手法で解決できます。

ダメな例

double ***array = new double[size_x][size_y][size_z]; // ダメ

正しい例

double ***array = new double**[size_x];
for (int i = 0; i < size_x; i++) {
    array[i] = new double*[size_y];
    for (int j = 0; j < size_y; j++) {
        array[i][j] = new double[size_z];
    }
}

for (int i = 0; i < size_x; i++) {
    for (int j = 0; j < size_y; j++) {
        delete[] array[i][j];
    }
    delete[] array[i];
}
delete[] array;

Windows のテキストエディタ

Thursday, January 5, 2012

Windows に付属するテキストエディタは「メモ帳」だけではありません。MS-DOS 時代からの「MS-DOS Editor」と「EDLIN」が,現在の Windows でもサポートされています (32-bit 版のみ)。ここでは,これら 3 つのテキストエディタを紹介します。

メモ帳

Notepad on Windows 7 (32-bit)

Notepad on Windows 7 (32-bit)

おなじみの notepad.exe です。

MS-DOS Editor

MS-DOS Editor on Windows 7 (32-bit)

MS-DOS Editor on Windows 7 (32-bit)

コマンドプロンプトを開いて edit と打つと起動します。edit hoge.txt のようにファイル名を指定して起動することもできます。

EDILN

EDLIN (エドリン) on Windows 7 (32-bit)

EDLIN on Windows 7 (32-bit)

「エドリン」と読みます。コマンドプロンプトから edlin hoge.txt のようにファイル名を指定して起動します。ラインエディタと呼ばれるタイプのエディタです。

正しいスペーシング

Wednesday, September 14, 2011

英文で単語と単語の間にスペースを入れるのは常識ですが,約物まわりのスペーシングとなると日本人はかなりいい加減です (悪い意味で)。見た目にも整った文章を書くためには,正しいスペーシングのルールを知る必要があります。

以下の例では,着目すべき部分のスペースを「△」で明示しています。英語を基準にしていますが,適宜フランス語の例を参考として入れました。

コンマ

(正) Aaa bbb ccc,△ddd eee.
(誤) Aaa bbb ccc△,△ddd eee.
(誤) Aaa bbb ccc,ddd eee.

なお,全角のコンマは [半角のコンマ] + [半角のスペース] という構成になっているため,和文中で全角コンマを使う際はいちいちスペースを挿入する必要はありません。これはコンマに限らず他の全角約物についても同様です。

(正) ははは,ははいい。
(誤) ははは,△ははいい。

ピリオド

(正) Aaa bbb ccc.△Ddd eee.
(正) Aaa bbb ccc.△△Ddd eee.
(誤) Aaa bbb ccc△.△Ddd eee△.
(誤) Aaa bbb ccc.Ddd eee.

(正) A.△M.△Turing (人名)
(誤) A.M.Turing

(正) p.△21 (ページ等)
(誤) p.21

(正) 3.14 (小数)
(誤) 3.△14
(参考) 3,14 (仏語)

(正) U.S. (頭字語)
(誤) U.△S.

セミコロン・コロン

(正) Aaa bbb ccc;△ddd eee. (英語)
(誤) Aaa bbb ccc;ddd eee.
(参考) Aaa bbb ccc△;△ddd eee. (仏語)

(正) 15:30 (時刻)
(誤) 15:△30
(参考) 15△h△30 (仏語)

疑問符・感嘆符

(正) Aaa bbb ccc?△Ddd eee. (英語)
(参) Aaa bbb ccc△?△Ddd eee. (仏語)

括弧

(正) Aaa bbb△(ccc ddd)△eee.
(誤) Aaa bbb(ccc ddd)eee.
(誤) Aaa bbb△(△ccc ddd△)△eee.

(正) Aaa bbb△(ccc ddd).
(誤) Aaa bbb△(ccc ddd)△.

引用符

(正) Aaa bbb△“ccc ddd”△eee. (英語)
(誤) Aaa bbb“ccc ddd”eee.
(誤) Aaa bbb△“△ccc ddd△”△eee.
(参考) Aaa bbb△«△ccc ddd△»△eee. (仏語)

単位など

(正) 32△bits (複数の s が必要)
(正) 32-bit (形容詞として)
(誤) 32bit, 32bits, 32△bit, 32-bits など

「データ量が 32 bits」「32-bit のプロセッサ」のように使い分けられます。和文中ではカタカナで「ビット」と書いたほうが無難かもしれません。

(正) 256△MB
(誤) 256MB

(正) 50% (The Chicago Manual of Style など)
(正) 50△% (Le Système International d’Unités など)

(正) 15°
(誤) 15△°

stdio.h の意味と読み方

Thursday, August 4, 2011

プログラミングを独学していて困るのが,特殊な用語の意味と読み方です。例えば,C を習うと真っ先に出てくる #include <stdio.h> とか。私はこの “stdio.h” を “studio.h” と勘違いしてしまい,エラーメッセージとにらめっこするハメになってしまったことがありました。こんな間違いを無くすためにも,また,理解を助けるためにも,”stdio.h” のようなものは意味と読み方を欄外にでも書いておいてほしいものです。

まず “stdio” の意味(というよりは省略前の形)は,”standard I/O” (standard input/output) です。日本語に訳せば「標準入出力」となります。”.h” は,ヘッダファイルの拡張子です。”stdio.h” というヘッダファイルは標準入出力に使われる printfscanf といった関数の宣言が書かれたファイルであり,こうした関数が使えるようにヘッダファイル “stdio.h” を読み込むのが冒頭の #include <stdio.h> です。

“stdio” の読み方は,特に決まったものはないのですが,そのまま「エス ティー ディー アイ オー」と読んでもいいし,「スタンダード アイ オー」と読んでもいいと思います。”.h” の部分も「ドット エイチ」「テン エイチ」「エイチ」など人によって様々な読まれ方をします。

記号や用語の読み方については,次のようなサイトが参考になります。

写研フォント in PDF ファイル

Saturday, May 28, 2011

写研フォントの埋め込まれた PDF ファイルが見られるサイトを私の知っている範囲で紹介します。(2011-05-28 現在)


フォントは埋め込まれていませんが,参考までに。

しんどい不定積分の問題

Tuesday, March 22, 2011

私が過去に悩まされた数学 III の不定積分の問題。

不定積分 \( \displaystyle \int \kern-.4em \sqrt{x^2 + 1} \, dx \) を求めよ。

最終的な答えはこちら。

\( \displaystyle \int \kern-.4em \sqrt{x^2 + 1} \, dx = \frac{1}{2} \left\{ x \sqrt{x^2 + 1} + \log(x + \sqrt{x^2 + 1}) \right\} + C \) (\(C\) は積分定数)

置換積分を 2 回使うか,部分積分と置換積分の合わせ技で解ける。