2

My code is reading from a c++ DLL, where one of the properties I need to read Coordinates is a type of Vector3f:

internal struct Entity
{
    public IntPtr Info;
    public IntPtr EntityModel;
    public Vector3f Coordinates;
}

Here is an example of how I read it:

var pAddressOfFunctionToCall = GetProcAddress(GetModuleHandle("Core.dll"), "GetTruckEntity");
var getEntity = (GetEntity)Marshal.GetDelegateForFunctionPointer(pAddressOfFunctionToCall, typeof(GetEntity));
var p = (Entity)Marshal.PtrToStructure(getEntity(), typeof(Entity));
Console.WriteLine(p.Coordinates.X);
Console.WriteLine(p.Coordinates.Y);
Console.WriteLine(p.Coordinates.Z);

The result is always 0 which is wrong so I assume the Class I made has not served as replacement to the one used by the c++.

Further looking into the DLL the Vector3f is as follow:

#ifndef VECTOR3_H
#define VECTOR3_H

#include <math.h>

#define Epsilon 0.00001f
#define EqualWithEpsilon(a,b) (((a) + Epsilon >= (b)) && ((a) - Epsilon <= (b)))
#define NullWithEpsilon(a) (((a) + Epsilon >= 0) && ((a) - Epsilon <= 0))

template <class T>
class Vector3
{
public:     // interface
    //! Default constructor (null vector).
        Vector3() {m_Data[0] = m_Data[1] = m_Data[2] = (T)0;}
        //! Constructor with three different values
        Vector3(T nx, T ny, T nz) {m_Data[0] = nx; m_Data[1] = ny; m_Data[2] = nz;}
        //! Constructor with the same value for all elements
        explicit Vector3(T n) {m_Data[0] = m_Data[1] = m_Data[2] = n;}
        //! Copy constructor
        Vector3(const Vector3<T>& other) {m_Data[0] = other.X(); m_Data[1] = other.Y(); m_Data[2] = other.Z();}

    //
    //  operators

    // operator: indexing
    const T&    operator[] (int i) const    { return m_Data[i]; }
    T&          operator[] (int i)      { return m_Data[i]; }

    // operators: math
    Vector3<T> operator-() const { return Vector3<T>(-X(), -Y(), -Z()); }

    Vector3<T>& operator=(const Vector3<T>& other) { X() = other.X(); Y() = other.Y(); Z() = other.Z(); return *this; }

    Vector3<T> operator+(const Vector3<T>& other) const { return Vector3<T>(X() + other.X(), Y() + other.Y(), Z() + other.Z()); }
    Vector3<T>& operator+=(const Vector3<T>& other) { X()+=other.X(); Y()+=other.Y(); Z()+=other.Z(); return *this; }
    Vector3<T> operator+(const T val) const { return Vector3<T>(X() + val, Y() + val, Z() + val); }
    Vector3<T>& operator+=(const T val) { X()+=val; Y()+=val; Z()+=val; return *this; }

    Vector3<T> operator-(const Vector3<T>& other) const { return Vector3<T>(X() - other.X(), Y() - other.Y(), Z() - other.Z()); }
    Vector3<T>& operator-=(const Vector3<T>& other) { X()-=other.X(); Y()-=other.Y(); Z()-=other.Z(); return *this; }
    Vector3<T> operator-(const T val) const { return Vector3<T>(X() - val, Y() - val, Z() - val); }
    Vector3<T>& operator-=(const T val) { X()-=val; Y()-=val; Z()-=val; return *this; }

    Vector3<T> operator*(const Vector3<T>& other) const { return Vector3<T>(X() * other.X(), Y() * other.Y(), Z() * other.Z()); }
    Vector3<T>& operator*=(const Vector3<T>& other) { X()*=other.X(); Y()*=other.Y(); Z()*=other.Z(); return *this; }
    Vector3<T> operator*(const T v) const { return Vector3<T>(X() * v, Y() * v, Z() * v); }
    Vector3<T>& operator*=(const T v) { X()*=v; Y()*=v; Z()*=v; return *this; }

    Vector3<T> operator/(const Vector3<T>& other) const { return Vector3<T>(X() / other.X(), Y() / other.Y(), Z() / other.Z()); }
    Vector3<T>& operator/=(const Vector3<T>& other) { X()/=other.X(); Y()/=other.Y(); Z()/=other.Z(); return *this; }
    Vector3<T> operator/(const T v) const { T i=(T)1.0/v; return Vector3<T>(X() * i, Y() * i, Z() * i); }
    Vector3<T>& operator/=(const T v) { T i=(T)1.0/v; X()*=i; Y()*=i; Z()*=i; return *this; }

    // sort in order X, Y, Z. Equality with rounding tolerance.
    bool operator<=(const Vector3<T>& other) const
    {
        return  (X()<other.X() || EqualWithEpsilon(X(), other.X())) ||
            (EqualWithEpsilon(X(), other.X()) && (Y()<other.Y() || EqualWithEpsilon(Y(), other.Y()))) ||
            (EqualWithEpsilon(X(), other.X()) && EqualWithEpsilon(Y(), other.Y()) && (Z()<other.Z() || EqualWithEpsilon(Z(), other.Z())));
    }

    // sort in order X, Y, Z. Equality with rounding tolerance.
    bool operator>=(const Vector3<T>&other) const
    {
        return  (X()>other.X() || EqualWithEpsilon(X(), other.X())) ||
            (EqualWithEpsilon(X(), other.X()) && (Y()>other.Y() || EqualWithEpsilon(Y(), other.Y()))) ||
            (EqualWithEpsilon(X(), other.X()) && EqualWithEpsilon(Y(), other.Y()) && (Z()>other.Z() || EqualWithEpsilon(Z(), other.Z())));
    }

    // sort in order X, Y, Z. Difference must be above rounding tolerance.
    bool operator<(const Vector3<T>&other) const
    {
        return  (X()<other.X() && !EqualWithEpsilon(X(), other.X())) ||
            (EqualWithEpsilon(X(), other.X()) && Y()<other.Y() && !EqualWithEpsilon(Y(), other.Y())) ||
            (EqualWithEpsilon(X(), other.X()) && EqualWithEpsilon(Y(), other.Y()) && Z()<other.Z() && !EqualWithEpsilon(Z(), other.Z()));
    }

    // sort in order X, Y, Z. Difference must be above rounding tolerance.
    bool operator>(const Vector3<T>&other) const
    {
        return  (X()>other.X() && !EqualWithEpsilon(X(), other.X())) ||
            (EqualWithEpsilon(X(), other.X()) && Y()>other.Y() && !EqualWithEpsilon(Y(), other.Y())) ||
            (EqualWithEpsilon(X(), other.X()) && EqualWithEpsilon(Y(), other.Y()) && Z()>other.Z() && !EqualWithEpsilon(Z(), other.Z()));
    }

    // use weak float compare
    bool operator==(const Vector3<T>& other) const
    {
        return this->equals(other);
    }

    bool operator!=(const Vector3<T>& other) const
    {
        return !this->equals(other);
    }

    // functions

    //! returns if this vector equals the other one, taking floating point rounding errors into account
    bool equals(const Vector3<T>& other) const
    {
        return EqualWithEpsilon(X(), other.X()) &&
            EqualWithEpsilon(Y(), other.Y()) &&
            EqualWithEpsilon(Z(), other.Z());
    }

    //
    // named functions

    // Set Value
    void Set(T x, T y, T z);

    // return length of vector
    float       GetLength() const;

    // return length 2D of vector
    float       GetLength2() const;

    // return length of vector squared
    float       GetSquareLength() const;

    // return length 2D of vector squared
    float       GetSquareLength2() const;

    // normalize a vector
    void        Normalize();

    // perform dot product
    T           Dot(const Vector3<T>&) const;

    // perform cross product(same as operator*=)
    Vector3<T>   Cross(const Vector3<T>&) const;

    // accessor functions
    T&          X()         { return m_Data[0]; }
    const T&    X() const   { return m_Data[0]; }
    T&          Y()         { return m_Data[1]; }
    const T&    Y() const   { return m_Data[1]; }
    T&          Z()         { return m_Data[2]; }
    const T&    Z() const   { return m_Data[2]; }

    const T*    GetData() const    { return m_Data; }

    // static usefull methods
    static const Vector3<T>& GetZero() {return ms_Zero;}
    static const Vector3<T>& GetBaseI() {return ms_BaseI;}
    static const Vector3<T>& GetBaseJ() {return ms_BaseJ;}
    static const Vector3<T>& GetBaseK() {return ms_BaseK;}

private:
    T m_Data[3];

    // static usefull vectors
    static Vector3<T> ms_Zero;
    static Vector3<T> ms_BaseI;
    static Vector3<T> ms_BaseJ;
    static Vector3<T> ms_BaseK;
};
typedef Vector3<float> Vector3f;

template <class T> Vector3<T> Vector3<T>::ms_Zero = Vector3<T>((T)0);
template <class T> Vector3<T> Vector3<T>::ms_BaseI = Vector3<T>((T)1, (T)0, (T)0);
template <class T> Vector3<T> Vector3<T>::ms_BaseJ = Vector3<T>((T)0, (T)1, (T)0);
template <class T> Vector3<T> Vector3<T>::ms_BaseK = Vector3<T>((T)0, (T)0, (T)1);

template <class T> inline T
Vector3<T>::Dot(const Vector3<T>& v) const
{
    return (T)(X() * v.X() + Y() * v.Y() + Z() * v.Z());
}

template <class T> inline Vector3<T>
Vector3<T>::Cross(const Vector3<T>& v) const
{
    return Vector3<T> (  Y() * v.Z() - Z() * v.Y(),
                          -(X() * v.Z() - Z() * v.X()),
                          X() * v.Y() - Y() * v.X());
}

template <class T> inline void
Vector3<T>::Set(T x, T y, T z) 
{
    m_Data[0] = x;
    m_Data[1] = y;
    m_Data[2] = z;
}

template <class T> inline float
Vector3<T>::GetLength() const
{
    return ::sqrt(GetSquareLength());
}


template <class T> inline float
Vector3<T>::GetSquareLength() const
{
    return (X() * X() + Y() * Y() + Z() * Z());
}

template <class T> inline float
Vector3<T>::GetLength2() const
{
    return ::sqrt(GetSquareLength2());
}


template <class T> inline float
Vector3<T>::GetSquareLength2() const
{
    return (X() * X() + Y() * Y());
}

template <class T> inline void
Vector3<T>::Normalize()
{
    float len = GetLength();

    if (len != 0) 
    {
        float f = 1.0f / len;

        *this *= f;
    }
}

#endif // VECTOR3_H

I've attempted to write a basic version of Vector3f as such just in a foul attempt to retrieve the X,Y,Z:

public struct Vector3f
{
    public Vector3f(float x, float y, float z)
    {
        X = x;
        Y = y;
        Z = z;
    }

    public float X;
    public float Y;
    public float Z;
}

But of course that doesn't seem to work.

  • How can I read the Coordinates into my application?

I've looked around but couldn't find how to make it work for a property with a class like that, I can read the other data from it but not Vector3f.

Guapo
  • 3,446
  • 9
  • 36
  • 63
  • Check this http://stackoverflow.com/questions/13275264/sharing-variables-between-c-sharp-and-c – Sanju Jul 16 '15 at 01:06
  • @Sanju I am afraid I am failing to understand that example, I can read the other 2 IntPtr as expected, except for the Vector3d, I don't have any access or power to change the c++ dll except for peeking at some of its properties/code. – Guapo Jul 16 '15 at 01:18
  • You should write a wrapper in C++/CLI, to create an intermediate dll that can talk to both C++ and your NET code. – thepirat000 Jul 16 '15 at 02:35
  • In case you do not have access to the change c++ dll , then I would second theprit000's suggestion of having an intermediate dll to talk to both of your dll's . You can create a .net library giving access to Co-ordinates in the original C++ dll and just import that in your c# code. Just in case required http://www.codeproject.com/Articles/14180/Using-Unmanaged-C-Libraries-DLLs-in-NET-Applicatio – Sanju Jul 16 '15 at 03:21
  • @thepirat000, Sanju so both of you are saying there is no way to do it for Vector3d as it stands or just that it would be easier having a intermediate solution to communicate? – Guapo Jul 16 '15 at 07:08
  • [Unmanaged C++ classes can't be marshaled](http://www.codeproject.com/Articles/18032/How-to-Marshal-a-C-Class) – thepirat000 Jul 16 '15 at 15:01
  • @thepirat000 I see, correct me if I am wrong, from that example I see the possibility of instantiate a class given it is a pointer, which would be a IntPtr at c# then from there it would be possible to call a function within it, so if Vector3f was a pointer with a function that out's x,y,z I would be technically able to retrieve x,y,z? – Guapo Jul 16 '15 at 17:01
  • Yes, but you'll be able to retrieve it *from unmanaged C++ code*, not from managed code. That's why I suggested to use a C++/CLI wrapper. – thepirat000 Jul 16 '15 at 17:16
  • @thepirat000 does that applies to enum types as well say for example `EntityType Type` I wouldnt be able to read it if my structure in c# uses a enum called EntityType but would if I make it an int within my structure? – Guapo Jul 16 '15 at 17:24
  • I'm not sure about enums, but I think if underlying values are the same it should work. – thepirat000 Jul 16 '15 at 17:47

1 Answers1

2

If you decide to go for the C++/CLI solution, this can probably help. What I suggest to do is:

  1. Create a common assembly in C#, containing the managed Vector3f public class.
  2. Create a C++/CLI wrapper assembly, and add a reference to both: the new common assembly and the unmanaged C++ library. Add code to convert an unmanaged Vector3 pointer to an instance of a managed Vector3f.
  3. In your existing managed assembly, add a reference to the two assemblies previously created, and use the wrapper to convert the vector.

Point 1 is trivial, for the point 2, your C++/CLI code should look like:

// Marshaler.h (in Marshaler.dll)
#include "OriginalVector3f.h"
#pragma once

using namespace System;

namespace UnmanagedNamespace {
    using namespace ManagedNamespace::Common;
    public ref class Marshaler
    {
    private:
    public:
        static Vector3f^ MarshalVector(IntPtr vectorPtr)
        {
            // Cast the IntPtr to the unmanaged pointer
            Vector3* unmanaged = static_cast<Vector3*>(vectorPtr.ToPointer());
            // Create a new managed object
            Vector3f^ managed = gcnew Vector3f();
            // Map  info
            managed->X = unmanaged->X;
            managed->Y = unmanaged->Y;
            managed->Z = unmanaged->Z;
            return managed;
        }
    };
}

Here I'm assuming the following:

  • The unmanaged Vector3 is defined on file "OriginalVector3f.h".
  • The managed Vector3f is on the namespace ManagedNamespace.Common

For the point 3, you should create a class implementing ICustomMarshaler, as shown here, but for simplicity, start by manually calling the MarshalVector method. For example:

ManagedNamespace.Common.Vector3f vector = UnmanagedNamespace.Marshaler.MarshalVector(Entity.Coordinates)

Assuming Entity.Coordinates is an IntPtr.

thepirat000
  • 12,362
  • 4
  • 46
  • 72
  • 1
    Thanks a lot for taking the time to write this basic sample, once I have some time I will try to test it. – Guapo Jul 16 '15 at 19:02
  • If I tell you it worked using `[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]float[] Pos`, would you believe me? :P – Guapo Jul 16 '15 at 23:40
  • Yes I think it's because of the `operator[] (int i) const { return m_Data[i]; }` that allows to marshal the class to a fixed length array. Glad you avoid using C++, but you will need it sooner or later :P – thepirat000 Jul 17 '15 at 01:36
  • 1
    Perfectly done, helped me too, exactly for the same purpose – Dharani Kumar Jul 17 '15 at 14:35
  • @thepirat000 I went to try your method but I am having 1 issue, since the managed `Vector3f` is a class `Vector3f^` returns an error saying `argument list for class template "Vector3f" is missing` – Guapo Jul 19 '15 at 23:41
  • @thepirat000 yes, inside the common dll – Guapo Jul 22 '15 at 10:47