Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

I am trying to connect a classic System Dynamics tool (Vensim DSS) with Mathematica using a DLL that is included in Vensim DSS. Reading though the instructions on how to define DLL functions on how to declare arguments - here strings - I found that if there are [out] strings one should use the .NET class System.Text.StringBuilder.

So applying the advice given will look like this:

(* definitions *)
$maxBufLen = 1000;
strOutput = NETNew["System.Text.StringBuilder", $maxBufLen];
vensimGetInfo = DefineDLLFunction["vensim_get_info", vensimDLL,
"int", {"int", "System.Text.StringBuilder", "int"}];

(* usage *)
infoWanted = 3; bufLen = $maxBufLen;
vensimGetInfo[infoWanted, strOutput, bufLen];

Unfortunately this only works properly for a single string and some information will be returned by the DLL function as a series of null terminated strings that is finally ended by a double null. In such a case the above procedure will only return the first string omitting the rest.

What can be done?

IMO, this is really a Windows/.Net question rather than a Mathematica issue per se. Relevant: blogs.msdn.com/b/oldnewthing/archive/2009/10/08/9904646.aspx, snipplr.com/view/27336. The answer would seem to be that System.Text.StringBuilder does not support this type of string and something else should be used instead. I personally have no experience with .Net, so I will let someone else make the suggestion as to what that should be. –  Oleksandr R. Nov 24 '13 at 12:26

1 Answer 1

up vote 5 down vote accepted

First off, I should mention that Vensim also has a Java interface that you could call using J/Link. The Java signature of this particular function is

static String[] get_info(int infowanted);

so you can see that the different strings are conveniently returned in an array for you.

But you can also do what you want using .NET/Link's DefineDLLFunction. You have to drop down to low-level memory allocations. Declare the buffer that will be filled with the strings as an IntPtr:

vensimGetInfo = DefineDLLFunction["vensim_get_info", vensimDLL, "int", {"int", "IntPtr", "int"}];

Then allocate a buffer of a size large enough to hold all the data:

buf = Marshal`AllocHGlobal[$maxBufLen];

Call the DLL function:

infoWanted = 3;
numBytes = vensimGetInfo[infoWanted, buf, $maxBufLen];

One way to get the data from buf is to copy it into a .NET array, and then use NETObjectToExpression to convert it into a list of character values.

managedArray = NETNew["System.Byte[]", numBytes];
Marshal`Copy[buf, managedArray, 0, numBytes];
bytes = NETObjectToExpression[managedArray]

bytes will look something like {100, 105, ..., 0, 97, 100, ..., 0, 105, 102, ... 0, 0}. Use whatever method you like to convert these character values into separate strings, such as

FromCharacterCode[DeleteCases[SplitBy[bytes, # != 0 &], {0..}]]
Thanks a lot, Todd, that pretty much looks like THE answer to the problem. For completeness I will also point out a solution using the DLL function "vensim_get_substring" which Ventana posted on the Vensim Forum: vensim.com/documentation/index.html?26210.htm –  gwr Nov 25 '13 at 11:26

This site is currently not accepting new answers.

Not the answer you're looking for? Browse other questions tagged .