struct field marshaling failed on linux - .net-core

I'm using following struct for data marshaling with PInvoke
[StructLayout(LayoutKind.Sequential)]
struct Data
{
int id;
IntPtr state;
object obj;
}
And all works fine on Windows, but on Linux I'm getting the error:
Cannot marshal field 'obj' of type 'Data': Invalid managed/unmanaged type combination (Marshaling to and from COM interface pointers isn't supported).
Why?
Is it possible to disable marshaling for the specific field ?

object obj; property is invalid
You can try with IntPtr obj;
What is the actual type of obj?

Digging the CLR sources I've found that the issue is really Linux specific, and tied to COM interop feature, which is windows-only.
So I've fixed the problem using IntPtr for obj (as #Simonare suggested) and GCHandle.

Related

Flutter ObjectBox: Is List<String> supported for property converters?

The Flutter ObjectBox docs state:
ObjectBox (Java, Dart) has built-in support for String lists. ObjectBox for Java also has built-in support for String arrays.
However, using a List<String> in a converter triggers the following warning:
[WARNING] objectbox_generator:resolver on lib/model/diary_day.dart:
Skipping property 'doneExercises': type 'List' not supported, consider creating a relation for #Entity types (https://docs.objectbox.io/relations), or replace with getter/setter converting to a supported type (https://docs.objectbox.io/advanced/custom-types).
So the question now is, is this a bug, or am I misunderstanding the docs?
Just for reference, the converter looks like:
late List<DiaryExercise> doneExercises = [];
List<String> get dbDoneExercises {
List<String> retVal = [];
for (DiaryExercise thisExercise in doneExercises) {
retVal.add(jsonEncode(thisExercise.toJson()));
}
return retVal;
}
set dbDoneExercises(List<String> jsonStrings) {
for (String thisJson in jsonStrings)
DiaryExercise exercise = DiaryExercise.fromJson(jsonDecode(thisJson));
doneExercises.add(exercise);
}
}
Turned out, it's just a warning. A look into objectbox-model.json revealed the List<String> converter is used.

Error deserializing IEnumerable<byte> properties with json.net 6.0.3

Given the following class:
[DataContract]
public class Enumerables
{
[DataMember]
public IEnumerable<Byte> ByteMember { get; set; }
}
And an instance initialized as:
var bytes = new byte[] { ... };
var o = new Enumerables { ByteMember = bytes };
Serialization produces this:
{"ByteMember": "<<base-64-encoded-string>>"}
But this string cannot be deserialized. The error produced is:
Newtonsoft.Json.JsonSerializationException : Error converting value
"vbMBTToz9gyZj6gZuA59rE7ryu3fCfimjVMn8R6A0277Xs9u" to
type 'System.Collections.Generic.IEnumerable`1[System.Byte]'.
Path 'ByteMember', line 1, position 8084.
----> System.ArgumentException : Could not cast or convert from
System.String to System.Collections.Generic.IEnumerable`1[System.Byte].
I don't see this happening for byte[], List<byte> or Collection<byte> properties, which are correctly serialized to and from base-64 strings. And I don't see this happening for any IEnumerable<T> where T is not a byte -- for example, a property of type IEnumerable<int> deserializes to a List<double>, an effective implementation.
How an IEnumerable<byte> gets serialized depends on the concrete type that is assigned to it. If the concrete type is a byte[] then it will get serialized specially as a base-64 encoded string, whereas if it is some other concrete type like a List<byte>, it will be serialized as a normal array of numbers. The same is true of ICollection<byte> and IList<byte>. (DEMO)
On deserialization, Json.Net looks at the types of the member properties of the target class to determine what types of objects to create. When the member property is a concrete type, no problem; it creates an instance of that type and tries to populate it from the JSON. If the member type is an interface, then Json.Net has to make a guess, or throw an error. You could argue that Json.Net should be smart enough to guess that if the member variable is an IEnumerable<byte> and the JSON value is a base-64 encoded string, it should convert the string to a byte[]. But that is not how it is implemented. In fact, the special handling for base-64 encoded byte arrays is only triggered if the member property is byte[] specifically. With no special handling for IEnumerable<byte>, this results in an error because a string can't be assigned directly to an IEnumerable<byte>. Again, the same is true for ICollection<byte> or IList<byte>.
(DEMO)
If you want it to work the same for types implementing IEnumerable<byte> as it does for byte[], you can make a custom JsonConveter like this:
public class EnumerableByteConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(IEnumerable<byte>).IsAssignableFrom(objectType);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
byte[] bytes = ((IEnumerable<byte>)value).ToArray();
writer.WriteValue(Convert.ToBase64String(bytes));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
byte[] bytes = Convert.FromBase64String(reader.Value.ToString());
if (objectType == typeof(byte[]))
{
return bytes;
}
return new List<byte>(bytes);
}
}
To use the converter, create an instance of JsonSerializerSettings and add an instance of the converter to the Converters collection. Then, pass the settings to SerializerObject() and DeserializeObject() methods. For example:
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.Converters.Add(new EnumerableByteConverter());
string json = JsonConvert.SerializeObject(obj, settings);
Here is a working round-trip demo. Note, however, that this converter doesn't (and can't) handle every possible IEnumerable<byte> that might be out there. For example, it won't work with ISet<byte> as currently implemented. If you need support for this or other additional types, you will need to extend the ReadJson method to handle that. I leave this to you.

qml/qt: How to get QMetaObject from QDeclarativeComponent

I need to receive QMetaObject for object that would be created by given QDeclarativeComponent in C++ code.
For a moment I'm solving that by using a temporary object:
const QMetaObject* metaObject(QDeclarativeComponent *component)
{
QObject* object = component->create();
const QMetaObject* result = object->metaObject();
delete object;
return result;
}
But creation of temporary object might be expensive operation and I'd like to avoid that.
Is there any way to receive QMetaObject from QDeclarativeComponent without creation of temporary object?
Qt 4.8 is being used.
Thanks in advance.

Bind to an exported value by pinvoke

I have a dll that exports not only functions, but also values. The dll I'm interested in is from the R Project (http://www.r-project.org/), it's in the R.dll that contains the r language runtime. The declaration in the header file is:
LibExtern SEXP R_GlobalEnv;
And when when I run dumpbin /exports I can see:
194 C1 00326C08 R_GlobalEnv
But I can't seem to find any examples of how to bind such a value from C# or F#. Can anyone enlightenment me to how I might get a reference to it?
I don't known if there is a way directly from C# or F#. But I think that a C++/CLI wrapper should work.
After Nick pointed me in the direction of R.NET I was able to look and see how they solved this problem.
First they use the win32 api to load the library (or equivalent on other platforms):
#if MAC || LINUX
private static IntPtr LoadLibrary(string filename)
{
const int RTLD_LAZY = 0x1;
if (filename.StartsWith("/"))
{
return dlopen(filename, RTLD_LAZY);
}
string[] searchPaths = (System.Environment.GetEnvironmentVariable(LibraryPath, EnvironmentVariableTarget.Process) ?? "").Split(Path.PathSeparator);
foreach (string directory in searchPaths)
{
string path = Path.Combine(directory, filename);
if (File.Exists(path))
{
return dlopen(path, RTLD_LAZY);
}
}
return IntPtr.Zero;
}
[DllImport(DynamicLoadingLibraryName)]
private static extern IntPtr dlopen([MarshalAs(UnmanagedType.LPStr)] string filename, int flag);
#elif WINDOWS
[DllImport("kernel32.dll")]
private static extern IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)] string lpFileName);
Then they use get GetProcAddress function from kernel32.dll (or equivalent on other platforms) to get the actual address of the variable:
#if MAC
[DllImport("libdl.dylib", EntryPoint = "dlsym")]
#elif LINUX
[DllImport("libdl.so", EntryPoint = "dlsym")]
#elif WINDOWS
[DllImport("kernel32.dll", EntryPoint = "GetProcAddress")]
#endif
private static extern IntPtr GetSymbolPointer(IntPtr handle, [MarshalAs(UnmanagedType.LPStr)] string symbol);

How do i determine if a ConstructorInfo object has an unmanaged parameter?

My app uses reflection to analyze c++/cli code in runtime.
I need to determine if a type has a constructor without unmanaged parameters (pointers and such), because i want later on to use:
ConstructorInfo constructorInfo;
// ...
var ret = constructorInfo.Invoke(BindingFlags..., null, myParameters, null);
if the constructor has a pointer to an unmanaged object as a parameter, there is a casting exception when i pass null to it.
So i how do i determine that? there is no IsManaged... and IsPointer does not help in this case.
It's not clear what your problem actually is, but here is a short demonstration program that shows passing null to a constructor that takes a pointer as an argument and detects it with IsPointer:
using System;
using System.Reflection;
namespace pointers
{
unsafe class Program
{
public Program(int* x)
{
Console.WriteLine("It worked!");
}
static void Main(string[] args)
{
ConstructorInfo[] c = typeof(Program).GetConstructors();
c[0].Invoke(BindingFlags.Default, null, new object[] { null }, null);
Console.WriteLine(c[0].GetParameters()[0].ParameterType.IsPointer);
}
}
}
It prints:
It worked!
True
Try testing if the parameter is a value type. null isn't a valid value for any value type, whether unmanaged pointer or simply int.

Resources