| ページ一覧 | ブログ | twitter |  書式 | 書式(表) |

MyMemoWiki

「C Sharp Win32 API および DLL の利用」の版間の差分

提供: MyMemoWiki
ナビゲーションに移動 検索に移動
 
(同じ利用者による、間の5版が非表示)
1行目: 1行目:
==C# Win32 API および DLL の利用==
+
==[[C# Win32 API および DLL の利用]]==
[[C#][Visual Studio]]{{category Win32API}}
+
[[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やEnumWindowsで、構造体や、コールバック関数をどのように指定する?
+
*GetWindowRectやEnum[[Windows]]で、構造体や、コールバック関数をどのように指定する?
  
 
=====方法の概要=====
 
=====方法の概要=====
*.NETの利点の一つに、言語非依存があり、書いたクラスを他の言語でも使用できることがある。
+
*[[.NET]]の利点の一つに、言語非依存があり、書いたクラスを他の言語でも使用できることがある。
*しかし、アンマネージの DLL へは .NET のオブジェクトを 構造体や char* や関数ポインタへ変換(マーシャリング)する必要がある
+
*しかし、アンマネージの DLL へは [[.NET]] のオブジェクトを 構造体や char* や関数ポインタへ変換(マーシャリング)する必要がある
 
*マーシャリングは大きなトピックだが、それほど多くを学ぶ必要はない。
 
*マーシャリングは大きなトピックだが、それほど多くを学ぶ必要はない。
  
===C# から DLL 関数を呼び出すには===
+
===[[C#]] から DLL 関数を呼び出すには===
====まず、C#では、DLLImportを行う。====
+
====まず、[[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.InteropServices; // DLL Import
+
  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>
  
====LPTSTRに対応する====
+
====LPTST[[R]]に対応する====
 
*上記 SetWindwText を呼び出してみる
 
*上記 SetWindwText を呼び出してみる
  
49行目: 49行目:
  
 
====なぜこのようなことができるのか? - デフォルトのマーシャリング型====
 
====なぜこのようなことができるのか? - デフォルトのマーシャリング型====
*コンパイラは user32.dll を探して、SetWindwText を認識しており呼び出し前に自動的に string を LPTSTR(TCHAR*)に変換する。
+
*コンパイラは user32.dll を探して、SetWindwText を認識しており呼び出し前に自動的に string を LPTST[[R]](TCHA[[R]]*)に変換する。
*すべての C# 型は、デフォルトのマーシャリング型を持っている(string は LPTSTR)ためこのようなことが可能。
+
*すべての [[C#]] 型は、デフォルトのマーシャリング型を持っている(string は LPTSTR)ためこのようなことが可能。
  
 
=====しかし string はそのままでは出力パラメータとして使用できない=====
 
=====しかし string はそのままでは出力パラメータとして使用できない=====
 
*入力パラメータとしては、上記のようにstringを使用できるが、出力パラメータとしてこれは使用できない(GetWindowTextで試してみるとよい)。
 
*入力パラメータとしては、上記のようにstringを使用できるが、出力パラメータとしてこれは使用できない(GetWindowTextで試してみるとよい)。
 
*なぜなら string は 変更不可(インミュータブル)だから。
 
*なぜなら string は 変更不可(インミュータブル)だから。
&lt;blockquote&gt;出力パラメータとして LPTSTR を使用する場合、StringBuilder を利用する&lt;/blockquote&gt;
+
&lt;blockquote&gt;出力パラメータとして LPTST[[R]] を使用する場合、StringBuilder を利用する&lt;/blockquote&gt;
  
 
=====例(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.InteropServices; // DLL Import
+
  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]]
  
&lt;blockquote&gt;StringBuilderのデフォルトのマーシャリング型も LPTSTR だが、GetWindowsText は内容を変更できる&lt;/blockquote&gt;
+
&lt;blockquote&gt;StringBuilderのデフォルトのマーシャリング型も LPTSTR だが、Get[[Windows]]Text は内容を変更できる&lt;/blockquote&gt;
  
 
====フォルトのマーシャリング型が希望の型でない場合====
 
====フォルトのマーシャリング型が希望の型でない場合====
90行目: 90行目:
 
====構造体を定義====
 
====構造体を定義====
 
  [StructLayout(LayoutKind.Sequential)]
 
  [StructLayout(LayoutKind.Sequential)]
  public struct RECT
+
  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 GetWindowRect(int hwnd, ref RECT rc);
+
     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)
 
  {
 
  {
     RECT rc = new RECT();
+
     [[R]]ECT rc = new [[R]]ECT();
 
     int hwnd = (int)this.Handle;
 
     int hwnd = (int)this.Handle;
     Win32Api.GetWindowRect(hwnd, ref rc);
+
     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]]
  
&lt;blockquote&gt;ref を使うとCLRは、関数がオブジェクトを変更できるように参照として渡す(無名のスタックコピーではなく)。&lt;/blockquote&gt;
+
&lt;blockquote&gt;ref を使うとCL[[R]]は、関数がオブジェクトを変更できるように参照として渡す(無名のスタックコピーではなく)。&lt;/blockquote&gt;
  
 
====クラスを構造体として渡す====
 
====クラスを構造体として渡す====
*上記のRECTが構造体ではなく、クラスの場合、以下のようにすればよい
+
*上記の[[R]]ECTが構造体ではなく、クラスの場合、以下のようにすればよい
 
  [DllImport("user32.dll")]
 
  [DllImport("user32.dll")]
 
  public static extern int  
 
  public static extern int  
   GetWindowRect(int hwnd,  
+
   GetWindow[[R]]ect(int hwnd,  
 
     [MarshalAs(UnmanagedType.LPStruct)] RECT rc);
 
     [MarshalAs(UnmanagedType.LPStruct)] RECT rc);
  
====C# に構造体が用意されている場合もある====
+
====[[C#]] に構造体が用意されている場合もある====
*C# では、ひとつのことを実現するのに多数のやり方がある
+
*[[C#]] では、ひとつのことを実現するのに多数のやり方がある
*System.Drawing に既に Rectangle 構造体が用意されており、Wn32 API のRECTにマーシャリングするすべを心得ている
+
*System.Drawing に既に [[R]]ectangle 構造体が用意されており、Wn32 API の[[R]]ECTにマーシャリングするすべを心得ている
  
  using System.Drawing; // Rectangle
+
  using System.Drawing; // [[R]]ectangle
 
  class Win32Api
 
  class Win32Api
 
  {
 
  {
 
     [DllImport("User32.Dll", EntryPoint = "GetWindowRect")]
 
     [DllImport("User32.Dll", EntryPoint = "GetWindowRect")]
     public static extern int GetWindowRect(int hwnd, ref Rectangle rc);
+
     public static extern int GetWindow[[R]]ect(int hwnd, ref [[R]]ectangle rc);
 
  }
 
  }
  
 
====そもそもWin32 APIを利用する必要もない====
 
====そもそもWin32 APIを利用する必要もない====
 
*上記のAPIは、コントロールのプロパティを利用すれば実現できる
 
*上記のAPIは、コントロールのプロパティを利用すれば実現できる
=====GetWindowRect =====
+
=====GetWindow[[R]]ect =====
  Rectangle r = mywnd.DisplayRectangle;
+
  [[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 EnumWindows(EnumWindowCB cb, int lparm);
+
     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.EnumWindows(cb, 0);
+
     Win32Api.Enum[[Windows]](cb, 0);
 
  }
 
  }
 
=====結果=====
 
=====結果=====
175行目: 175行目:
  
 
====ポインタを使う====
 
====ポインタを使う====
*EnumWindows においては、lparam で渡したポインターをコールバック関数で得ることができた。
+
*Enum[[Windows]] においては、lparam で渡したポインターをコールバック関数で得ることができた。
 
*.Netでは、IntPtr 、GCHandle を使ってラップする。
 
*.Netでは、IntPtr 、GCHandle を使ってラップする。
&lt;blockquote&gt;Free メソッドを呼び出すのを忘れないこと! C#では、時々自分でメモリを開放しなければならない!&lt;/blockquote&gt;
+
&lt;blockquote&gt;Free メソッドを呼び出すのを忘れないこと! [[C#]]では、時々自分でメモリを開放しなければならない!&lt;/blockquote&gt;
 
=====ラッパークラス側=====
 
=====ラッパークラス側=====
  using System.Runtime.InteropServices;
+
  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 EnumWindows2(EnumWindowCB2 cb, IntPtr lparm);
+
  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.EnumWindows2(cb, (IntPtr)gch);
+
         Win32Api.Enum[[Windows]]2(cb, (IntPtr)gch);
 
         gch.Free();
 
         gch.Free();
 
     }
 
     }
215行目: 215行目:
  
  
{{ref test.txt}}
+
==Tips==
 +
*[https://www.typea.info/blog/index.php/2022/07/27/net6_user32_dll_setwindowshookex_error_126/ .NET6 の Windows Formsから user32.dllの SetWindowsHookEx を呼び出すが ERROR_MOD_NOT_FOUND(126) エラーになる対処]

2022年7月31日 (日) 04:09時点における最新版

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としているが、クラス名は任意であり、ネームスペースに置くことも可能

ライブラリとして作成しておけば、任意の C# プロジェクトから利用可能

LPTSTRに対応する

  • 上記 SetWindwText を呼び出してみる
呼び出し側
private void button1_Click(object sender, EventArgs e)
{
    int hwnd = (int)this.Handle;
    string s = "SetWindowText Test.";
    Win32Api.SetWindowText(hwnd, s);
}
  • 起動時の状態

0295 set window text01.jpg

  • button1 押下後

0296 set window text02.jpg

なぜこのようなことができるのか? - デフォルトのマーシャリング型

  • コンパイラは 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());
}
  • 呼び出したところ

0294 get window text01.jpg

<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);
}

0293 get window rect01.jpg

<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);
}
結果

0291 enum windows01.jpg

ポインタを使う

  • 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 = "";
}
結果

0292 enum windows02.jpg


Tips