[VBA] Need help with MDL Wrapper Function for mdlSheetDef_setBorderAttachmentId

The documentation when followed generates the following error message:

Bad DLL calling convention (Error 49)

While this error has a help page, it does not explain any specific corrective action that can be done.

The Wrapper statement, from the MDL help is:

Declare Function mdlSheetDef_getBorderAttachmentId Lib "stdmdlbltin.dll" ( ByVal sheetDefIn As Long , ByRef borderAttachmentIdOut As DLong ) As Long

There is also a property in the VBA SheetDefinition object named MdlSheetDef that, according to its help "Retrieves the associated SheetDef pointer that a program can use as an argument to MDL mdlSheetDef functions."

With no example, we cannot determine how to use this or even if this is the way to use the subject wrapper function.

We seem to have hit a brick wall on this and need help from a Bentley Programmer.

Parents
  • I guess the approach would something like this …

     Declare Function mdlSheetDef_getBorderAttachmentId _
       Lib "stdmdlbltin.dll" ( _
       ByVal sheetDefIn As Long , _
       ByRef borderAttachmentIdOut As DLong ) As Long
     Dim oSheetDefinition As SheetDefinition
    Set oSheetDefinition = ' ... get Sheet Def from somewhere
    Dim mdlSheetPointer As Long
    ' MDL pointer treated as Long in VBA
    mdlSheetPointer = oSheetDefinition.MdlSheetDef
    Dim borderAttachmentID As DLong
    Const SUCCESS As Long = 0
    If SUCCESS = mdlSheetDef_getBorderAttachmentId ( _
          mdlSheetPointer, borderAttachmentID) Then
       Debug.Print "Sheet Border Attachment ID=" & _
          DLongToString (borderAttachmentID)
    Else
       MsgBox "MDL Function Call Failed"
    EndIf

    Regards, Jon Summers
    LA Solutions

     
    Regards, Jon Summers
    LA Solutions

  • If I set the border attachment to a reference file, your code returns success. With no border reference, it still returns success but the debug.print statement prints -1 as the ID

    If I then try and use the initial values (when a border reference is assigned) to use the set function vs the get border attachment ID function, it still throws the same error.

    Someone at Bentley clearly needs to research this and determine if there are other arguments or syntax for this to work.

    P.S. I also read the Dlong ID value from both the Attached reference file and the SheetDefAttachmentID and the numbers are the same. That's a good thing.


    Charles (Chuck) Rheault
    CADD Manager

    MDOT State Highway Administration

    • MicroStation user since IGDS, InRoads user since TDP.
    • AutoCAD, Land Desktop and Civil 3D, off and on since 1996
  • Bump

    We need assistance from a Bentley Colleague!


    Charles (Chuck) Rheault
    CADD Manager

    MDOT State Highway Administration

    • MicroStation user since IGDS, InRoads user since TDP.
    • AutoCAD, Land Desktop and Civil 3D, off and on since 1996
  • Hi caddcop,

    what do you want to achieve? For an existing sheet to set existing attached reference to be a new border for this sheet?

    Regards,

     Jan

  • The current InRoads sheet tools can make separate models with referenced in borders. But they are neither sheet models, nor therefore sheet border attachments.

    So manually changing these is time consuming and often, the precess needs to be redone due to design changes.

    The code I have will convert a model to a sheet but the mdl wraper function from the mdl documentation does not work and throw an error.

    If the mdl function works, I need the correct wrapper function or an explanation as to what is not correct in my code.


    Charles (Chuck) Rheault
    CADD Manager

    MDOT State Highway Administration

    • MicroStation user since IGDS, InRoads user since TDP.
    • AutoCAD, Land Desktop and Civil 3D, off and on since 1996
  • Hi caddcop,

    I wrote below code snippet to demonstrate mdlSheetDef_get[set]BorderAttachmentId functions.

        ElementID refId = INVALID_ELEMENTID;
        SheetDef* sheetDef = mdlSheetDef_new ();
        mdlModelRef_getSheetDef (ACTIVEMODEL, sheetDef);
        mdlSheetDef_getBorderAttachmentId (sheetDef, &refId);
        if (INVALID_ELEMENTID == refId || 0 == refId)
        {
            DgnModelRefP  modelRef;
            if (SUCCESS != mdlRefFile_attachCoincident (&modelRef, "d:\\atemp\\border_e.dgn", L"BORDER_E", NULL, REF_FILE_LEVEL_DISPLAY_DEFAULT, TRUE, TRUE))
            {
                mdlSheetDef_free (&sheetDef);
                return;
            }
            refId = mdlRefFile_attachmentIdFromModelRef (modelRef);
            mdlSheetDef_setBorderAttachmentId (sheetDef, refId);
            mdlModelRef_setSheetDef (ACTIVEMODEL, sheetDef);
            mdlSheetDef_free (&sheetDef);
        }

    From it, we can use if (INVALID_ELEMENTID == refId || 0 == refId) to determine if a sheet model exists a border attachment.

    For your VBA wrapper, the thread title asks setBorderAttachmentId but your first post shows getBorderAttachmentId wrapper. So I am not sure if you declare setBorderAttachmentId correctly. From VBA help, we can find following:

    Declare Function mdlSheetDef_getBorderAttachmentId Lib "stdmdlbltin.dll"
    (ByVal sheetDefIn As Long , ByRef borderAttachmentIdOut As DLong ) As Long
    Declare Function mdlSheetDef_setBorderAttachmentId Lib "stdmdlbltin.dll"
    (ByVal sheetDefIn As Long , ByVal borderAttachmentIdIn As DLong ) As Long

    You can notice the different parameter declarations.

    HTH, YongAn



  • Unknown said:
    Declare Function mdlSheetDef_setBorderAttachmentId Lib "stdmdlbltin.dll"
    (ByVal sheetDefIn As Long , ByVal borderAttachmentIdIn As DLong ) As Long

    DLong is a VBA User Defined Type (UDT).  UDTs must be passed ByRef.  The MDL documentation for that VBA wrapper is incorrect.

     
    Regards, Jon Summers
    LA Solutions

Reply Children
  • Sorry, it's my fault (plus doc's fault).

    In MDL side, mdlSheetDef_getBorderAttachmentId use ElementID * as its second parameter but mdlSheetDef_setBorderAttachmentId use ElementID as it. Using declaration ByVal borderAttachmentId As DLong is ok for  mdlSheetDef_getBorderAttachmentId but not ok for mdlSheetDef_setBorderAttachmentId.

    I will investigate this interesting question further and find a Bentley expert to help.

    YongAn



  • Unknown said:
    It's my fault

    It's not your fault.  This question has come up before.  The MDL document generator is an automatic process, that occasionally makes mistakes. 

    You can't pass a UDT ByVal; it must be ByRef — that's the way VB/VBA works.  From the C programmer's viewpoint, ByRef means pointer.  I guess Microsoft decided that they wanted to eliminate the possibility of passing a potentially huge chunk of data on the stack (MDL does exactly the same thing — structs are always passed by address).  Both set and get functions must pass a UDT ByRef, even though the get function is not modifying the variable.

    It's easy to test.  Write a couple of functions that take a Point3d.  Then see which compiles successfully …

     ' Point3d is a Bentley-defined User Defined Type (UDT)
    Sub TestUDT1 (ByVal p As Point3d)
       ' ...
    End Sub
     Sub TestUDT2 (ByRef p As Point3d)
       ' ...
    End Sub
     ' VBA's default calling convention is ByRef
    Sub TestUDT3 (p As Point3d)
       ' ...
    End Sub

    Regards, Jon Summers
    LA Solutions

     
    Regards, Jon Summers
    LA Solutions

  • Hi Jon,

    I have a mistyping. I want to type

    Using declaration ByRef borderAttachmentId As DLong is ok for  mdlSheetDef_getBorderAttachmentId because its second parameter's C declaration is ElementID * (a pointer to a structure. acutually ElementID is not a structure, it is just a base type __int64) but not ok for mdlSheetDef_setBorderAttachmentId because its second parameter's delcaration is ElementID.

    Thank you, YongAn



  • Unknown said:
    ElementID is not a structure, it is just a base type __int64

    Correct — but only for MDL and C++, which support the __int64 data type. Microsoft invented VBA before 64-bit processors existed and does not support the 64-bit integer data type, which is why Bentley Systems invented the DLong UDT. MDL's ElementID is an alias for __int64.

    The problem is this …

     ElementID id = 0;
    mdlSheetDef_getBorderAttachmentId (..., &id);

    In the above fragment, the variable that contains the element ID (a 64-bit number) is passed by address. C/C++ languages can modify the contents of an address. It's the equivalent of VBA ByRef. However, because VBA can't cope with 64-bit integers, we have to use the DLong UDT. In this case the MDL wrapper declaration fo the getter function from MDL help is correct …

    Declare Function mdlSheetDef_getBorderAttachmentId _
       Lib "stdmdlbltin.dll" ( _
       ByVal sheetDefIn As Long , _
       ByRef borderAttachmentIdOut As DLong ) As Long

    The MDL wrapper for the setter function is incorrect, because it wants to pass a DLong by value …

    Declare Function mdlSheetDef_setBorderAttachmentId _
    Lib "stdmdlbltin.dll" ( _
    ByVal sheetDefIn As Long , _
    ByVal borderAttachmentIdIn As DLong ) As Long

    We can edit that declaration so that VBA will parse it correctly …

    Declare Function mdlSheetDef_setBorderAttachmentId _
    Lib "stdmdlbltin.dll" ( _
    ByVal sheetDefIn As Long , _
    ByRef borderAttachmentIdIn As DLong ) As Long

    However, changing that declaration does not change the underlying code. We don't know how that MDL wrapper is implemented. I suspect that there may be some internal confusion in the conversion from the internal __int64 and the VBA DLong passed by address.

    Regards, Jon Summers
    LA Solutions

     
    Regards, Jon Summers
    LA Solutions