RSS

C#: Win32 API の使い方

C# は unsafe キーワードを使って C 言語のコードを呼び出すことができる。Win32 API は 64ビット版 Windows でもそのまま使用できる。

その際の要件を下に示す。

  • Win32 API 関数の定義を示すクラスを用意する。
  • そのクラスでは System.Runtime.InteropServices 名前空間をインポートする。
  • 上記のクラスを使うコードは unsafe ブロックに含める。
  • ビルドの際には dotnet build /property:AllowUnsafeBlocks=true のように AllowUnsafeBlocks スイッチを有効にする。

次に具体的な例を示す。

Program.cs

// Win32 API
//  ビルド方法: dotnet build /property:AllowUnsafeBlocks=true
using Win32Api;

Action<Object> println = o => Console.WriteLine(o.ToString());
Action<String, Object> printf = (format, data) => Console.WriteLine(format, data);
Func<bool> isWindows = () => Environment.OSVersion.Platform == PlatformID.Win32NT;
Action<int> exit = code => Environment.Exit(code);

if (! isWindows())
{
  println("Error: Windows のみで実行可能。");
  exit(1);
}

// Win32 API の GlobalMemoryStatusEx() 関数を呼び出す。
unsafe
{
  Win32.MEMORYSTATUSEX buffer = new Win32.MEMORYSTATUSEX();
  buffer.dwLength = (uint)sizeof(Win32.MEMORYSTATUSEX);
  Win32.GlobalMemoryStatusEx(ref buffer);

  printf("物理メモリ総量 {0} バイト", buffer.ullTotalPhys);
}

Win32Api.cs

using System.Runtime.InteropServices;

// ディスクやメモリ情報を取得する Win32 API 関数を呼び出すためのクラス定義
namespace Win32Api
{
  class Win32
  {
     // ディスクやメモリ情報を受け取るための構造体
     public struct MEMORYSTATUSEX
     {
        public uint dwLength;
        public uint dwMemoryLoad;
        public ulong ullTotalPhys;
        public ulong ullAvailPhys;
        public ulong ullTotalPageFile;
        public ulong ullAvailPageFile;
        public ulong ullTotalVirtual;
        public ulong ullAvailVirtual;
        public ulong ullAvailExtendedVirtual;
     }

     // ィスクやメモリ情報を取得する Win32 API 関数
     [DllImport("kernel32.dll")]
     public static extern void GlobalMemoryStatusEx(ref MEMORYSTATUSEX Buffer);
    }
}
 
コメントする

投稿者: : 2024/04/29 投稿先 C#, dotNET

 

タグ: ,

C#: 正規表現

C# で正規表現を使うには、System.Text.RegularExpressions 名前空間をインポートする必要がある。

その名前空間に含まれる Regex クラスのメソッドを利用することにより、様々な正規表現処理が可能である。

// 正規表現
using System.Text.RegularExpressions;

Action<Object> println = o => Console.WriteLine(o.ToString());
Func<String> getPwd = () => Environment.CurrentDirectory;

println("<< 正規表現 >>");
var pwd = getPwd();  // カレントディレクトリを得る。
println(pwd);

// カレントディレクトリが Windows 風か調べる。
var b = Regex.IsMatch(pwd, @"\w:\\w*");
println(b);

// パターンに一致したワードを列挙する。(ドライブ名とサブディレクトリ名)
MatchCollection mc = Regex.Matches(pwd, @"\w+");
foreach (Match m in mc)
{
  println(m.Value);
}

 
コメントする

投稿者: : 2024/04/28 投稿先 C#, dotNET

 

タグ:

C#: 乱数

乱数は Random クラスをインスタンス化して、そのメソッドを使う。

整数の乱数の他、バイトや浮動小数点数の乱数も発生可能である。

乱数の応用として、コレクションのランダム化(シャッフル)も可能である。

// 乱数
Action<Object> println = o => Console.WriteLine(o.ToString());
Action<Object> print = o => Console.Write(o.ToString());

println("<< 乱数 >>");
// 初期化
var rand = new Random();

// バイトの乱数
var bytes = new byte[10];
rand.NextBytes(bytes);
for (int i = 0; i < 10; i++)
{
  print($"{bytes[i]},");
}
println("");

// Int32 の 1024 未満の乱数
var ints = new int[10];
rand.Next();
for (int i = 0; i < 10; i++)
{
  print($"{rand.Next(1024)},");
}
println("");

// 整数の配列をシャフルする。
int[] a = [1, 2, 3, 4, 5];
rand.Shuffle<int>(a);
for (int i = 0; i < a.Length; i++)
{
  print($"{a[i]},");
}
println("\nDone.");

 
コメントする

投稿者: : 2024/04/28 投稿先 C#, dotNET

 

タグ: ,

C#: ディレクトリ情報と操作

ディレクトリ情報の取得や操作は、Directory クラスの静的メソッドで行うことができる。あるいは、DirectoryInfo クラスをインスタンス化して、そのメソッドやプロパティを使うこともできる。

// DirectoryInfo
Action<Object> println = o => Console.WriteLine(o.ToString());
Action<String, Object> printf = (format, data) => Console.WriteLine(format, data);
Func<string> readln = () => Console.ReadLine() ?? "";
Action<int> exit = code => Environment.Exit(code);
Func<bool> isLinux = () => Environment.OSVersion.Platform == PlatformID.Unix;
Func<bool> isWindows = () => Environment.OSVersion.Platform == PlatformID.Win32NT;

println("<< ディレクトリ情報 >>");

string path = "";
if (args.Length > 0)
{
  path = args[0];
}

// ディレクトリが存在するか?
println(Directory.Exists(path));

// カレントディレクトリ
var current = Environment.CurrentDirectory;
println(current);
if (isWindows())
  Environment.CurrentDirectory = @"c:\temp";
else
  Environment.CurrentDirectory = "~/temp";
current = Environment.CurrentDirectory;
println(current);

// サブディレクトリ作成
string subdir = Environment.CurrentDirectory + "/subdir";
Directory.CreateDirectory(subdir);
println(Directory.Exists(subdir));

// 空のディレクトリ削除
Directory.Delete(subdir);
println(Directory.Exists(subdir));

// 親のディレクトリ (フルパスでなく名前のみ)
DirectoryInfo? di = Directory.GetParent(path);
if (di != null)
{
  println(path);
  println(di.Name);
}

 
コメントする

投稿者: : 2024/04/28 投稿先 C#, dotNET

 

タグ: ,

C#: ファイル情報

ファイル情報は File クラスのスタティックメソッドで取得できる。あるいは FileInfo クラスをインスタンス化して取得することもできる。

// FileInfo
Action<Object> println = o => Console.WriteLine(o.ToString());
Action<String, Object> printf = (format, data) => Console.WriteLine(format, data);
Func<string> readln = () => Console.ReadLine() ?? "";
Action<int> exit = code => Environment.Exit(code);
Func<bool> isLinux = () => Environment.OSVersion.Platform == PlatformID.Unix;
Func<bool> isWindows = () => Environment.OSVersion.Platform == PlatformID.Win32NT;

println("<< ファイル情報 >>");

string path = "";
if (args.Length > 0)
{
  path = args[0];
}

// ファイルが存在するか?
var b = File.Exists(path);
println(b);
if (!b)
{
  exit(1);
}

// ファイルサイズ (実サイズ)
var fi = new FileInfo(path);
println(fi.Length);

// 日付 (最終更新時)
var written = File.GetLastWriteTime(path);
printf("{0:yyyy-MM-dd hh:mm:ss}", written);

// Linux のファイルモード
if (isLinux())
{
  enum UnixFileMode fm = File.GetUnixFileMode(path);
  printf("{0:b16}", fm);
}

 
コメントする

投稿者: : 2024/04/28 投稿先 C#, dotNET

 

タグ: ,

C#: ファイル入出力 (同期型)

ここでは同期型 (従来型) のファイル入出力の例を示す。

なお、ここで示す例以外にもファイル入出力には様々な方式がある。

テキストファイル入力

// テキストファイルの読み込み

Action<Object> println = o => Console.WriteLine(o.ToString());
Action<int> exit = code => Environment.Exit(code);

Console.WriteLine("<< テキストファイルの読み込み >>");

if (args.Length == 0)
{
  println("Error: ファイルパスを指定してください。");
  exit(1);
}

var path = args[0];

// File.ReadAllText(path). 文字コードは自動判別
var s = File.ReadAllText(path);
println(s);

// File.ReadLines
var lines = File.ReadLines(path).ToList();
lines.ForEach(line => println(line));

// StreamReader を使って行ごとに読む。
using (var reader = File.OpenText(path))
{
  var s1 = reader.ReadLine();
  println(s1 ?? "null");
}

テキストファイル出力

// テキストファイルの書き込み
Action<Object> println = o => Console.WriteLine(o.ToString());
Action<int> exit = code => Environment.Exit(code);

if (args.Length == 0)
{
  println("Error: ファイルパスを指定してください。");
  exit(1);
}

var path = args[0];

const string content = "<< テキストファイルの書き込み >>";
Console.WriteLine(content);

// 一括書き込み
File.WriteAllText(path, content);

// 行ごとに書き込み
string[] lines1 = ["12", "345", "6789"];
File.AppendAllLines(path, lines1);

// StreamWriter
string[] lines2 = ["AB", "CDE", "FGHI"];
StreamWriter writer = File.AppendText(path);
lines2.ToList().ForEach(s => writer.WriteLine(s));
writer.Close();

println("終わり。");

バイナリーファイルの読み書き

// バイナリーファイルの読み書き
Action<Object> println = o => Console.WriteLine(o.ToString());
Action<String, Object> printf = (format, data) => Console.WriteLine(format, data);
Func<string> readln = () => Console.ReadLine() ?? "";
Action<int> exit = code => Environment.Exit(code);

if (args.Length == 0)
{
  println("Error: 書き込むためのファイルパスを指定してください。");
  exit(1);
}

string path = args[0];

if (File.Exists(path))
{
  println($"{path} が存在します。上書きされる恐れがあります。 (続行は 'y') > ");
  var ans = readln();
  if (ans != "y")
  {
    exit(1);
  }
}

// バイト列の書き込み
byte[] content = [0x01, 0x02, 0x03, 0x04];
File.WriteAllBytes(path, content);

// バイト列の読み込み
byte[] buffer = File.ReadAllBytes(path);

// 表示
buffer.ToList().ForEach(b => printf("{0:b16}", b));

println("> 終わり。");

 
コメントする

投稿者: : 2024/04/28 投稿先 C#, dotNET

 

タグ: , ,

C#: Nullable (null 許容型)

値型 T に対して型 T? は値だけでなく null も取ることができる。

T? は Nullable<T> の Alias である。

null 許容型の使用例を以下に示す。

// Nullable

// println() 関数
Action<Object> println = o => Console.WriteLine(o.ToString());

Console.WriteLine("<< Nullable >>");

// int? と Nullble<int> は同じ。
int? a0 = 1;
Nullable<int> a1 = a0;
// 次のような代入はコンパイルエラーになる。
// int b = a0;

// Nullable.HasValue : その値が null かどうか判別
println(a1.HasValue);  // true
a1 = null;
println(a1.HasValue);  // false

// int? から int への変換: int をキャストする。
if (a0.HasValue)
{
  println((int)a0);
}

// Nullable<T> も演算可能だが、 値が null だった場合の結果は演算により異なる。
a1 = 2;
println(a0 + a1);

// null 合体演算子 (??) : 値が null かどうかを判別し、null の場合には別の値を割り当てる演算子
int? a2 = 4;
a2 = a2 ?? 5;  // a2 は null でないので何もしない。
if (a2.HasValue == false)
  println("null");
else
  println(a2);
a2 = null;
a2 = a2 ?? 7;  // a2 は null なので代わりに 7 になる。
// ??= を使うと 前のような if 文が簡単になる。 下の例では null だったときは 0 が表示される。
println(a2 ??= 0);
 
コメントする

投稿者: : 2024/04/27 投稿先 C#, dotNET

 

C#: タプル (Tuple)

Tuple は C# 7 から使用できるデーや型で、複数の値を含む関数値を返す時などに使用できる。

Python のタプルと似ているが、次のような違いがある。

  • 要素の型付けが必要。ただし、型推論ができるので型付けの明示はしなくてもよい。
  • 要素の変更が可能
  • それぞれの要素に名前を付けることができる。例 (a:int, b:string)
  • メタデータはサポートされない。
  • 分解構文で要素を簡単に取り出せる。
  • Python では要素を配列要素のように [] で扱えるが、C# では Item1, Item2, … という変数を使う。

次に Tuple の使用例を示す。

// Tuple

// 整数のペアを表す Tuple
using IntPair = (int, int);

var println = (Object o) => Console.WriteLine(o.ToString());

// 乱数を初期化
var rand = new Random();

// 整数乱数ペアを生成する関数
var getRandomPair = IntPair () => ((int)rand.NextInt64((long)Int32.MaxValue), (int)rand.NextInt64((long)Int32.MaxValue));

println("Tutple");

// Tuple の要素は [] でアクセスできない。その代わり Item1, Item2, ... でアクセスする。
//  Item128 まであるが、可読性が悪くなるので、要素に名前を付けるほうが現実的である。
for (int i = 0; i < 5; i++)
{
  IntPair p = getRandomPair();
  println($"{p.Item1}, {p.Item2}");
}

// Tuple の要素は [] でアクセスできない。 次のように要素に名前を付けてアクセスすることもできる。
for (int i = 0; i < 5; i++)
{
  var (first, last) = getRandomPair();
  println($"{first}, {last}");
}

 
コメントする

投稿者: : 2024/04/27 投稿先 C#, dotNET

 

C#: 改行を含む文字列と文字列補完の拡張

C# 12 では “”” … “”” が追加された。

また、C# 12 など最近のバージョンでは、文字列補完が拡張されている。

  • $$””” …. {{ .. }} … “””
  • $@”…. {..} … “

サンプル

// Raw String Literal
var println = (Object o) => Console.WriteLine(o.ToString());

println("文字列リテラル");

// """ で囲まれた内側は \ や " がそのまま使える。
string s1 = """
 \ \\
  "ssss"
""";

println(s1);

// @"..." の中では \ はそのまま使えるが、" はダメ。
string s2 = @"\d \a 
\s ";

println(s2);

// $$""" ... """ の中では変数の埋め込みができる。
int n = 1024;

string s3 = $$"""
^^^^^^^
n = {{n}}
^^^^^^^
""";
println(s3);

// @$"", $@"" でも変数の埋め込みができる。
int x = 10;
int y = 12;
string s4 = $@"{x} + {y}";
println(s4);

 
コメントする

投稿者: : 2024/04/24 投稿先 C#, dotNET

 

C#: プライマリコンストラクタ

C# 12 では、クラスのコンストラクタを record のように書くことができる。

次の Class1.cs ではクラス定義と一緒にコンストラクタの定義を行っている。

Class1.cs

// Primary constructor
class Class1(string s)
{
  public string Name = s;
  
  public override string ToString()
  {
    return Name;
  }
}

Program.cs

// Primary Constructor
using static Class1;

void println(Object o)
{
  Console.WriteLine(o.ToString());
}

println("Primary Constructor");
var c = new Class1("Class1");
println(c);

 
コメントする

投稿者: : 2024/04/24 投稿先 C#, dotNET