[v8i VBA] inroads lib return value

Hi,

On a quest to get some basic information from DTM and ALG files, using the Bentley provided SDK.

Using the function:

Public Declare Function scadSDK_dtmSurfaceInfoGet Lib "SCadSDKAT" ( _
    ByRef name As longString, _
    ByRef desc As longString, _
    ByRef file As longString, _
    ByRef mat As longString, _
    ByRef pref As longString, _
    ByRef secsymb As longString, _
    ByRef prfsymb As longString, _
    maxtrilength As Double, _
    useftrsonlyxsect As Long, _
    useFtrsOnlyPrf As Long, _
    ByVal srf As Long) As Long

I can get name and description into a byte array. Once translated as ASCII, I get the proper readable name and description. (from a loaded DTM surface in inRoads/powercivil/roaddesigner..whatever its called these days. I. dont. care ;)

However, using the similar function (for loaded ALG files)

Public Declare Function scadSDK_cogoAlignmentInfoGet Lib "SCadSDKAT" ( _
    ByRef name As longString, _
    ByRef desc As longString, _
    ByRef feat As Long, _
    ByRef pAlg As Long) As Long

The name and description when translated from a bytearray is total gibberish?

As all the declarations in the provided SDK is byVal and declared as Long, I had to add the byRef and change type to longstring.

Longstring:

Type longString
   s(256) As Byte
End Type

The provided helpfiles says this about the two functions, but there is a mismatch between the descriptions and the SDK examples.

ScadSDK_dtmSurfaceInfoGet

            #include <SCadDTM.h>
            
            int scadSDK_dtmSurfaceInfoGet
            (
                wchar_t *nameP,             // (o) surface's name (or NULL) (size is DTM_C_NAMSIZ)
                wchar_t *descP,             // (o) surface's desc (or NULL) (size is CIV_C_NAMSIZ)
                wchar_t *fileP,             // (o) surface's file (or NULL) (size is CIV_MAX_FNAME)
                wchar_t *matP,              // (o) surface's material (or NULL) (size is CIV_MAX_NAMSIZ)
                wchar_t *prefP,             // (o) surface's preference (or NULL) (size is CIV_C_NAMSIZ)
                wchar_t *secsymbP,          // (o) surface's xsection symb (or NULL) (size is CIV_C_NAMSIZ)
                wchar_t *prfSymbP,          // (o) surface's profile symb (or NULL) (size is CIV_C_NAMSIZ)
                double *maxTriLength,       // (o) max. tri. length (or NULL)
                long *useFtrsOnlyXSect,     // (o) use ftrs only for xsects.(or NULL)
                long *useFtrsOnlyPrf,       // (o) use ftrs only for profiles(or NULL)*/
                struct CIVdtmsrf *srfP      // (i) surface to use
            );
        
Description:  This function returns information about a particular surface.  String parameters must be pre-allocated to predefined sizes.  Setting any of these returns to an initial value of NULL instructs the function to ignore that value.
 
Returns: 

Standard

scadSDK_cogoAlignmentInfoGet

            #include <SCadCogo.h>
            
            int scadSDK_cogoAlignmentInfoGet
            (
                wchar_t *name,      // (o) alignment name (size is ALGNAMSZ)
                wchar_t *desc,      // (o) alignment description (size is ALGDSCSZ)
                wchar_t *style,     // (o) alignment style (size is ALGFTRSZ)
                ALGalign *alg       // (i) pointer to alignment
            );
        
Description:  This function returns information about a particular alignment.  The strings must be large enough to hold the information.  If name, desc or style is set to NULL, it will be ignored and nothing will be returned for that parameter.
 
Returns:  Standard

Any help is appreciated, as all I need for my monster CAD-scanner at this moment is getting the name and description of the ALG files :)

/Torben

Parents
  • Hi Torben,

    I recommend to check MicroStation VBA help file, chapter "Calling DLL functions from VBA", there is a chapter about different way how to handle Unicode string passed from/to MDL wrappers. I have not any civil product installed, so I am not able to test it.

    I am thinking about your declaration of longString and I am not sure if you can use Byte (8 bits) if wchar_t (Unicode 2 bytes) is used internally.

    With regards,

    Jan
  • Unknown said:
    Hi Torben,

    I recommend... [RTFM]

    Hi Jan, thanks for the heads up. I never bothered looking in the microstation vba manual. *blush*

    I am currently trying to make heads and tails of it.

    I tried this:

    Public Declare Function scadSDK_cogoProjectInfoGet Lib "SCadSDKAT" ( _
        ByRef sfile As String, _
        ByRef name As longString, _
        ByRef desc As longString, _
        prj As Long) As Long

    Where sfile is a string = space(512)

    And it friggin worked! The string looked empty but when printed showed a readable string!

    Sadly it only worked ONE time, even after a reboot?.. heh!?

    System: Win7 64bit 16GB Ram - microStation V8i SS3 08.11.09.578. + PoinTools CONNECT. - Intel i7-4800MQ CPU@2.70GHz, 4 core / 8 Logic proc.

  • Hi Torben,

    Unknown said:
    I never bothered looking in the microstation vba manual.

    Your fault. Based on short research it seems in the mentioned chapter the complete answer is provided already.

    I am a bit curious where you found the function definition using ByRef. What I was able to find in PowerCivil SDK (too busy now, so just extract msi file, not install) is:

    Public Declare Function scadSDK_dtmSurfaceInfoGet Lib "SCadSDK.3.1.dll" (
      ByVal name As Long, 
      ByVal desc As Long, 
      ByVal file As Long, 
      ByVal mat As Long, 
      ByVal pref As Long, 
      ByVal secsymb As Long, 
      ByVal prfSymb As Long, 
      ByVal maxTriLength As Double, 
      ByVal useFtrsOnlyXSect As Long, 
      ByVal useFtrsOnlyPrf As Long, 
      ByVal srf As Long
    ) As Long

    For this specific case this approach is described in MicroStation VBA help:

    ' Allocate the buffer 
    bufferSize = 2000 
    
    Dim name as String
    name = Space(bufferSize)
    
    Dim description as String
    description = Space(bufferSize)
    
    ' Call SDK function
    scadSDK_dtmSurfaceInfoGet (StrPtr(name), StrPtr(description)...
    

    Have had no chance to test it, but I used StrPtr in the past several times (but mostly to pass string, not to retrieve) and it works fine.

    With regards,

      Jan

  • Hi all,

    Why I use ByVal/ByRef

    I simply used ByRef in order to get something back into the passed variable, as byval passes the value only - and that actually works nicely in the DTM function mentioned above. I can pass and get strings and values, but had to change the SDK function's parameters to byref and that "longstring"-bytearray thingy.

    StrPtr, (ObjPtr, VarPtr)

    I had no idea there were such things. It does however not work for me out-of-the-box. I used the unmodified function (using byval and passing longs), and all I get back is either nothing or garbage. "?? ††††††††††....

    Function used for testing:

    If SCadCogo.scadSDK_cogoIsValidAlignmentPointer(algs.surfPtr(j)) = 1 Then
        
        sFilename = Space(1024)
        sName = Space(1024)
        sDesc = Space(1024)
        
        SCadCogo.scadSDK_cogoAlignmentInfoGet _
            StrPtr(sName), _
            StrPtr(sDesc), _
            StrPtr(featcount), _
            algs.surfPtr(j)
        
        Debug.Print j & ":" & _
            StrConv(sName, vbFromUnicode), _
            StrConv(sDesc, vbFromUnicode), _
            featcount
    End If

    and

    Public Declare Function scadSDK_cogoAlignmentInfoGet Lib "SCadSDKAT" ( _
        ByVal name As Long, _
        ByVal desc As Long, _
        ByRef feat As Long, _
        pAlg As Long) As Long

    I'll try some more today :)

    System: Win7 64bit 16GB Ram - microStation V8i SS3 08.11.09.578. + PoinTools CONNECT. - Intel i7-4800MQ CPU@2.70GHz, 4 core / 8 Logic proc.

  • Hi Torben,

    a few more notes, maybe it will make this topic clearer...

    Unknown said:
    I simply used ByRef in order to get something back into the passed variable

    This is not easy and simple to explain shortly, because several different topics merge together in this issue. And furthermore it requires knowledge of some C features (pointers, how strings are implemented and handled etc.), which is not required for pure VBA and as such is not known by the most of VBA coders.

    The first and mandatory rule is to always follow what documentation says. In the case of MicroStation VBA the wrapper declarations are mentioned in help file, in the case of Civil SDK it's mentioned in .bas files delivered with PowerCivil installation. At first, if you will use different declaration, it can work, but usually not, and often it looks like it works, but not always, which is confusing and hard to track down the bug. At second, why to make life harder if somebody else prepared and documented the right way?

    That's not always true (like in this case) you have to pass a parameter using ByRef to get data back. If you pass pointer or reference to a buffer (not correct VBA terms, but good for the explanation), this value is used to find the buffer and fill it. But the value of the pointer is not modified, so it can be passed by value. An analogy from real life is a mailbox. Postman don't need the mailbox itself, but only its address to find it and put something in.

    Another problem is how strings are defined and stored:

    • If they are 8bits (C know nothing like type "string", so strings are array of chars and are addressed using pointer to the first char), VBA can handle the whole process automatically, because also VBA strings are 8bits, so it's easy and you don't have to take care about anything. Using example from MicroStation VBA help, if C function is declared as mdlFile_find (char *outname, const char *inname, const char *envvar, const char *iext); VBA String variables can be used directly. But even in this case the output string is passed as ByVal, not ByRef.
    • If Unicode strings (arrays of MSWChar or w_chart) are used by C functions, which seems to be the case of the whole Civil SDK), VBA is not able to handle these types directly, because VBA is very old and is not aware about Unicode. Because of it, pointers are passed instead of strings and StrPtr function has to be used to add extra conversion between C and VBA worlds.

    Unknown said:
    StrPtr, (ObjPtr, VarPtr): I had no idea there were such things.

    It's not necessary to know these things until you have to use external dlls in VBA code. StrPtr is the most common, ObjPtr and VarPtr are really specific and used rarely.

    Unknown said:
    It does however not work for me out-of-the-box.

    It seems some more processing is required, but unfortunatley because I am not able to test it, it's just a theory. You can try two solutions:

    In the mentioned chapter in MicroStation VBA help there is another extra step described. Adapted to my previous code, it would looks like this:

    ' Truncate at C's end-of-string 
    name = Left$(name, InStr(1, name, vbNullChar) - 1) 


    In some blog I found on Internet, another approach was described:

    name = StrConv(name, vbFromUnicode)

    I am not sure if one way is better than the second one, so you should try both and I will cross my fingers it will work ;-)

    With regards,

      Jan

    Answer Verified By: Torben 

  • Hi Jan,

    You are correct. I assumed falsely that something had to be wrong with the documentation and tried fixing it the best I knew how to. It seems to have been working all along for the initiated, using strPtr to pass the pointer to the functions. There is no need for a translation (in my case anyways)

    Thanks for sticking with me on this one! :)

    A complete example (excluding the SDK functions) for loading and getting information from an ALG file in microStation VBA is:

    Sub ALGinfoDump()
    
        Dim prjPtr As Long
        
        inRoadsInitialize ' function loads inroads and sets INROADS_RUNNING to true if success
                
        If INROADS_RUNNING Then
        
            ' Load ALG:
            inRoadsLoadALG prjPtr, "[INSERT .ALG FILENAME]"
            
            Debug.Print "Project count:" & scadSDK_cogoCountNumberOfProjects ' should be 1 now
                    
            Dim sFilename As String
            sFilename = Space(ALGFILSZ)
            
            Dim sName As String
            sName = Space(ALGNAMSZ)
            
            Dim sDesc As String
            sDesc = Space(ALGDSCSZ)
                    
            If scadSDK_cogoIsValidProjectPointer(prjPtr) = SDK_TRUE Then
            
                SCadCogo.scadSDK_cogoProjectInfoGet _
                    StrPtr(sFilename), _
                    StrPtr(sName), _
                    StrPtr(sDesc), _
                    prjPtr
                
                Debug.Print "File:" & sFilename
                Debug.Print "Name:" & sName
                Debug.Print "Desc:" & sDesc
                
                ' Try to get at the alignments
                
                Dim algs As moreLongs2 ' Simple UT with just long in it
                Dim countHorizontalAlignmentsInProject As Long
                Dim featcount As Long
                
                countHorizontalAlignmentsInProject = scadSDK_cogoCountNumberOfHorizontals(prjPtr)
                scadSDK_cogoGetHorizontalAlignments algs, countHorizontalAlignmentsInProject, prjPtr
    
                Dim alignmentCounter As Integer
                
                For alignmentCounter = 0 To countHorizontalAlignmentsInProject - 1
                    If SCadCogo.scadSDK_cogoIsValidAlignmentPointer(algs.surfPtr(alignmentCounter)) = SDK_TRUE Then
                        
                        sFilename = Space(1024)
                        sName = Space(1024)
                        sDesc = Space(1024)
                        
                        SCadCogo.scadSDK_cogoAlignmentInfoGet _
                            StrPtr(sName), _
                            StrPtr(sDesc), _
                            featcount, _
                            algs.surfPtr(alignmentCounter)
                        
                            sName = Left$(sName, InStr(1, sName, vbNullChar) - 1)
                            sDesc = Left$(sDesc, InStr(1, sDesc, vbNullChar) - 1)
                        
                        Debug.Print "Alignment number " & alignmentCounter & ":" & _
                            sName, _
                            sDesc, _
                            featcount
                    End If
                Next alignmentCounter
            End If
            
            ' release the ALG project:
            scadSDK_cogoRemoveProject prjPtr
        
        End If
        
    End Sub

    System: Win7 64bit 16GB Ram - microStation V8i SS3 08.11.09.578. + PoinTools CONNECT. - Intel i7-4800MQ CPU@2.70GHz, 4 core / 8 Logic proc.

Reply
  • Hi Jan,

    You are correct. I assumed falsely that something had to be wrong with the documentation and tried fixing it the best I knew how to. It seems to have been working all along for the initiated, using strPtr to pass the pointer to the functions. There is no need for a translation (in my case anyways)

    Thanks for sticking with me on this one! :)

    A complete example (excluding the SDK functions) for loading and getting information from an ALG file in microStation VBA is:

    Sub ALGinfoDump()
    
        Dim prjPtr As Long
        
        inRoadsInitialize ' function loads inroads and sets INROADS_RUNNING to true if success
                
        If INROADS_RUNNING Then
        
            ' Load ALG:
            inRoadsLoadALG prjPtr, "[INSERT .ALG FILENAME]"
            
            Debug.Print "Project count:" & scadSDK_cogoCountNumberOfProjects ' should be 1 now
                    
            Dim sFilename As String
            sFilename = Space(ALGFILSZ)
            
            Dim sName As String
            sName = Space(ALGNAMSZ)
            
            Dim sDesc As String
            sDesc = Space(ALGDSCSZ)
                    
            If scadSDK_cogoIsValidProjectPointer(prjPtr) = SDK_TRUE Then
            
                SCadCogo.scadSDK_cogoProjectInfoGet _
                    StrPtr(sFilename), _
                    StrPtr(sName), _
                    StrPtr(sDesc), _
                    prjPtr
                
                Debug.Print "File:" & sFilename
                Debug.Print "Name:" & sName
                Debug.Print "Desc:" & sDesc
                
                ' Try to get at the alignments
                
                Dim algs As moreLongs2 ' Simple UT with just long in it
                Dim countHorizontalAlignmentsInProject As Long
                Dim featcount As Long
                
                countHorizontalAlignmentsInProject = scadSDK_cogoCountNumberOfHorizontals(prjPtr)
                scadSDK_cogoGetHorizontalAlignments algs, countHorizontalAlignmentsInProject, prjPtr
    
                Dim alignmentCounter As Integer
                
                For alignmentCounter = 0 To countHorizontalAlignmentsInProject - 1
                    If SCadCogo.scadSDK_cogoIsValidAlignmentPointer(algs.surfPtr(alignmentCounter)) = SDK_TRUE Then
                        
                        sFilename = Space(1024)
                        sName = Space(1024)
                        sDesc = Space(1024)
                        
                        SCadCogo.scadSDK_cogoAlignmentInfoGet _
                            StrPtr(sName), _
                            StrPtr(sDesc), _
                            featcount, _
                            algs.surfPtr(alignmentCounter)
                        
                            sName = Left$(sName, InStr(1, sName, vbNullChar) - 1)
                            sDesc = Left$(sDesc, InStr(1, sDesc, vbNullChar) - 1)
                        
                        Debug.Print "Alignment number " & alignmentCounter & ":" & _
                            sName, _
                            sDesc, _
                            featcount
                    End If
                Next alignmentCounter
            End If
            
            ' release the ALG project:
            scadSDK_cogoRemoveProject prjPtr
        
        End If
        
    End Sub

    System: Win7 64bit 16GB Ram - microStation V8i SS3 08.11.09.578. + PoinTools CONNECT. - Intel i7-4800MQ CPU@2.70GHz, 4 core / 8 Logic proc.

Children