potisanのプログラミングメモ

趣味のプログラマーがプログラミング関係で気になったことや調べたことをいつでも忘れられるようにメモするブログです。はてなブログ無料版なので記事の上の方はたぶん広告です。記事中にも広告挿入されるみたいです。

C# 8 P/Invoke時の「System.MissingMethodException: '.ctor'」例外の意味

結論

戻り値の型(クラス)がデフォルトコンストラクタを定義していない場合に発生します。自作クラスであればデフォルトコンストラクタを定義してください。なお、'.ctor'はコンストラクタを意味します。

Microsoft.Win32.SafeHandleZeroOrMinusOneIsInvalidクラスの派生クラスで既定コンストラクタを定義しないことでタイトルのエラーを発生できます。

using System;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;

class Program
{
    static void Main()
    {
        var path = Environment.GetFolderPath(Environment.SpecialFolder.Windows);
        using var handle = NativeMethods.FindFirstStreamW(
            path,
            STREAM_INFO_LEVELS.FindStreamInfoStandard,
            out var data,
            0);
    }

    private static class NativeMethods
    {
        [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)]
        public static extern SafeFindHandle FindFirstStreamW(
            [In] string lpFileName,
            STREAM_INFO_LEVELS InfoLevel,
            out WIN32_FIND_STREAM_DATA lpFindStreamData,
            uint dwFlags);
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    struct WIN32_FIND_STREAM_DATA
    {
        public long StreamSize;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260 + 36)]
        public string cStreamName;
    }

    enum STREAM_INFO_LEVELS
    {
        FindStreamInfoStandard = 0,
        FindStreamInfoMaxInfoLevel
    }
}

sealed class SafeFindHandle : SafeHandleZeroOrMinusOneIsInvalid
{
    private static class NativeMethods
    {
        [DllImport("kernel32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool FindClose(IntPtr hFindFile);
    }

    // 以下のコメントアウトを解除すると例外は発生しません。
    // public SafeFindHandle()
    //     : base(true)
    // {
    // }

    public SafeFindHandle(bool ownsHandle)
        : base(ownsHandle)
    {
    }

    protected override bool ReleaseHandle()
    {
        return NativeMethods.FindClose(handle);
    }
}