クリティカルセクションのWindowsAPIを呼び出すとアプリケーションが予期せずクラッシュする

現象

C#にてクリティカルセクションのWindows API

  • InitializeCriticalSection
  • EnterCriticalSection
  • LeaveCriticalSection

を呼び出すとアプリが意図せずクラッシュすることがあります。特にフォームと閉じた時、などでクラッシュします。
クリティカルセクションのWindowsAPIを呼び出すとアプリケーションが予期せずクラッシュする:画像1

原因

CriticalSection系のWindows APIを呼び出す際に、CRITICAL_SECTION構造体が正しく定義されていないとこの問題が発生します。

対策

対策1

CRITICAL_SECTION構造体を正しく定義します。

コード例 (定義部)

  [DllImport("kernel32.dll")]
  static extern void InitializeCriticalSection(out CRITICAL_SECTION lpCriticalSection);

  [DllImport("kernel32.dll")]
  static extern void EnterCriticalSection(ref CRITICAL_SECTION lpCriticalSection);

  [DllImport("kernel32.dll")]
  static extern void LeaveCriticalSection(ref CRITICAL_SECTION lpCriticalSection);

  [StructLayout(LayoutKind.Sequential)]
  public struct CRITICAL_SECTION{
    public IntPtr DebugInfo;
    public long LockCount;
    public long RecursionCount;
    public uint OwningThread;
    public uint LockSemaphore;
    public int Reserved;
  }

コード例 (呼び出し部)

  CRITICAL_SECTION CriticalSection;
  
  InitializeCriticalSection(out CriticalSection);

  EnterCriticalSection(ref CriticalSection);
  try {
    (処理...)
  }
  finally {
    LeaveCriticalSection(ref CriticalSection);
  }

対策2

CRITICAL_SECTION構造体は変更不可のため、構造体を定義せずにメモリ領域のみを確保しても正しく動作します。
メモリの確保はMarshal.AllocHGlobal()メソッドを用います。

コード例 (定義部)

  [DllImport("kernel32.dll")]
  static extern void InitializeCriticalSection(IntPtr lpCriticalSection);

  [DllImport("kernel32.dll")]
  static extern void EnterCriticalSection(IntPtr lpCriticalSection);

  [DllImport("kernel32.dll")]
  static extern void LeaveCriticalSection(IntPtr lpCriticalSection);

コード例 (呼び出し部)

  IntPtr CriticalSection = Marshal.AllocHGlobal(40);

  InitializeCriticalSection(CriticalSection );

  EnterCriticalSection(CriticalSection );
  try {
    (処理...)
  }
  finally {
    LeaveCriticalSection(CriticalSection );
  }

補足

Marshal.AllocHGlobal()に与えているサイズ40は以下のバイト数の和になります。

  public struct CRITICAL_SECTION{
    public IntPtr DebugInfo; //4(32bit) / 8(64bit) 
    public long LockCount; //8
    public long RecursionCount; //8
    public uint OwningThread; //4
    public uint LockSemaphore; //4
    public int Reserved; //4
  }


64ビットの場合、合計が36になります。(コードでは、構造体終端等を考慮して40バイトのメモリを確保しています。)

AuthorPortraitAlt
著者
iPentecのメインプログラマー
C#, ASP.NET の開発がメイン、少し前まではDelphiを愛用
作成日: 2014-05-21
Copyright © 1995–2025 iPentec all rights reserverd.