potisanのプログラミングメモ

プログラミング素人です。昔の自分を育ててくれたネット情報に少しでも貢献できるよう、情報を貯めていこうと思っています。Windows環境のC++やC#がメインです。

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