Tuesday, August 11, 2009

Nesting implicit styles in WPF

All I wanted to do was define a style at Page level to control the font size of Label controls, and then define other styles for parts of the page where Label controls need to be further customised.

e.g. set font size to 8pt at the top-level, and for some parts of the page, set each label to a specific width.

I Google-d around and thought the Style BasedOn attribute should do it for me.

Alas, it appeared not to.

But then I noticed a major bug in the VS.NET 2008 XAML designer : it was not properly applying style changes!

If I changed the text in a Label control, the XAML designer would reflect that, but if I changed relevant styles, the XAML designer would often miss it.

And that's where I realised : BasedOn IS the key, it's just that the VS.NET XAML designer is so pathetic that you have to close and re-open any XAML document you are editing in order to see the effect of nested style changes.

And of course, to make matters worse, VS.NET is famous for its idiotically slow load times for XAML files in large projects that contain lots of XAML files. So, hello lots more time being wasted now that we're forced to close and re-open XAML files just to get the designer to refresh properly...

Clarity Consulting wrote a nice little piece showing how to use BasedOn with nested implicit styles. The missing piece of the puzzle is simply the need to close and re-open the XAML designer each time you make a change. Ugggh. But at least it eventually gets to the desired effect...

Saturday, June 27, 2009

Flash AS3 Dictionary - bliss!

I came in on ActionScript at version 3, and with a strong javascript and .NET background.

Compared to javascript (which I quite like), I think ActionScript is even better. (They both have major issues, but I still quite like them, even if I hate the Flash software development tools which are "crappy as"...)

Sometimes you need to iterate a set of ActionScript object instances, and keep a record of some piece of data for each object instance.

In javascript it's easy - just add a new dynamic data field to each object instance.

But ActionScript adds the concept of "sealed" classes, which prevent the dynamic addition of new data fields.

So we're stuffed?

Well, there is a solution. Only one, but it works very well.

It's the new ActionScript 3 Dictionary.

It is an associative array keyed not by a string but by an object instance.

I use Dictionary objects all the time in other languages, so I was hoping ActionScript might have one, and I finally went looking for it, and found it yesterday.

Here are two articles I felt did a great job of introducing this powerful Dictionary object :

Viva la Dictionary!

Microsoft Silverlight stumbles at the gate

I have in the past predicted that Adobe Flash will lose out to Microsoft Silverlight.

But Microsoft keeps slipping up in one key area : ubiquity.

Silverlight was supposedly a technology that would run everywhere - including on mobile devices.

But Microsoft can't even get Silverlight running on their own Windows Mobile platform, let alone any other mobile devices.

On the other hand, Flash has Flash Lite, which is already on millions of handheld devices worldwide.

Yes, the Microsoft development tools actually work reliably, and yes, the Flash development tools are riddled with bugs, poorly documented, and priced uncompetitively.

But with the meteoric rise of handheld devices, and the lack of Silverlight support for any handheld devices (despite announcements over the past two years or so that "it's coming"), Microsoft is giving Adobe a massive breather.

A breather that may cost Microsoft years in their attempt to dominate this particular market.

If Adobe would make their tools and documentation at least as good as Microsoft's, then they could justify their price structure, and leave few incentives for anyone to use Silverlight.

But on the other hand, if Adobe continues to prove incompetent in QC for software development tools and documentation, and Microsoft finally gets their act together with the long-promised and I'm-growing-tired-of-waiting-for "Silverlight for mobile", then my original prediction stands, and Silverlight will move out of the realm of predominantly line-of-business apps, more and more into the mainstream, until the days of Adobe's dominance are a distant memory like the Commodore 64.

Thursday, June 18, 2009

JetStart - the end of Start Menu clutter

JetStart is awesome!

It's rare that I'm yelling in excitement just reading about a product, or tripping over myself in my eagerness to tell the world about an amazing piece of software when I've only used it for two minutes, but such is the privileged case with JetStart.

As a programmer, I have dozens of tools & utilities installed over the years, and navigating my Start Menu is a nightmare. Painus Maximum, as my wife likes to say.

So often I find myself saying "I wish I could just type a few letters and see every Start Menu entry that matches". It would be so much quicker to start IZArc or PDF Creator or any of my other occassionally-used tools if I didn't have to pore over an entire screen covered by my sprawling Start Menu.

JetStart is my dream come true. Its integration into the Windows experience is neat and inobtrusive.

And it's fast.

Press the Start key, then start typing. Instant response!

Now THIS is good software. Yeah baby!

Top kudos to the guys at codesector.com who made it.

And no, its free, and I'm not getting any kickbacks for raving about it. I reckon you'll rave too if you're a power user like me with an overbloated Start menu. Give it a shot - what've you got to lose? :o)

Tuesday, June 9, 2009

Adobe Subscription Manager hiccups and workarounds

Adobe has released Adobe Creative Suite CS4 Design Edition on a Subscription basis to the Australian market only. It's an experiment, presumably, to determine whether it's worth doing on a worldwide basis.

It has a few unique glitches I had to fight with myself, so for the benefit of those who come after...

-

1) There is ABSOLUTELY NO STANDARD WAY to install the Subscription Edition if you have an expired trial version installed at the same time.

For example, I had a trial version of Flash.

The Subscription Edition installer said "oh - you already have Flash installed, so I don't need to install it again". But the problem is, the Flash that is installed with the Subscription Edition is a slightly different version of Flash that knows how to accept subscription-based license keys and otherwise interoperate with the Adobe Subscription Manager. The Flash trial, in contrast, will never recognise the Adobe Subscription Manager, even if your subscription is fully paid.

i.e. you're forced to uninstall everything, then re-install using the Subscription Edition DVDs.

A pain, but at least it works...

-

2) If your subscription expires, and you want to renew it, Adobe documentation says you need to use the Adobe Subscription Manager which, supposedly, is installed on your computer.

The problem is, there is no link to the Adobe Subscription Manager. Anywhere.

Even doing an exhaustive search of "C:\Program Files\Adobe" (including subfolders) failed to find it, using the wildcarded search term "*sub*.exe", and when that failed, the wildcarded search term "*man*.exe".

So I called Adobe tech support.

I note that they have a special phone queue just for Subscription Edition customers.

Hmmm - smacks of there being a lot of other people experiencing problems...

... but to their credit, I was speaking to a human within about 30 seconds! I was very impressed.

They wanted to know my customer number, which took me a while to dig out, but when finally they had linked me to my order, they were ready to answer any questions I might have.

I asked how to find the Subscription Manager. The woman on the other end found the answer very quickly - they must have a good search tool for their internal help docs, or else it must be a very common question (or both) - and it lives in C:\Program Files\Common Files\Adobe\Adobe Subscription Manager CS4 and is called AdobeSubscriptionManager.exe. Hurrah!

So I double-clicked.

And nothing happened.

Something fishy going on here...

I opened Task Manager and saw a program called ADOBES~1.EXE. Ah! I remembered seeing that earlier in the day. Suddenly it dawned on me - the Adobe Subscription Manager is probably supposed to be launched automatically, but at least in my case, it failed to ever show a UI, and got stuck running in the background.

But because there was one stuck instance of Adobe Subscription Manager running in the background, it prevented any new instances from starting.

Easy fix - I killed ADOBES~1.EXE using Task Manager, and double-clicked AdobeSubscriptionManager.exe again.

Waiting...

And after several seconds, huzzah! There it was.

I thanked the Adobe technician and proceeded to reactivate my subscription.

-

So there you have it, ladies & gentlemen - some little known issues with Adobe Subscription Manager, and corresponding workarounds.

Wednesday, May 20, 2009

Using Adobe BridgeTalk synchronously

So let's face it : writing synchronous code is a lot easier than writing asynchronous code. I can write asynchronous code easily enough, but it looks so much more cluttered, and/or takes so many more lines of code. I'm a big believer in simplicity.

So, if you want to whip up a simple script, and it doesn't need to win design awards, then I suggest you check out xbytor's cool little trick for using BridgeTalk synchronously. (Search on that page for "Send a synchronous message".)

Nice one, xbytor.

And in case that forum goes down, here's a copy of the most pertinent bit. (See the original thread for more questions, answers and examples.)

First, some utility code :

// Send a synchronous message. The result is returned.
// If a result doesn't come back in 'timeout' seconds, undefined is returned.
BridgeTalk.prototype.sendSynch = function(timeout) {
var self = this;
self.onResult = function(res) {
this.result = res.body;
this.complete = true;
}
self.complete = false;

self.send();

if (timeout) {
for (var i = 0; i < timeout; i++) {
BridgeTalk.pump(); // process any outstanding messages
if (!self.complete) {
$.sleep(1000);
} else {
break;
}
}
}

var res = self.result;
self.result = self.complete = self.onResult = undefined;
return res;
};
// for typos, provide an alias
BridgeTalk.prototype.sendSync = BridgeTalk.prototype.sendSynch;

And now an example of how to use it :

function test() {
var bridgeApp = "bridge-1.0";

if (!BridgeTalk.isRunning(bridgeApp)) {
BridgeTalk.launch(bridgeApp);
}

var bt = new BridgeTalk();
bt.target = bridgeApp;
bt.body = "new Date().toString()";
var res = bt.sendSynch(10);
alert(res);
};

test();

Once again, mega kudos to xbytor. Some coding purists might gag at the internal polling loop, but remember, we're not talking about production scripts released to thousands of users worldwide - we're talking about helping every developer be more productive by writing their own scripts more easily.

Adobe BridgeTalk woes

As just another look at how mind-numbingly idiotic a large proportion of Adobe APIs are, consider this attempt to use BridgeTalk :

var bt = new BridgeTalk();
bt.send();
alert('The message was sent successfully.');

We haven't specified a recipient, and we haven't specified an actual message.

But run the code, and guess what? Apparently, the message was sent successfully.

Especially for such a poorly-documented class as BridgeTalk is, developers rely on meaningful error messages (including input validation error messages) to figure out what's going wrong and how on God's beautiful green earth we're supposed to use the thing.

My original script looked more like this :

function BridgeTalkResultHandler(result)
{
alert('Hurrah! The callback came!' + result.body);
}
var bt = new BridgeTalk();
bt.targetApp = 'illustrator';
bt.body = 'alert("hello");';
bt.onResult = BridgeTalkResultHandler;
bt.send();
alert('The message was sent successfully.');

Translation : get Illustrator to show a message. Just an elementary test.

But plug this into the ExtendScript ToolKit and run it, and all you get is meaningless nonsense.

'The message was sent successfully' is given every time you run it, but Illustrator doesn't budge.

Well, thought I, perhaps Illustrator refuses to interact with the user (e.g. with the alert method) under some circumstances. So I changed the script to app.activeDocument.close(), and I left a document open in Illustrator, so I could see if something happened. Still nothing.

I removed the onResult handler to see if somehow that was causing the problem. Nothing changed.

And I stripped out more and more things, until I got to the code at the start of this article.

The behaviour is identical, for every version of the script.

In other words, BridgeTalk is doing absolutely nothing - not even any validation.

I even tried crazy things like :

bt.targetApp = 'illustraaaaaaaaaaaator';

... but there wasn't any complaint about the non-existent target.

Examples of BridgeTalk are not as plentiful as one might wish for, but my original code looks very much like the few examples I found.

All I can say is : this is typical of Adobe APIs. Poorly documented, counter-intuitive, and requiring wasteful trial-and-error to master.

If it was open-source, or some cheapo product, I'd excuse it. But when they want to charge thousands for their suite of products, you expect a LOT better than this.



Got it working - bt.target is the correct property name, not bt.targetApp. I'm not sure whether I read a bad example, or whether I misread an example, but in either case, my thesis stands - Adobe APIs are a dog to work with except only when everything is working perfectly. A helpful input validation error message, such as 'target property is required', would have saved me valuable time! C'mon Adobe, you know you can do better than this... What's worst is that Microsoft is now actually pretty good at making meaningful error messages (something they were hopeless at ten years ago) - so it's possible to have a moderately-self-documenting API, and that means Adobe has dropped the ball.

Tuesday, May 19, 2009

Automate Adobe Flash using VB.NET or C#

Adobe is very thin as usual on technical details, so I've had to poke around and try different things.

Here's what I've found :

* .NET can communicate directly with Illustrator, but not directly with Flash, nor with BridgeTalk.

* .NET can send javascript to Illustrator, including javascript which utilises BridgeTalk to communicate between Illustrator and Flash.

* So it is possible to automate both Illustrator and Flash using VB.NET/C#/other-.NET-languages, but it requires .NET <-> Illustrator <-> BridgeTalk <-> Flash.

* In VB.NET, you connect to Illustrator as follows :

Dim app As Object = CreateObject("Illustrator.Application")

* To get Illustrator to run arbitrary javascript :

app.DoJavaScript(myJavaScript);

e.g. to get Illustrator to greet us :

app.DoJavaScript("alert('Hello!');");

* To get Illustrator to return a useful value from a script we have sent it, don't use the "return" keyword - just include the return value as the final expression in the script block.

e.g. :

Dim ReturnValueFromIllustrator As Object = app.DoJavaScript("1 + 2;")
MsgBox(ReturnValueFromIllustrator) ' Shows "3"

* The .NET <-> Illustrator leg is synchronous, even though the Illustrator <-> BridgeTalk <-> Flash legs are ASYNCHRONOUS. (Yeah - just to make life more interesting.)

Hmmm - in short, I wouldn't say it's "easy", but it is possible.

Maybe Adobe will enter the 21st century sometime and really make all their apps easily cross-scriptable, and their scripting engines a lot more robust. Until then...

... your brother in the pain of scripting Adobe products.



P.S. If you're using C#, or if you're using VB.NET and want a better Intellisense experience (and better runtime performance, and better compile-time checking) than just using VB.NET's CreateObject method, add a reference to the "Adobe Illustrator CS4 Type Library". Once you've done that, you can connect to Adobe Illustrator as simply as "Dim app As New Illustrator.Application" (or in C#, "Illustrator.Application app = new Illustrator.Application();").

ExtendScript: instanceof Number

var xyz = 123.45;
alert(xyz instanceof Number);

Always false. Not very intuitive at first.

Searching for ExtendScript instanceof Number didn't help, but searching for Javascript instanceof Number did.

Unfortunately, the short of it is that we have to resort to the slowness of string comparisons. C'mon - I thought this was the 21st century!

var xyz = 123.45;
alert(typeof xyz == 'number');

But there are situations where instanceof Number DOES work :

var xyz = new Number(123.45);
alert(xyz instanceof Number); // true

... and in such instances, the typeof trick DOESN'T work :

var xyz = new Number(123.45);
alert(xyz instanceof Number); // true
alert(typeof xyz == 'number'); // false

There is a reason for it, and when you understand the reason, it makes sense enough, but it does prove that javascript and all its derivatives are a little idiosyncratic and obscure at times.

For more info :

StackOverflow - why does instanceof return false for some literals?

More technical info (hands-on) about javascript and instanceof

Thanks to the two articles I just listed! They helped solve for me what was seeming a great (and annoying) mystery.

Sunday, May 17, 2009

Running two Engin voiceboxes on the same network

Again, a post that'll only help a very few of you, but for the sake of those few...

Engin is a fabulous VoIP (internet telephony) company. We have two accounts with them - one for our business, and one for our home.

Everything was working perfectly at our old address, with TPG as our ISP.

We've relocated, and TPG doesn't cover our outlying suburb, so we're now with Telstra.

Problem : With Telstra's ADSL2+ modem, only one of our VoIP lines works properly.

Specifically, both make outbound calls just fine, but inbound calls only work consistently for one of the two lines.

If I reset the Engin voiceboxes (which internally are SipuraSPAs), and wait a few seconds for them to re-register with Engin, then inbound calls work fine for both lines, for about five minutes, and after five minutes, its just the one line that inbound calls work for.

I contacted Engin tech support last week, and they changed one of my Engin voiceboxes to use the "bring-your-own" Engin settings. I thought that fixed the problem, but it turned out that the test call had been made within five minutes of a "soft reset" of the Engin device, and that's the only reason the test inbound call worked at that time. i.e. problem not solved.

Today I got it working, at last, and permanently.

It's not intuitive, but I set up two port forwarding ranges on my ADSL2+ modem, one for UDP ports 16384 through 18383, and the other for UDP ports 18384 through 20384. I pointed one port forwarding range to the one Engin voicebox, and the other range to the other voicebox.

It's about half an hour later, and inbound calls for both lines are still working perfectly. Nice...

In my previous attempts to resolve this issue, Google-ing for "two Engin voiceboxes" and such like turned up a grand total of zero people discussing problems with running two Engin voiceboxes side-by-side. So hopefully, for the next person who needs a little help, this post might provide a few ideas...



UPDATE : A few hours later, and I discovered it's NOT working, but I figured out why, and here's the low-down :

It turns out that the port forwarding DIDN'T HELP ONE BIT.

But I've pinpointed exactly what the problem is :

The Engin voiceboxes re-register with Engin every nearly-30-minutes. (It's every roughly 29 and a half minutes.)

But the Telstra ADSL2+ router closes UDP "connections" after about 300 seconds (five minutes) of inactivity.

So every half hour, there is a five minute window of opportunity during which inbound calls will work, and then nearly 25 minutes where they won't.

My confirmation test earlier today must have been in that five minute window of opportunity.

The fix?

Well, I trawled through the options in the admin panel for this Telstra ADSL2+ modem, and don't seem to have any power to change the default timeout.

So I contacted Engin tech support again, and asked them to remotely log in to my machine and change the default timeout on both my Engin voiceboxes down to 280 seconds. That gives them roughly 20 seconds (far more than enough) to execute their re-registration process before the ADSL2+ router deems their UDP "connection" disposable. The re-registration process resets the modem's five minute timer, and this process, continually repeating itself, twelve times an hour, ensures that inbound Engin calls will work at any time.

And I've removed those ADSL2+ port forwards, as they are now completely useless...

Thursday, May 14, 2009

lsass.exe high CPU usage and disk I/O

One of my old computers has had a problem for YEARS.

Windows XP, service pack 2.

Within minutes of reboot, lsass.exe goes to near 100% CPU utilisation, and sits there for sometimes around half an hour.

Rendering the computer pretty much unusable.

ALL attempts to resolve the problem had failed.

And after wasting lots and lots of time, I resigned myself to simply hibernating not rebooting where possible, and losing half an hour of time every time I wanted to reboot the computer.

Killing lsass.exe fails, because the Task Manager informs you that it is a system process.

Trying to reduce its priority, so it hogs less CPU, fails for the same reason.

It is exactly the same file (same file size, and same file bytes - I used fc.exe to compare) as the lsass.exe on my other Windows XP computers - none of which exhibit this problem.

I haven't found a solution, but things have improved now.

1) Earlier today, based on comments I saw on one website, I went into the properties for each hard disk, and unchecked the "Allow Indexing Service to index this disk for fast file searching" option. When I clicked "Apply", it asked whether I want to apply this change to all subfolders as well, and I said yes. It took ages, but it did finally finish applying the change.

2) Note that I had already long prior disabled the Indexing Service itself.

3) Also earlier today, based on suggestions on another website, I completely disabled the Terminal Services service. (I also disabled Fast User Switching, prior to disabling said Terminal Services service.)

lsass.exe still hogs CPU. It's I/O read and write count goes up to over a hundred thousand. But then it stops. At approximately the same I/O read/write count each time. And once it stops, my computer runs fine.

And fortunately, I don't know what's changed, but it only seems to take about five minutes now. Which is really stupid. But much more bearable than half an hour.

It doesn't seem to be a worm or trojan or anything such like. Antivirus scans turn up nothing.

handle.exe reports exactly the same files open by lsass.exe each time I run it. That's a lot of I/O to generate for the same set of files!!! Unless it's doing some sneaky direct-to-disk I/O and bypassing the usual file-handle system...

And besides, what's with all those writes? Well over a hundred thousand disk writes within the first five minutes of bootup?

Google-ing for lsass.exe issues reveals lots of people experiencing problems, but it turns up remarkably few solutions.

I haven't found a complete solution, but perhaps somehow between unchecking that "Allow Indexing Service ..." option, and disabling the Terminal Services service, it has taken a bite out of the ridiculous amount of time that lsass.exe now wastes each time I boot...

Cross-scripting Adobe Illustrator and Adobe Flash

21st century, expensive products, same company makes them all, and sells them all in the same package.

You'd think they'd play happily together.

And Adobe documentation here and there hints that they do.

But finding out how is mighty difficult.

You fire up the ExtendScript Toolkit, and the documentation makes it sound like you can do almost anything from there.

Play around with Adobe Illustrator scripting in the ExtendScript Toolkit - sweet!

But now try to script Adobe Flash...

... oops, um, well, there doesn't really seem to be a way to do it.

Does Adobe document that you can't? Well, not that I've managed to find.

In fact, one other commentator stopped short of giving a definitive answer that Flash isn't supported by the ExtendScript Toolkit, and instead just said that it seems it isn't.

C'mon Adobe, give us clear, informative, unambiguous documentation!

In fact, if you put "#target flash" in a script in the ExtendScript Toolkit, and try to run it, it actually launches Flash, and then tells you that it failed to connect to the Flash scripting engine. What the???

An Adobe brochure about Flash CS3 claims that it is now compatible with ExtendScript, but frankly, I don't see how, and we're now up to CS4!!! ("Flash CS3 also adds ExtendScript and BridgeTalk support for cross-application scripting.")

So after many hours of wasted time, I've figured out how to make Illustrator and Flash sing to each other.

I'm not impressed by the obtuse steps required.

And I'm not impressed by the extremely non-definitive documentation on Adobe's part.

Here's how you do it :

1) Your Adobe Illustrator scripts will be written using the ExtendScript language, whereas your Adobe Flash scripts must be written using a language called JSFL. Both languages are javascript-based, and have many similarities, but they also have many differences. (e.g. native filesystem access in ExtendScript is done via the File class, whereas in JSFL it's done via the FLfile class. Hello, any reason for that, anybody?)

2) Adobe Bridge is not Adobe BridgeTalk. The names are very similar, but the use is very different, and unfortunately, the difference is not clearly articulated in Adobe docs.

Adobe Bridge is a moderately useless file cataloging and file browsing tool.

In contrast, Adobe BridgeTalk is a simple but powerful inter-process communication tool.

So, we're going to use Adobe BridgeTalk to communicate between Illustrator and Flash.

3) It is very hard to find Adobe BridgeTalk documentation. (e.g. Google-ing for "adobe bridgetalk reference" is close to useless.)

I suspect this is in part because BridgeTalk is SO simple that there's hardly anything to document, and thus, Adobe is content to let developers learn by example.

And learn by example is how I had to do it.

4) One of the best sources of examples of Adobe BridgeTalk scripting is actually in the Adobe Bridge SDK. Yes, that sounds confusing, but it's true.

Download said Adobe Bridge SDK. It's about 4MB of compressed files. Unzip, and inside are a few interesting PDFs, and a bunch of jsx files showing cool tricks using BridgeTalk (amongst other things).

(I suggest you read the short section "Communicating with Other Applications" in the "Bridge CS4 JavaScript Guide.pdf" that comes in the docs folder in the Adobe Bridge SDK.)

5) In brief, BridgeTalk sends javascript as a string, from one application to another (e.g. Illustrator to Flash), and sends the value of the last-executed-statement in said script back to the calling application. It's a bit obtuse, but it's very powerful.

6) Note that BridgeTalk is ASYNCHRONOUS. And there is no synchronous mode. So you have to be patient with writing the extra code to handle breaking a single synchronous procedure into smaller asynchronous steps.

7) There is an extremely-poorly-documented means of launching ANY application via BridgeTalk. e.g. you can run a Windows batch file, or trigger your source code versioning system, or invoke a custom tool you wrote in a non-Adobe language. You can pretty much run anything you like.

It's based around a function called "system".

As with most Adobe stuff, this "system" function is extremely poorly documented.

The only way I've found to use it is to call app.system('command-line.exe bla bla'); from within Bridge.

So - get this - if you want to fire up a useful external program (e.g. source code control) from within a script that is automating Illustrator or Flash, you need to use BridgeTalk to send a script to Bridge, and the script you send to Bridge needs to include the app.system(<command-line>) invocation. Talk about convoluted!

But at least it works.

Oh - and app.system(...) is SYNCHRONOUS, unlike BridgeTalk which is ASYNCHRONOUS. Just to make sure your neurons are really getting a workout...

8) If you've understood everything to this point, and want a cool trick to make it much easier to generate lengthy scripts that get shared between different Adobe products, you can use the toSource() function, as described by Justin of Ajar Productions in an article entitled "Easy (and Readable) ExtendScript to JSFL Workflow".

Remember : JSFL = scripting the Flash* development ("authoring") environment, whilst ExtendScript is used e.g. for Illustrator. Justin's article covers writing an ExtendScript for Adobe Illustrator, that also uses BridgeTalk to get Flash to do some things, yet all within a single script file, and without much messy code. Very nice work, Justin!

(* Technically, JSFL is used for a few other products, not just Flash, but for my present purposes, it is accurate enough to say JSFL = scripting the Flash development environment.)



Conclusion?

Adobe makes things horribly painful to learn.

Oh - and to use. e.g. obtuse error messages.

But despite the pain along the way, you can actually accomplish a huge amount. If you have spare hair and a high pain threshold, you might find (as I am finding) that ExtendScript, JSFL and BridgeTalk are extremely rewarding tools to be able to use.

Wednesday, May 6, 2009

BSOD, rdbss.sys, network dropouts, unwise_.exe, and the Win32/Heur virus/trojan

This will only help a very small number of you, but for the sake of these few...

I have lost HOURS and HOURS this week with the internet connection on my email computer dropping out.

"Repair Connection" worked a few times, but in the last few days even that stopped helping.

Switching between LAN and wireless also helped initially, but in the past few days, not at all.

It got to the point where I would lose ALL network access within a few minutes of boot.

Just long enough to check email once.

And it got to the point where I could only "fix" it by rebooting.

So get this : my (slow) email machine, that takes roughly five minutes to do a shutdown/reboot cycle, had to be turned off and turned back on practically every time I wanted to check email or send an email!!!!!!! Very frustrating!

(Nor was it just a case of "set & forget". The laptop requires password entry at two points during the boot cycle, so rebooting it is a major distraction.)

I tried using Internet Explorer's diagnose-connection-problems tool. It informed me that "Windows has detected a problem with the winsock provider catalog on this computer", and offered to repair the problem. But that didn't help.

At around the same time, I began to find that the laptop would bring up the infamous "Blue Screen Of Death" (BSOD) during shutdown. Every time. Yet another source of pain in the email-checking routine.

And of course, I'm used to checking email at least every hour, as it is a primary means of communication with customers...

The BSOD was a red herring. It said that rdbss.sys had been unloaded "without canceling pending operations" - whatever that means. I searched and searched, and of course, found nothing that helped.

I tried resetting the Winsock stack - as suggested by some articles - but the problem persisted.

What if... what if this is a symptom of a common virus?

I haven't had a virus on any of my computers in something like six or seven years.

I don't actually have a virus scanner installed - they usually slow a computer down horribly.

As a very technically-oriented programmer, I am very familiar with the limitations of technology, and instinctively manage to steer clear of most sources of viruses.

But whatever was going on with my email computer was very strange, and it was wasting hours and hours, so I decided to give it a virus scan.

AVG became my tool of choice.

I downloaded it onto another computer and transferred the installer across via USB stick.

Fortunately, it allowed me to install without needing to access the internet - e.g. for updated virus signatures.

Pretty quickly, the virus scan turned up a "Win32/Heur" virus in a file called "C:\Windows\fonts\unwise_.exe". Aha! Likely the culprit!

And sure enough it was. More on the virus in a moment, but I used Task Manager to kill the running instance of unwise_.exe, and within minutes, my network access was working properly again!

It might even be that unwise_.exe and its Win32/Heur were not directly killing the network, but perhaps were engaging in network activity that my network router found objectionable. Or maybe the problem was indeed entirely within that infected laptop.

Either way, virus gone, problem solved.

And the BSODs have disappeared too!

So if you're having strange network dropouts minutes after boot... it might just be a virus.

---

A couple of thoughts about this particular virus :

It uses the file name "unwise_.exe" - note the underscore. I find this intriguing. "unwise.exe" (no underscore) is (IIRC) the name of a popular program uninstaller that works with a very large number of programs. (Is it part of the "InstallShield" suite? I'm hazy on the details now...)

So presumably, the virus writers chose the name "unwise_.exe" (note the underscore) because of its similarity to "unwise.exe" (no underscore) - i.e. to try to make it look more innocuous.

But to the trained eye, it was every bit suspicious.

First up, whilst "unwise.exe" (no underscore) is a common name, "unwise_.exe" (note the underscore) is NOT a common name, and uncommon file names that are so suspiciously similar to common file names don't just happen by accident!

Secondly, the file had both the SYSTEM and HIDDEN file attributes set. Oh yeah - that means you won't actually be able to find it yourself, unless you do something like go to the Command Prompt and type something like "dir C:\Windows\fonts\*.exe /ah".

Thirdly, no executables ever live in the C:\Windows\fonts folder. That alone is almost sufficient proof that the file is malicious. But what a clever choice - the C:\Windows\fonts folder cannot actually be browsed in Windows Explorer like other folders can. If you launch Windows Explorer and navigate to C:\Windows\fonts, you'll see a special "fonts list" view, which fails to show the offending "unwise_.exe", even though it is in that folder.

So whoever wrote this "unwise_.exe" thing knew what they were doing, and went to great lengths to hide their malicious program's presence on affected systems.

Note further that this virus installs itself to start at system boot (or user logon - one or the other, and I'm not quite sure which) and to run under the SYSTEM account. That's another clue that it's a bad boy - the real "unwise.exe", being an uninstaller, and thus always launched by the USER not the SYSTEM, would not be found in the Task Manager running under the SYSTEM account.

---

Oh - and before you flame me - someone else had been using my email computer quite heavily around the time of the infection (1-May-2009 was the date of infection, based on the file datetime stamp on the C:\Windows\fonts\unwise_.exe file), and so whilst that probably means I need to install an anti-virus program permanently if I'm going to let them keeping using my computer, it does potentially vindicate my "I don't need an anti-virus program for myself" attitude of many years.

---

Thanks for reading! I hope this helps some of you...

Wednesday, April 15, 2009

Flash, translate transform, and x/y co-ordinates

"Self Documenting Code" is my mantra - code that is intuitive.

Flash is not.

I lost yet another hour or two trying to get a simple translate transform working. The doco fails to mention that :

If you set x or y for a DisplayObject, it effectively overwrites any translate transform in the current transformation matrix for the DisplayObject.

I hate to be harsh, but you could at least mention this in the documentation! XAML transformations apply in addition to any x/y co-ordinates set on an object. Not so in Flash.

And so if you apply a transformation, and then set a DisplayObject's x/y properties, you might be puzzled wondering why the translation component of the transformation is not taking effect.

That's why.

It makes sense enough when you understand it, but it proves once again that Adobe is often worse than useless at writing documentation and at designing intuitive APIs. Why did I have to spend an hour or two figuring this out for myself? It should have been documented, or been more apparent in the API itself.

Thursday, April 9, 2009

Flash automatically merges shapes at compile time

Here be dragons.

Flash automatically combines shapes at compile time.

So if you were expecting to place two or more shapes on the stage and be able to access them via the stage's getChildAt method, you're in for an interesting time.

It's not as simple as "Flash combines all shapes into a single shape". It seems to work based on z-index.

Suppose you add two shapes to the stage, then add a textbox, then add a third shape.

You'll discover at runtime that the first two shapes have been merged into one, but the third has not.

If you play with the z-index ("send to back", "send to front") in the Flash IDE, you'll discover that the ONLY thing that controls whether or not two particular shapes get merged at compile time is whether or not they are neighbours in the z-index.

(Technically, they need to be part of the same unbroken sequence of shapes in the z-index, so for example if you add three shapes to the stage in succession, they will be combined into a single shape at compilation time, because they will appear one after the other in the z-index.)

Unfortunately, I failed to find any documentation which mentions this, or any other Flash developers having their plans thwarted by this 'feature'. So I hereby post my "here be dragons" flag.

The solution? If you NEED the shapes to be drawn in Flash (as opposed to drawn in ActionScript at runtime) and you NEED them to be separate shapes at runtime, then cut them out of your document and paste them into a temporary document, then paste one shape back, then create an off-stage dynamic text field (to form a z-index divider), then paste in the next shape, and so on, and so forth.

Of course, it should be very rare that you need to do this kind of thing, but I came across such an instance today, and lost several hours due to this seemingly undocumented 'feature'.

Tuesday, April 7, 2009

Accessing your Ubuntu partition from Windows

Virtual Volumes is beta software, but for read-only access to an Ubuntu partition, its everything I need.

I have a relatively recent installation of Ubuntu, and it is brilliant. Its native support of NTFS means I can access all my Windows files from Ubuntu. But going the other way is trickier. Virtual Volumes fills this gap (for my simple requirements).

Virtual Volumes is a desktop app, not a kernel driver. For my purposes, that was a benefit, because it meant I didn't need to reboot after installation, and there is nothing consuming extra system resources when I'm not accessing my Linux partition.

The same guy who created Virtual Volumes also created a very popular tool called explore2fs, but for no apparent reason, it failed to work with my recent Ubuntu installation.

I also tried DiskInternals' Linux-reader which again, is popular and probably works for many Linux distributions, but failed to work with my recent Ubuntu installation.

In short, Virtual Volumes is well worth a try, either as your first choice, or as a backup option if other options fail.

(Thanks to UbuntuGeek.com for pointing me to the DiskInternals Linux-Reader tool.)

P.S. If you want a kernel-mode driver, fs-driver.org looks promising, but I can't vouch for it.

P.P.S. Huge kudos to chrysocome.net! Virtual Volumes is my friend. :o)

aspnet_compiler and its mysterious silent failures

Around the year 2000, Microsoft started actually doing some things extremely well.

Internet Explorer 4 was the most abysmal web browser ever to disgrace the human race, but Internet Explorer 5 was remarkably solid.

Windows ME was their buggiest and single worst desktop operating system ever, but Windows 2000 was impressive.

Visual Basic 6, ASP, VBA, COM and all that stuff was a bit same-old, same-old - a clunky development experience. But the .NET framework truly ushered in a new era of programming productivity.

The .NET framework itself is awesome. Microsoft has never produced anything better, in my opinion, than the .NET framework.

But within the .NET framework lives an incredibly buggy tool called aspnet_compiler.

Unfortunately, some of us are forced to rely upon it.

It frequently fails with no error message.

Nada on the console.

Nada in the event log.

Just absolute silence.

It pretends it has happily successfully finished, but in actual fact, something has gone wrong and it absolutely refuses to tell you about it.

And that wastes hours. And hours. And did I mention, hours.

Today I lost about an hour and a half. Aspnet_compiler would succeed if I excluded any one of my dozen or so subfolders in my web root folder. But if I tried to include all my subfolders, it would grind away as if it was working successfully, and then it would suddenly fail without any error message.

I got it to the point that I could compile successfully if I excluded any one of several files in one particular folder, but if I tried to include them all, it would fail with no error message.

But if I removed a different subfolder from the root folder, I could include all those files that previously I could include all-but-one of.

It was aspnet_compiler demonstrating its classic fickleness. I think Microsoft invented it for no other purpose than to waste developers' time. OK, maybe that's getting a little too harsh...

Anyhow, lo-and-behold, when I copied the entire problematic precompilation-input folder to another location and tried again, it failed, but this time with a compilation error message that at least I can do something about.

What was the problem?

It seems that aspnet_compiler is unable to cope with long file paths. It seems that once the absolute file path to any file in the precompilation input exceeds some threshhold, aspnet_compiler becomes very fragile, and sometimes succeeds, but with slightly different input, sometimes fails.

By copying my source code to a temporary folder near the root of my drive, I shortened the maximum file path, and thus avoided aspnet_compiler's file-path-length issues.

But that's not the only stupid thing aspnet_compiler is known to do.

For example, sure, at least I'm getting a compilation error now. But the compilation error I'm getting actually has nothing to do with any of the files which, if I exclude, it all compiles successfully. i.e. the thing its tripping over now is not a real issue. But at least it gives me something to focus more specifically on.

Such are the vagueries of life with aspnet_compiler.

But wait, there's more!

Suppose you have two ASPX files in the same namespace and with the same base class name, and suppose it works fine during testing in IIS before precompilation. All good and well, you'd think. And then you try to precompile. And aspnet_compiler dies saying nothing more than that it "failed with error code 1" (or is it 0? it's a while since I last had this problem, though I've had it too many times before). The cause? aspnet_compiler requires that all class names throughout the entire web app be unique within their respective namespaces. Fair enough. It's a reasonably intuitive rule now that you mention it. But I had to figure it out for myself. 'Coz aspnet_compiler doesn't tell you what's wrong in this instance - it just dies with that very obscure error message.

And so I regret that I ever bought Microsoft's hype that aspnet_compiler is now the best and recommended pathway to shipping websites. The tool is unstable, unreliable, and frequently complains about non-existent problems.

But aside from aspnet_compiler, I love the .NET framework. It's the best thing Microsoft has ever done.

Monday, April 6, 2009

Mail server setup, mailing lists, and Microsoft's "Smart Network Data Services"

I received a weird email today, and it first it looked like spam. It claimed to be from staff@hotmail.com, and I don't interact with Hotmail.com.

But on closer examination of the message, I realised there was something here worth investigating further.

Dear Jonathan Field,

Smart Network Data Services has determined that the trusted email address for your network 203.89.209.146 through 203.89.209.150 has either changed or the email address previously trusted is no longer valid. Consequently, your access to data for this network has been disabled, pending re-authorization.

Please visit the Smart Network Data Services site to remove the network and re-add the network with a valid trusted email address.

You are receiving this because you have signed up to be part of a preview release of Smart Network Data Services, or a Smart Network Data Services user has requested that this email be sent to this address. Smart Network Data Services is a revolutionary Windows Live Mail initiative, designed to allow everyone who owns IP space to contribute to the fight against spam and protect e-mail as a valued communications, productivity and commerce tool. If you have questions about our privacy policy, please read our privacy statement available at http://privacy.live.com.

I don't have a clue what "Smart Network Data Services" is, and I'm pretty sure I never signed up for a preview of it, but I Google it and it's actually quite interesting.

Smart Network Data Services home page.

Yeah - their website is hard to navigate, and poorly written, but there's some very useful data inside.

If you run a mail server and/or have clients with mailing lists, Smart Network Data Services promises to be of some assistance in the process of identifying how Hotmail treats messages from your mail servers.

I only just came across it now, and I found it extremely hard to navigate, so don't ask me for help using it, but I did manage to sign up and access an "IP address status" page - which thankfully told me I'm not a spammer. :o)

Its sad but true that managing a legitimate mailing list or mail server in the 21st century requires highly developed technical skills, and constant monitoring of the ever-changing anti-spam landscape. Within that context, Microsoft's Smart Network Data Services is a new tool to help keep the emails flowing.

Flash vs SilverLight : SilverLight will win (my prediction)

I've been living in Adobe Flash / ActionScript land for the last few weeks, and there has been a very unexpected side-effect.

Today, when I was working in Microsoft Visual Studio again, it was a joy to use!

I always used to think Microsoft Visual Studio was HIDEOUSLY slow and inefficient - and it is - but after inflicting Adobe Flash on myself for a week and a half, Visual Studio seems absolutely awesome.

As yet another example of the sheer stupidity which pervades Adobe Flash and its implementation of ActionScript :

1) Grab a big Adobe Flash project - you know, one that takes ten seconds to build.
2) Open it in Flash CS4.
3) Press Ctrl+Enter to run it in test mode.
4) Close the runner.
5) Press Ctrl+Enter to run it again, as if you just wanted to check out one more thing.

Guess what?

Adobe Flash is so stupid, that it doesn't notice that nothing has changed since the last build, and so it rebuilds the project, wasting more time.

And it doesn't seem to have any incremental build either. If you have a large project, and change one tiny thing ANYWHERE in the project, you face the entire laborious build process once again.

On a fast (3ghz QuadCore) computer, I am finding Flash makes debugging a major chore by making tedious to iteratively change and test.

Visual Studio in contrast? Do a debug run, and it will build and run. Run again without changing anything? It knows it doesn't need to rebuild, so it starts instantly. Change anything? It supports incremental build to a certain extent.

It makes Adobe Flash look like it comes from the 90s, or earlier. Except, that it's actually Adobe Flash CS4 - the very latest and greatest in 2009. Ouch - that sucks.

Oh - and I thought Visual Studio was bad, because the incremental build is actually quite limited. Turns out, Flash seems to have no incremental build at all.

And don't talk about load times - I thought Visual Studio load times were bad. But after Flash, it feels zippy.

And today, Flash crashed on me literally, hmmm, about ten times. I mean, crashed, dead, have to restart the app, and sorry about any unsaved work. That kind of crash.

Visual Studio is INCREDIBLY inefficient, but at least it hardly ever crashes. And Flash's inefficiencies make Visual Studio look like a racecar.

Add to this the documentation errors and erroneous compiler errors that define Flash 10 with ActionScript 3, and you have the perfect recipe for the most imperfect program I ever imagined could wear the Adobe name.

If it were a house, the "for sale" sign out front would say "full of potential".

Translation : it needs a LOT of work.

My bet?

Adobe will drop the ball (or continue to drop the ball).

SilverLight will win.

And it will take roughly 8 to 10 years to happen.

Sunday, April 5, 2009

Graphical add/commit in Bazaar without the slowness of TortoiseBZR

How can you quickly & easily pick which files you want to add to Bazaar?

I've had too many painful experiences (not Bazaar's fault) of going "bzr add", and then discovering that amidst the dozens of files I DID want to add, there were a whole bunch that I DIDN'T.

Removing them is a bit of a pain - I tend to use very long self-descriptive folder names and file names, so if a big "bzr add" operation added dozens of files, and I needed to manually remove perhaps a dozen or more, it was very time inefficient.

Fortunately, Bazaar has the answer! I just didn't discover it until yesterday.

bzr qadd

That little "q" at the beginning of the "add" command brings up a GUI window, allowing you to very quickly & easily select exactly which files you want added.

Bazaar, I love you! :o)



P.S. You can also use "bzr qcommit", which is a FABULOUS way to do a partial commit. And "bzr qdiff" if you want a diff that is easier to read than "bzr diff".

UPDATE : The preceding works fine if you used the Windows installer for Bazaar. If you are using Linux, you need to install a Bazaar plugin called QBzr - see bialix's comments on this article.

Flash SimpleButton, and MOUSE_OVER and MOUSE_OUT not firing

Adobe Flash documentation is often worse than useless. Useless would be "tells us nothing we didn't already know". WORSE than useless is "tells us information that is NOT correct, causing us to waste time".

Their flash.display.SimpleButton is the culprit once again.

I was dynamically generating SimpleButton instances in ActionScript 3, and adding MOUSE_OVER and MOUSE_OUT event handlers.

But the event handlers were never firing.

I reviewed the documentation - the SimpleButton definitely supports the MOUSE_OVER and MOUSE_OUT events.

I tried adding the event handlers to the flash.text.TextField instances which formed the button states, but that didn't work either - the events simply never ever fired.

I finally figured out what's going wrong. It's that SimpleButton constructor once again.

Here's what the doco says (false info - worse than useless) about the SimpleButton constructor :

Any or all of the display objects that represent the various button states can be set as parameters in the constructor.

ANY OR ALL ... CAN BE. In other words, it is OPTIONAL to set the various states using the constructor.

In a previous blog post, I noted that, despite the CLAIM that the constructor arguments are optional, the first argument is actually MANDATORY.

If you don't specify the first argument, the button never transitions between states, even if you set the states later using the upState, overState etc properties.

But if you DO specify the first argument, the up-and-over animation works correctly.

That was from that previous blog post.

Well, the new lesson for today's blog post is that in fact ALL FOUR arguments to the constructor are MANDATORY if you wish to get MOUSE_OVER and MOUSE_OUT events from the SimpleButton.

It doesn't make sense, and it makes mockery of their (poor as too often the case) documentation, and in fact makes mockery of Adobe's ability to design an intuitive API, but it is the case.

The one saving grace is that we can specify the SAME DisplayObject for all four constructor arguments. e.g.

var sb:SimpleButton = new SimpleButton(MySprite, MySprite, MySprite, MySprite);

(And of course, the other way you can do it is subclass SimpleButton and set the upState, overState etc properties in your own constructor. Basically, by the end of the constructor, those values must be set.)



As a final note, I was just about to paste the simple test code I whipped up, to make it extremely easy for you to reproduce my test results, but as I did so, Adobe Flash suddenly died on me and completely disappeared from the taskbar without even its usual "Adobe Flash just died" popup message.

All I can say is that I am disgusted with Adobe Flash - not only is the documentation often worse than useless, but the program itself is fragile, and crashes on me usually more than once per work day.

And this is Flash CS4 - their latest and greatest...

Friday, April 3, 2009

Merging unrelated branches in Bazaar

The skinny : 'cd <working-tree-to-merge-into>', then 'bzr merge <branch-to-merge-with> -r0..-1', and then 'bzr commit "Merged with {bla}"'.

I continue to love Bazaar.

I've wondered for a while : "How can I merge two completely unrelated branches?"

Scenario : I'm working on a large website with an extensive ASP.NET section, and an extensive Flash section. The ASP.NET section is very mature. The Flash section has just begun. I want to be able to version control the Flash stuff in the same repository as the ASP.NET stuff, but in unrelated branches, and eventually merge the branches once the Flash stuff matures somewhat.

Can do! But it took a bit of figuring out.

Along the way, I kept bumping my head against this error message from Bazaar :

bzr: ERROR: Branches have no common ancestor, and no merge base revision was specified.

Suppose my folders were :

C:\BzrRepo\AspDotNetStuff

and

C:\BzrRepo\FlashStuff

I was trying to do :

C:\BzrRepo\AspDotNetStuff> bzr merge ..\FlashStuff

That wasn't working, so I tried using the --revision= option to specify a base revision (as hinted by the error message) :

C:\BzrRepo\AspDotNetStuff> bzr merge ..\FlashStuff --revision=1

Still no go.

So I looked at the --change= option.

C:\BzrRepo\AspDotNetStuff> bzr merge ..\FlashStuff --change=1..27

(27 being the latest revision number in the FlashStuff branch.)

Still no go - it tells me that the --change= option cannot handle revision ranges - it can only handle one specific revision number.

OK, so what if I try to integrate just the very latest change?

C:\BzrRepo\AspDotNetStuff> bzr merge ..\FlashStuff --change=27

That didn't work either. Instead, it created lots of conflicts.

Ah - finally I think I see the light!

C:\BzrRepo\AspDotNetStuff> bzr merge ..\FlashStuff --change=1

Yes! Progress! It worked! i.e. I have to start with the very first change in the unrelated branch.

Now will it let me merge the remaining changes?

C:\BzrRepo\AspDotNetStuff> bzr merge ..\FlashStuff

Nope - it complains that I have uncommitted changes.

Easily fixed :

C:\BzrRepo\AspDotNetStuff> bzr commit -m "Merged with FlashStuff revision 1"

Good.

And now let's try the merge again :

C:\BzrRepo\AspDotNetStuff> bzr merge ..\FlashStuff

Bonanza! It worked!

And I have now successfully merged two COMPLETELY UNRELATED branches that lived under the same repository.

In summary, the quick way to do it :

C:\BzrRepo\AspDotNetStuff> bzr merge ..\FlashStuff --change=1
C:\BzrRepo\AspDotNetStuff> bzr commit -m "Merged with FlashStuff revision 1"
C:\BzrRepo\AspDotNetStuff> bzr merge ..\FlashStuff
C:\BzrRepo\AspDotNetStuff> bzr commit -m "Merged fully with FlashStuff"

And I'm a happy camper. :o)



UPDATE : Thanks to bialix (in the comments) for pointing out a simpler way to accomplish the same thing :

C:\BzrRepo\AspDotNetStuff> bzr merge ..\FlashStuff -r0..-1
C:\BzrRepo\AspDotNetStuff> bzr commit -m "Merged with FlashStuff"

Slightly different syntax, accomplishes the same thing, but more concise. Did I mention I love Bazaar?

Thursday, April 2, 2009

Flash bugs: dynamic TextField and letterSpacing issue

I'm creating TextField instances in ActionScript, modeled after one TextField instance in the Flash UI designer.

There are two stupid problems.

Problem 1:

The first is that Flash is very forgetful.

Suppose I go :

tb.text = 'Some new text';

Just like that, key TextField settings are lost! Idiotic. Things like letterSpacing get reset to zero.

The solution (noted by others) is to :

var tf:flash.text.TextFormat = tb.getTextFormat();
tb.text = 'Some new text';
tb.setTextFormat(tf);

If that makes sense to anyone, I suppose there probably are airborne sows.

But it kinda does the trick. Kinda.

Enter: problem the second

If I'm doing all this with an embedded font, the aforesaid hack DOES NOT WORK.

At least, not without a bit more effort.

I had the original textbox, built in to the Flash stage. Let's call it tbOriginal.

Then I have my dynamic textboxes, modelled off the original.

tbCreatedAtRuntime.text = 'Some text here';
tbCreatedAtRuntime.setTextFormat(tbOriginal.getTextFormat());

Nice - does the trick, except that the letterSpacing is shot.

So I play with the letter spacing. I try all sorts of values.

tbCreatedAtRuntime.text = 'Some text here';
var tf:flash.text.TextFormat = tbOriginal.getTextFormat();
tf.letterSpacing = 0.55; /* I tried lots of different values here. */
tbCreatedAtRuntime.setTextFormat(tf);

The problem was that, regardless of what letterSpacing value I set, it would often make no difference whatsoever to the overall length of the string.

But it was weirder than that. Once I got down to about 0.54, the string length jumped down very significantly. It was like the letterSpacing had a very course rounding applied, such that there was only one "real" letterSpacing option per 0.5 increment of the letterSpacing value.

But I knew this couldn't be the total story, because the tbOriginal was using a letterSpacing of 0.75, and on screen it was longer than tbCreatedAtRuntime with letterSpacing around 0.5, and shorter than tbCreatedAtRuntime with letterSpacing around 1, and as stated earlier, tbCreatedAtRuntime would treat letterSpacing of 0.75 the same as letterSpacing of 1.0.

What gives?

Fortunately, whilst not intuitive (after all, this is Flash isn't it?), the documentation did this time give me the clue I needed.

It turns out that Flash only supports integer values(1) for letterSpacing for "device fonts". My brain whirred away for a microsecond and I realised what the problem was.

I was using a special font, but I hadn't explicitly told my dynamically-created TextField to use font embedding. I just presumed that, since it got its TextFormat from tbOriginal, it would get the same font embedding as tbOriginal. Not so.

So now I explicitly tell tbCreatedAtRuntime to use the same font embedding as tbOriginal :

tbCreatedAtRuntime.embedFonts = tbOriginal.embedFonts;

... and now at last, letterSpacing works as expected. Whew!




But can you permit me to lament once more just how STUPID this design (or lack of design) is?

Adobe might be good at graphics, but they are very bad at development tools and APIs.

What SHOULD happen is that if ANY text field ANYWHERE in the Flash file has embedded a font, and ANY other text field anywhere else in the same movie (or even created dynamically) attempts to use the same font, the embedded font should be automatically applied to that other text field.

Now, failing that, at the very least, copying the TextFormat from one text field to another should ensure that if the first had an embedded font, the second uses the same embedded font.

Adobe, lift your game! Microsoft's APIs make yours look pathetic. You can do better than this!

(1) The integer-letter-spacing-for-device-fonts limitation is mentioned in the Flash 8 documentation. I'm using Flash 10, and I am surmising that Flash 10 supports 0.5 increments, in contrast to the Flash 8 doco which claims only 1.0 increments are supported in Flash 8.

Flash bugs: dynamic SimpleButton gotcha

I'm creating a SimpleButton in ActionScript and it ain't showing up.

var b:flash.display.SimpleButton = new flash.display.SimpleButton();
b.upState = bla;
b.overState = bla;
b.downState = bla;
b.hitTestState = bla;
b.useHandCursor = true;
this.addChild(b);

The problem is, the button, she no appear. Nada.

I thought maybe my problem is that I'm using the same sprite for the different states, but changing that didn't make any difference.

Turns out, Flash is proving its inferiority once again.

The SimpleButton constructor, it so happens, accepts four OPTIONAL arguments, being the four SimpleButton states.

Given that they are OPTIONAL, you would think you could omit them ALL and just set the four state properties after construction.

You'd think so. And so did I.

But it turns out that, even though the documentation and the in-editor code helps all say the states are optional, THE UP STATE IS ACTUALLY MANDATORY.

Ridiculous.

In short, this works, because it supplies the mandatory-though-pretending-to-be-optional UpState argument to the SimpleButton constructor :

var b:flash.display.SimpleButton = new flash.display.SimpleButton(bla);
/*b.upState = bla; - no longer needed*/
b.overState = bla;
b.downState = bla;
b.hitTestState = bla;
b.useHandCursor = true;
this.addChild(b);

And of course, you might prefer to just save lines and go :

var b:flash.display.SimpleButton = new flash.display.SimpleButton(bla, bla, bla, bla);
b.useHandCursor = true;
this.addChild(b);




On a related note, I was trying to figure out WHY Flash is so STUPID in this regard. (Stupidity and unintuitive behaviour are more common than I would have expected in ActionScript. I really honestly thought Microsoft's XAML was passable and that Adobe would be as good or better. Sadly, Adobe is actually far behind M$ in the 'we make things that actually make sense' game, even if M$ itself isn't perfect.)

My first guess was that the constructor determines the SimpleButton's dimensions from the upState, and thus, when no upState is passed to the constructor, the SimpleButton has no idea how big it needs to be.

If that were the case, I could simply set the SimpleButton's width and height, and it would work.

But no go.

Here's the code :

var b:flash.display.SimpleButton = new flash.display.SimpleButton();
b.width = 100;
b.height = 100;
b.upState = bla;
b.overState = bla;
b.downState = bla;
b.hitTestState = bla;
b.useHandCursor = true;
this.addChild(b);

And it is as dead and useless as the very first code listing in this article.

In short, it seems that the SimpleButton is COMPLETELY UNABLE TO HANDLE UPSTATE BEING OMITTED FROM THE CONSTRUCTOR INVOCATION.

Which makes me ask why Adobe's documentation is so poor that there is no comment about it. And why their compiler doesn't complain when that argument is missing from the constructor invocation. And in fact why, overall, they make such an unintuitive design.

Here's what I would have done, since I believe in making APIs as flexible as possible :

1) All constructor arguments are optional.

2) Button width and height are calculated just-in-time, or at least, calculation is deferred until upState gets set for the first time on a given SimpleButton instance.

Voila! Talk about intuitive! In contrast, I really don't know how Adobe muck up like this, and at multiple places throughout their API. Shame, Adobe. Shame. Please lift your game.

Sunday, March 29, 2009

Flash ain't so flash

I thought Microsoft SilverLight was just a rip-off of Adobe Flash, and so I wanted Flash to remain dominant.

I finally started learning Flash, and have decided it sucks compared to SilverLight.

Flash is great for creating pre-defined animations, but for programming complex dynamic animations, it has issues.

Worse yet, it's code editor is EXTREMELY hard to use.

And the ActionScript compiler even produces incorrect error messages.

All up, whilst a week ago I had high hopes of adding my own bit of momentum to the Flash technology, I am now inclined to believe that SilverLight will annihilate it, due if nothing else to vastly better development tools for complex programmatically-controlled animations.

So, for example, I wanted to accomplish the simple task of creating a button in Flash that, when clicked, would respond visually so I knew I was correctly handling the mouse click event.

I decided to make my button turn semi-transparent when clicked - just an elementary visual confirmation that my event handler fired.

I drew my button (an arrow) with the crappy drawing tools in Flash. (I see now why the average Flash artist would start in Illustrator, and only go to Flash once all their artwork was finished.)

I converted it to a symbol, and in the library, chose the "Button" type (instead of MovieClip or Graphic).

All should be well, or so I thought. I then proceeded to use the stock-standard ActionScript 3.0 Flash CS4 button event handling code :

function myHandler()
{
myButton.alpha = 0.1;
}
myButton.addEventHandler(MouseEvent.CLICK, myHandler);

Right off the bat, the editor is TERRIBLE. Microsoft has this thing called "Intellisense", that I just took for granted. But seeing that one of their chief rivals, namely Adobe, can't create something even half as good, makes me realise that Microsoft is actually quite good at creating code editors.

Oh - for sure - Flash has an "Intellisense" equivalent, but only if you declare a strongly-typed variable. But why should I have to do that? Flash already knows that "myButton" is a reference to that button on-screen, so why should I have to declare yet another variable just to get strong typing?

Anyhow, I dutifully do so :

var myStronglyTypedButtonReference:? = myButton;
function myHandler()
{
myStronglyTypedButtonReference.alpha = 0.1;
}
myStronglyTypedButtonReference.addEventHandler(MouseEvent.CLICK, myHandler);

Snag. What type to use?

Again, the code editor is crappy to the max.

Fortunately, there is a very-slow-to-use (no search functionality) class list on the left-hand-side of the editor. It is categorised, which means that whilst my intuition tells me I probably need to find a class called "Button", I need to search through package after package after package. Oh - at the very end there is an "Index" option, which I use. On my powerful computer, it still takes too many seconds to prepare the index, but I am finally rewarded. In the index I find a Button class, and it is in the fl.controls package.

var myStronglyTypedButtonReference:fl.controls.Button = myButton;
function myHandler()
{
myStronglyTypedButtonReference.alpha = 0.1;
}
myStronglyTypedButtonReference.addEventHandler(MouseEvent.CLICK, myHandler);

You'd think that would work. But no.

Flash now complains that "Type was not found or was not a compile-time constant: Button". Fat lot of good that is!

I search and search. The answer almost everyone gives is "just add a button to your stage then delete it straight away, and this will add the necessary library code to your project".

Ummm - but don't I already have a button on my stage? I mean, this arrow I drew is very definitely a button.

I add another copy of the arrow button from the Library to the ActionScript layer, just in case that will help, but the problem persists.

Prompted by one web page, I look at my class path, but that has nothing to do with the problem.

Finally, I find a web page that gives me the clue I need. Check out WarpZone's contribution here. Turns out you need to open the "Components" window, and drag a "Button" control from that "Components" window onto your stage.

So I do it.

And now Flash complains that it can't convert a SimpleButton to a Button!!!

Can I just say that this is really really really stupid. In SilverLight, it is quick & easy to determine the data type of any control on the "stage". But in Flash, it seems you have to just guess, then look for compiler error messages or runtime error messages that would indicate if you guessed wrong!

So I need to use "SimpleButton", not "Button".

var myStronglyTypedButtonReference:fl.controls.SimpleButton = myButton;
function myHandler()
{
myStronglyTypedButtonReference.alpha = 0.1;
}
myStronglyTypedButtonReference.addEventHandler(MouseEvent.CLICK, myHandler);

Problem : SimpleButton is not in the fl.controls package. Groan. Back to the Index. Oh - guess what - it's so dumb it hasn't cached the results of the last time I opened the index. So it spends way too much time once more opening the index.

But at least I can find SimpleButton without too much more pain. It lives in flash.display.

var myStronglyTypedButtonReference:flash.display.SimpleButton = myButton;
function myHandler()
{
myStronglyTypedButtonReference.alpha = 0.1;
}
myStronglyTypedButtonReference.addEventHandler(MouseEvent.CLICK, myHandler);

Extremely unintuitive so far! But surely I have arrived, no?

But no.

When I run, Flash tells me :

ArgumentError: Error #1063: Argument count mismatch on Untitled_fla::MainTimeline/myHandler(). Expected 0, got 1.

Wait a moment - expected ZERO, got ONE?

Adobe, do you know how to write compiler error messages?

My function takes ZERO arguments. If ZERO are expected, then things should work fine!

But, suspecting sloppiness on Adobe's part, I figure that they probably intended to say "expected ONE, got ZERO". So more searching on the web, and I find a code example that uses a single argument of type Object for event handlers. I try it, and at last it works :

var myStronglyTypedButtonReference:flash.display.SimpleButton = myButton;
function myHandler(eventObj:Object)
{
myStronglyTypedButtonReference.alpha = 0.1;
}
myStronglyTypedButtonReference.addEventHandler(MouseEvent.CLICK, myHandler);

Once again, were it SilverLight, it would have been much quicker & easier for me to identify which argument(s) are required for the event handler, and what data type each argument needs to be.

Oh - and SilverLight does at least tend to give ACCURATE error messages.

And this is just a small sliver of my Flash woes. It is horribly unintuitive.

Flash, I wanted to support you, to cheer you on in the race against Microsloth, but I am deeply shocked, saddened, and surprised to find that from a programmability perspective, Microsoft has already won the race. You suck.

I hope it doesn't stay this way.

UPDATE : Despite its many faults and failings, Flash can do really powerful things when you finally figure out how to do things. (It's the "finally figure out" process that Adobe has made waaay too hard.) So for example, here are two pages with EXCELLENT introductory information that has helped me immensely in my quest for complex dynamically-generated Flash animations :

Core Display Classes in Adobe Flash

How to use the Adobe Flash Drawing API at runtime

Monday, March 16, 2009

Bazaar - managing local feature branches with lightweight checkouts

Bazaar is the BEST source code versioning system in the world, or at least, the best non-commercial one (I can't comment on commercial ones except Visual SourceSafe, which epitomises dog-ness).

Well, that's my opinion, after using SVN and Visual SourceSafe, and reading fairly extensively about other systems.

But it can be hard to get started, just because it is SO powerful and has SO many options and can be used in SO many different ways.

If you're a single developer who just uses it to track their own work, and never makes feature branches, life is very simple.

bzr init
bzr add
bzr commit -m "My project is now in Bazaar! Woot, woot!"
... edit your project ...
bzr add
bzr commit -m "I just made edits to my project, and Bazaar has been updated accordingly."

It's really sweet. Suppose you ship a new version, and you want to be able to access the exact source code that was used in that version :

bzr tag "Released version 1.2.3"

And to review previous tags? Simple :

bzr tags

If you mucked up?

bzr revert

(Throws away your changes since the last commit.)

That's all very sweet and simple. (Sure beats SVN and Visual SourceSafe!!!!!!!!!!!!!!!)

But I wanted to use feature branches.

There are various ways to do it, but here's a simplified version of how I'm doing it, with great success :

Create a root development folder. For sake of demonstration, let's call it "src".

src> bzr init-repo --no-trees
src> md trunk
src> cd trunk
src\trunk> bzr init

At this point, you have a trunk, with nothing in it. Not much use, but press on...

src\trunk> cd..
src> md wt

Here, "wt" is an acronym I've chosen somewhat arbitrarily. It stands for "working tree". It's the only place I'm going to have a copy of the (often extensive) set of files in the project.

Get it?

Bazaar lets you have as many branches as you want, without clogging your hard disk! THAT is AWESOME!

Now any time you embark on a change, you can afford to do it in a branch. That keeps your work well organised, and means you don't end up stuck with half-finished changes in your trunk. Very nice!

Did I mention that Bazaar is the best source code versioning system in the world? :o)

Ah - but we're not done yet. It's still useless at this point. Press on...

src> cd wt
src\wt> bzr checkout ..\trunk --lightweight

Woot, woot! At this point, copy any existing files for the project into src\wt, then proceed.

src\wt> bzr add
src\wt> bzr commit -m "Initial commit, directly into trunk. After this point, we will work on branches, not on the trunk."

Extra wootness! Now we have a trunk, and a working tree, and we are ready to start branching.

src\wt> cd..
src> bzr branch trunk "Branch001 - completely revolutionise colour scheme"
src> bzr branch trunk "Branch002 - add new powerful print-preview system"
src> bzr branch trunk "Small changes"

Yep - you guessed it. If you think there's a reasonable chance you'll get stuck halfway through a set of changes, and not have them finished before you have to work on some other aspect of the source code, then that's a good candidate for a branch. But if it's a REALLY small change (e.g. update the copyright notice in a single file), no need to create a new branch just for that. You could conceivably make that change directly in the trunk (although I advise against it, for reasons that are beyond the scope of this article). My solution? I have a permanent branch called "Small changes", and that's where I make small changes! :o)

So now, let's start work!

src> cd wt
src\wt> bzr switch "..\Branch001 - completely revolutionise colour scheme"
src\wt> bzr revert

At this point, the working tree matches the contents of Branch001.

Start work on that colour scheme.

Oh no! The boss has walked in and said the new print-preview system has to take priority.

Gotta change to a different branch.

Easy done :

src\wt> bzr commit -m "These are my changes so far."
src\wt> bzr switch "..\Branch002 - add new powerful print-preview system"
src\wt> bzr revert

Voila! Now the working tree matches Branch002. Code away merrily...

In comes the accounts lady. "The last digit on the bank account number should be seven, not three."

This change should be done immediately. No point waiting two weeks for the new uber-funky print-preview system, nor five months for the new colour scheme to finally be done.

src\wt> bzr commit -m "Well, working on Branch002 was fun and all, but necessity demands my (temporary) departure."
src\wt> bzr switch "..\Small changes"
src\wt> bzr revert

Now fix that account number. And then proceed...

src\wt> bzr commit -m "Maggie informed me that the account number was incorrect. Fixed now."

Now, chances are, we want that change to go into the trunk right now. Let's do that.

A clue : BAZAAR IS NOT INTUITIVE WHEN IT COMES TO COMBINING FEATURE BRANCHES INTO THE TRUNK.

Oh - don't flame me. The problem is that most documentation talks about the 'bzr push" and "bzr pull" options.

But these will destroy everything. You'll lose the farm, the house, the boat...

There is a very simple rule to keep in mind when working on feature branches on your own computer :

bzr merge!!!!!!!

Yes - Bazaar's merging features are second-to-none. They are truly impressive.

So despite the myriad examples out there telling you to "bzr push" your changes to the central server - ignore it! Those instructions apply in certain common situations, but do not apply for our current purposes.

So here's how we get the latest changes from a branch into the trunk :

src\wt> bzr merge ..\trunk

That's the first step. Remember - whilst we've been busily working away on a branch, there might have been other branches committed to trunk. So the trunk we once knew is not necessarily the current trunk. We need to ensure that what we're about to commit to the trunk is compatible with the current version of the trunk.

And the way we do that is to first merge the latest changes from the TRUNK back into the current branch.

Having done that, we run our unit tests. If everything compiles and runs fine, we're happy chaps. If not, fix it! :o)

When we're happy with the merge :

src\wt> bzr commit -m "Merged with trunk"

Cool. So now we know that our changes in this branch are compatible with the trunk. That means it's safe to merge this branch into the trunk.

src\wt> bzr switch ..\trunk
src\wt> bzr revert
src\wt> bzr merge "..\Small changes"

There shouldn't be any conflicts or other problems at this point, because we already merged the trunk into our branch, and any problems should have been encountered then not now.

src\wt> bzr commit -m "Merged with branch 'Small changes'"

Woot, woot!

Oh - there's the boss again. I'd forgotten about the print preview. Better get back onto it :

src\wt> bzr switch "..\Branch002 - add new powerful print-preview system"
src\wt> bzr revert

Code away.

Oh no! I just discovered that the print preview cannot possibly work unless the account number is correct - but the account number was fixed in a different branch!

Not a problem.

src\wt> bzr merge "..\Small changes"
src\wt> bzr commit -m "Merged with branch 'Small changes'"

Ah - wonderful. Now the changes in the "Small changes" branch are also in Branch002.

What's better yet is that Bazaar is so smart that you can merge those two branches together as many times as you want, and Bazaar will keep a record of what has been previously merged and what hasn't been, so that you'll never have trouble with the one change being included multiple times!

THAT is POWERFUL.

In fact, suppose we made Branch003 for some purpose or other :

src\wt> cd..
src> bzr branch trunk Branch003
src> cd wt
src\wt> bzr switch ..\Branch003
src\wt> bzr revert

Suppose it adds some cool new features to our library of helper functions.

Suppose we finish with the branch :

src\wt> bzr merge ..\trunk
... compile and run unit tests ...
src\wt> bzr commit -m "Merged with trunk"
src\wt> bzr switch ..\trunk
src\wt> bzr revert
src\wt> bzr merge ..\Branch003
src\wt> bzr commit -m "Merged with Branch003"

At this point, Branch003, trunk, and "Small changes" ALL contain the account number change, and Branch003 and trunk both contain the new helper functions.

We don't need Branch003 any more, so we're going to switch away from it and delete it. (We could switch to any branch, but hey, for the time being, I'll switch to the trunk, 'coz I don't know where I'm going next.)

src\wt> bzr switch ..\trunk
src\wt> bzr revert
src\wt> rd ..\Branch003 /s
... and type "y" and Enter to confirm that you want to mercilessly annihilate Branch003.

Now, in some other source code versioning systems, you have to manually track which changes have been merged where. (Not mentioning any names... <cough - SVN> )

But with Bazaar, things just work, and they work perfectly!

We return to the print preview branch :

src\wt> bzr switch "..\Branch002 - add new powerful print-preview system"
src\wt> bzr revert

Oh - turns out we need those new helper functions.

If Branch003 still existed, we could get those new helper functions by merging it in to Branch002.

Or we can merge the trunk into Branch002.

But Bazaar is so cool, that it doesn't matter which one we do, or even if do both, or even if we do both multiple times! It just works!

src\wt> bzr merge ..\trunk
src\wt> bzr commit -m "Merged with trunk, to get those wonderful new helper functions."

So, merge is your friend.

See, because I grew up with Visual SourceSafe and SVN, "merge" had a negative connotation. I assumed that the change history, including the detailed comments I leave with every commit, would be marred by merging.

But with Bazaar, the history is preserved!

It's easy to prove :

src\wt> cd ..\trunk
src\trunk> bzr log

And now you get a nice big listing of all the changes that have happened. It doesn't matter how many times you merged, all changes and all commit messages are preserved.

THAT'S what I call a good source code versioning system (aka "revision control system").

So many branches and can't remember which one the working tree is currently bound to?

src\trunk> cd ..\wt
src\wt> bzr info

Ah! It tells us which branch we're bound to. Very nice.

Now, my setup is actually considerably more elaborate than this. I have multiple development machines that I switch between - one laptop for the road, and a much more powerful desktop that I prefer using when I can.

I wanted a "central trunk" from which I could run automated unit tests, and which could be my controlled release point. Plus, I have other developers help out here and there. So I really wanted a system that works this simply and this well, but works across multiple computers.

And I needed each computer to have its own "local trunk" so that the respective developers could branch away happily even when they were not connected to the internet and the central trunk.

So, in effect (although I accomplished it slightly differently), I have a folder called "central-trunk", on my controlled-release computer.

I have found that the Bazaar logs look nicest when you only have one level of indirection with merges. Since I like nice, readable logs, this is what I do :

I merge finished branches into the CENTRAL trunk, and I never delete a branch until I have merged it with that CENTRAL trunk (unless I'm abandoning the branch).

The LOCAL trunk exists ONLY TO ENSURE I CAN CREATE NEW LOCAL BRANCHES EASILY.

And to keep the local trunk in-sync with the central trunk, I run the following when connected :

src\trunk> bzr pull Z:\central-trunk

I NEVER merge or commit into my LOCAL trunk. If I'm working on two branches that need features from each other, I merge them into each other.

So basically there are three levels - the central trunk, the local trunk, and each feature branch.

The changes are "bzr pull"ed from the central trunk into the local trunk.

"bzr branch" from the local trunk creates local feature branches.

"bzr merge", NOT "bzr push", takes my local feature branch into the central trunk.

And "bzr merge" also handles inter-branch updates along the way.

-

Bazaar changed my programming life. It is a source code versioning system that works, and works brilliantly well, and isn't cumbersome. It is a pleasure to use. I enjoy the feeling of committing a change, and I enjoy merging a feature branch into the trunk. It feels like hammering another peg into the cliff face, as I climb the metaphorical cliff of whichever crazy software development endeavour I've embarked on. (And I usually embark on difficult ones! :o) )

It changed my life, even long before I started using feature branches. For a long time, I just used (and loved) the ability to track each version of the trunk, and tag releases.

Now with feature branches, things are even better again.

If I'd been given this information when I first found Bazaar, I would have accomplished more. Feature branches are a produtivity tool.

But the documentation was more focussed on contributing to open-source projects where you always branch directly off the head revision from a read-only URL somewhere, and where only specially-privileged people can commit to trunk. That's fine - that's a very powerful and very common use of Bazaar.

But it can do so much more! I hope in this article that I've helped some of you get up-to-speed with Bazaar in ways that will transform your programming life for the better.

Sunday, March 1, 2009

Beating the performance bogeys in the Intel SSDs

Intel changed the world forever with its new X25M and X18M SSDs.

But reports are circulating of major performance problems.

Everyone's puzzled, and Intel denies any performance problems at all.

What's the low-down?

It's simple, as far as I can tell.

First, let's review the kinds of problems being reported :

* Intel SSD is BLISTERINGLY fast when first installed, but after a few months, random write performance plummets.

* Testing the Intel SSD by hammering it with tiny random writes for even less than a few hours brings a brand-new SSD to its knees - write speeds lower than 10MB per second.

* Imaging a hard disk partition onto a brand-new Intel SSD, results in that SSD's write speed dropping to just a fraction of the advertised speed. (Search the linked page for 'All we did here was write the OS files')

You're not going to like this, but if you partition the drive - e.g. in half - and only ever use one of the two partitions, you won't see these problems.

Or at least, that's my bet, not actually owning one quite yet (although I've placed an order).

Can you see why?

The Intel SSD is so mind-bogglingly brilliant because it combines random writes into sequential writes. Others have already blogged about that very well, so I won't detail it here.

The problem is that the SSD doesn't know when virtual sectors are "available" again - it only knows which virtual sectors the operating system has ever written to.

If the operating system has EVER written to a virtual sector, the SSD controller studiously preserves the content of that virtual sector ever after, EVEN IF THE OPERATING SYSTEM LATER THINKS IT HAS DELETED FILES AND THAT THEREFORE THE SECTOR IS UNUSED AND AVAILABLE FOR RE-USE.

Thus, if you use the wrong imaging tool, and write an 80GB partition to your brand-new Intel SSD, BAM - as far as the SSD is concerned, you have 0% free space. Yup - your partition might have contained 60% free space, but if the imaging tool does a sector-by-sector copy of the source disk to the SSD, then the imaging tool will write to every virtual sector in the SSD, and the poor SSD controller will be left diligently trying to preserve the contents of empty sectors.

SO, the trick to ensuring that the Intel SSD controller has some "breathing space" in which to perform its magic, is to ensure you NEVER COMPLETELY FILL the SSD.

Sad, but true.

Yup - that means that if you fill your 80GB SSD with 80GB of data (whether test data for random write tests, or 80GB worth of your data files), then read speed will stay great (it does slow down, but you're not likely to notice without a performance monitoring tool), but write speed will plummet. The poor SSD controller won't be able to turn random writes into sequential writes, 'coz there's no free space!

Actually, there is free space - a tiny bit. See, apparently, the Intel SSD comes with a few GBs spare storage space beyond the advertised capacity.

Without this spare storage space "up its sleeve", the Intel SSD could, in worst-case performance scenarios, have random write speeds as abysmally low as say the OCZ Core V2 (which I am suffering with).

So by having more storage space available than it lets the computer directly use, the SSD controller always has a little bit of room to move - and that's why even when things go horribly pear shaped, the Intel SSD can still maintain a random write speed of well over 1 megabyte per second (reports vary up to even still around 20 megabytes per second) - which is WAY more than the competitors do from the very outset!!! (For comparison, my OCZ Core V2 120GB has a random write speed for 4kb blocks somewhere under, well, I'm not entirely sure, but I think less than 100 kilobytes per second - i.e. less than 0.1 megabytes per second.)

So let's take the case of the hardcore random-write test. We write a special program that randomly chooses any sector across the entire 80GB of the SSD, and writes just to that sector, and then chooses another randomly, and writes to that, and does so as fast as the SSD can handle it, for hours on end.

For the first 80GB-worth of random writes, it zooms along at the mind-boggling speed of 70MB per second or more.

But it quickly hits a wall. The SSD controller was attaining these astounding speeds by combining the random writes into sequential writes, but shortly after 80GB worth of writes, it runs out of unused sequential storage locations. (Remember that it has a bit of breathing room, but that gets used up quickly too...)

In this condition, the effort required to combine even just two random writes into one sequential write is multiplied. On the virgin drive, it could cram 2MB of random writes into each 2MB write block (or whatever write block size it actually uses - I pick 2MB for demonstration purposes, but the principle remains true whatever the actual write block size). But on the drive in this random-write-crammed-full condition, if there was no "breathing space" (additional storage space beyond what the host computer is aware of), then each 4 kilobyte (0.004 megabyte) random write would require an entire 2 megabyte write block to be written. That's a slowdown factor of 2 / 0.004 = 500!!!!!!! No wonder people report speed problems!

Of course, Intel was smart to include the "breathing space" - it ensures that the worst-case random write speed never actually gets as bad as that.

SO - what's a man to do?

The reality is, this problem is not as bothersome as you might think - if you know what triggers it and how to avoid it.

The simplest thing to do - and what I plan to do as soon as mine arrives - is to give it even a bit more breathing space than comes built-in.

e.g. partition it into 70GB + 10GB, and never touch the 10GB. That gives the controller an extra 10GB of "breathing space". In a more extreme case, if you - for example - chopped the drive in half, and kept 40GB in an untouched partition, where the SSD controller can have a huge amount of "breathing space", then you should find that the worst-case random write performance never drops below roughly half of the best-case random-write performance.

So why is it that Intel claims they can't reproduce the reported problems? I bet they're not testing with a chokkers drive. Pack that drive full - every last sector - and you'll get random write speed issues double-quick time. But leave more "breathing space" than the default built-in, and things will improve.

Does it seem sad to lose a big chunk of an already-very-expensive-per-gigabyte SSD?

It sure does. But on the flipside, do you need all 80GB of super-speed goodness?

In my case, I reckon I can survive with say just 40GB or 60GB of primary partition, tools, and source code, and keep my photo libraries and downloads and other huge files on a traditional spinning platter.

But do I even care that much? Suppose random write speed dropped to 10MB/s? Given that it has practically zero seek time, and given that developing for the Microsoft .NET Framework results in HUGE numbers of small relatively random writes, 10MB/s sustained on an SSD will probably still out-pace my current hard disk by a good margin.

I think I'll set aside 20GB as unused additional "breathing room" initially, and then chip into that later if I can make more precise calculations about worst-case random write performance with different amounts of breathing room...

So talk to me - did it work for you?




UPDATE: If your Intel SSD has hit the doledrums (i.e. has lost its random write performance), check out the HDD Erase tool and accompanying notes.