I've been a whole day looking for a way to read a binary file from the local file system, do some modifications to it and save it back to disk. The script must be run on batch, since other scripts must be run afterwards.
Reading is not an issue: you can always read it as text, and then convert the characters to bytes. The problem is writting it to disk. ActiveXObject("Scripting.FileSystemObject")
facility can't be used, since some bytes don't map to characters and then Write
methods throw an exception.
I've read a post, somewhere else suggesting to use ADODB.Stream
and this is as far as I can go:
var foo = ...
var stream = new ActiveXObject('ADODB.Stream');
stream.Type = 1; //Means "binary".
stream.Open();
stream.Write(foo);
stream.SaveToFile('C:\\foo.bin', 2); //2 means save/create/overwrite
Regardles of which type of variable I put in foo
, Windows Script Host claims:
Error: Arguments are of the wrong type, are out of acceptable range, or are in conflict with one another.
Code: 800A0BB9
Source: ADODB.Stream
It seems that ADODB Stream.Write method expects A Variant that contains an array of bytes to be written. Since such things doesn't exist in Javascript, I tried with an array filled with numbers, a string, a single number, a single char, an hex expression... I've found VBArray but you can't actually write anything in those.
Does anybody know which type must be used? Or any other mean to save binary data?
Adapted from ADODB.Stream writing binary file, JScript only. Commented for explanation in code.
var adTypeBinary = 1
var adTypeText = 2
var adSaveCreateOverWrite = 2
var stdout = WScript.StdOut;
// Read a binary file, particularly for debugging purposes
var inStream = new ActiveXObject("ADODB.Stream");
inStream.Type = adTypeBinary;
inStream.Open();
inStream.LoadFromFile("d:\\bat\\cliParser.exe");
inStream.Position = 0;
var binData = inStream.Read();
inStream.Close();
stdout.WriteLine( "binData " + typeof(binData)); // returns: "undefined"
// Convert binary value of "undefined" data type to "string" data type
var objRS = new ActiveXObject("ADODB.Recordset");
var DefinedSize = 1024; /* A Long value that represents the defined size,
in characters or bytes, of the new field. Fields that have a DefinedSize
greater than 255 bytes are treated as variable length columns. */
var adFldLong = 0x80; /* Indicates that the field is a long binary field.
Also indicates that you can use the AppendChunk and GetChunk methods. */
var adVarChar = 201; /* Indicates a long string value. */
objRS.Fields.Append("test", adVarChar, DefinedSize, adFldLong);
objRS.Open();
objRS.AddNew();
objRS.Fields("test").AppendChunk(binData);
var binString = objRS("test").value;
objRS.close();
stdout.WriteLine( "binString " + typeof(binString)); // returns: "string"
// String is now manipulable (unlike "undefined" data type)
// Write string to a file (converting string in one-byte encoding schema)
var outStreamW = new ActiveXObject("ADODB.Stream");
outStreamW.Type = adTypeText;
// Charset: the default value seems to be `UTF-16` (BOM `0xFFFE` for text files)
outStreamW.Open();
outStreamW.WriteText(binString);
outStreamW.Position = 0;
var outStreamA = new ActiveXObject("ADODB.Stream");
outStreamA.Type = adTypeText;
outStreamA.Charset = "windows-1252"; // important, see `cdoCharset Module Constants`
outStreamA.Open();
outStreamW.CopyTo(outStreamA); // convert encoding
outStreamA.SaveToFile("D:\\test\\fooRus.exe", adSaveCreateOverWrite);
outStreamW.Close();
outStreamA.Close();
// Done. Make certain of input and output file oneness!
Output:
==> cscript D:\VB_scripts\JScripts\33330187my.js
binData unknown
binString string
==> echo n|COMP "d:\bat\cliParser.exe" "D:\test\fooRus.exe" 2>NUL
Comparing D:\bat\cliParser.exe and D:\test\fooRus.exe...
Files compare OK
==>