Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / .NET Framework / Interop / January 2004

Tip: Looking for answers? Try searching our database.

Problems with MarshalDirectiveException

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
Nicholas Schweitzer - 02 Jan 2004 00:44 GMT
Ok... I'm extremely confused.  I'm attempting to create a tessellation
class... and am having issues with the Combine Callback...

The callback is defined as in GLU as:

void combine( GLdouble coords[3], void* vertex_data[4], GLfloat
weight[4], void** outData );

I've defined the delegate in C# this way:

internal delegate void GluTessCombineCallback(
[MarshalAs( UnmanagedType.LPArray, SizeConst=3 )] double[] coords,
[MarshalAs( UnmanagedType.LPArray, SizeConst=4 )] IntPtr[]
vertex_data,
[MarshalAs( UnmanagedType.LPArray, SizeConst=4 )] float[] weight,
ref IntPtr outData );

The function that registers the callback is this:

[DllImport( "glu32.dll" )]
internal static extern void gluTessCallback( IntPtr tobj, TessCallback
which,
[MarshalAs( UnmanagedType.FunctionPtr )] GluTessCombineCallback fn );

When I make a call to this registration function in the class
constructor, I get a MarshalDirectiveException thrown with a Message
of "array size control parameter must be an integral type".

What I don't understand is that I set the SizeConst in the delegate
declaration.  I saw an earlier thread that had similar issues here
(unfortunately I couldn't reply to it... other issues):

http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&oe=UTF-8&threadm=usvzMxdgDHA.
1764%40TK2MSFTNGP09.phx.gbl&rnum=1&prev=/groups%3Fsourceid%3Dnavclient%26ie%3DUT
F-8%26oe%3DUTF-8%26q%3D%2522array%2Bsize%2Bcontrol%2Bparameter%2Bmust%2Bbe%2Ban%
2Bintegral%2Btype%2522


Is there any workaround to this problem... or am I missing something?

Thanks
Bart Jacobs - 03 Jan 2004 03:08 GMT
The workaround is to do your own marshalling. That is, declare the
delegate as follows:

internal delegate void GluTessCombineCallback(
    double* coords,
    IntPtr* vertex_data,
    float *weight,
    out IntPtr outData);

One could also write the wrapper in Managed Extensions for C++, as follows:

==== Begin Tesselator.cpp ==============================================

#using <mscorlib.dll>

#include <windows.h>
#include <gl\Glu.h>
#include <stdio.h>

class GLUtesselator {};

typedef System::IntPtr IntPtr;
typedef System::Runtime::InteropServices::GCHandle GCHandle;
typedef System::Object Object;
typedef System::Console Console;

namespace Glu {

public __value struct Coords {
    double X;
    double Y;
    double Z;

    Coords() {}
    Coords(double x, double y, double z) : X(x), Y(y), Z(z) {}

    System::String *ToString() {
        System::Text::StringBuilder *builder =
            new System::Text::StringBuilder();
        builder->Append((__wchar_t) '(');
        builder->Append(X);
        builder->Append((__wchar_t) ',');
        builder->Append(Y);
        builder->Append((__wchar_t) ',');
        builder->Append(Z);
        builder->Append((__wchar_t) ')');
        return builder->ToString();
    }
};

public __value enum PrimitiveType {
    Triangles = GL_TRIANGLES,
    TriangleStrip = GL_TRIANGLE_STRIP,
    TriangleFan = GL_TRIANGLE_FAN
};

public __gc __interface ITesselatorResultSink {
    void Begin(PrimitiveType type);
    void Vertex(IntPtr vertex);
    void End();
    IntPtr Combine(
        Coords coords,
        IntPtr vertices __gc [],
        float weights __gc []);
};

ITesselatorResultSink *GetResultSink(void *data) {
    IntPtr ptr = IntPtr::op_Explicit(data);
    GCHandle handle = GCHandle::op_Explicit(ptr);
    Object *object = handle.get_Target();
    return dynamic_cast<ITesselatorResultSink *>(object);
}

void __stdcall tess_beginData(GLenum type, void *polygon_data) {
    GetResultSink(polygon_data)->Begin((PrimitiveType) type);
}

void __stdcall tess_vertexData(void *vertex_data, void *polygon_data) {
    GetResultSink(polygon_data)->Vertex(
        IntPtr::op_Explicit(vertex_data));
}

void __stdcall tess_endData(void *polygon_data) {
    GetResultSink(polygon_data)->End();
}

void __stdcall tess_combineData(
        GLdouble coordsArray[3],
        void *vertex_data[4],
        GLfloat weight[4],
        void **outData,
        void *polygon_data) {
    Coords coords =
        Coords(coordsArray[0], coordsArray[1], coordsArray[2]);
    IntPtr vertices __gc [] = __gc new IntPtr[4];
    float weightArray __gc [] = __gc new float __gc [4];
    for (int i = 0; i < 4; i++) {
        vertices[i] = IntPtr::op_Explicit(vertex_data[i]);
        weightArray[i] = weight[i];
    }
    *outData = (void *)
        GetResultSink(polygon_data)->Combine(
            coords, vertices, weightArray);
}

public __sealed __gc class Tesselator : public System::IDisposable {
    GLUtesselator *tesselator;
    GCHandle resultSinkHandle;

public:
    Tesselator() {
        tesselator = gluNewTess();

        gluTessCallback(
            tesselator, GLU_TESS_BEGIN_DATA,
            (void (__stdcall *)(void)) &tess_beginData);
        gluTessCallback(
            tesselator, GLU_TESS_VERTEX_DATA,
            (void (__stdcall *)(void)) &tess_vertexData);
        gluTessCallback(
            tesselator, GLU_TESS_END_DATA,
            (void (__stdcall *)(void)) &tess_endData);
        gluTessCallback(
            tesselator, GLU_TESS_COMBINE_DATA,
            (void (__stdcall *)(void)) &tess_combineData);
    }

    void Dispose() {
        gluDeleteTess(tesselator);
    }

    void BeginPolygon(ITesselatorResultSink *resultSink) {
        resultSinkHandle = GCHandle::Alloc(resultSink);
        IntPtr ptr = (IntPtr) resultSinkHandle;
        void *data = (void *) ptr;
        gluTessBeginPolygon(tesselator, data);
    }

    void BeginContour() {
        gluTessBeginContour(tesselator);
    }

    void Vertex(Coords coords, IntPtr vertex) {
        double cs[3] = {coords.X, coords.Y, coords.Z};
        gluTessVertex(tesselator, cs, (void *) vertex);
    }

    void EndContour() {
        gluTessEndContour(tesselator);
    }

    void EndPolygon() {
        gluTessEndPolygon(tesselator);
        resultSinkHandle.Free();
    }
};

}

==== End Tesselator.cpp ================================================

Clients of this wrapper need to represent vertices as IntPtrs; however,
it is easy to write a wrapper around this wrapper that allows vertices
to be represented as objects:

==== Begin TesselatorEx.cs =============================================

using System;
using System.Collections;
using Glu;

namespace GluEx {

public interface ITesselatorExResultSink {
    void Begin(PrimitiveType type);
    void Vertex(object vertex);
    void End();
    object Combine(Coords coords, object[] vertices, float[] weights);
}

public class TesselatorEx : IDisposable {
    class ResultSink : ITesselatorResultSink {
        TesselatorEx outer;

        public ResultSink(TesselatorEx outer) {
            this.outer = outer;
        }

        public void Begin(PrimitiveType type) {
            outer.exResultSink.Begin(type);
        }

        public void Vertex(IntPtr ptr) {
            object vertex = outer.GetVertexFromIntPtr(ptr);
            outer.exResultSink.Vertex(vertex);
        }

        public void End() {
            outer.exResultSink.End();
        }

        public IntPtr Combine(
                Coords coords, IntPtr[] ptrs, float[] weight) {
            object[] vertices = new object[ptrs.Length];
            for (int i = 0; i < ptrs.Length; i++) {
                vertices[i] = outer.GetVertexFromIntPtr(ptrs[i]);
            }
            object result =
                outer.exResultSink.Combine(coords, vertices, weight);
            return outer.GetIntPtrFromVertex(result);
        }
    }

    Tesselator tesselator = new Tesselator();
    ResultSink resultSink;
    ArrayList list;
    Hashtable table;
    ITesselatorExResultSink exResultSink;

    IntPtr GetIntPtrFromVertex(object vertex) {
        object index = table[vertex];
        int i;
        if (index == null) {
            i = list.Count;
            list.Add(vertex);
            table[vertex] = i;
        } else {
            i = (int) index;
        }
        return (IntPtr) (i + 1);
    }

    object GetVertexFromIntPtr(IntPtr ptr) {
        return list[(int) ptr - 1];
    }

    public TesselatorEx() {
        resultSink = new ResultSink(this);
    }

    public void BeginPolygon(ITesselatorExResultSink exResultSink) {
        this.exResultSink = exResultSink;
        list = new ArrayList();
        table = new Hashtable();
        tesselator.BeginPolygon(resultSink);
    }

    public void BeginContour() {
        tesselator.BeginContour();
    }

    public void Vertex(Coords coords, object vertex) {
        tesselator.Vertex(coords, GetIntPtrFromVertex(vertex));
    }

    public void EndContour() {
        tesselator.EndContour();
    }

    public void EndPolygon() {
        tesselator.EndPolygon();
        table = null;
        list = null;
        exResultSink = null;
    }

    public void Dispose() {
        tesselator.Dispose();
    }
}

}

==== End TesselatorEx.cs ===============================================

This code can be tested as follows:

==== Begin Test.cs =====================================================

using Glu;
using GluEx;
using System;
using Color = System.Drawing.Color;

class Vertex {
    static int counter;

    public int id = ++counter;
    public Coords coords;
    public Color color;

    public Vertex(Coords coords, Color color) {
        this.coords = coords;
        this.color = color;
    }
}

class MyResultSink : ITesselatorExResultSink {
    public void Begin(PrimitiveType type) {
        Console.WriteLine(
            "MyResultSink::Begin(type == {0}) called", type);
    }

    public void Vertex(object vertex) {
        Console.WriteLine(
            "MyResultSink::Vertex(vertex == {0}) called",
            ((Vertex) vertex).id);
    }

    public void End() {
        Console.WriteLine("MyResultSink::End() called");
    }

    public object Combine(
            Coords coords, object[] vertices, float[] weights) {
        float alpha = 0;
        float red = 0;
        float green = 0;
        float blue = 0;
        for (int i = 0; i < vertices.Length; i++) {
            Vertex vertex = (Vertex) vertices[i];
            float weight = weights[i];
            alpha += weight * vertex.color.A;
            red += weight * vertex.color.R;
            green += weight * vertex.color.G;
            blue += weight * vertex.color.B;
        }
        Vertex result =
            new Vertex(
                coords,
                Color.FromArgb(
                    (int) alpha, (int) red, (int) green, (int) blue));
        Console.WriteLine(
            "MyResultSink::Combine(" +
                "coords == {0}, " +
                "vertices == {{{1},{2},{3},{4}}}, " +
                "weights == {{{5},{6},{7},{8}}}" +
            ") called; result == {9}",
            coords.ToString(),
            ((Vertex) vertices[0]).id,
            ((Vertex) vertices[1]).id,
            ((Vertex) vertices[2]).id,
            ((Vertex) vertices[3]).id,
            weights[0], weights[1], weights[2], weights[3],
            result.id
        );
        return result;
    }
}

class Test {
    static void Vertex(
            TesselatorEx tesselator, Coords coords, Color color) {
        tesselator.Vertex(coords, new Vertex(coords, color));
    }

    static void Main() {
        TesselatorEx tesselator = new TesselatorEx();
        MyResultSink sink = new MyResultSink();
        tesselator.BeginPolygon(sink);
        tesselator.BeginContour();
        Vertex(tesselator, new Coords(0, 0, 0), Color.Black);
        Vertex(tesselator, new Coords(2, 0, 0), Color.Red);
        Vertex(tesselator, new Coords(2, 2, 0), Color.Green);
        Vertex(tesselator, new Coords(0, 2, 0), Color.Blue);
        tesselator.EndContour();
        tesselator.BeginContour();
        Vertex(tesselator, new Coords(1, 1, 0), Color.White);
        Vertex(tesselator, new Coords(3, 1, 0), Color.Yellow);
        Vertex(tesselator, new Coords(3, 3, 0), Color.Cyan);
        Vertex(tesselator, new Coords(1, 3, 0), Color.Magenta);
        tesselator.EndContour();
        tesselator.EndPolygon();
        tesselator.Dispose();
    }
}

==== End Test.cs =======================================================

The only thing to watch out for is the Mixed DLL Loading Problem
<http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dv_vstechart/ht
ml/vcconmixeddllloadingproblem.asp
>.

Greetings

Bart Jacobs

Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.