Sunday, October 21, 2007

Windows XP Visual Style Controls with wxRuby

wxRuby uses the native Microsoft Windows common controls when displaying widgets on a Windows OS. You may have noticed, however, that the form controls do not have the Windows XP look and feel. They have the flat and 2-dimensional look of earlier Windows versions...



...rather than having the rounded corners and 3-dimensional look of Windows XP...



Note, among other things, the "glowing button" effect when the mouse hovered over the "Get Films Data" button.

This is because Windows XP comes with both version 5 and version 6 of the Common Controls, but unless specifically instructed to use version 6, it will default to using version 5 controls.

So, how do you instruct Windows to use the version 6 controls for your wxRuby GUI application? By including a simple manifest XML file with your application.

Copy the following text into a new text file. Save the file as "rubyw.exe.manifest" in your ruby/bin folder, the same folder that contains rubyw.exe. You might also save a copy to the same folder as "ruby.exe.manifest".


<?xml version="1.0" encoding="UTF-8" standalone="yes"?>

<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity version="1.8.6.0" processorArchitecture="X86"
name="Microsoft.Winweb.Ruby" type="win32"/>
<description>Ruby interpreter</description>
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="X86"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
</assembly>

When the ruby.exe or rubyw.exe interpreter is run, Windows will look for a manifest file matching the program name in the same folder. When it finds our manifest file, it will then use the version 6 controls, as defined in the <dependency> section of the manifest.

We place the file in our c:\ruby\bin directory because that is where ruby.exe and rubyw.exe are located. We name the file ruby.exe.manifest or rubyw.exe.manifest to match the name of the interpreter being run.

If you are compiling your application with RubyScript2Exe, copy the manifest file into the folder with your script, then embed the manifest file in your compiled executable using the RUBYSCRIPT2EXE.bin statement:

RUBYSCRIPT2EXE.bin = ["rubyw.exe.manifest"]

When your compiled executable is run, your manifest file will be extracted to the same temporary "/bin" folder as the rubw.exe (or ruby.exe) interpreter.

Questions? Comments? Suggestions? Post a comment here or send me email.

Thanks for stopping by!

Digg my article

Sunday, October 7, 2007

Hide & Seek: Using NTFS Alternate Data Streams

The NTFS file system utilized by Windows (NT and later) includes support for Alternate Data Streams (ADS). ADS was implemented in NTFS to provide compatibility with Apple's Macintosh Hierarchical File System (HFS), which uses resource forks to store icons and other information for a file, but such streams can be used to store any type of data that a normal file would store. You could, for example, use an ADS to store metadata about the file to which the ADS is attached. You could even store binary data inside an ADS. This might be useful for storing backup versions of a file before making changes.

To work with an alternate data stream for a file, simply append a colon and the stream name to the filename. The following code appends a stream named 'stream1' to file.txt:


open('file.txt:stream1', 'w') do |f|
f.puts('Your Text Here')
end

If file.txt did not exist, it would have been created and would be 0 bytes in size, since no data was written to the main stream. But you can append streams to an existing file, as well. The reported size of the file would not change, though it now includes alternate data streams of embedded data.

Reading from a stream is just as simple:

stream_text = open('file.txt:stream1').read

Alternate data streams can also be used for storing binary data, including executables:

bytes = open('MyApp.exe', 'rb').read
open('file.txt:MyApp.exe', 'wb') do |f|
f.write(bytes)
end

bytes = open('file.txt:MyApp.exe','rb').read
open('MyApp2.exe', 'wb') do |f|
f.write(data)
end

A file can contain multiple data streams, so you could, for example, include a data stream for text and another stream for binary data.

Another possible use for ADS would be to create a backup of the file which could be restored later:

def create_backup(filename)
open("#{filename}:backup", "wb") do |f|
f.write(open(filename, "rb").read)
end
end

def restore_backup(filename)
if not File.exist?("#{filename}:backup")
puts("Backup stream does not exist!")
return
end
backup = open("#{filename}:backup", "rb").read
open("#{filename}", "wb") do |f|
f.write(backup)
end
end

Note: A file's alternate data streams will be preserved on the NTFS disk, but would be stripped off of a file when copied to a non-NTFS disk, such as a flash drive or CD/DVD disk; or when a file is copied via ftp. For this reason you may not want to rely on alternate data streams for storing critical data.

There you have it. Sound interesting? Further details on Alternate Data Streams can be found here and here.

Thanks for stopping by!

Digg my article