Search code examples
vbscriptcharacter-encodingwixwindows-installerfilesystemobject

Currupted file in non-english locale (encoding problem?)


In my MSI Windows Installer I have a custom VBScript action which extracts some files from the 'Binary' table to the filesystem. This is the code I'm using:

Inspired by: https://www.itninja.com/question/how-to-call-an-exe-which-is-stored-in-a-binary-table-through-a-vbscript-custom-action-in-the-msi

Function ExtractFromBinary(ByVal binaryName, ByVal binaryOutputFile)

 Dim oFSO : Set oFSO = CreateObject("Scripting.FileSystemObject")

 Const msiReadStreamInteger = 0
 Const msiReadStreamBytes = 1
 Const msiReadStreamAnsi = 2 
 Const msiReadStreamDirect = 3

 Dim binaryView : Set binaryView = Session.Database.OpenView("SELECT Data FROM Binary WHERE Name = '" & binaryName & "'") 
 binaryView.Execute

 Dim binaryRecord : Set binaryRecord = binaryView.Fetch 
 Dim binaryData : binaryData = binaryRecord.ReadStream(1, binaryRecord.DataSize(1), msiReadStreamAnsi) 
 Set binaryRecord = Nothing

 Dim binaryStream : Set binaryStream = oFSO.CreateTextFile(binaryOutputFile, True, False) 
 binaryStream.Write binaryData
 binaryStream.Close
 Set binaryStream = Nothing 

End Function

This has been used without any issues in production for 2-3 years now. However now we have a case on a Japanese Windows installation where the extracted binary files are corrupted:

enter image description here

As you can see, the problem typically after a '?' where the script either inserts an 'E', or overwrites the following character.

Both the ReadStream method and the CreateTextFile method have a parameter which affect encoding. The combination shown above seems to be the only one which works on my English Windows 10.

What do I need to change in the code above to make it work also on a Japanese system?


Solution

  • Here is what I ended up with.

    As suggested by Stein Åsmul I rewrote the custom action using C# (.NET / DTF). Initially I was hesitant to writing custom actions in C# as it introduces additional prerequisites to the installer. But it turns out that if the custom action targets .NET Framework 2.0, it should be supported on most machines without the need to manually install the framework (see here).

    So here is my code:

    public static class TemporaryFilesExtractor
    {
    
        [CustomAction]
        public static ActionResult ExtractTemporaryFiles(Session session)
        {
            ExtractFromBinary(session, "binaryname1", "<filePath1>");
            ExtractFromBinary(session, "binaryname2", "<filePath2>");
            return ActionResult.Success;
        }
    
        private static void ExtractFromBinary(Session session, string binaryName, string binaryOutputFile)
        {
            session.Log($"Extracting {binaryName} to {binaryOutputFile}");
            byte[] buffer = new byte[4096];
    
            using (var view = session.Database.OpenView("SELECT Data FROM Binary WHERE Name = '{0}'", binaryName))
            {
                view.Execute();
                using (var record = view.Fetch())
                using (var dbStream = record.GetStream(1))
                using (var fileStream = File.OpenWrite(binaryOutputFile))
                {
                    int count;
                    while ((count = dbStream.Read(buffer, 0, buffer.Length)) != 0)
                        fileStream.Write(buffer, 0, count);
                }
            }
        }
    
    }