C# Materials on levels in MicroStation v8i

Hi

We are doing the c# equivalent of: (Using dll imports)
Functionality developed in MicroStation v8i (08.11.09.357 )


//Code
mdlMaterial_initialize()
mdlMaterial_load(...)
MaterialProperties* material = mdlMaterial_create()

mdlMaterial_findByLevelName(material, app.ActiveDesignFile.MdlModelRefP(), level.Name, 0, NULL)
mdlMaterial_free(material)
mdlMaterial_cleanup()
//Snippet

and the mdlMaterial_findByLevelName returns 0 (ok) but the material pointer does not update.
I have even looked in the memory to see if the bytes change but they don’t…

How can I get the material Used by a level?
The mdlMaterial_findFromId does not seem to return anything either…

I can get the mdlMaterial_loadTable to work an find all the materials in the table but I need to get the materials used by level.

Any help is greatly appreciated

Regards Per Bodin - AEC AB

Parents
  • We are doing the c#
    MaterialProperties* material = mdlMaterial_create()

    It's not clear whether you're writing C# or MDL.  The mdlMaterial_api is dependent on pointers and memory allocation, which don't translate readily to other languages.

    mdlMaterial_findByLevelName(material, app.ActiveDesignFile.MdlModelRefP(), level.Name, 0, NULL)

    That statement looks like you're writing MDL but calling VBA COM using an InterOp.  You're asking for help for an app. that you're writing in three computer languages?  That's going to be a nightmare to maintain.  Why not stick with a single language that works with pointers: MDL or C++?

     
    Regards, Jon Summers
    LA Solutions

  • Hi Jon, Thanks for your interest.!
    Some background:

    We are not doing one app we are doing around 8, and a stand alone application as well. The apps are communicating and exchanging information between them. The apps are for Microstation, AutoCAD, Revit, Word, Excel etc etc with the main aim to deliver quality checked infornmation and deliveries to ProjectWise (and some other document managing systems). To be able to maintain all of theese apps our goto language is C# .net, since we have a lot of experience and 3rd party tools to speed up the dev process Mainly around the UI and database stuff. This exactly to be able to maintain it as you say. 

    Sometimes we fall back on c++ if needed but whenever P/Invoke is a possibility we try to go that way instead, and it has worked beutifully even with pointers and stuff, if we just write the structs correctly.

    Our current approach has worked great in Microstation as well, but when it comes to getting Materials from Levels I am stuck and desperate and am currently trying anything and everything. So the mixing of stuff is by reason and reason of insanity :). I would preferr to keep stuff as is since it would be a nightmare to rewrite all of our UI stuff and api's in c++.

    But the question: Do you know if theese functions work in pure c++ / MDL or if there's a problem there as well? And I bet I am doing something wrong or missing a step or something since I do not get the expected behaviour.

    Thanks in advance

    Hakan WIkemar

    Developer/Consultant

    AEC AB

  • Do you know if theese functions work in pure c++ / MDL

    Yes, the mdlMaterials_api works.

    However, it is not easy to use, even in MDL.  The mdlMaterials_api uses dynamically-allocated structures with pointers into those structures.  It's not always simple to know who owns the dynamically-allocated memory, so who should deallocate that memory?

    Languages such as C# and C++ help to overcome those problems when the API is written to match those languages.  Unfortunately there is a mis-match between MDL and those object-oriented languages having built-in memory management.

    mdlMaterial_load(...)

    What do you pass to that function?  What language is that code? 

    MaterialProperties* material = mdlMaterial_create()

    That looks like a VB statement (no semi-colon), in which case where is MaterialProperties defined, and what does * mean?

     
    Regards, Jon Summers
    LA Solutions

  • Hi!

    I really appreciate the help!

    mdlMaterial_load(...) the input is the pointer to the ModelRef (app.ActiveDesignFile.MdlModelRefP()) 
    and this pointer works fine for other mdl Calls such as: mdlLevel_getTransparency(ref trans, ModelRef, LevelId);

    And sorry about the MaterialProperties*, that was "pseudo code" from another post, I am using: mdlMaterial_create() which I thought returns a pointer to MatrialProperties,
    I do get a pointer which I pass back to  mdlMaterial_find(materialPointer, modelRefIn, LevelId, color, nameP);

    I think this is where I might be doing something wrong but I can't figure out how to make it work.. using c#

    IntPtr materialPointer = mdlMaterial_create();

    int iMatResult = mdlMaterial_find(materialPointer, modelRefIn, LevelId, color, nameP);

    If ther's another way of getting to the Material on a Level I'm up for trying that too, just a bit short on ideas right now..

    FYI:
    We got stuff like  double trans = mdlLevel_getTransparency(app.ActiveDesignFile.MdlModelRefP(), level.ID); which is working great.


    /Håkan

  • I do get a pointer which I pass back to  mdlMaterial_find(materialPointer, modelRefIn, LevelId, color, nameP);
    using c#
    mdlMaterial_find(ref materialPointer, modelRefIn, LevelId, color, nameP);

    C# requires us to identify with a keyword a variable that is being modified (i.e. ref or out).  I think you also need that in the import declaration.

     
    Regards, Jon Summers
    LA Solutions

  • Hi!

    I'm fairly sure I tried this (I'm getting more and more unsure by the minute :) ), without sucess and the api documentation looked a bit strange on this particular call. I will definetly try it again, because that was also my own first reflection when I wrote this code. (Makes perfect sense) I'll get back to you as soon as I verify it. Crossing my fingers! Thanks!!

    /Håkan

  • without sucess and the api documentation looked a bit strange on this particular call

    The MDL Reference Manual of course defines a C-style function.  That description provides the declaration statement for VBA.  If using .NET, you must interpret that VBA declaration to conform to .NET expectation.

     
    Regards, Jon Summers
    LA Solutions

  • I would suggest you use.

    Public StatusInt mdlLevel_getElementMaterialId
    (
    ElementID * materialIdOut,
    DgnModelRefP modelRefIn,
    LevelID levelIdIn
    );

    The above function will look at the level table information to find the material id.

    mdlMaterial_findByLevelName will just look in the material table for materials which are associated with a particular level and colour. It should copy the material into the materialP argument if the material is found.

    Materials directly associated with levels were implemented after the material system functionality, there is little overlap between them.

    Regards

    Paul



  • Materials directly associated with levels were implemented after the material system functionality

    FWIW the materials API in MicroStation CONNECT is a vast improvement over the V8 API.  It's a coherent set of C++ classes that has a .NET implementaton as well.

     
    Regards, Jon Summers
    LA Solutions

  • Hi Paul!

    Yes, this works great for me but then when I do: "static extern IntPtr mdlMaterial_findFromId(int materialIdIn);" on the material id I get back I always get a null pointer :(.

  • Yes, I'm longing for CONNECT, .Net should be a lot more developed thare from what I've heard. But, our customers are still working with the V8 version so not yet...

  • when I do: "static extern IntPtr mdlMaterial_findFromId(int materialIdIn);" on the material id I get back I always get a null pointer
    1. What's your C# declaration of mdlMaterial_findFromId?
    2. How do you get materialIdIn?

     
    Regards, Jon Summers
    LA Solutions

Reply Children
  • //Find from Id
    [DllImport("stdrender.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
    static extern IntPtr mdlMaterial_findFromId(int materialIdIn);
    public static IntPtr GetMaterialPropertiesbyId(int materialIdIn)
    {
    return mdlMaterial_findFromId(materialIdIn);
    }

    //To get Material Id
    int test = MdlAPI.GetMaterialId(ref matid, ref matoverride, app.ActiveDesignFile.MdlModelRefP(), level.ID);
    //returns a matid with a example int value of 559
    [DllImport("stdmdlbltin.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
    static extern int mdlLevel_getMaterialId(ref int materialIdOut, ref bool materialOverrideOut, int modelRefIn, int levelIdIn);

    public static int GetMaterialId(ref int materialIdOut, ref bool materialOverrideOut, int ModelRef, int LevelId)
    {
    return mdlLevel_getMaterialId(ref materialIdOut, ref materialOverrideOut, ModelRef, LevelId);
    }

    //I've also looked at: (Which gives mi the name just perfectly but since MaterialProperties does not Contain the Material Id?? I haven't persued this any further and I don't know how to handle infofieldls pointer in c# (the examples I find seems to suggest a array of pointers?)
    int testload = MdlAPI.LoadMaterialTable(null, ref pallette, ref material, ref parameter, ref table, app.ActiveDesignFile.MdlModelRefP(), null);
    int size = MdlAPI.StringListSize(material);
    for (int i = 0; i < size; i++)
    {
    int res = MdlAPI.StringListGetMember(ref ptrString, ref infofields, material, i);
    string matname = Marshal.PtrToStringAnsi(ptrString); //Gives the name of all materials just fine :)
    }
    [DllImport("stdrender.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
    static extern int mdlMaterial_loadTable(string fullName, ref IntPtr paletteListPP, ref IntPtr materialListPP, ref IntPtr parameterListPP, ref IntPtr tablePP, int modelRef, string TableName);

    public static int LoadMaterialTable(string fullName, ref IntPtr paletteListPP, ref IntPtr materialListPP, ref IntPtr parameterListPP, ref IntPtr tablePP, int modelRef, string TableName)
    {
    return mdlMaterial_loadTable(fullName, ref paletteListPP, ref materialListPP, ref parameterListPP, ref tablePP, modelRef, TableName);
    }
    static extern int mdlStringList_size(IntPtr pStringList);
    [DllImport("stdmdlbltin.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
    public static int StringListSize(IntPtr pStringList)
    {
    return mdlStringList_size(pStringList);
    }
    [DllImport("stdmdlbltin.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
    static extern int mdlStringList_getMember(ref IntPtr ppString, ref IntPtr ppInfoFields, IntPtr pStringList, int memberIndex);
    public static int StringListGetMember(ref IntPtr ppString, ref IntPtr ppInfoFields, IntPtr pStringList, int memberIndex)
    {
    return mdlStringList_getMember(ref ppString, ref ppInfoFields, pStringList, memberIndex);
    }

  • Hi Jon, have you had any chanse too look at my aswer? Really apprecieate any input Slight smileThanks

    /Håkan

  • I don't know how to handle infofieldls pointer in c#

    As I wrote, there's a fault-line between the MDL functions and other languages.  You're making things hard for yourself.

    static extern int mdlStringList_size(IntPtr pStringList)

    The StringList is an antique C data structure provided by MDL since MicroStation 4...

    • It is dynamically-allocated and requires manual deallocation
      • The no. of info fields is specified when the StringList is created and cannot be changed
      • Each info field is an integer
    • For each member of a StringList:
      • The string member is dynamically allocated
      • It has zero or more info fields that are allocated when a member is inserted into the StringList
      • You can retrieve a pointer to the string
      • You can retrieve a pointer, which may be empty, to the info field array

    MicroStation V8 introduced the ListModel that in many APIs replaces the StringList.  Unfortunately, the V8 Materials API prefers the old way of doing things.

     
    Regards, Jon Summers
    LA Solutions