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.
Type longString s(256) As ByteEnd Type
The provided helpfiles says this about the two functions, but there is a mismatch between the descriptions and the SDK examples.
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
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
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 :)
Labyrinth Technology | dev.notes() | cad.point
Jan Slegr 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.
Torben 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.
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), _ featcountEnd If
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 :)
a few more notes, maybe it will make this topic clearer...
Torben 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:
Torben 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.
Torben 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 ;-)
Answer Verified By: Torben
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:
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
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)
sName = Left$(sName, InStr(1, sName, vbNullChar) - 1)
sDesc = Left$(sDesc, InStr(1, sDesc, vbNullChar) - 1)
Debug.Print "Alignment number " & alignmentCounter & ":" & _
' release the ALG project:
Hi Torben, it's good to know it works now! I personally would refactor the code a bit, but as long as it works, it's fine ;-) Regards, Jan