「C Sharp Win32 API および DLL の利用」の版間の差分
ナビゲーションに移動
検索に移動
1行目: | 1行目: | ||
− | ==C# Win32 API および DLL の利用== | + | ==[[C# Win32 API および DLL の利用]]== |
− | [[C Sharp]] | [[Visual Studio]] | | + | [[C Sharp]] | [[Visual Studio]] | [[Category:Win32API]] |
このページからメモ | このページからメモ | ||
*http://msdn.microsoft.com/en-us/magazine/cc301501.aspx | *http://msdn.microsoft.com/en-us/magazine/cc301501.aspx | ||
8行目: | 8行目: | ||
===概要=== | ===概要=== | ||
− | ====どのようにして DLL や Win32 API の関数を C# から呼び出すか==== | + | ====どのようにして DLL や Win32 API の関数を [[C#]] から呼び出すか==== |
=====やりたいことは・・・===== | =====やりたいことは・・・===== | ||
*(char*) が戻り値の場合、どのように取得する? | *(char*) が戻り値の場合、どのように取得する? | ||
− | * | + | *GetWindowRectやEnum[[Windows]]で、構造体や、コールバック関数をどのように指定する? |
=====方法の概要===== | =====方法の概要===== | ||
− | *. | + | *[[.NET]]の利点の一つに、言語非依存があり、書いたクラスを他の言語でも使用できることがある。 |
− | *しかし、アンマネージの DLL へは .NET のオブジェクトを 構造体や char* や関数ポインタへ変換(マーシャリング)する必要がある | + | *しかし、アンマネージの DLL へは [[.NET]] のオブジェクトを 構造体や char* や関数ポインタへ変換(マーシャリング)する必要がある |
*マーシャリングは大きなトピックだが、それほど多くを学ぶ必要はない。 | *マーシャリングは大きなトピックだが、それほど多くを学ぶ必要はない。 | ||
− | ===C# から DLL 関数を呼び出すには=== | + | ===[[C#]] から DLL 関数を呼び出すには=== |
− | ==== | + | ====まず、[[C#]]では、DLLImportを行う。==== |
*[http://msdn.microsoft.com/en-us/library/ms633546%28VS.85%29.aspx SetWindowText] | *[http://msdn.microsoft.com/en-us/library/ms633546%28VS.85%29.aspx SetWindowText] | ||
− | using System.Runtime. | + | using System.Runtime.InteropSer[[vi]]ces; // DLL Import |
class Win32Api | class Win32Api | ||
29行目: | 29行目: | ||
} | } | ||
− | *C# では、DLLImportを利用して、コンパイラにどこにラッパー関数とバンドルするエントリーポイントが存在するのかを伝える | + | *[[C#]] では、DLLImportを利用して、コンパイラにどこにラッパー関数とバンドルするエントリーポイントが存在するのかを伝える |
*例ではWin32としているが、クラス名は任意であり、ネームスペースに置くことも可能 | *例ではWin32としているが、クラス名は任意であり、ネームスペースに置くことも可能 | ||
− | <blockquote>ライブラリとして作成しておけば、任意の C# プロジェクトから利用可能</blockquote> | + | <blockquote>ライブラリとして作成しておけば、任意の [[C#]] プロジェクトから利用可能</blockquote> |
− | ==== | + | ====LPTST[[R]]に対応する==== |
*上記 SetWindwText を呼び出してみる | *上記 SetWindwText を呼び出してみる | ||
49行目: | 49行目: | ||
====なぜこのようなことができるのか? - デフォルトのマーシャリング型==== | ====なぜこのようなことができるのか? - デフォルトのマーシャリング型==== | ||
− | *コンパイラは user32.dll を探して、SetWindwText を認識しており呼び出し前に自動的に string を | + | *コンパイラは user32.dll を探して、SetWindwText を認識しており呼び出し前に自動的に string を LPTST[[R]](TCHA[[R]]*)に変換する。 |
− | *すべての C# 型は、デフォルトのマーシャリング型を持っている(string は LPTSTR)ためこのようなことが可能。 | + | *すべての [[C#]] 型は、デフォルトのマーシャリング型を持っている(string は LPTSTR)ためこのようなことが可能。 |
=====しかし string はそのままでは出力パラメータとして使用できない===== | =====しかし string はそのままでは出力パラメータとして使用できない===== | ||
*入力パラメータとしては、上記のようにstringを使用できるが、出力パラメータとしてこれは使用できない(GetWindowTextで試してみるとよい)。 | *入力パラメータとしては、上記のようにstringを使用できるが、出力パラメータとしてこれは使用できない(GetWindowTextで試してみるとよい)。 | ||
*なぜなら string は 変更不可(インミュータブル)だから。 | *なぜなら string は 変更不可(インミュータブル)だから。 | ||
− | <blockquote>出力パラメータとして | + | <blockquote>出力パラメータとして LPTST[[R]] を使用する場合、StringBuilder を利用する</blockquote> |
=====例(GetWindowText)===== | =====例(GetWindowText)===== | ||
*[http://msdn.microsoft.com/en-us/library/ms633520%28VS.85%29.aspx GetWindowText] | *[http://msdn.microsoft.com/en-us/library/ms633520%28VS.85%29.aspx GetWindowText] | ||
using System.Text; // String Builder | using System.Text; // String Builder | ||
− | using System.Runtime. | + | using System.Runtime.InteropSer[[vi]]ces; // DLL Import |
class Win32Api | class Win32Api | ||
80行目: | 80行目: | ||
[[File:0294_get_window_text01.jpg]] | [[File:0294_get_window_text01.jpg]] | ||
− | <blockquote>StringBuilderのデフォルトのマーシャリング型も LPTSTR | + | <blockquote>StringBuilderのデフォルトのマーシャリング型も LPTSTR だが、Get[[Windows]]Text は内容を変更できる</blockquote> |
====フォルトのマーシャリング型が希望の型でない場合==== | ====フォルトのマーシャリング型が希望の型でない場合==== | ||
90行目: | 90行目: | ||
====構造体を定義==== | ====構造体を定義==== | ||
[StructLayout(LayoutKind.Sequential)] | [StructLayout(LayoutKind.Sequential)] | ||
− | public struct | + | public struct [[R]]ECT |
{ | { | ||
public int left; | public int left; | ||
100行目: | 100行目: | ||
{ | { | ||
[DllImport("User32.Dll", EntryPoint="GetWindowRect")] | [DllImport("User32.Dll", EntryPoint="GetWindowRect")] | ||
− | public static extern int | + | public static extern int GetWindow[[R]]ect(int hwnd, ref [[R]]ECT rc); |
} | } | ||
*呼び出し元 | *呼び出し元 | ||
private void button4_Click(object sender, EventArgs e) | private void button4_Click(object sender, EventArgs e) | ||
{ | { | ||
− | + | [[R]]ECT rc = new [[R]]ECT(); | |
int hwnd = (int)this.Handle; | int hwnd = (int)this.Handle; | ||
− | Win32Api. | + | Win32Api.GetWindow[[R]]ect(hwnd, ref rc); |
string msg = String.Format("top={0}, left={1}, right={2}, bottom={3}." | string msg = String.Format("top={0}, left={1}, right={2}, bottom={3}." | ||
115行目: | 115行目: | ||
[[File:0293_get_window_rect01.jpg]] | [[File:0293_get_window_rect01.jpg]] | ||
− | <blockquote>ref | + | <blockquote>ref を使うとCL[[R]]は、関数がオブジェクトを変更できるように参照として渡す(無名のスタックコピーではなく)。</blockquote> |
====クラスを構造体として渡す==== | ====クラスを構造体として渡す==== | ||
− | * | + | *上記の[[R]]ECTが構造体ではなく、クラスの場合、以下のようにすればよい |
[DllImport("user32.dll")] | [DllImport("user32.dll")] | ||
public static extern int | public static extern int | ||
− | + | GetWindow[[R]]ect(int hwnd, | |
[MarshalAs(UnmanagedType.LPStruct)] RECT rc); | [MarshalAs(UnmanagedType.LPStruct)] RECT rc); | ||
− | ====C# に構造体が用意されている場合もある==== | + | ====[[C#]] に構造体が用意されている場合もある==== |
− | *C# では、ひとつのことを実現するのに多数のやり方がある | + | *[[C#]] では、ひとつのことを実現するのに多数のやり方がある |
− | *System.Drawing に既に | + | *System.Drawing に既に [[R]]ectangle 構造体が用意されており、Wn32 API の[[R]]ECTにマーシャリングするすべを心得ている |
− | using System.Drawing; // | + | using System.Drawing; // [[R]]ectangle |
class Win32Api | class Win32Api | ||
{ | { | ||
[DllImport("User32.Dll", EntryPoint = "GetWindowRect")] | [DllImport("User32.Dll", EntryPoint = "GetWindowRect")] | ||
− | public static extern int | + | public static extern int GetWindow[[R]]ect(int hwnd, ref [[R]]ectangle rc); |
} | } | ||
====そもそもWin32 APIを利用する必要もない==== | ====そもそもWin32 APIを利用する必要もない==== | ||
*上記のAPIは、コントロールのプロパティを利用すれば実現できる | *上記のAPIは、コントロールのプロパティを利用すれば実現できる | ||
− | ===== | + | =====GetWindow[[R]]ect ===== |
− | + | [[R]]ectangle r = mywnd.Display[[R]]ectangle; | |
=====Get/SetWindowText===== | =====Get/SetWindowText===== | ||
mywnd.Text = "Window Text"; | mywnd.Text = "Window Text"; | ||
156行目: | 156行目: | ||
[DllImport("User32.Dll")] | [DllImport("User32.Dll")] | ||
− | public static extern int | + | public static extern int Enum[[Windows]](EnumWindowCB cb, int lparm); |
} | } | ||
169行目: | 169行目: | ||
{ | { | ||
Win32Api.EnumWindowCB cb = new Win32Api.EnumWindowCB(MyEnumWindowCB); | Win32Api.EnumWindowCB cb = new Win32Api.EnumWindowCB(MyEnumWindowCB); | ||
− | Win32Api. | + | Win32Api.Enum[[Windows]](cb, 0); |
} | } | ||
=====結果===== | =====結果===== | ||
175行目: | 175行目: | ||
====ポインタを使う==== | ====ポインタを使う==== | ||
− | * | + | *Enum[[Windows]] においては、lparam で渡したポインターをコールバック関数で得ることができた。 |
*.Netでは、IntPtr 、GCHandle を使ってラップする。 | *.Netでは、IntPtr 、GCHandle を使ってラップする。 | ||
− | <blockquote>Free メソッドを呼び出すのを忘れないこと! C#では、時々自分でメモリを開放しなければならない!</blockquote> | + | <blockquote>Free メソッドを呼び出すのを忘れないこと! [[C#]]では、時々自分でメモリを開放しなければならない!</blockquote> |
=====ラッパークラス側===== | =====ラッパークラス側===== | ||
− | using System.Runtime. | + | using System.Runtime.InteropSer[[vi]]ces; |
public delegate bool EnumWindowCB2(int hwnd, IntPtr lparm); | public delegate bool EnumWindowCB2(int hwnd, IntPtr lparm); | ||
[DllImport("User32.Dll", EntryPoint = "EnumWindows")] | [DllImport("User32.Dll", EntryPoint = "EnumWindows")] | ||
− | public static extern int | + | public static extern int Enum[[Windows]]2(EnumWindowCB2 cb, IntPtr lparm); |
=====呼び出し側===== | =====呼び出し側===== | ||
203行目: | 203行目: | ||
GCHandle gch = GCHandle.Alloc(ewi); | GCHandle gch = GCHandle.Alloc(ewi); | ||
Win32Api.EnumWindowCB2 cb = new Win32Api.EnumWindowCB2(MyEnumWindowCB2); | Win32Api.EnumWindowCB2 cb = new Win32Api.EnumWindowCB2(MyEnumWindowCB2); | ||
− | Win32Api. | + | Win32Api.Enum[[Windows]]2(cb, (IntPtr)gch); |
gch.Free(); | gch.Free(); | ||
} | } |
2020年2月16日 (日) 04:22時点における版
目次
C# Win32 API および DLL の利用
C Sharp | Visual Studio | このページからメモ
非常にわかりやすく実践的な良書。
概要
どのようにして DLL や Win32 API の関数を C# から呼び出すか
やりたいことは・・・
- (char*) が戻り値の場合、どのように取得する?
- GetWindowRectやEnumWindowsで、構造体や、コールバック関数をどのように指定する?
方法の概要
- .NETの利点の一つに、言語非依存があり、書いたクラスを他の言語でも使用できることがある。
- しかし、アンマネージの DLL へは .NET のオブジェクトを 構造体や char* や関数ポインタへ変換(マーシャリング)する必要がある
- マーシャリングは大きなトピックだが、それほど多くを学ぶ必要はない。
C# から DLL 関数を呼び出すには
まず、C#では、DLLImportを行う。
using System.Runtime.InteropServices; // DLL Import class Win32Api { [DllImport("User32.Dll", EntryPoint="SetWindowText")] public static extern void SetWindowText(int hwnd, String text); }
- C# では、DLLImportを利用して、コンパイラにどこにラッパー関数とバンドルするエントリーポイントが存在するのかを伝える
- 例ではWin32としているが、クラス名は任意であり、ネームスペースに置くことも可能
<blockquote>ライブラリとして作成しておけば、任意の C# プロジェクトから利用可能</blockquote>
LPTSTRに対応する
- 上記 SetWindwText を呼び出してみる
呼び出し側
private void button1_Click(object sender, EventArgs e) { int hwnd = (int)this.Handle; string s = "SetWindowText Test."; Win32Api.SetWindowText(hwnd, s); }
- 起動時の状態
- button1 押下後
なぜこのようなことができるのか? - デフォルトのマーシャリング型
- コンパイラは user32.dll を探して、SetWindwText を認識しており呼び出し前に自動的に string を LPTSTR(TCHAR*)に変換する。
- すべての C# 型は、デフォルトのマーシャリング型を持っている(string は LPTSTR)ためこのようなことが可能。
しかし string はそのままでは出力パラメータとして使用できない
- 入力パラメータとしては、上記のようにstringを使用できるが、出力パラメータとしてこれは使用できない(GetWindowTextで試してみるとよい)。
- なぜなら string は 変更不可(インミュータブル)だから。
<blockquote>出力パラメータとして LPTSTR を使用する場合、StringBuilder を利用する</blockquote>
例(GetWindowText)
using System.Text; // String Builder using System.Runtime.InteropServices; // DLL Import class Win32Api { [DllImport("User32.Dll", EntryPoint="GetWindowText")] public static extern int GetWindowText(int hwnd, StringBuilder buf, int nMaxCount); }
- 呼び出し側
private void button2_Click(object sender, EventArgs e) { int hwnd = (int)this.Handle; StringBuilder buf = new StringBuilder(256); Win32Api.GetWindowText(hwnd, buf, buf.Capacity); MessageBox.Show(buf.ToString()); }
- 呼び出したところ
<blockquote>StringBuilderのデフォルトのマーシャリング型も LPTSTR だが、GetWindowsText は内容を変更できる</blockquote>
フォルトのマーシャリング型が希望の型でない場合
GetClassName
- デフォルトのマーシャリング型をオーバーライドすることができる
[DllImport("User32.Dll", EntryPoint = "GetClassName")] public static extern int GetClassName(int hwnd, [MarshalAs(UnmanagedType.LPStr)] StringBuilder buf, int nMaxCount);
構造体を定義
[StructLayout(LayoutKind.Sequential)] public struct RECT { public int left; public int top; public int right; public int bottom; } class Win32Api { [DllImport("User32.Dll", EntryPoint="GetWindowRect")] public static extern int GetWindowRect(int hwnd, ref RECT rc); }
- 呼び出し元
private void button4_Click(object sender, EventArgs e) { RECT rc = new RECT(); int hwnd = (int)this.Handle; Win32Api.GetWindowRect(hwnd, ref rc); string msg = String.Format("top={0}, left={1}, right={2}, bottom={3}." , rc.top, rc.left, rc.right, rc.bottom); MessageBox.Show(msg); }
<blockquote>ref を使うとCLRは、関数がオブジェクトを変更できるように参照として渡す(無名のスタックコピーではなく)。</blockquote>
クラスを構造体として渡す
- 上記のRECTが構造体ではなく、クラスの場合、以下のようにすればよい
[DllImport("user32.dll")] public static extern int GetWindowRect(int hwnd, [MarshalAs(UnmanagedType.LPStruct)] RECT rc);
C# に構造体が用意されている場合もある
- C# では、ひとつのことを実現するのに多数のやり方がある
- System.Drawing に既に Rectangle 構造体が用意されており、Wn32 API のRECTにマーシャリングするすべを心得ている
using System.Drawing; // Rectangle class Win32Api { [DllImport("User32.Dll", EntryPoint = "GetWindowRect")] public static extern int GetWindowRect(int hwnd, ref Rectangle rc); }
そもそもWin32 APIを利用する必要もない
- 上記のAPIは、コントロールのプロパティを利用すれば実現できる
GetWindowRect
Rectangle r = mywnd.DisplayRectangle;
Get/SetWindowText
mywnd.Text = "Window Text";
どのような時にWin32 API ラッパーを利用するのか
- コントロールの派生クラスではなく、HWND しか取得できない場合
- コールバック関数
コールバック関数の使い方
- アンマネージのコールバック関数を使うには、デリゲートを使うだけ
- デリゲートは単なる宣言のため、実装は自分のクラスで行う。
ラッパークラス側
class Win32Api { public delegate bool EnumWindowCB(int hwnd, int lparm); [DllImport("User32.Dll")] public static extern int EnumWindows(EnumWindowCB cb, int lparm); }
呼び出し側
// デリゲートの実装 public static bool MyEnumWindowCB(int hwnd, int lparam) { System.Diagnostics.Debug.WriteLine(String.Format("{0:X}", hwnd)); return true; } private void button6_Click(object sender, EventArgs e) { Win32Api.EnumWindowCB cb = new Win32Api.EnumWindowCB(MyEnumWindowCB); Win32Api.EnumWindows(cb, 0); }
結果
ポインタを使う
- EnumWindows においては、lparam で渡したポインターをコールバック関数で得ることができた。
- .Netでは、IntPtr 、GCHandle を使ってラップする。
<blockquote>Free メソッドを呼び出すのを忘れないこと! C#では、時々自分でメモリを開放しなければならない!</blockquote>
ラッパークラス側
using System.Runtime.InteropServices; public delegate bool EnumWindowCB2(int hwnd, IntPtr lparm); [DllImport("User32.Dll", EntryPoint = "EnumWindows")] public static extern int EnumWindows2(EnumWindowCB2 cb, IntPtr lparm);
呼び出し側
public partial class Form1 : Form { static bool MyEnumWindowCB2(int hwnd, IntPtr lparam) { GCHandle gch = (GCHandle)lparam; EnumWindowInfo ewi = (EnumWindowInfo)gch.Target; System.Diagnostics.Debug.WriteLine(String.Format("{0:X}:{1}", hwnd, ewi.msg)); return true; } private void button7_Click(object sender, EventArgs e) { EnumWindowInfo ewi = new EnumWindowInfo(); ewi.msg = "this is IntPtr Test."; GCHandle gch = GCHandle.Alloc(ewi); Win32Api.EnumWindowCB2 cb = new Win32Api.EnumWindowCB2(MyEnumWindowCB2); Win32Api.EnumWindows2(cb, (IntPtr)gch); gch.Free(); } } public class EnumWindowInfo { public string msg = ""; }
結果
© 2006 矢木浩人