I'm trying to abstract marshaling IntPtr to and from structs (actually class, so it's reference type) of data shared with native code.
I have this helper class:
class NativeStruct<StructType> : IDisposable
{
public NativeStruct()
{
_DataPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(StructType)));
Marshal.PtrToStructure(_DataPtr, _Data);
}
public NativeStruct(IntPtr dataPtr)
{
_DataPtr = dataPtr;
Marshal.PtrToStructure(_DataPtr, _Data);
}
void IDisposable.Dispose()
{
Marshal.StructureToPtr(_Data, _DataPtr, true);
}
public StructType Data { get { return _Data; } }
IntPtr _DataPtr;
public StructType _Data;
}
Is there anyway to make this code implicit:
using (Shared_s data = new Toolbox.NativeStruct<DataStruct>(myIntPtr).Data)
{
data.someMember = someValue;
}
Some way to change this to
EditStruct(DataStruct, myIntPtr)
{
data.someMember = someValue;
}
In C++ I would have used a macro like
#define EditStruct(StructType, IntPtr) using \
(Shared_s data = new Toolbox.NativeStruct<StructType>(IntPtr).data)
The closest thing you can do here is use lambda expressions:
EditStruct<DataStruct>(myIntPtr, data =>
{
data.someMember = someValue;
});
Definition of EditStruct():
void EditStruct<TStruct>(IntPtr dataPtr, Action<TStruct> action)
where TStruct : struct
{
using (var s = new Toolbox.NativeStruct<TStruct>(dataPtr))
{
action(s.Data);
}
}
C# 2.0 version:
EditStruct<DataStruct>(myIntPtr, delegate(DataStruct data)
{
data.someMember = someValue;
});
void EditStruct<TStruct(IntPtr dataPtr, Action<StructType> action) where TStruct : struct
{
using (Toolbox.NativeStruct<StructType> s = new Toolbox.NativeStruct<StructType>(dataPtr))
{
action(s.Data);
}
}
Related
Hello this question is a continuation of this question. Provided below is a short version of the code that is loading the data into the structure. The HDF5DotNet library is found here using the 32 bit version with vs2015. My question is, will MyStruct leak memory when SomeCallerClass call HDF5GetSubGroups?
using HDF5DotNet;
namespace CommonClass
{
public class HDF5FileReader
{
public static List<string> HDF5GetSubGroups(string filePath)
{
var returnList = new List<string>();
//... some HDF5 instantiating -- commented out for brevity
var myStruct = new MyStruct[numberOfThingsToRead];
H5A.read(someAttribute, someAttributeType, new H5Array<MyStruct>(myStruct));
string myStructVariableString = IntPtrToString(myStruct[0].intPtr);
returnList.Add(myStructVariableString);
//... closing some HDF5 instantiating -- commented out for brevity
return returnList
}
private string IntPtrToString(IntPtr ipp)
{
string s = Marshal.PtrToStringAnsi(ipp)
//need to free ipp?
return s;
}
}
public struct MyStruct
{
public Int int1;
public IntPtr intPtr;
}
}
namespace CallerClass
{
public class SomeCallerClass()
{
string someFilePath = "Path\To\HDF5File.h5"
var stringList = HDF5FileReader.HDF5GetSubGroups(someFilePath)
//Do Something with StringList -- add to observablecollection
}
public class UnloadingSomeCallerClass()
{
//Clear the observablecollection
}
}
I'm trying to marshal a delegate to a function pointer. The return type of the delegate is an array of strings while the return type of the function pointer is a char**
The code below throws Invalid managed/unmanaged type combination exception. How do I solve this?
// unmanaged code
typedef char** (*MyFuncPtr)(void);
class __declspec(dllexport) MyUnmanagedClass{
private:
MyFuncPtr mFunPtr
public:
MyUnmanagedClass(MyFuncPtr funPtr)
{
mFunPtr = funPtr;
}
char* Func1()
{
// callback
mFunPtr();
return "something";
}
};
// managed wrapper (CLI)
public delegate cli::array<String^>^ MyDelegate();
public class MyCliClass{
private:
MyDelegate mDel;
public:
MyCliClass(MyDelegate del)
{
mDel = del;
}
String^ Func2()
{
MyFuncPtr funPtr = static_cast<MyFuncPtr>(Marshal::GetFunctionPointerForDelegate(mDel).ToPointer());
MyUnmanagedClass* muc = new MyUnmanagedClass(funPtr);
char* retValPtr = muc->Func1();
return context->marshal_as<String^>(retValPtr);
}
};
// managed client (C#)
class Program
{
static void Main( string[] args )
{
MyCliClass mcc = new MyCliClass(Func3);
mcc.Func2();
}
static string[] Func3()
{
return new[] { "Some 1", "Some 2" };
}
}
This is how I finally solved it...
// unmanaged code
class __declspec(dllexport) NativeInterface
{
public:
vector<string> NativeInterfaceFunc() = 0;
};
class __declspec(dllexport) UnmanagedClass{
private:
NativeInterface* mNativeInterface
public:
UnmanagedClass(NativeInterface* nativeInterface)
{
mNativeInterface = nativeInterface;
}
string UnmanagedClassFunc()
{
vector<string> values = mNativeInterface->NativeInterfaceFunc();
ostringstream oss;
copy(values.begin(), values.end(), ostream_iterator<string>(oss, ","));
return oss.str();
}
};
// managed wrapper (CLI)
class NativeClass : NativeInterface
{
public:
NativeInterface(ManagedInterface^ managedInterface)
{
mManagedInterface = managedInterface
};
vector<string> NativeInterfaceFunc()
{
IEnumerable<String^> managedReturn = mManagedInterface->ManagedInterfaceFunc();
vector<string> nativeReturn;
marshal_context^ context = new marshal_context();
for each(String^ manRet in managedReturn)
{
nativeReturn.push_back(context->marshal_as<const char*>(manRet))
}
delete context;
return nativeReturn;
};
private:
gcroot<ManagedInterface^> mManagedInterface;
};
public ref class CliClass{
public:
CliClass(ManagedInterface^ managedInterface)
{
mNativeInterface = new NativeClass(managedInterface);
mUnmanagedClass = new UnmanagedClass(mNativeInterface);
}
~CliClass()
{
delete mUnmanagedClass;
delete mNativeInterface;
}
String^ CliFunc()
{
string nativeReturn = mUnmanagedClass->UnmanagedClassFunc();
return gcnew String(nativeReturn.c_str());
}
private:
UnmanagedClass mUnmanagedClass;
NativeInterface mNativeInterface;
};
// managed interface (C#)
interface ManagedInterface
{
IEnumerable<string> ManagedInterfaceFunc();
}
// managed interface implementation(C#)
public class ManagedClass : ManagedInterface
{
public IEnumerable<string> ManagedInterfaceFunc()
{
return new List<string> { "Some 1", "Some 2" };
}
}
// managed client (C#)
class Program
{
static void Main( string[] args )
{
MyCliClass mcc = new MyCliClass(new ManagedClass());
Console.WriteLine(mcc.CliFunc());
mcc.Dispose();
}
}
This is a basically a class library project which is somehow exposed as a WCF service. The code below is a part of the Data Access Layer. 'db' is an object of a DataContext class. To save a file, we do the following-
public static Guid SaveFile(FileDetails fileDetails)
{
System.Nullable<Guid> id = null;
SystemDataContext.UsingWrite(db =>
{
db.SaveFileData(fileDetails.RunId, fileDetails.FileData, fileDetails.FileExtension, ref id);
});
return id ?? Guid.Empty;
}
Then, the below would execute-
public static void UsingWrite(Action<SoftCashCreditDBDataContext> action)
{
using (var context = new SystemDataContext())
{
try
{
action(context.Write);
}
catch (Exception ex)
{
DataAccessExceptionHandler.HandleExcetion(ex, Config.DataLayerPolicy);
}
}
}
public SystemDataContext()
{
if (_stack == null)
{
_stack = new Stack<SystemDataContext>();
this.Depth = 1;
this.Read = new SoftCashCreditDBDataContext(Config.ReadDatabaseConnection);
this.Write = new SoftCashCreditDBDataContext(Config.WriteDatabaseConnection);
}
else
{
var parent = _stack.Peek();
/// Increment level of node.
this.Depth = parent.Depth + 1;
/// Copy data context from the parent
this.Read = parent.Read;
this.Write = parent.Write;
}
_stack.Push(this);
}
public int Depth { get; private set; }
public bool IsRoot { get { return this.Depth == 1; } }
[ThreadStatic]
private static Stack<SystemDataContext> _stack = null;
public SoftCashCreditDBDataContext Read { get; private set; }
public SoftCashCreditDBDataContext Write { get; private set; }
#region IDisposable Members
public void Dispose()
{
var context = _stack.Pop();
if (context.IsRoot == true)
{
context.Read.Dispose();
context.Write.Dispose();
_stack = null;
}
}
#endregion
}
They have implemented LINQ to SQL here, and created a DBContext class. The 'SaveFileData()' method is actually part of that class, where it just calls an SP inside to save the file.
What I did not follow-
What exactly does the call to UsingWrite() do here? What is passed to the 'Action action' parameter, and what is it doing?
I understand your confusion. They use 2 delegates.
This is passed to the action parameter:
db =>
{
db.SaveFileData(fileDetails.RunId, fileDetails.FileData, fileDetails.FileExtension, ref id);
}
So when UsingWrite is called, the SoftCashCreditDBDataContext delegate which was set in the Write delegate will call SaveFileData.
A simplified example to help you understand Action:
public void Main()
{
Test(x => Debug.Write(x));
}
private void Test(Action<string> testAction)
{
testAction("Bla");
}
This function will call Debug.Write with the argument x, which is a string that is passed to the test action function.
I would like to be able to present a choice to the user - whether to use 16bit indices (in OpenGL) or 32bit indices. In C++, I'd probably just create an alias for int or short, but I don't seem to have the option in C#. Basically what I'm going for can be summed up in the class below:
using System;
namespace Something
{
public class Conditional
{
public Conditional(Boolean is16Bit)
{
if (is16Bit)
{
SOMETYPE is Int16
}
else
{
SOMETYPE is Int32
}
}
private List<SOMETYPE> _something;
}
}
The aliasing (if it can be done) would be vastly better - I just don't want to force anyone using this code into writing #define statements, is that possible?
Thanks
Seems like you could use a generic for this:
namespace Something
{
public class Conditional<T>
{
private List<T> _something = new List<T>();
private Conditional()
{
// prevents instantiation except through Create method
}
public Conditional<T> Create()
{
// here check if T is int or short
// if it's not, then throw an exception
return new Conditional<T>();
}
}
}
And to create one:
if (is16Bit)
return Conditional<short>.Create();
else
return Conditional<int>.Create();
You can use an interface and a factory, something like this:
public interface IConditional
{
void AddIndex(int i);
}
private class Conditional16 : IConditional
{
List<Int16> _list = new List<Int16>();
public void AddIndex(int i)
{
_list.Add((short)i);
}
}
private class Conditional32 : IConditional
{
List<Int32> _list = new List<Int32>();
public void AddIndex(int i)
{
_list.Add(i);
}
}
public static class ConditionalFactory
{
public static IConditional Create(bool is16Bit)
{
if (is16Bit)
{
return new Conditional16();
}
else
{
return new Conditional32();
}
}
}
Your code (and callers of it) can do everything against IConditional without caring which of the concrete representations it is.
This code:
public interface IInter { }
public class Concrete : IInter { /*... body ...*/ }
var t = (List<IInter>)new List<Concrete>();
Yields this error:
Cannot convert type 'System.Collections.Generic.List<Concrete>' to 'System.Collections.Generic.List<IInter>'
Why is it ? how do I overcome it ? my goal is this:
var t = new List<List<IInter>>()
{
new List<ConcreteA>(){/* ... data ... */},
new List<ConcreteB>(){/* ... data ... */},
// ...
new List<ConcreteX>(){/* ... data ... */},
};
Edit:
Thanks for all your help. Ahh, I kinda abstracted things, to make it easier to read... but my real problem is this:
public class SingletonFactory<T> where T : IToken
{
private SingletonFactory() { }
private static SingletonFactory<T> _instance = new SingletonFactory<T>();
public static SingletonFactory<T> Instance { get { return _instance; } }
public T Produce(int position) { return (T)Activator.CreateInstance(typeof(T), position); }
public T Produce(int position, string token) { return (T)Activator.CreateInstance(typeof(T), position, token); }
}
And then:
var keywords = new Dictionary<string, SingletonFactory<IToken>>()
{
{ "abc", SingletonFactory<Abc>.Instance },
{ "xyz", SingletonFactory<Xyz>.Instance },
{ "123", SingletonFactory<Num>.Instance }
};
So I guess it's much more complicated...
I'm using c# 4.0
var res = new List<Concrete>().Cast<IInter>().ToList();
A List<Concrete> is not a List<IInter> although Concrete implements IInter.
You can use:
List<IInter> t = ConcreteList.Cast<IInter>().ToList();
Covariance and Contravariance FAQ
Because you cannot assign a List<ConcreteA> to a List<IInter>, otherwise you could do this:
concreteAList = newList<ConcreteA>();
List<Inter> interList = concreteAList as List<Inter>; // seems harmless
interList.Add(new ConcreteB()); // not allowable
You could do:
var t = new List<List<IInter>>()
{
new List<IInter>(){/* ... fill with ConcreteAs ... */},
new List<IInter>(){/* ... fill with ConcreteBs ... */},
// ...
new List<IInter>(){/* ... fill with ConcreteXs ... */},
};
But I don't know if that accomplishes what you want.
This is called covariance and C# supports covariance (and contravariance) on generic interfaces and not generic classes. This is the following works but your example does not:
IEnumerable<IInter> e = new List<Concrete>();
ICollection<IInter> c = new List<Concrete>();
Covariance is also supported on arrays:
IInter[] a = new Concrete[3];
Make a covrariant interface for the methods you need to access from SingletonFactory<T>. This will ensure that your type T is output type safe.
public interface IToken { }
public class Abc : IToken { }
public class Xyz : IToken { }
public class Num : IToken { }
public interface ISingletonFactory<out T> where T : IToken
{
T Produce(int position);
T Produce(int position, string token);
}
public class SingletonFactory<T> : ISingletonFactory<T> where T : IToken
{
private SingletonFactory() { }
private static SingletonFactory<T> _instance = new SingletonFactory<T>();
public static SingletonFactory<T> Instance { get { return _instance; } }
public T Produce(int position) { return (T)Activator.CreateInstance(typeof(T), position); }
public T Produce(int position, string token) { return (T)Activator.CreateInstance(typeof(T), position, token); }
}
var keywords = new Dictionary<string, ISingletonFactory<IToken>>()
{
{ "abc", SingletonFactory<Abc>.Instance },
{ "xyz", SingletonFactory<Xyz>.Instance },
{ "123", SingletonFactory<Num>.Instance }
};
One way is:
IEnumerable<IInter> t = new List<Concrete>();
Another is:
List<IInter> t = new List<Concrete>().ConvertAll(x => (IInter) x);
Edit, adding better sample to show that it works (the test passes):
[Test]
public void CovarianceTest()
{
var concrete = new Concrete();
IEnumerable<IInter> t = new List<Concrete> { concrete };
Assert.IsTrue(new[] { concrete }.SequenceEqual(t));
List<IInter> t2 = new List<Concrete> { concrete }.ConvertAll(x => (IInter)x);
Assert.IsTrue(new[] { concrete }.SequenceEqual(t2));
}