[Connect Update 16 C# + C++] Calling methods in DLL (p/invoke)

Ok, so i have some questions regarding the p/invoking of methods out of a dll:

Does this look right for dllimport if both DLLs exist in the same folder? I am using an example project to test with. Both projects work separately, now I just want to understand how i can call a method from managed to native.

 class UstnNative
    {
        [DllImport("TextUpdateCpp.dll", CharSet = CharSet.Unicode)]
        private static extern int createALine(DPoint3d dPoint);

        public static void CreateLine()
        {
            createALine(new DPoint3d(0, 0, 0));
        }
    }

Here's the cpp file method:

void createALine(DPoint3dCR basePt)

{

	EditElementHandle eeh;

	DSegment3d seg;

	seg.Init(basePt, DPoint3d::From(basePt.x + g_1mu * 2, basePt.y + g_1mu));

	ICurvePrimitivePtr pCurve = ICurvePrimitive::CreateLine(seg);

	DraftingElementSchema::ToElement(eeh, *pCurve, nullptr, ACTIVEMODEL->Is3d(), *ACTIVEMODEL);

	eeh.AddToModel();

}

Can I pass in the DPoint3d from GeometryNET.DPoint3d to native Bentley.DPoint3D? Or do i need to use doubles and create a new point in native?

  • Hi Viktor,

    fortunately the consuming native dll in managed code is common NET Framework feature, so it is well and in detail described in Microsoft NET Framework documentation. And because it's is complex and often used feature, there are plenty of discussions and tutorials available.

    I recommend to see e.g. general description and also description specifically about structures.

    With regards,

      Jan

  • Hi Viktor,

    Yes you can pass the DPoint directly as you are doing, it will be marshalled by the System.Runtime.InteropServices.Marshal, which will make a copy of the data to pass through.

    You can also pass pointers to the data which will not create copies. (Without having to enable the unsafe compile flag on the C# side)

    For instance if you had a C function to extract a Z value from a surface (receiving a point pointer and a surface Id):

    int FnSurfaceZ(Dpoint3d* Pt, int I);

    You can call it like this:

    [DllImport("xxxx.dll", EntryPoint = "FnSurfaceZ",
                CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Auto)]
    private static extern int FnSurfaceZ(IntPtr point3d, int surfaceId);
    
    public bool SurfaceZ(ref BCOM.Point3d point, int surfaceId)
    {
        //Allocate new unmanaged memory
        IntPtr pointer = Marshal.AllocHGlobal(Marshal.SizeOf(point));
        try
        {
            //copy the value of the reference point to the unmanaged memory
            Marshal.StructureToPtr(point, pointer, false);
            //Pass the pointer to MicroStation to pass to Terra Model
            int answer = FnSurfaceZ(pointer, surfaceId);
            //Copy the answer in unmanaged memory back to the reference point
            point = (BCOM.Point3d)Marshal.PtrToStructure(pointer, typeof(BCOM.Point3d));
            //return true or false indicating the success of the operation
            return (answer == SUCCESS);
        }
        finally
        {
            //Free the unmanaged memory
            Marshal.FreeHGlobal(pointer);
        }
    }

    This is explicitly creating the pointers.

    You can also implicitly pass the pointers using:

    [DllImport("xxxx.dll", EntryPoint = "FnSurfaceZ",
                CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Auto)]
    private static extern int FnSurfaceZ(ref DPoint3d point3d, int surfaceId);
    
    public bool SurfaceZ(ref BCOM.Point3d point, int surfaceId)
    {
        int answer = FnSurfaceZ(ref point, surfaceId);
        //return true or false indicating the success of the operation
        return (answer == SUCCESS);
    
    }

    This can however not be used for struct arrays.

    I prefer the first mechanism because then passing points or arrays of points follows the same logic, and I have control over how it happens.

    HTH,

    Francois