最終更新日 2024-09-25

引数や返り値の型を問わない関数

概要

ここまでの説明を一読された方であれば、

の2つのページから、「数値」も「文字列」も、両方「IntPtr」で取り扱う方法があることに気づいたことでしょう。

ということは、秀丸から呼び出す際に、

引数が「数値」でも「文字列」でも、そして、返り値が「数値」でも「文字列」でも、
どちらでも動作する「関数abc」を定義する方法があるのではないか?

と思い巡らすかもしれません。
そしてそれはややこしい実装方法ではありますが、正しく実装する方法が存在します。

「数値」or「文字列」の引数、「数値」or「文字列」の返り値、どちらでも機能する関数の定義方法

下記ソース中の、「StaticWStrPtrHandle」については「文字列の扱い方③ SafeHandleを継承した文字列ラッパークラス」を参照してください。

ClassLibrary36.cs
using System;
using System.Runtime.InteropServices;

namespace ClassLibrary36
{

    public class Class1
    {
        // 片方でも文字列だった場合のC#内での処理用のメソッド
        private static String dynamic_func(String a, String b)
        {
            return a + b;
        }

        // 引数両方が数値だった場合のC#内での処理用のメソッド
        private static IntPtr dynamic_func(IntPtr a, IntPtr b)
        {
            return (IntPtr)((long)a + (long)b);
        }

        static StaticWStrPtrHandle returnStringObject = null;

        [DllExport]
        public unsafe static IntPtr abc(IntPtr a, IntPtr b)
        {
            object arg1 = null;
            object arg2 = null;


            // 1番目の引数(=a)の型
            Hidemaru_DllFuncParamType param_type = (Hidemaru_DllFuncParamType)Hidemaru_GetDllFuncCalledType(1);
            // 引数は数値系
            if (param_type == Hidemaru_DllFuncParamType.INT || param_type == Hidemaru_DllFuncParamType.DOUBLE)
            {
                arg1 = (IntPtr)a;
            }
            // 引数は文字列
            else if (param_type == Hidemaru_DllFuncParamType.WCHAR_PTR)
            {
                arg1 = (String)Marshal.PtrToStringUni(a);
            }


            // 2番目の引数(=b)の型
            param_type = (Hidemaru_DllFuncParamType)Hidemaru_GetDllFuncCalledType(2);
            // 引数は数値系
            if (param_type == Hidemaru_DllFuncParamType.INT || param_type == Hidemaru_DllFuncParamType.DOUBLE)
            {
                arg2 = (IntPtr)b;
            }
            // 引数は文字列
            else if (param_type == Hidemaru_DllFuncParamType.WCHAR_PTR)
            {
                arg2 = (String)Marshal.PtrToStringUni(b);
            }

            // C# 側で使う関数へと振り分けて呼び出し
            object my_ret = null;
            // 2つの引数が両方数値系の場合に呼ぶ関数
            if (arg1 is IntPtr && arg2 is IntPtr)
            {
                my_ret = dynamic_func((IntPtr)arg1, (IntPtr)arg2);

            // どちらかが文字列の場合に呼ぶ関数
            } else
            {
                my_ret = dynamic_func(arg1.ToString(), arg2.ToString());
            }


            // 返り値として要求されている型をチェック
            Hidemaru_DllFuncReturnType rtn_type = (Hidemaru_DllFuncReturnType)Hidemaru_GetDllFuncCalledType(0);
            // 返り値は数値系が要求されている。
            if (rtn_type == Hidemaru_DllFuncReturnType.INT || rtn_type == Hidemaru_DllFuncReturnType.DOUBLE)
            {
                Decimal result = 0;
                bool success = Decimal.TryParse(my_ret.ToString(), out result);
                if (success)
                {
                    // オーバーフローなどのチェックは今回省略
                    return (IntPtr)result;
                }

                if (my_ret is String)
                {
                    // 文字列でパースに失敗したら、返すものないので、文字列の長さでもかえしておこうか。
                    return (IntPtr)(my_ret as String).Length;
                }

            }
            // 返り値は文字列が要求されている。
            else if (rtn_type == Hidemaru_DllFuncReturnType.WCHAR_PTR)
            {
                returnStringObject?.Dispose();
                returnStringObject = new StaticWStrPtrHandle(my_ret.ToString());
                return returnStringObject.DangerousGetHandle();
            }

            return (IntPtr)0;
        }


        // 以下、秀丸側から提供されている、関数呼び出しの「引数」や「返り値」の型などをチェックするための宣言
        enum Hidemaru_DllFuncReturnType
        {
            NOTCALL = 0,        // 呼ばれていない
            INT = 1,            // 数値型を返すべき(dllfuncまたはdllfuncw)
            CHAR_PTR = 2,       // char *を返すべき(dllfuncstr)
            WCHAR_PTR = 3,      // WCHAR*を返すべき(dllfuncstrw)
            DOUBLE = 4          // doubleを返すべき(dllfuncまたはdllfuncwで浮動小数点数が有効)
        };
        enum Hidemaru_DllFuncParamType
        {
            NOPARAM = 0,        // 指定されているn番目の引数は存在しない
            INT = 0x0100,       // 数値型の引数
            CHAR_PTR = 0x0200,  // char *の引数
            WCHAR_PTR = 0x0300, // WCHAR*の引数
            DOUBLE = 0x0400     // doubleの引数
        };

        // この引数や返り値の型を判定する関数が、秀丸本体から提供されている。
        [DllImport("Hidemaru.exe", CallingConvention = CallingConvention.Winapi)]
        extern static int Hidemaru_GetDllFuncCalledType(int n);
    }
}

呼び出し側

ClassLibrary36.mac
#DLL = loaddll( currentmacrodirectory + @"\ClassLibrary36.dll");

#_ = dllfuncw( #DLL, "abc", 100, 200);
message(str(#_));

$_ = dllfuncstrw( #DLL, "abc", "あいうえお", 2000);
message($_);
 
$_ = dllfuncstrw( #DLL, "abc", 100, 200);
message($_);

#_ = dllfuncw( #DLL, "abc", "かきくけこ", "たちつてと");
message("文字数" + str(#_));