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];
strOuput@ToString[] 

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?

share|improve this question
2  
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
add comment

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:

LoadNETType["System.Runtime.InteropServices.Marshal"];
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..}]]
share|improve this answer
    
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
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.