diff --git a/BMapBindings/BMapSharp/.editorconfig b/BMapBindings/BMapSharp/.editorconfig
new file mode 100644
index 0000000..ca51e64
--- /dev/null
+++ b/BMapBindings/BMapSharp/.editorconfig
@@ -0,0 +1,364 @@
+root = true
+
+# All files
+[*]
+indent_style = space
+
+# Xml files
+[*.xml]
+indent_size = 2
+
+# C# files
+[*.cs]
+
+#### Core EditorConfig Options ####
+
+# Indentation and spacing
+indent_size = 4
+tab_width = 4
+
+# New line preferences
+end_of_line = crlf
+insert_final_newline = false
+
+#### .NET Coding Conventions ####
+[*.{cs,vb}]
+
+# Organize usings
+dotnet_separate_import_directive_groups = false
+dotnet_sort_system_directives_first = false
+file_header_template = unset
+
+# this. and Me. preferences
+dotnet_style_qualification_for_event = false:silent
+dotnet_style_qualification_for_field = false:silent
+dotnet_style_qualification_for_method = false:silent
+dotnet_style_qualification_for_property = false:silent
+
+# Language keywords vs BCL types preferences
+dotnet_style_predefined_type_for_locals_parameters_members = true:silent
+dotnet_style_predefined_type_for_member_access = true:silent
+
+# Parentheses preferences
+dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent
+dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent
+dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
+dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent
+
+# Modifier preferences
+dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
+
+# Expression-level preferences
+dotnet_style_coalesce_expression = true:suggestion
+dotnet_style_collection_initializer = true:suggestion
+dotnet_style_explicit_tuple_names = true:suggestion
+dotnet_style_null_propagation = true:suggestion
+dotnet_style_object_initializer = true:suggestion
+dotnet_style_operator_placement_when_wrapping = beginning_of_line
+dotnet_style_prefer_auto_properties = true:suggestion
+dotnet_style_prefer_compound_assignment = true:suggestion
+dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion
+dotnet_style_prefer_conditional_expression_over_return = true:suggestion
+dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
+dotnet_style_prefer_inferred_tuple_names = true:suggestion
+dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
+dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
+dotnet_style_prefer_simplified_interpolation = true:suggestion
+
+# Field preferences
+dotnet_style_readonly_field = true:warning
+
+# Parameter preferences
+dotnet_code_quality_unused_parameters = all:suggestion
+
+# Suppression preferences
+dotnet_remove_unnecessary_suppression_exclusions = none
+
+#### C# Coding Conventions ####
+[*.cs]
+
+# var preferences
+csharp_style_var_elsewhere = false:silent
+csharp_style_var_for_built_in_types = false:silent
+csharp_style_var_when_type_is_apparent = false:silent
+
+# Expression-bodied members
+csharp_style_expression_bodied_accessors = true:silent
+csharp_style_expression_bodied_constructors = false:silent
+csharp_style_expression_bodied_indexers = true:silent
+csharp_style_expression_bodied_lambdas = true:suggestion
+csharp_style_expression_bodied_local_functions = false:silent
+csharp_style_expression_bodied_methods = false:silent
+csharp_style_expression_bodied_operators = false:silent
+csharp_style_expression_bodied_properties = true:silent
+
+# Pattern matching preferences
+csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
+csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
+csharp_style_prefer_not_pattern = true:suggestion
+csharp_style_prefer_pattern_matching = true:silent
+csharp_style_prefer_switch_expression = true:suggestion
+
+# Null-checking preferences
+csharp_style_conditional_delegate_call = true:suggestion
+
+# Modifier preferences
+csharp_prefer_static_local_function = true:warning
+csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:silent
+
+# Code-block preferences
+csharp_prefer_braces = true:silent
+csharp_prefer_simple_using_statement = true:suggestion
+
+# Expression-level preferences
+csharp_prefer_simple_default_expression = true:suggestion
+csharp_style_deconstructed_variable_declaration = true:suggestion
+csharp_style_inlined_variable_declaration = true:suggestion
+csharp_style_pattern_local_over_anonymous_function = true:suggestion
+csharp_style_prefer_index_operator = true:suggestion
+csharp_style_prefer_range_operator = true:suggestion
+csharp_style_throw_expression = true:suggestion
+csharp_style_unused_value_assignment_preference = discard_variable:suggestion
+csharp_style_unused_value_expression_statement_preference = discard_variable:silent
+
+# 'using' directive preferences
+csharp_using_directive_placement = outside_namespace:silent
+
+#### C# Formatting Rules ####
+
+# New line preferences
+csharp_new_line_before_catch = false
+csharp_new_line_before_else = false
+csharp_new_line_before_finally = false
+csharp_new_line_before_members_in_anonymous_types = true
+csharp_new_line_before_members_in_object_initializers = true
+csharp_new_line_before_open_brace = none
+csharp_new_line_between_query_expression_clauses = true
+
+# Indentation preferences
+csharp_indent_block_contents = true
+csharp_indent_braces = false
+csharp_indent_case_contents = true
+csharp_indent_case_contents_when_block = true
+csharp_indent_labels = one_less_than_current
+csharp_indent_switch_labels = true
+
+# Space preferences
+csharp_space_after_cast = false
+csharp_space_after_colon_in_inheritance_clause = true
+csharp_space_after_comma = true
+csharp_space_after_dot = false
+csharp_space_after_keywords_in_control_flow_statements = true
+csharp_space_after_semicolon_in_for_statement = true
+csharp_space_around_binary_operators = before_and_after
+csharp_space_around_declaration_statements = false
+csharp_space_before_colon_in_inheritance_clause = true
+csharp_space_before_comma = false
+csharp_space_before_dot = false
+csharp_space_before_open_square_brackets = false
+csharp_space_before_semicolon_in_for_statement = false
+csharp_space_between_empty_square_brackets = false
+csharp_space_between_method_call_empty_parameter_list_parentheses = false
+csharp_space_between_method_call_name_and_opening_parenthesis = false
+csharp_space_between_method_call_parameter_list_parentheses = false
+csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
+csharp_space_between_method_declaration_name_and_open_parenthesis = false
+csharp_space_between_method_declaration_parameter_list_parentheses = false
+csharp_space_between_parentheses = false
+csharp_space_between_square_brackets = false
+
+# Wrapping preferences
+csharp_preserve_single_line_blocks = true
+csharp_preserve_single_line_statements = true
+
+#### Naming styles ####
+[*.{cs,vb}]
+
+# Naming rules
+
+dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.symbols = types_and_namespaces
+dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.style = pascalcase
+
+dotnet_naming_rule.interfaces_should_be_ipascalcase.severity = suggestion
+dotnet_naming_rule.interfaces_should_be_ipascalcase.symbols = interfaces
+dotnet_naming_rule.interfaces_should_be_ipascalcase.style = ipascalcase
+
+dotnet_naming_rule.type_parameters_should_be_tpascalcase.severity = suggestion
+dotnet_naming_rule.type_parameters_should_be_tpascalcase.symbols = type_parameters
+dotnet_naming_rule.type_parameters_should_be_tpascalcase.style = tpascalcase
+
+dotnet_naming_rule.methods_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.methods_should_be_pascalcase.symbols = methods
+dotnet_naming_rule.methods_should_be_pascalcase.style = pascalcase
+
+dotnet_naming_rule.properties_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.properties_should_be_pascalcase.symbols = properties
+dotnet_naming_rule.properties_should_be_pascalcase.style = pascalcase
+
+dotnet_naming_rule.events_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.events_should_be_pascalcase.symbols = events
+dotnet_naming_rule.events_should_be_pascalcase.style = pascalcase
+
+dotnet_naming_rule.local_variables_should_be_camelcase.severity = suggestion
+dotnet_naming_rule.local_variables_should_be_camelcase.symbols = local_variables
+dotnet_naming_rule.local_variables_should_be_camelcase.style = camelcase
+
+dotnet_naming_rule.local_constants_should_be_camelcase.severity = suggestion
+dotnet_naming_rule.local_constants_should_be_camelcase.symbols = local_constants
+dotnet_naming_rule.local_constants_should_be_camelcase.style = camelcase
+
+dotnet_naming_rule.parameters_should_be_camelcase.severity = suggestion
+dotnet_naming_rule.parameters_should_be_camelcase.symbols = parameters
+dotnet_naming_rule.parameters_should_be_camelcase.style = camelcase
+
+dotnet_naming_rule.public_fields_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.public_fields_should_be_pascalcase.symbols = public_fields
+dotnet_naming_rule.public_fields_should_be_pascalcase.style = pascalcase
+
+dotnet_naming_rule.private_fields_should_be__camelcase.severity = suggestion
+dotnet_naming_rule.private_fields_should_be__camelcase.symbols = private_fields
+dotnet_naming_rule.private_fields_should_be__camelcase.style = _camelcase
+
+dotnet_naming_rule.private_static_fields_should_be_s_camelcase.severity = suggestion
+dotnet_naming_rule.private_static_fields_should_be_s_camelcase.symbols = private_static_fields
+dotnet_naming_rule.private_static_fields_should_be_s_camelcase.style = s_camelcase
+
+dotnet_naming_rule.public_constant_fields_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.public_constant_fields_should_be_pascalcase.symbols = public_constant_fields
+dotnet_naming_rule.public_constant_fields_should_be_pascalcase.style = pascalcase
+
+dotnet_naming_rule.private_constant_fields_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.private_constant_fields_should_be_pascalcase.symbols = private_constant_fields
+dotnet_naming_rule.private_constant_fields_should_be_pascalcase.style = pascalcase
+
+dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.symbols = public_static_readonly_fields
+dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.style = pascalcase
+
+dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.symbols = private_static_readonly_fields
+dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.style = pascalcase
+
+dotnet_naming_rule.enums_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.enums_should_be_pascalcase.symbols = enums
+dotnet_naming_rule.enums_should_be_pascalcase.style = pascalcase
+
+dotnet_naming_rule.local_functions_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.local_functions_should_be_pascalcase.symbols = local_functions
+dotnet_naming_rule.local_functions_should_be_pascalcase.style = pascalcase
+
+dotnet_naming_rule.non_field_members_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.non_field_members_should_be_pascalcase.symbols = non_field_members
+dotnet_naming_rule.non_field_members_should_be_pascalcase.style = pascalcase
+
+# Symbol specifications
+
+dotnet_naming_symbols.interfaces.applicable_kinds = interface
+dotnet_naming_symbols.interfaces.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.interfaces.required_modifiers =
+
+dotnet_naming_symbols.enums.applicable_kinds = enum
+dotnet_naming_symbols.enums.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.enums.required_modifiers =
+
+dotnet_naming_symbols.events.applicable_kinds = event
+dotnet_naming_symbols.events.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.events.required_modifiers =
+
+dotnet_naming_symbols.methods.applicable_kinds = method
+dotnet_naming_symbols.methods.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.methods.required_modifiers =
+
+dotnet_naming_symbols.properties.applicable_kinds = property
+dotnet_naming_symbols.properties.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.properties.required_modifiers =
+
+dotnet_naming_symbols.public_fields.applicable_kinds = field
+dotnet_naming_symbols.public_fields.applicable_accessibilities = public, internal
+dotnet_naming_symbols.public_fields.required_modifiers =
+
+dotnet_naming_symbols.private_fields.applicable_kinds = field
+dotnet_naming_symbols.private_fields.applicable_accessibilities = private, protected, protected_internal, private_protected
+dotnet_naming_symbols.private_fields.required_modifiers =
+
+dotnet_naming_symbols.private_static_fields.applicable_kinds = field
+dotnet_naming_symbols.private_static_fields.applicable_accessibilities = private, protected, protected_internal, private_protected
+dotnet_naming_symbols.private_static_fields.required_modifiers = static
+
+dotnet_naming_symbols.types_and_namespaces.applicable_kinds = namespace, class, struct, interface, enum
+dotnet_naming_symbols.types_and_namespaces.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.types_and_namespaces.required_modifiers =
+
+dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
+dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.non_field_members.required_modifiers =
+
+dotnet_naming_symbols.type_parameters.applicable_kinds = namespace
+dotnet_naming_symbols.type_parameters.applicable_accessibilities = *
+dotnet_naming_symbols.type_parameters.required_modifiers =
+
+dotnet_naming_symbols.private_constant_fields.applicable_kinds = field
+dotnet_naming_symbols.private_constant_fields.applicable_accessibilities = private, protected, protected_internal, private_protected
+dotnet_naming_symbols.private_constant_fields.required_modifiers = const
+
+dotnet_naming_symbols.local_variables.applicable_kinds = local
+dotnet_naming_symbols.local_variables.applicable_accessibilities = local
+dotnet_naming_symbols.local_variables.required_modifiers =
+
+dotnet_naming_symbols.local_constants.applicable_kinds = local
+dotnet_naming_symbols.local_constants.applicable_accessibilities = local
+dotnet_naming_symbols.local_constants.required_modifiers = const
+
+dotnet_naming_symbols.parameters.applicable_kinds = parameter
+dotnet_naming_symbols.parameters.applicable_accessibilities = *
+dotnet_naming_symbols.parameters.required_modifiers =
+
+dotnet_naming_symbols.public_constant_fields.applicable_kinds = field
+dotnet_naming_symbols.public_constant_fields.applicable_accessibilities = public, internal
+dotnet_naming_symbols.public_constant_fields.required_modifiers = const
+
+dotnet_naming_symbols.public_static_readonly_fields.applicable_kinds = field
+dotnet_naming_symbols.public_static_readonly_fields.applicable_accessibilities = public, internal
+dotnet_naming_symbols.public_static_readonly_fields.required_modifiers = readonly, static
+
+dotnet_naming_symbols.private_static_readonly_fields.applicable_kinds = field
+dotnet_naming_symbols.private_static_readonly_fields.applicable_accessibilities = private, protected, protected_internal, private_protected
+dotnet_naming_symbols.private_static_readonly_fields.required_modifiers = readonly, static
+
+dotnet_naming_symbols.local_functions.applicable_kinds = local_function
+dotnet_naming_symbols.local_functions.applicable_accessibilities = *
+dotnet_naming_symbols.local_functions.required_modifiers =
+
+# Naming styles
+
+dotnet_naming_style.pascalcase.required_prefix =
+dotnet_naming_style.pascalcase.required_suffix =
+dotnet_naming_style.pascalcase.word_separator =
+dotnet_naming_style.pascalcase.capitalization = pascal_case
+
+dotnet_naming_style.ipascalcase.required_prefix = I
+dotnet_naming_style.ipascalcase.required_suffix =
+dotnet_naming_style.ipascalcase.word_separator =
+dotnet_naming_style.ipascalcase.capitalization = pascal_case
+
+dotnet_naming_style.tpascalcase.required_prefix = T
+dotnet_naming_style.tpascalcase.required_suffix =
+dotnet_naming_style.tpascalcase.word_separator =
+dotnet_naming_style.tpascalcase.capitalization = pascal_case
+
+dotnet_naming_style._camelcase.required_prefix = _
+dotnet_naming_style._camelcase.required_suffix =
+dotnet_naming_style._camelcase.word_separator =
+dotnet_naming_style._camelcase.capitalization = camel_case
+
+dotnet_naming_style.camelcase.required_prefix =
+dotnet_naming_style.camelcase.required_suffix =
+dotnet_naming_style.camelcase.word_separator =
+dotnet_naming_style.camelcase.capitalization = camel_case
+
+dotnet_naming_style.s_camelcase.required_prefix = s_
+dotnet_naming_style.s_camelcase.required_suffix =
+dotnet_naming_style.s_camelcase.word_separator =
+dotnet_naming_style.s_camelcase.capitalization = camel_case
+
diff --git a/BMapBindings/BMapSharp/BMapSharp/BMap.cs b/BMapBindings/BMapSharp/BMapSharp/BMap.cs
index 549b0a9..00b9894 100644
--- a/BMapBindings/BMapSharp/BMapSharp/BMap.cs
+++ b/BMapBindings/BMapSharp/BMapSharp/BMap.cs
@@ -23,16 +23,18 @@ namespace BMapSharp {
/// The message content need to be printed.
public delegate void OutputCallback([In, MarshalAs(UnmanagedType.LPUTF8Str)] string msg);
+ #region Custom Marshalers
+
+ // References:
+ // https://stackoverflow.com/questions/18498452/how-do-i-write-a-custom-marshaler-which-allows-data-to-flow-from-native-to-manag
+ // https://learn.microsoft.com/en-us/dotnet/fundamentals/runtime-libraries/system-runtime-interopservices-icustommarshaler
+ //
+ // NOTE: I do not create a member to store the object we are marshaling.
+ // Because my binding do not have In, Out parameter. All parameters are In OR Out.
+ // So there is no reason to keep that member.
+
/// The custom marshaler for BMap string array.
public class BMStringArrayMarshaler : ICustomMarshaler {
- // References:
- // https://stackoverflow.com/questions/18498452/how-do-i-write-a-custom-marshaler-which-allows-data-to-flow-from-native-to-manag
- // https://learn.microsoft.com/en-us/dotnet/fundamentals/runtime-libraries/system-runtime-interopservices-icustommarshaler
- //
- // NOTE: I do not create a member to store the object we are marshaling.
- // Because my binding do not have In, Out parameter. All parameters are In OR Out.
- // So there is no reason to keep that member.
-
private static readonly BMStringArrayMarshaler g_Instance = new BMStringArrayMarshaler();
public static ICustomMarshaler GetInstance(string pstrCookie) => g_Instance;
@@ -48,13 +50,9 @@ namespace BMapSharp {
// 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.
- private static readonly int szLengthHeaderSize = Marshal.SizeOf();
- private static readonly int szArrayItemSize = Marshal.SizeOf();
- private static readonly int szStringItemSize = Marshal.SizeOf();
-
- public IntPtr MarshalManagedToNative(object ManagedObj) {
+ public nint MarshalManagedToNative(object ManagedObj) {
// Check nullptr object.
- if (ManagedObj is null) return IntPtr.Zero;
+ if (ManagedObj is null) return nint.Zero;
// Check argument type.
string[] castManagedObj = ManagedObj as string[];
if (castManagedObj is null)
@@ -62,97 +60,73 @@ namespace BMapSharp {
// Allocate string items first
int szArrayItemCount = castManagedObj.Length;
- IntPtr[] apStrings = new IntPtr[szArrayItemCount];
+ int szArrayItemSize = Marshal.SizeOf();
+ nint[] apString = new nint[szArrayItemCount];
for (int i = 0; i < szArrayItemCount; ++i) {
- // Encode string first.
- byte[] encString = Encoding.UTF8.GetBytes(castManagedObj[i]);
- // Allocate string memory with extra NULL terminal.
- int szStringItemCount = encString.Length;
- IntPtr pString = Marshal.AllocHGlobal(szStringItemSize * (szStringItemCount + 1) + szLengthHeaderSize);
- // Setup length field
- Marshal.WriteInt32(pString, 0, szStringItemCount);
- // Copy string data with offset.
- IntPtr pFakeString = pString + szLengthHeaderSize;
- Marshal.Copy(encString, 0, pFakeString, szStringItemCount);
- // Set NULL terminal.
- Marshal.WriteByte(pFakeString, szStringItemSize * szStringItemCount, 0);
- // Set item in string pointer
- apStrings[i] = pFakeString;
+ // Check null string
+ string stringObj = castManagedObj[i];
+ if (stringObj is null) apString[i] = nint.Zero;
+ else apString[i] = BMStringMarshaler.ToNative(stringObj);
}
// Allocate array pointer now.
- IntPtr pArray = Marshal.AllocHGlobal(szArrayItemSize * szArrayItemCount + szLengthHeaderSize);
- // Setup length field
- Marshal.WriteInt32(pArray, 0, szArrayItemCount);
- // Copy string pointer data with offset.
- IntPtr pFakeArray = pArray + szLengthHeaderSize;
- Marshal.Copy(apStrings, 0, pFakeArray, szArrayItemCount);
+ nint pArray = Marshal.AllocHGlobal(szArrayItemSize * (szArrayItemCount + 1));
+ // Copy string pointer data
+ Marshal.Copy(apString, 0, pArray, szArrayItemSize * szArrayItemCount);
+ // Setup NULL ternimal
+ Marshal.WriteIntPtr(pArray + (szArrayItemSize * szArrayItemCount), nint.Zero);
// Return value
- return pFakeArray;
+ return pArray;
}
- public object MarshalNativeToManaged(IntPtr pNativeData) {
+ public object MarshalNativeToManaged(nint pNativeData) {
// Check nullptr
- if (pNativeData == IntPtr.Zero) return null;
+ if (pNativeData == nint.Zero) return null;
- // Get real array pointer
- IntPtr pFakeArray = pNativeData;
- IntPtr pArray = pFakeArray - szLengthHeaderSize;
-
- // Get the count of array and read string pointers
- int szArrayItemCount = Marshal.ReadInt32(pArray, 0);
- IntPtr[] apStrings = new IntPtr[szArrayItemCount];
- Marshal.Copy(pFakeArray, apStrings, 0, szArrayItemCount);
+ // Get the length of array
+ int szArrayItemCount = BMStringArrayMarshaler.GetArrayLength(pNativeData);
+ int szArrayItemSize = Marshal.SizeOf();
+ // Prepare array cache and read it.
+ nint[] apString = new nint[szArrayItemCount];
+ Marshal.Copy(pNativeData, apString, 0, szArrayItemSize * szArrayItemCount);
// Iterate the array and process each string one by one.
string[] ret = new string[szArrayItemCount];
for (int i = 0; i < szArrayItemCount; ++i) {
// Get string pointer
- IntPtr pFakeString = apStrings[i];
- if (pFakeString == IntPtr.Zero) {
+ nint pString = apString[i];
+ if (pString == nint.Zero) {
ret[i] = null;
continue;
}
- IntPtr pString = pFakeString - szLengthHeaderSize;
- // Read string length
- int szStringItemCount = Marshal.ReadInt32(pString, 0);
- // Read string body
- byte[] encString = new byte[szStringItemCount];
- Marshal.Copy(pFakeString, encString, 0, szStringItemCount);
- // Decode string with UTF8
- ret[i] = Encoding.UTF8.GetString(encString);
+ // Extract string
+ ret[i] = BMStringMarshaler.ToManaged(pString);
}
// Return result
return ret;
}
- public void CleanUpNativeData(IntPtr pNativeData) {
+ public void CleanUpNativeData(nint pNativeData) {
// Check nullptr
- if (pNativeData == IntPtr.Zero) return;
+ if (pNativeData == nint.Zero) return;
- // Get real array pointer
- IntPtr pFakeArray = pNativeData;
- IntPtr pArray = pFakeArray - szLengthHeaderSize;
+ // Get the length of array
+ int szArrayItemCount = BMStringArrayMarshaler.GetArrayLength(pNativeData);
+ int szArrayItemSize = Marshal.SizeOf();
+ // Prepare array cache and read it.
+ nint[] apString = new nint[szArrayItemCount];
+ Marshal.Copy(pNativeData, apString, 0, szArrayItemSize * szArrayItemCount);
+ // Free array self
+ Marshal.FreeHGlobal(pNativeData);
- // Get the count of array and read string pointers
- int szArrayItemCount = Marshal.ReadInt32(pArray, 0);
- IntPtr[] apStrings = new IntPtr[szArrayItemCount];
- Marshal.Copy(pFakeArray, apStrings, 0, szArrayItemCount);
-
- // Iterate the array and free them one by one.
- for (int i = 0; i < szArrayItemCount; ++i) {
- // Get string pointer
- IntPtr pFakeString = apStrings[i];
- if (pFakeString == IntPtr.Zero) continue;
- IntPtr pString = pFakeString - szLengthHeaderSize;
+ // Iterate the string pointer array and free them one by one.
+ foreach (nint pString in apString) {
// Free string pointer
+ if (pString == nint.Zero) continue;
Marshal.FreeHGlobal(pString);
}
-
- // Free array self
- Marshal.FreeHGlobal(pArray);
}
public void CleanUpManagedData(object ManagedObj) {
@@ -164,8 +138,112 @@ namespace BMapSharp {
return -1;
}
+ ///
+ /// Return the length of array created by this marshaler.
+ ///
+ /// The pointer to array for checking.
+ /// The length of array (NULL terminal exclusive).
+ internal static int GetArrayLength(nint ptr) {
+ int count = 0, unit = Marshal.SizeOf();
+ while (Marshal.ReadIntPtr(ptr) != nint.Zero) {
+ ptr += unit;
+ ++count;
+ }
+ return count;
+ }
}
+ public class BMStringMarshaler : ICustomMarshaler {
+ private static readonly BMStringMarshaler g_Instance = new BMStringMarshaler();
+ public static ICustomMarshaler GetInstance(string pstrCookie) => g_Instance;
+
+ public nint MarshalManagedToNative(object ManagedObj) {
+ // Check requirements.
+ if (ManagedObj is null) return nint.Zero;
+ string castManagedObj = ManagedObj as string;
+ if (castManagedObj is null)
+ throw new MarshalDirectiveException("BMStringMarshaler must be used on a string.");
+ // Call self
+ return BMStringMarshaler.ToNative(castManagedObj);
+ }
+
+ public object MarshalNativeToManaged(nint pNativeData) {
+ // Check nullptr
+ if (pNativeData == nint.Zero) return null;
+ // Call self
+ return BMStringMarshaler.ToManaged(pNativeData);
+ }
+
+ public void CleanUpNativeData(nint pNativeData) {
+ // Check nullptr
+ if (pNativeData == nint.Zero) return;
+ // Free native pointer
+ Marshal.FreeHGlobal(pNativeData);
+ }
+
+ public void CleanUpManagedData(object ManagedObj) {
+ // Do nothing, because managed data do not need any clean up.
+ }
+
+ public int GetNativeDataSize() {
+ // Return -1 to indicate the managed type this marshaler handles is not a value type.
+ return -1;
+ }
+
+ ///
+ /// Return the length in byte of given pointer represented C style string.
+ ///
+ /// The pointer for checking.
+ /// The length of C style string (NUL exclusive).
+ internal static int GetCStringLength(nint ptr) {
+ int count = 0, unit = Marshal.SizeOf();
+ while (Marshal.ReadByte(ptr) != (byte)0) {
+ ptr += unit;
+ ++count;
+ }
+ return count;
+ }
+
+ ///
+ /// Convert given string object to native data.
+ /// This function is shared by 2 marshalers.
+ ///
+ /// String object. Caller must make sure this object is not null.
+ /// The created native data pointer.
+ internal static nint ToNative(string obj) {
+ // Encode string first
+ byte[] encString = Encoding.UTF8.GetBytes(obj);
+ // Allocate string memory with extra NUL.
+ int szStringItemCount = encString.Length;
+ int szStringItemSize = Marshal.SizeOf();
+ nint pString = Marshal.AllocHGlobal(szStringItemSize * (szStringItemCount + 1));
+ // Copy encoded string data
+ Marshal.Copy(encString, 0, pString, szStringItemSize * szStringItemCount);
+ // Setup NUL
+ Marshal.WriteByte(pString + (szStringItemSize * szStringItemCount), (byte)0);
+ // Return value
+ return pString;
+ }
+ ///
+ /// Extract managed string from given native pointer holding C style string data.
+ /// This function is shared by 2 marshalers.
+ ///
+ /// Native pointer holding string data. Caller must make sure this pointer is not nullptr.
+ /// The extracted managed string data.
+ internal static string ToManaged(nint ptr) {
+ // Get the length of given string.
+ int szStringItemCount = BMStringMarshaler.GetCStringLength(ptr);
+ int szStringItemSize = Marshal.SizeOf();
+ // Prepare cache and copy string data
+ byte[] encString = new byte[szStringItemCount];
+ Marshal.Copy(ptr, encString, 0, szStringItemSize * szStringItemCount);
+ // Decode string and return
+ return Encoding.UTF8.GetString(encString);
+ }
+ }
+
+ #endregion
+
// Decide the file name of loaded DLL.
#if BMAP_OS_WINDOWS
diff --git a/BMapBindings/BMapSharp/BMapSharpTestbench/BMapSharpTestbench.csproj b/BMapBindings/BMapSharp/BMapSharpTestbench/BMapSharpTestbench.csproj
index 2150e37..90c53ef 100644
--- a/BMapBindings/BMapSharp/BMapSharpTestbench/BMapSharpTestbench.csproj
+++ b/BMapBindings/BMapSharp/BMapSharpTestbench/BMapSharpTestbench.csproj
@@ -4,7 +4,6 @@
Exe
net8.0
enable
- enable