目次

キーワード

概要

.NET Frameworkには豊富なライブラリが提供されていて、C#やVisual Basicなどの.NET Framework上で動くプログラミング言語だけを使ってたいていのことができます。 しかし、その他のプログラミング言語との相互運用をしたい場面も出てくるでしょう。

特に、OSに深く食い込むような機能はいわゆるネイティブ コードで書かれたネイティブ ライブラリです。 .NET Frameworkはネイティブ ライブラリ中の機能を呼び出すための機能を備えていて、 これをP/Invoke (Platform Invoke: プラットフォーム呼び出し)と呼びます。

ここでは、C#から、このP/Invokeを使う(ネイティブ コードを呼び出す)方法について説明します。

ポイント
  • .NET Framework はネイティブ ライブラリ呼び出し用の命令を持っている。
  • C# でネイティブコード呼び出しをするには、DllImport 属性とかを使う。
サンプル

ネイティブ コード

C# から呼び出せるネイティブ コードには以下のようなものがあります。

  • C-Style 関数
  • COM オブジェクト
  • WinRT コンポーネント

C-Style 関数

C-Style 関数は、C言語で書いた関数や、C++ で「extern "C"」内に書いた関数です。Unix系OSのAPIや、初期のWindows API (Win32 APIと呼ばれています)はこの形式で提供されています。

DllImport

C# から C-Stlye 関数を呼び出すには、DllImport属性(System.Runtime.InteropServices名前空間)を使います。 例えば、以下のように書きます。

using System;
using System.Runtime.InteropServices;

namespace NativeInterop
{
    class DllImportSample
    {
        static void Main()
        {
            SYSTEMTIME t;
            GetLocalTime(out t);

            Console.WriteLine($"{t.wYear}/{t.wMonth}/{t.wDay} {t.wHour}:{t.wMinute}:{t.wSecond}");
        }

        [DllImport("kernel32.dll")]
        static extern void GetLocalTime(out SYSTEMTIME lpSystemTime);
    }

    [StructLayout(LayoutKind.Sequential, Pack = 2)]
    struct SYSTEMTIME
    {
        public ushort wYear;
        public ushort wMonth;
        public ushort wDayOfWeek;
        public ushort wDay;
        public ushort wHour;
        public ushort wMinute;
        public ushort wSecond;
        public ushort wMilliseconds;
    }
}
2015/8/15 1:42:37

このコードで、kernel32.dll という Windows のネイティブ ライブラリ中にあるGetLocalTimeという関数を呼び出せます。

上記の例を見ての通り、結構なコードを書く必要があります。以下のようなものが必要です。

  • DllImport属性の引数に、参照したいネイティブ ライブラリの名前を書く
  • メソッド名は、呼び出したい関数の名前をそのまま付ける
  • C# 側の型とネイティブ側の型には対応関係があるので、適切に置き換えて戻り値や引数を並べる
  • 引数で構造体が使われている場合、同じレイアウトの構造体を C# コード中に書く必要がある

使いたい関数に対して一つ一つこの作業が必要ですが、さすがにめんどくさいので、Win32 API名からP/Invoke用のコードを検索できるサイトがあったりします。

extern 修飾子

ちなみに、このDllImportでは、「実装が外にあるメソッド」を書くことになります。 この例の場合、GetLocalTimeには実装(メソッドの本体)がない代わりに、extern修飾子がついています。 externは実装が外にあることを示すための修飾子で、P/Invoke 用の機能です。

マーシャリング

DllImport属性を使った P/Invoke の手順の中に、 「C# 側の型とネイティブ側の型には対応関係があるので、適切に置き換えて戻り値や引数を並べる」 というものがありました。

この、C# 側の型とネイティブ側の型の対応関係に基づいて型を置き換える処理のことをマーシャリング (marshalling: 整列)といいます。

marshal という単語には、整列の他に、元帥(軍事司令官)、(パレードなどの)式典担当という意味があります。 つまり、marshal は、同じ整列でも sort や order よりも「統括者がいて、責任をもって並べる」というような意味合いが強くなります。 C# とネイティブの境界では、そういう「誰かが責任を持った整列」が行われないと危険ということです。

C# とネイティブの間に限らず、C# 同士であっても、セキュリティ的に隔離したい2つのプログラムの間では、同様にマーシャリングと呼ばれる過程(誰かが責任をもってデータを受け渡しする)を通したデータの受け渡しが必要になります。

サンプル

キー入力を全部記録して、それをマクロ的に再生するプログラム。 昔、ブラウザー ゲームでボット プレイするために作ったもの。

SetWindowsHookExとかSendInputとかの Win32 API を使っています。 このあたりのAPIの使い方は、以下のページを参考にして作りました。

保守していないし今ちゃんと動く保証なし。

更新履歴

ブログ