From b319e0fcb678494a2c60577664b9032dc70fc2bc Mon Sep 17 00:00:00 2001 From: yyc12345 Date: Sat, 5 Oct 2024 11:58:25 +0800 Subject: [PATCH] feat: finish basic function of BMapSharp. - fix weird C sharp behavior about calling FreeNativeData without calling ManagedToNative, which cause segment fault. - disable unhandled exception handler in debug mode for BMap. - change all associated code involving these issues. --- BMap/BMExports.cpp | 4 +- BMapBindings/BMapSharp/BMapSharp/BMap.cs | 94 +++++++++++++------ .../BMapSharp/BMapSharp/BMapWrapper.cs | 2 +- .../BMapSharpTestbench.csproj | 4 + .../BMapSharp/BMapSharpTestbench/Program.cs | 60 ++++++++++-- BMapBindings/PyBMap/testbench.py | 2 +- CodeGen/BMapBindings/CSharpWriter.java | 12 ++- 7 files changed, 135 insertions(+), 43 deletions(-) diff --git a/BMap/BMExports.cpp b/BMap/BMExports.cpp index 457461a..2a68665 100644 --- a/BMap/BMExports.cpp +++ b/BMap/BMExports.cpp @@ -55,7 +55,7 @@ bool BMInit() { if (CheckInited()) return false; // register exception handler if we are in Windows. -#if YYCC_OS == YYCC_OS_WINDOWS +#if defined(LIBCMO_BUILD_RELEASE) && (YYCC_OS == YYCC_OS_WINDOWS) YYCC::ExceptionHelper::Register(); #endif @@ -89,7 +89,7 @@ bool BMDispose() { LibCmo::CK2::CKShutdown(); // unregister exception handler if we are in Windows -#if YYCC_OS == YYCC_OS_WINDOWS +#if defined(LIBCMO_BUILD_RELEASE) && (YYCC_OS == YYCC_OS_WINDOWS) YYCC::ExceptionHelper::Unregister(); #endif diff --git a/BMapBindings/BMapSharp/BMapSharp/BMap.cs b/BMapBindings/BMapSharp/BMapSharp/BMap.cs index 00b9894..acd700c 100644 --- a/BMapBindings/BMapSharp/BMapSharp/BMap.cs +++ b/BMapBindings/BMapSharp/BMapSharp/BMap.cs @@ -19,10 +19,6 @@ namespace BMapSharp { public static class BMap { - /// The callback function of BMap. - /// The message content need to be printed. - public delegate void OutputCallback([In, MarshalAs(UnmanagedType.LPUTF8Str)] string msg); - #region Custom Marshalers // References: @@ -33,24 +29,38 @@ namespace BMapSharp { // Because my binding do not have In, Out parameter. All parameters are In OR Out. // So there is no reason to keep that member. + // IDK why Microsoft try to call ICustomMarshaler.CleanUpNativeData without calling ICustomMarshaler.MarshalManagedToNative. + // It is trying to free the pointer managed by LibCmo self (for example, it will try to free we got string when getting object name)! + // So as the compromise, we use "cookie" feature to explicit specify the marshaler In/Out behavior when getting it. + [Flags] + internal enum MarshalerType { + None = 0b0, + In = 0b1, + Out = 0b10 + } + /// The custom marshaler for BMap string array. public class BMStringArrayMarshaler : ICustomMarshaler { - private static readonly BMStringArrayMarshaler g_Instance = new BMStringArrayMarshaler(); - public static ICustomMarshaler GetInstance(string pstrCookie) => g_Instance; + private static readonly BMStringArrayMarshaler g_InInstance = new BMStringArrayMarshaler(MarshalerType.In); + private static readonly BMStringArrayMarshaler g_OutInstance = new BMStringArrayMarshaler(MarshalerType.Out); + public static ICustomMarshaler GetInstance(string pstrCookie) { + if (pstrCookie == "In") return g_InInstance; + else if (pstrCookie == "Out") return g_OutInstance; + else throw new MarshalDirectiveException("Not supported cookie string for BMStringArrayMarshaler."); + } + + private readonly MarshalerType m_MarshalerType; + private BMStringArrayMarshaler(MarshalerType marshaler_type) { + m_MarshalerType = marshaler_type; + } // For respecting the standard of BMap, // the native memory we created is a simple array and each item is a pointer to a NULL-terminated UTF8 string. - // Please note the array self is not NULL-terminated because its length is provided by another argument in function calling. - // However, this memory layout is not good for our marshaling. - // We can not know the size of array we created. Because we need iterate it when freeing or fetching data. - // We also can not know the size of string we created because we need read them when parsing them to C# string. - // - // So the solution we made is adding an uint32_t header before the array to indicate the size of array. - // And also add an uint32_t header for each string to indicate the length of string (in bytes, NULL exclusive). - // So the pointer put in array is not the address we allocated, it has an offset. - // Also we return native pointer is not the address we allocated, it also has an offset. + // Please note the array self is also NULL-terminated otherwise we don't know its length. public nint MarshalManagedToNative(object ManagedObj) { + // Check marshaler type + if (!m_MarshalerType.HasFlag(MarshalerType.In)) return nint.Zero; // Check nullptr object. if (ManagedObj is null) return nint.Zero; // Check argument type. @@ -72,7 +82,7 @@ namespace BMapSharp { // Allocate array pointer now. nint pArray = Marshal.AllocHGlobal(szArrayItemSize * (szArrayItemCount + 1)); // Copy string pointer data - Marshal.Copy(apString, 0, pArray, szArrayItemSize * szArrayItemCount); + Marshal.Copy(apString, 0, pArray, szArrayItemCount); // Setup NULL ternimal Marshal.WriteIntPtr(pArray + (szArrayItemSize * szArrayItemCount), nint.Zero); @@ -81,6 +91,8 @@ namespace BMapSharp { } public object MarshalNativeToManaged(nint pNativeData) { + // Check marshaler type + if (!m_MarshalerType.HasFlag(MarshalerType.Out)) return null; // Check nullptr if (pNativeData == nint.Zero) return null; @@ -89,7 +101,7 @@ namespace BMapSharp { int szArrayItemSize = Marshal.SizeOf(); // Prepare array cache and read it. nint[] apString = new nint[szArrayItemCount]; - Marshal.Copy(pNativeData, apString, 0, szArrayItemSize * szArrayItemCount); + Marshal.Copy(pNativeData, apString, 0, szArrayItemCount); // Iterate the array and process each string one by one. string[] ret = new string[szArrayItemCount]; @@ -109,6 +121,8 @@ namespace BMapSharp { } public void CleanUpNativeData(nint pNativeData) { + // Check marshaler type + if (!m_MarshalerType.HasFlag(MarshalerType.In)) return; // Check nullptr if (pNativeData == nint.Zero) return; @@ -117,7 +131,7 @@ namespace BMapSharp { int szArrayItemSize = Marshal.SizeOf(); // Prepare array cache and read it. nint[] apString = new nint[szArrayItemCount]; - Marshal.Copy(pNativeData, apString, 0, szArrayItemSize * szArrayItemCount); + Marshal.Copy(pNativeData, apString, 0, szArrayItemCount); // Free array self Marshal.FreeHGlobal(pNativeData); @@ -154,10 +168,22 @@ namespace BMapSharp { } public class BMStringMarshaler : ICustomMarshaler { - private static readonly BMStringMarshaler g_Instance = new BMStringMarshaler(); - public static ICustomMarshaler GetInstance(string pstrCookie) => g_Instance; + private static readonly BMStringMarshaler g_InInstance = new BMStringMarshaler(MarshalerType.In); + private static readonly BMStringMarshaler g_OutInstance = new BMStringMarshaler(MarshalerType.Out); + public static ICustomMarshaler GetInstance(string pstrCookie) { + if (pstrCookie == "In") return g_InInstance; + else if (pstrCookie == "Out") return g_OutInstance; + else throw new MarshalDirectiveException("Not supported cookie string for BMStringMarshaler."); + } + + private readonly MarshalerType m_MarshalerType; + private BMStringMarshaler(MarshalerType marshaler_type) { + m_MarshalerType = marshaler_type; + } public nint MarshalManagedToNative(object ManagedObj) { + // Check marshaler type + if (!m_MarshalerType.HasFlag(MarshalerType.In)) return nint.Zero; // Check requirements. if (ManagedObj is null) return nint.Zero; string castManagedObj = ManagedObj as string; @@ -168,6 +194,8 @@ namespace BMapSharp { } public object MarshalNativeToManaged(nint pNativeData) { + // Check marshaler type + if (!m_MarshalerType.HasFlag(MarshalerType.Out)) return null; // Check nullptr if (pNativeData == nint.Zero) return null; // Call self @@ -175,6 +203,8 @@ namespace BMapSharp { } public void CleanUpNativeData(nint pNativeData) { + // Check marshaler type + if (!m_MarshalerType.HasFlag(MarshalerType.In)) return; // Check nullptr if (pNativeData == nint.Zero) return; // Free native pointer @@ -218,7 +248,7 @@ namespace BMapSharp { int szStringItemSize = Marshal.SizeOf(); nint pString = Marshal.AllocHGlobal(szStringItemSize * (szStringItemCount + 1)); // Copy encoded string data - Marshal.Copy(encString, 0, pString, szStringItemSize * szStringItemCount); + Marshal.Copy(encString, 0, pString, szStringItemCount); // Setup NUL Marshal.WriteByte(pString + (szStringItemSize * szStringItemCount), (byte)0); // Return value @@ -236,7 +266,7 @@ namespace BMapSharp { int szStringItemSize = Marshal.SizeOf(); // Prepare cache and copy string data byte[] encString = new byte[szStringItemCount]; - Marshal.Copy(ptr, encString, 0, szStringItemSize * szStringItemCount); + Marshal.Copy(ptr, encString, 0, szStringItemCount); // Decode string and return return Encoding.UTF8.GetString(encString); } @@ -244,6 +274,10 @@ namespace BMapSharp { #endregion + /// The callback function of BMap. + /// The message content need to be printed. + internal delegate void OutputCallback([In, MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(BMStringMarshaler))] string msg); + // Decide the file name of loaded DLL. #if BMAP_OS_WINDOWS @@ -281,7 +315,7 @@ namespace BMapSharp { /// True if no error, otherwise False. [DllImport(g_DllName, EntryPoint = "BMFile_Load", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] [return: MarshalAs(UnmanagedType.U1)] - internal static extern bool BMFile_Load([In, MarshalAs(UnmanagedType.LPUTF8Str)] string file_name, [In, MarshalAs(UnmanagedType.LPUTF8Str)] string temp_folder, [In, MarshalAs(UnmanagedType.LPUTF8Str)] string texture_folder, [In, MarshalAs(UnmanagedType.FunctionPtr)] OutputCallback raw_callback, [In, MarshalAs(UnmanagedType.U4)] uint encoding_count, [In, MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(BMStringArrayMarshaler))] string[] encodings, [Out, MarshalAs(UnmanagedType.SysInt)] out IntPtr out_file); + internal static extern bool BMFile_Load([In, MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(BMStringMarshaler), MarshalCookie = "In")] string file_name, [In, MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(BMStringMarshaler), MarshalCookie = "In")] string temp_folder, [In, MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(BMStringMarshaler), MarshalCookie = "In")] string texture_folder, [In, MarshalAs(UnmanagedType.FunctionPtr)] OutputCallback raw_callback, [In, MarshalAs(UnmanagedType.U4)] uint encoding_count, [In, MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(BMStringArrayMarshaler), MarshalCookie = "In")] string[] encodings, [Out, MarshalAs(UnmanagedType.SysInt)] out IntPtr out_file); /// BMFile_Create /// Type: LibCmo::CKSTRING. /// Type: LibCmo::CKSTRING. @@ -292,7 +326,7 @@ namespace BMapSharp { /// True if no error, otherwise False. [DllImport(g_DllName, EntryPoint = "BMFile_Create", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] [return: MarshalAs(UnmanagedType.U1)] - internal static extern bool BMFile_Create([In, MarshalAs(UnmanagedType.LPUTF8Str)] string temp_folder, [In, MarshalAs(UnmanagedType.LPUTF8Str)] string texture_folder, [In, MarshalAs(UnmanagedType.FunctionPtr)] OutputCallback raw_callback, [In, MarshalAs(UnmanagedType.U4)] uint encoding_count, [In, MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(BMStringArrayMarshaler))] string[] encodings, [Out, MarshalAs(UnmanagedType.SysInt)] out IntPtr out_file); + internal static extern bool BMFile_Create([In, MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(BMStringMarshaler), MarshalCookie = "In")] string temp_folder, [In, MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(BMStringMarshaler), MarshalCookie = "In")] string texture_folder, [In, MarshalAs(UnmanagedType.FunctionPtr)] OutputCallback raw_callback, [In, MarshalAs(UnmanagedType.U4)] uint encoding_count, [In, MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(BMStringArrayMarshaler), MarshalCookie = "In")] string[] encodings, [Out, MarshalAs(UnmanagedType.SysInt)] out IntPtr out_file); /// BMFile_Save /// Type: BMap::BMFile*. /// Type: LibCmo::CKSTRING. @@ -302,7 +336,7 @@ namespace BMapSharp { /// True if no error, otherwise False. [DllImport(g_DllName, EntryPoint = "BMFile_Save", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] [return: MarshalAs(UnmanagedType.U1)] - internal static extern bool BMFile_Save([In, MarshalAs(UnmanagedType.SysInt)] IntPtr map_file, [In, MarshalAs(UnmanagedType.LPUTF8Str)] string file_name, [In, MarshalAs(UnmanagedType.U4)] uint texture_save_opt, [In, MarshalAs(UnmanagedType.U1)] bool use_compress, [In, MarshalAs(UnmanagedType.I4)] int compreess_level); + internal static extern bool BMFile_Save([In, MarshalAs(UnmanagedType.SysInt)] IntPtr map_file, [In, MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(BMStringMarshaler), MarshalCookie = "In")] string file_name, [In, MarshalAs(UnmanagedType.U4)] uint texture_save_opt, [In, MarshalAs(UnmanagedType.U1)] bool use_compress, [In, MarshalAs(UnmanagedType.I4)] int compreess_level); /// BMFile_Free /// Type: BMap::BMFile*. /// True if no error, otherwise False. @@ -537,7 +571,7 @@ namespace BMapSharp { /// True if no error, otherwise False. [DllImport(g_DllName, EntryPoint = "BMObject_GetName", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] [return: MarshalAs(UnmanagedType.U1)] - internal static extern bool BMObject_GetName([In, MarshalAs(UnmanagedType.SysInt)] IntPtr bmfile, [In, MarshalAs(UnmanagedType.U4)] uint objid, [Out, MarshalAs(UnmanagedType.LPUTF8Str)] out string out_name); + internal static extern bool BMObject_GetName([In, MarshalAs(UnmanagedType.SysInt)] IntPtr bmfile, [In, MarshalAs(UnmanagedType.U4)] uint objid, [Out, MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(BMStringMarshaler), MarshalCookie = "Out")] out string out_name); /// BMObject_SetName /// Type: BMap::BMFile*. The pointer to corresponding BMFile. /// Type: LibCmo::CK2::CK_ID. The CKID of object you accessing. @@ -545,7 +579,7 @@ namespace BMapSharp { /// True if no error, otherwise False. [DllImport(g_DllName, EntryPoint = "BMObject_SetName", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] [return: MarshalAs(UnmanagedType.U1)] - internal static extern bool BMObject_SetName([In, MarshalAs(UnmanagedType.SysInt)] IntPtr bmfile, [In, MarshalAs(UnmanagedType.U4)] uint objid, [In, MarshalAs(UnmanagedType.LPUTF8Str)] string name); + internal static extern bool BMObject_SetName([In, MarshalAs(UnmanagedType.SysInt)] IntPtr bmfile, [In, MarshalAs(UnmanagedType.U4)] uint objid, [In, MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(BMStringMarshaler), MarshalCookie = "In")] string name); /// BMGroup_AddObject /// Type: BMap::BMFile*. The pointer to corresponding BMFile. /// Type: LibCmo::CK2::CK_ID. The CKID of object you accessing. @@ -578,7 +612,7 @@ namespace BMapSharp { /// True if no error, otherwise False. [DllImport(g_DllName, EntryPoint = "BMTexture_GetFileName", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] [return: MarshalAs(UnmanagedType.U1)] - internal static extern bool BMTexture_GetFileName([In, MarshalAs(UnmanagedType.SysInt)] IntPtr bmfile, [In, MarshalAs(UnmanagedType.U4)] uint objid, [Out, MarshalAs(UnmanagedType.LPUTF8Str)] out string out_filename); + internal static extern bool BMTexture_GetFileName([In, MarshalAs(UnmanagedType.SysInt)] IntPtr bmfile, [In, MarshalAs(UnmanagedType.U4)] uint objid, [Out, MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(BMStringMarshaler), MarshalCookie = "Out")] out string out_filename); /// BMTexture_LoadImage /// Type: BMap::BMFile*. The pointer to corresponding BMFile. /// Type: LibCmo::CK2::CK_ID. The CKID of object you accessing. @@ -586,7 +620,7 @@ namespace BMapSharp { /// True if no error, otherwise False. [DllImport(g_DllName, EntryPoint = "BMTexture_LoadImage", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] [return: MarshalAs(UnmanagedType.U1)] - internal static extern bool BMTexture_LoadImage([In, MarshalAs(UnmanagedType.SysInt)] IntPtr bmfile, [In, MarshalAs(UnmanagedType.U4)] uint objid, [In, MarshalAs(UnmanagedType.LPUTF8Str)] string filename); + internal static extern bool BMTexture_LoadImage([In, MarshalAs(UnmanagedType.SysInt)] IntPtr bmfile, [In, MarshalAs(UnmanagedType.U4)] uint objid, [In, MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(BMStringMarshaler), MarshalCookie = "In")] string filename); /// BMTexture_SaveImage /// Type: BMap::BMFile*. The pointer to corresponding BMFile. /// Type: LibCmo::CK2::CK_ID. The CKID of object you accessing. @@ -594,7 +628,7 @@ namespace BMapSharp { /// True if no error, otherwise False. [DllImport(g_DllName, EntryPoint = "BMTexture_SaveImage", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] [return: MarshalAs(UnmanagedType.U1)] - internal static extern bool BMTexture_SaveImage([In, MarshalAs(UnmanagedType.SysInt)] IntPtr bmfile, [In, MarshalAs(UnmanagedType.U4)] uint objid, [In, MarshalAs(UnmanagedType.LPUTF8Str)] string filename); + internal static extern bool BMTexture_SaveImage([In, MarshalAs(UnmanagedType.SysInt)] IntPtr bmfile, [In, MarshalAs(UnmanagedType.U4)] uint objid, [In, MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(BMStringMarshaler), MarshalCookie = "In")] string filename); /// BMTexture_GetSaveOptions /// Type: BMap::BMFile*. The pointer to corresponding BMFile. /// Type: LibCmo::CK2::CK_ID. The CKID of object you accessing. diff --git a/BMapBindings/BMapSharp/BMapSharp/BMapWrapper.cs b/BMapBindings/BMapSharp/BMapSharp/BMapWrapper.cs index e73eaff..a354e8f 100644 --- a/BMapBindings/BMapSharp/BMapSharp/BMapWrapper.cs +++ b/BMapBindings/BMapSharp/BMapSharp/BMapWrapper.cs @@ -229,7 +229,7 @@ namespace BMapSharp.BMapWrapper { BMapException.ThrowIfFailed(BMap.BMFile_GetMeshCount(this.getPointer(), out uint out_count)); return out_count; } - public IEnumerable GetMeshs() { + public IEnumerable GetMeshes() { uint count = GetMeshCount(); for (uint i = 0; i < count; ++i) { BMapException.ThrowIfFailed(BMap.BMFile_GetMesh(this.getPointer(), i, out uint out_id)); diff --git a/BMapBindings/BMapSharp/BMapSharpTestbench/BMapSharpTestbench.csproj b/BMapBindings/BMapSharp/BMapSharpTestbench/BMapSharpTestbench.csproj index 90c53ef..cc70e30 100644 --- a/BMapBindings/BMapSharp/BMapSharpTestbench/BMapSharpTestbench.csproj +++ b/BMapBindings/BMapSharp/BMapSharpTestbench/BMapSharpTestbench.csproj @@ -1,5 +1,9 @@  + + + + Exe net8.0 diff --git a/BMapBindings/BMapSharp/BMapSharpTestbench/Program.cs b/BMapBindings/BMapSharp/BMapSharpTestbench/Program.cs index fe613b6..7bd5376 100644 --- a/BMapBindings/BMapSharp/BMapSharpTestbench/Program.cs +++ b/BMapBindings/BMapSharp/BMapSharpTestbench/Program.cs @@ -1,12 +1,58 @@ using System; +using System.Text; + +namespace BMapSharpTestbench { + internal class Program { + static void Main(string[] args) { + // Check environment + Console.OutputEncoding = Encoding.UTF8; + if (!BMapSharp.BMapWrapper.Utils.IsBMapAvailable()) { + Console.WriteLine("Fail to initialize native BMap."); + Environment.Exit(0); + } + + // Waiting debugger + int pid = System.Diagnostics.Process.GetCurrentProcess().Id; + Console.WriteLine($"C# PID is {pid}. Waiting debugger, press any key to continue..."); + Console.ReadKey(true); + + // Start testbench + string file_name = "Level_02.NMO"; + string temp_folder = "Temp"; + string texture_folder = "F:\\Ballance\\Ballance\\Textures"; + string[] encodings = ["cp1252", "gb2312"]; + + using (var reader = new BMapSharp.BMapWrapper.BMFileReader(file_name, temp_folder, texture_folder, encodings)) { + Console.WriteLine("===== Groups ====="); + foreach (var gp in reader.GetGroups()) { + Console.WriteLine(gp.GetName()); + } + + Console.WriteLine("===== 3dObjects ====="); + foreach (var obj in reader.Get3dObjects()) { + Console.WriteLine(obj.GetName()); + } + + Console.WriteLine("===== Meshes ====="); + foreach (var mesh in reader.GetMeshes()) { + Console.WriteLine(mesh.GetName()); + } + + Console.WriteLine("===== Materials ====="); + foreach (var mtl in reader.GetMaterials()) { + Console.WriteLine(mtl.GetName()); + } + + Console.WriteLine("===== Textures ====="); + foreach (var tex in reader.GetTextures()) { + Console.WriteLine(tex.GetName()); + } + + } + + Console.WriteLine("===== Done ====="); + Console.ReadKey(true); -namespace BMapSharpTestbench // Note: actual namespace depends on the project name. -{ - internal class Program - { - static void Main(string[] args) - { - Console.WriteLine("Hello World!"); } } } \ No newline at end of file diff --git a/BMapBindings/PyBMap/testbench.py b/BMapBindings/PyBMap/testbench.py index 647f9e8..eef74c1 100644 --- a/BMapBindings/PyBMap/testbench.py +++ b/BMapBindings/PyBMap/testbench.py @@ -13,7 +13,7 @@ def main() -> None: for gp in reader.get_groups(): print(gp.get_name()) - print('===== Objects =====') + print('===== 3dObjects =====') for obj in reader.get_3dobjects(): print(obj.get_name()) diff --git a/CodeGen/BMapBindings/CSharpWriter.java b/CodeGen/BMapBindings/CSharpWriter.java index bd1c3f8..55406ba 100644 --- a/CodeGen/BMapBindings/CSharpWriter.java +++ b/CodeGen/BMapBindings/CSharpWriter.java @@ -45,14 +45,22 @@ public class CSharpWriter { // use "switch" to check variable type switch (vt_base_type) { case "CKSTRING": + // decide direction cookies + String direction_cookie = ""; + if (paramdecl.mIsInput) { + direction_cookie = "In"; + } else { + direction_cookie = "Out"; + } // only allow 0 and 1 pointer level for string. switch (vt_pointer_level) { case 0: - ret.mMarshalAs = "UnmanagedType.LPUTF8Str"; + ret.mMarshalAs = "UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(BMStringMarshaler), MarshalCookie = \"" + direction_cookie + "\""; + // ret.mMarshalAs = "UnmanagedType.LPUTF8Str"; ret.mCsType = "string"; break; case 1: - ret.mMarshalAs = "UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(BMStringArrayMarshaler)"; + ret.mMarshalAs = "UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(BMStringArrayMarshaler), MarshalCookie = \"" + direction_cookie + "\""; ret.mCsType = "string[]"; break; }