How to return a managed Boolean from a C++/CLI assembly - c++-cli

I have a C++/CLI assembly that provides some managed classes that wrap native code. Is it possible to return a managed Boolean from methods in the managed classes?
For instance, if I have this method in one such class:
bool Func1() { return true; }
then, using ILSpy, the return type is marshaled as:
[return: MarshalAs(UnmanagedType.U1)]
public bool Func1()
{
return true;
}
I've also tried returning a System::Boolean:
System::Boolean Func2() { return true; }
but it also marshals using the same unmanaged type:
[return: MarshalAs(UnmanagedType.U1)]
public bool Func2()
{
return true;
}
Is it possible to return a managed bool without any marshaling?

Related

Map SQLDecimal property in NHibernate

I'm trying to read a decimal (38,16) from a SQL Server DB and struggling. After much reading around I'm trying to implement a custom type for SQL Decimal with the following code:
public class BigDecimal : IUserType
{
public bool Equals(object x, object y)
{
return object.Equals(x,y);
}
public int GetHashCode(object x)
{
return x.GetHashCode();
}
public object NullSafeGet(IDataReader rs, string[] names, object owner)
{
int index = rs.GetOrdinal(names[0]);
object result = rs.GetValue(index);
return result;
}
public void NullSafeSet(IDbCommand cmd, object value, int index)
{
//Not got here yet
}
public object DeepCopy(object value)
{
return value;
}
public object Replace(object original, object target, object owner)
{
return original;
}
public object Assemble(object cached, object owner)
{
return cached;
}
public object Disassemble(object value)
{
return value;
}
public SqlType[] SqlTypes { get { return new[] {SqlTypeFactory.Decimal}; } }
public Type ReturnedType { get { return typeof (SqlDecimal); } }
public bool IsMutable { get { return false; } }
}
but the output of rs.GetValue is a decimal not at SQLDecimal which causes an OverflowException.
The class looks like this:
public class Billy
{
public BigDecimal TheNumber {get;set;}
}
and the mapping looks like this:
public class BillyMap : ClassMap<Billy>
{
public BillyMap()
{
Map(b=>b.TheNumber).CustomType<BigDecimal>();
}
}
Please can someone tell me where I'm going wrong.
I think you need to cast the reader to SqlDataReader so you can access either GetSqlDecimal() or GetSqlValue(), as GetValue() will convert to a basic .Net Framework type. From 'https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqldatareader.getsqlvalue%28v=vs.110%29.aspx':
GetSqlValue returns data using the native SQL Server types. To retrieve data using the .NET Framework types, see GetValue.
In the end I made a something which performs a convert in the SQL and made it a Property Part and just use it on all the mapping files:
private const string DECIMAL_CONVERSION = "(CONVERT(decimal(28,6), [{0}]))";
private static string MapDecimalProperty(string fieldName)
{
return string.Format(DECIMAL_CONVERSION, fieldName.Trim('[',']'));
}
public static PropertyPart LongDecimal(this PropertyPart part, string fieldName)
{
return part.Formula(MapDecimalProperty(fieldName));
}
the on the mapping:
Map(ep => ep.BigDecimalField).EDWDecimal("[BigDecimalField]");
this works for now. I've informed the data architecture team that this is happening and they don't think that it will be a problem with any current data and will consider it for future developments.

Return string from c++ function pointer invoked from c#

I need to call a c++ callback function from c# that returns a String. When I try with the code below the application crashes hard (with a message saying that it may be due to a corruption of the heap).
Here's the c++ code:
static String^ CppFunctionThatReturnsString()
{
return gcnew String("From C++");
}
void main()
{
CSharp::CSharpFunction(IntPtr(CppFunctionThatReturnsString));
}
And here's the c# code:
public class CSharp
{
private delegate string CppFuncDelegate();
public static void CSharpFunction(IntPtr cppFunc)
{
var func = (CppFuncDelegate)Marshal.GetDelegateForFunctionPointer(cppFunc, typeof(CppFuncDelegate));
func(); // Crash
}
}
Do I have to do some kind of marshaling magic with the string before returning it?
Why are you using function pointers in the first place? Just pass an instance of the delegate to the C# code:
C++:
static String^ CppFunctionThatReturnsString()
{
return gcnew String("From C++");
}
void main()
{
CSharp::CSharpFunction(new CSharp::CppFuncDelegate(CppFuncThatReturnsString));
}
C#:
public class CSharp
{
private delegate string CppFuncDelegate();
public static void CSharpFunction(CppFuncDelegate d)
{
d();
}
}
I think you may need to put CppFuncThatReturnsString inside a class.
I found the answer on this ten year old page.
c++:
static const char* __stdcall CppFunctionThatReturnsString()
{
return "From C++";
}
void main()
{
CSharp::CSharpFunction(IntPtr(CppFunctionThatReturnsString));
}
c#:
public class CSharp
{
private delegate IntPtr CppFuncDelegate();
public static void CSharpFunction(IntPtr cppFunc)
{
var func = (CppFuncDelegate)Marshal.GetDelegateForFunctionPointer(cppFunc, typeof(CppFuncDelegate));
Marshal.PtrToStringAnsi(func());
}
}
That is, pass it as an IntPtr and marshal it into a string on the C# side.

SQL 2008 HierarchyID support in NHibernate

Searched various NHibernate lists and haven't come up with a definitive answer. The SQL2008 dialect doesn't appear to have support for the HierarchyID data type - new date and time types only.
Does anyone have a good implementation or an effective workaround? I'd really like to leverage HierarchyID in a new app of mine. Support for this interesting and powerful data type is sorely lacking in MS's own tools so I'm not shocked that NHibernate doesn't have support.
There are some approaches out there that I haven't delved into yet. Wondering if anyone has some experience in what works, what is more performant, etc.'
Full disclosure: I'm working with Castle ActiveRecord but this seems like an NHibernate issue.
I've given Needles' answer a test run. It's a very good answer but there are some changes needed to make it function (at least in .NET 4). Here's what I've come up with for my project:
Update: the following code can be download over at GitHub and will be updated there. NHiberntate.HierarchyId.UserType
SqlHierarchyId IUserType
namespace NHibernate.UserTypes
{
using SqlTypes;
using System;
using System.Data;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Types;
public class HierarchyId : IUserType
{
#region Properties
public SqlType[] SqlTypes
{
get { return new[] { NHibernateUtil.String.SqlType }; }
}
public Type ReturnedType
{
get { return typeof(SqlHierarchyId); }
}
public bool IsMutable
{
get { return true; }
}
#endregion Properties
#region Methods
new public bool Equals(object x, object y)
{
if (ReferenceEquals(x, y)) return true;
if (x == null || y == null) return false;
return x.Equals(y);
}
public int GetHashCode(object x)
{
return x.GetHashCode();
}
public object NullSafeGet(IDataReader rs, string[] names, object owner)
{
object prop1 = NHibernateUtil.String.NullSafeGet(rs, names[0]);
if (prop1 == null) return null;
return SqlHierarchyId.Parse(new SqlString(prop1.ToString()));
}
public void NullSafeSet(IDbCommand cmd, object value, int index)
{
if (value == null)
((IDataParameter)cmd.Parameters[index]).Value = DBNull.Value;
else if (value is SqlHierarchyId)
((IDataParameter)cmd.Parameters[index]).Value = ((SqlHierarchyId)value).ToString();
}
public object DeepCopy(object value)
{
if (value == null) return null;
return SqlHierarchyId.Parse(((SqlHierarchyId)value).ToString());
}
public object Replace(object original, object target, object owner)
{
return DeepCopy(original);
}
public object Assemble(object cached, object owner)
{
return DeepCopy(cached);
}
public object Disassemble(object value)
{
return DeepCopy(value);
}
#endregion Methods
}
}
Mapping
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="DataLayer" namespace="NHibernate.Map">
<class name="NHibernate.Map.OrganizationUnit, DataLayer" table="`orgunit`">
<property name="HierarchyId" column="`ou_hid`" type="NHibernate.UserTypes.HierarchyId, DataLayer" />
...
</class>
</hibernate-mapping>
Object with HierarchyId
namespace NHibernate.Map
{
using Microsoft.SqlServer.Types;
public class OrganizationUnit
{
#region Fields
private SqlHierarchyId _hierarchyId;
...
#endregion Fields
#region Properties
public virtual SqlHierarchyId HierarchyId
{
get { return _hierarchyId; }
set { _hierarchyId = value; }
}
...
#endregion Properties
}
}
Disclaimer: Im not an NHibernate expert, however, we are using it with Fluent in an upcoming project which uses SQL Server 2008 R2 and Hierarchy IDs. The code below is what we are using currently on our dev environment and is not fully tested/refined. I copied the majority of the code from elsewhere (sorry I lost the link!)
You need to create a User Defined Type and then use it in your mappings. The mapping below is Fluent, Im not aware how to do it using ActiveRecord but Im guessing it should be similar!
User Defined Type
namespace YourNamespace {
public class SqlHierarchyIdUserType : IUserType {
public bool Equals(object x, object y) {
if(ReferenceEquals(x, y))
return true;
if(x == null || y == null)
return false;
return x.Equals(y);
}
public int GetHashCode(object x) {
return x.GetHashCode();
}
public object NullSafeGet(IDataReader rs, string[] names, object owner) {
object prop1 = NHibernateUtil.String.NullSafeGet(rs, names[0]);
if(prop1 == null)
return null;
return SqlHierarchyId.Parse(new SqlString(prop1.ToString()));
}
public void NullSafeSet(IDbCommand cmd, object value, int index) {
if(value == null) {
((IDataParameter)cmd.Parameters[index]).Value = DBNull.Value;
} else {
if(value is SqlHierarchyId) {
SqlHierarchyId hId = (SqlHierarchyId)value;
((IDataParameter)cmd.Parameters[index]).Value = hId.ToString();
}
}
}
public object DeepCopy(object value) {
if(value == null)
return null;
var sourceTarget = (SqlHierarchyId)value;
SqlHierarchyId copy = SqlHierarchyId.Parse(sourceTarget.ToString());
return copy;
}
public object Replace(object original, object target, object owner) {
return DeepCopy(original);
}
public object Assemble(object cached, object owner) {
return DeepCopy(cached);
}
public object Disassemble(object value) {
return DeepCopy(value);
}
public SqlType[] SqlTypes {
get { return new[] { NHibernateUtil.String.SqlType }; }
}
public Type ReturnedType {
get { return typeof(SqlHierarchyId); }
}
public bool IsMutable {
get { return true; }
}
}
}
Fluent Mapping
Map(e => e.YourSqlHierarchyIdProperty)
.Column("YourSqlHierarchyIdFieldName")
.CustomType<SqlHierarchyIdUserType>();
Reading this post:
Castle ActiveRecord: Map to IUserType wihtin Class in C#
ActiveRecord uses a [Property] attribute to map User Defined Types. So for you it would look something like this:
public class YourDataObject {
[Property(ColumnType="YourNamespace.SqlHierarchyIdUserType, YourNamespace")
public virtual SqlHierarchyId YourSqlHierarchyIdProperty;
}
Hope it helps!

Unit testing NHibernate w/ SQLite and DateTimeOffset mappings

Porting over an application to use NHibernate from a different ORM.
I've started to put in place the ability to run our unit tests against an in memory SQLite database. This works on the first few batches of tests, but I just hit a snag. Our app would in the real world be talking to a SQL 2008 server, and as such, several models currently have a DateTimeOffset property. When mapping to/from SQL 2008 in non-test applications, this all works fine.
Is there some mechanism either in configuring the database or some other facility so that when I use a session from my SQLite test fixture that the DateTimeOffset stuff is "auto-magically" handled as the more platform agnostic DateTime?
Coincidentally, I just hit this problem myself today :) I haven't tested this solution thoroughly, and I'm new to NHibernate, but it seems to work in the trivial case that I've tried.
First you need to create an IUserType implementation that will convert from DateTimeOffset to DateTime. There's a full example of how to create a user type on the Ayende blog but the relevant method implementations for our purposes are:
public class NormalizedDateTimeUserType : IUserType
{
private readonly TimeZoneInfo databaseTimeZone = TimeZoneInfo.Local;
// Other standard interface implementations omitted ...
public Type ReturnedType
{
get { return typeof(DateTimeOffset); }
}
public SqlType[] SqlTypes
{
get { return new[] { new SqlType(DbType.DateTime) }; }
}
public object NullSafeGet(IDataReader dr, string[] names, object owner)
{
object r = dr[names[0]];
if (r == DBNull.Value)
{
return null;
}
DateTime storedTime = (DateTime)r;
return new DateTimeOffset(storedTime, this.databaseTimeZone.BaseUtcOffset);
}
public void NullSafeSet(IDbCommand cmd, object value, int index)
{
if (value == null)
{
NHibernateUtil.DateTime.NullSafeSet(cmd, null, index);
}
else
{
DateTimeOffset dateTimeOffset = (DateTimeOffset)value;
DateTime paramVal = dateTimeOffset.ToOffset(this.databaseTimeZone.BaseUtcOffset).DateTime;
IDataParameter parameter = (IDataParameter)cmd.Parameters[index];
parameter.Value = paramVal;
}
}
}
The databaseTimeZone field holds a TimeZone which describes the time zone that is used to store values in the database. All DateTimeOffset value are converted to this time zone before storage. In my current implementation it is hard-coded to the local time zone, but you could always define an ITimeZoneProvider interface and have it injected into a constructor.
To use this user type without modifying all my class maps, I created a Convention in Fluent NH:
public class NormalizedDateTimeUserTypeConvention : UserTypeConvention<NormalizedDateTimeUserType>
{
}
and I applied this convention in my mappings, as in this example (the new NormalizedDateTimeUserTypeConvention() is the important part):
mappingConfiguration.FluentMappings.AddFromAssembly(Assembly.GetExecutingAssembly())
.Conventions.Add(
PrimaryKey.Name.Is(x => x.EntityType.Name + "Id"),
new NormalizedDateTimeUserTypeConvention(),
ForeignKey.EndsWith("Id"));
Like I said, this isn't tested thoroughly, so be careful! But now, all I need to do is to alter one line of code (the fluent mappings specification) and I can switch between DateTime and DateTimeOffset in the database.
Edit
As requested, the Fluent NHibernate configuration:
To build a session factory for SQL Server:
private static ISessionFactory CreateSessionFactory(string connectionString)
{
return Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008.ConnectionString(connectionString))
.Mappings(m => MappingHelper.SetupMappingConfiguration(m, false))
.BuildSessionFactory();
}
For SQLite:
return Fluently.Configure()
.Database(SQLiteConfiguration.Standard.InMemory)
.Mappings(m => MappingHelper.SetupMappingConfiguration(m, true))
.ExposeConfiguration(cfg => configuration = cfg)
.BuildSessionFactory();
Implementation of SetupMappingConfiguration:
public static void SetupMappingConfiguration(MappingConfiguration mappingConfiguration, bool useNormalizedDates)
{
mappingConfiguration.FluentMappings
.AddFromAssembly(Assembly.GetExecutingAssembly())
.Conventions.Add(
PrimaryKey.Name.Is(x => x.EntityType.Name + "Id"),
ForeignKey.EndsWith("Id"));
if (useNormalizedDates)
{
mappingConfiguration.FluentMappings.Conventions.Add(new NormalizedDateTimeUserTypeConvention());
}
}
Another proposal which allow to keep track of the original timezone offset:
public class DateTimeOffsetUserType : ICompositeUserType
{
public string[] PropertyNames
{
get { return new[] { "LocalTicks", "Offset" }; }
}
public IType[] PropertyTypes
{
get { return new[] { NHibernateUtil.Ticks, NHibernateUtil.TimeSpan }; }
}
public object GetPropertyValue(object component, int property)
{
var dto = (DateTimeOffset)component;
switch (property)
{
case 0:
return dto.UtcTicks;
case 1:
return dto.Offset;
default:
throw new NotImplementedException();
}
}
public void SetPropertyValue(object component, int property, object value)
{
throw new NotImplementedException();
}
public Type ReturnedClass
{
get { return typeof(DateTimeOffset); }
}
public new bool Equals(object x, object y)
{
if (ReferenceEquals(x, null) && ReferenceEquals(y, null))
return true;
if (ReferenceEquals(x, null) || ReferenceEquals(y, null))
return false;
return x.Equals(y);
}
public int GetHashCode(object x)
{
return x.GetHashCode();
}
public object NullSafeGet(IDataReader dr, string[] names, ISessionImplementor session, object owner)
{
if (dr.IsDBNull(dr.GetOrdinal(names[0])))
{
return null;
}
var dateTime = (DateTime)NHibernateUtil.Ticks.NullSafeGet(dr, names[0], session, owner);
var offset = (TimeSpan)NHibernateUtil.TimeSpan.NullSafeGet(dr, names[1], session, owner);
return new DateTimeOffset(dateTime, offset);
}
public void NullSafeSet(IDbCommand cmd, object value, int index, ISessionImplementor session)
{
object utcTicks = null;
object offset = null;
if (value != null)
{
utcTicks = ((DateTimeOffset)value).DateTime;
offset = ((DateTimeOffset)value).Offset;
}
NHibernateUtil.Ticks.NullSafeSet(cmd, utcTicks, index++, session);
NHibernateUtil.TimeSpan.NullSafeSet(cmd, offset, index, session);
}
public object DeepCopy(object value)
{
return value;
}
public bool IsMutable
{
get { return false; }
}
public object Disassemble(object value, ISessionImplementor session)
{
return value;
}
public object Assemble(object cached, ISessionImplementor session, object owner)
{
return cached;
}
public object Replace(object original, object target, ISessionImplementor session, object owner)
{
return original;
}
}
Fluent NNibernate convention from DateTimeOffset ICompositeUserType would be:
public class DateTimeOffsetTypeConvention : IPropertyConvention, IPropertyConventionAcceptance
{
public void Accept(IAcceptanceCriteria<IPropertyInspector> criteria)
{
criteria.Expect(x => x.Type == typeof(DateTimeOffset));
}
public void Apply(IPropertyInstance instance)
{
instance.CustomType<DateTimeOffsetUserType>();
}
}
As i'm short on rep I can not add this as a comment to the accepted answer, but wanted to add some additional information I found while implementing the solution in the accepted answer. I too was getting the error that the dialect doesn't support DateTimeOffset when calling schema export. After adding in log4net logging support, I was able to figure out that my properties that were of type DateTimeOffset? were not being handled by the convention. That is, the convention wasn't being applied to nullable DateTimeOffset properties.
To solve this I created a class which derrives from NormalizedDateTimeUserType and overrides the ReturnedType property (had to mark the original as virtual). Then I created a second UserTypeConvention for my derrived class, and finally added the second convention to my configuration.

How to implement IEnumerable<int> from this c# code?

I'm trying to convert this c# code into c++/cli code:
class MyRange : IEnumerable<int>
{
public IEnumerator<int> GetEnumerator() { return null; }
IEnumerator IEnumerable.GetEnumerator() { return null; }
}
Here is my attempt:
namespace Tests {
public ref class MyRange : System::Collections::Generic::IEnumerable<int> {
private:
virtual System::Collections::IEnumerator^ GetEnumerator() = System::Collections::IEnumerable::GetEnumerator {
return nullptr;
}
virtual System::Collections::Generic::IEnumerable<int>^ GetEnumerator() {
return nullptr;
}
};
}
It's giving me so many errors (like 20), that I don't even think it's worth putting them here.
I've googled it all and it seems like a lot of people are having the same problem as me.
Ok, after a lot of fighting found some working code:
namespace Tests {
ref class MyCollection : public Generic::IEnumerable<int>
{
public:
virtual System::Collections::IEnumerator^ GetEnumeratorNonGeneric() = System::Collections::IEnumerable::GetEnumerator
{
return GetEnumerator();
}
virtual Generic::IEnumerator<int>^ MyCollection::GetEnumerator()
{
return nullptr;
}
};
}
It's often helpful to disassemble your C# in Reflector using Managed C++ as the target language and then from there do the translation to C++/CLI.