cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
CloudMonkey
Level 3

Convert a float to a string

I'm building a Basic MSI installer that needs to read a float from a file and then convert it into a string. No math operations are required. I can't find a working solution which only uses InstallScript and the win32 api. The code snippet below is what I'm currently trying, but the installer aborts when it executes the wsprintf function. It appears that my use of the win32 function is incorrect, but I can't find a working example. I don't get a useful error message, just the standard dialog stating the installer encountered an error. Any help would be greatly appreciated.

prototype user32.wsprintf(BYREF STRING, BYREF STRING, POINTER);

export prototype Test(HWND);
function Test(hMSI)
STRING text;
STRING chars; // This variable will simulate data read from a file
STRING temp;
begin
// This will crash
temp = "hello";
wsprintf(text, "%s", &temp);
MessageBox(text, INFORMATION);

// This will also crash
chars[0] = 0xAA;
chars[1] = 0xBB;
chars[2] = 0xFA;
chars[3] = 0x43;
wsprintf(text, "%f", &chars);
MessageBox(text, INFORMATION);
end;
Labels (1)
0 Kudos
(5) Replies
Kelter
Level 10

Make sure you specify the calling convention in the prototype.
0 Kudos
CloudMonkey
Level 3

I added the calling convention specifier, and the wsprintf function no longer causes the installer to crash. stdcall is the standard win32 calling convention, but this function uses the cdecl calling convention. However, I'm still not getting the desired/expected results. My modified code is below. For each test, the result (the value of the text string) is the same as the format string, except for test 1B. For that test, the result should be "% %" and the format string is "%% %%". Since all of the other tests are returning the format string, I was expecting this test to return "%% %%", but instead it is returning either "%% % " or "%% %X" (where X is a strange arrow character). I'm not sure the reason for getting two different (but both incorrect) results for this test. The change occurs when I reorder the tests.

prototype cdecl user32.wsprintf(BYREF STRING, BYVAL STRING, POINTER);

export prototype Test(HWND);
function Test(hMSI)
STRING text;
STRING chars; // This variable will simulate data read from a file
STRING temp;
NUMBER a;
begin
// Test 1A
temp = "Something";
wsprintf(text, "%%", &temp);
MessageBox(text, INFORMATION);

// Test 1B
temp = "Something";
wsprintf(text, "%% %%", &temp);
MessageBox(text, INFORMATION);

// Test 1C
temp = "Something";
wsprintf(text, "%% %% %%", &temp);
MessageBox(text, INFORMATION);

// Test 2
a = 4;
wsprintf(text, "The number value is: %u", &a);
MessageBox(text, INFORMATION);

// Test 3
temp = "hello";
wsprintf(text, "The string value is: %s", &temp);
MessageBox(text, INFORMATION);

// Test 4
chars[0] = 0xAA;
chars[1] = 0xBB;
chars[2] = 0xFA;
chars[3] = 0x43;
wsprintf(text, "The floating point value is: %f", &chars);
MessageBox(text, INFORMATION);
end;
0 Kudos
Kelter
Level 10

I haven't used InstallScript in a while, but I think you want to use a "wstring" instead of a "string" since you are passing it into functions that expect unicode.
0 Kudos
CloudMonkey
Level 3

The code provided below correctly prototypes and uses the wsprintf function. However, I now believe that converting a float to a string is not possible (using just InstallScript and win32). The wsprintf function does not support floats or doubles, but using msvcrt.sprintf and other functions does not work either. InstallScript passes all of it's variables as 4 byte values so passing a double is not possible. All of the available value to string functions taking a float/decimal make use of varargs. Unfortunately, varargs places 4 byte floats on the stack as 8 bytes. The value of the float can't be passed as a NUMBER and then converted by the called external function. If a ftoa (float to ascii) function existed, it would be possible to convert a float to a string, but a dtoa (double to ascii function) would still not work.

prototype cdecl user32.wsprintf(BYREF WSTRING, BYVAL WSTRING, POINTER);
export prototype Test(HWND);
function Test(hMSI)
WSTRING text;
WSTRING temp;
NUMBER a;
begin

// Test 1A
temp = "Something";
wsprintf(text, "%%", &temp);
MessageBox(text, INFORMATION);

// Test 1B
temp = "Something";
wsprintf(text, "%% %%", &temp);
MessageBox(text, INFORMATION);

// Test 1C
temp = "Something";
wsprintf(text, "%% %% %%", &temp);
MessageBox(text, INFORMATION);

// Test 2
a = 4;
wsprintf(text, "The number value is: %i", a);
MessageBox(text, INFORMATION);

// Test 3
temp = "hello";
wsprintf(text, "The string value is: %S", &temp);
MessageBox(text, INFORMATION);
end;
0 Kudos
Kelter
Level 10

If the values you are reading from the file exceed the domain of a UINT, then write your own custom action DLL and implement the logic to read, manipulate, and store the non-IS-conformant datatypes. If any of this data is necessary outside of this custom action, pass the float value back as a string. if it needs to be manipulated as a float, then you'll have to write the logic to do that.

If .NET is a requirement of your product already, then I'd recommend a Managed Custom Action DLL, but otherwise, just make an MSI DLL that exports all of the custom logic that you need.


I avoid InstallScript at all costs. While it's very easy to whip some script together and have it compile and build with the rest of the package, C/C++ is more powerful, works outside of IS, it's easier to write unit tests for it, it's easier to debug on the target system, it doesn't tie you to InstallShield, more people know it, more people use it, more people can help you with it, there's less of a chance that you'd need help with it, it's been vetted in the field for the past 40 years, and you can do absolutely anything with it... if you need any more reasons to avoid InstallScript just let me know, and i'll think of a few more.
0 Kudos