|
|
Firstly a disclaimer : I don’t really know what I’m talking about here, this entry is a summary of a series of (hopefully educated) guesswork.
That said, todays task was to make an installer for an AIR application that could reside on CD/DVD, detect if AIR is already installed on the users system (And install it if not), and then install the application. All in a familiar (to users of our products, in any case) style of installer and without any reliance on an internet connection.
You should note that I am a big fan of the AIR “badge” installers, but reliance on the presence of flash player v9.1.1.15 + and an internet connection to download the AIR installer makes it rather unsuitable for our purposes.
Why would I want to do such a thing? Well, our marketing department seem to think (rightly or wrongly) that’s it’s a requirement.
Anyway, on with the tech stuff.
We use NSIS installers for our current product line. Not my decision, I was gifted it by a previous (and now departed) programmer.
Personally I have little experience of it, I normally manage to use an existing install script and change a few variable names, possibly hack in something from their support forums or examples and just about get the job done.
All in a “hack at it till it looks like it works” style (which I will continue here).
No doubt anyone who actually knows what they’re doing with NSIS will be laughing at me here, but hopefully they will kindly correct any glaring errors I may make.
I believe the general approach I take here will hold true for any other windows installer you may use, however.
The first task is really to determine whether AIR is installed or not, which I’ve chosen to do by reading a registry string that the AIR runtime install seems to write when it is installed (I don’t have any information or documentation on this, by the way, I just compared registry settings pre/post install). It would appear to tell the system where the application “Adobe AIR Application Installer.exe” is installed, and lives in HKEY_CLASSES_ROOT/AIR.InstallerPackage/shell/open/command.
In the nsis case, the the code to detect/install the AIR runtime looks something like this (probably! It’s all a bit “GoTo” for me, but it seems to work ok)
ReadRegStr $R0 “HKCR” “AIR.InstallerPackage\shell\open\command” “”
StrCmp $R0 “” airNotInstalled copyApplication
airNotInstalled:
MessageBox MB_YESNO “You first need to install Adobe AIR Runtime to run this product. Would you like to install it?” IDYES yes IDNO done
yes:
DetailPrint “installing AIR RUNTIME”
Push $CMDLINE
Call GetParent
Pop $R0
ExecWait “$R0\adobe_air_installer\AdobeAIRInstaller.exe”
;ReadRegStr $R0 “HKCR” “AIR.InstallerPackage\shell\open\command” “”
;DetailPrint “AIR RUNTIME is installed at: $R0″
;MessageBox MB_OK “AIR is installed at $R0″
Now, if you merely wanted to do an offline CD based installer, then I think that would be more or less all you needed.
If you pass the path to your .air archive as an argument to “Adobe AIR Application Installer.exe” then it will handle the install for you in the usual AIR runtime manner with the same dialogues you’ll no doubt be used to by now. Handy!
However, in my case I need to install a “protected” executable rather than the small “stub” exe that AIR sticks in your programs folder, so I need to get my installer to copy the files for me. (Please don’t start me on this “copy protection” thing, I tend to rant profusely about it, but my hands are tied once again). The “protected” executable is the original with a whole load of super secret gubbins wrapped into it that ties the program to the disk that we supply the product on. Apparently. Somehow. We have 3rd party software that will do that to an existing .exe, once done that .exe will not run without the original DVD being present in the drive of the users pc.
It turns out that if you install an application via AIR in the usual way, then head to it’s folder in “program files” and grab the files straight from there then you can install them on another system and they will work (if the AIR runtime is present).
(I wrapped the air application .exe file with the “copy protection” at this time, before making an installer)
I initially didn’t expect this to work, reckoning that there would be some sort of check of integrity of that file by the runtime, but as far as I can tell (in my limited testing) that’s not the case.
As far as I can tell, you will need the files/folders “your_application_name.exe”, “META-INF”, “mimetype”, “your_application_name.swf” and any of the assets that you may have included when you packaged up your AIR app.
In my case, the lines
———————–
copyApplication:
File /r data
File /r img
File /r META-INF
File /r movies
File /r pics
File /r web
File mimetype
File myApp.exe
File myApp.swf
———————–
will grab those files/folders (the “/r” makes the copy recursive) from the directory in which I make the installer and compresses/packages them into the installer (which will unpack them when run).
(NSIS actually does very good lzma compression, which means that you may get a smaller installer than your original .air archive (if you’re lucky, which I was )).
This all seems to work very well, and once installed the application appears to run as it normally would.
Never the less, I would be careful. For instance, I made a tiny test application that just used AIR’s update mechanism to update itself. In the end, it updated itself to the location from which I’d copied the files for the installer on my machine, rather than where my custom installer had placed them on another. Obviously the original AIR install location is stored somewhere, but I have no idea where. So for updates you’d have to build your own system, and I can only guess where there may be other gotchas that I’ve not discovered as yet.
It’s worth noting that this method avoids the user seeing any warnings about code signing (which they would if they were using the AIR installer, if the app is not signed).
Since drafting this article I’ve very briefly tried the “shu-player” installer, which appears to wrap both your AIR application and the runtime into one .exefile which can be run from DVD without any tedious “install the runtime” dialogues for the user.
My first impressions were good, it’s easy to use and worked well in my quick first use test.
It appears to uncompress the runtime and your application into the users temp folder, then attempt to remove them when the app is shut down.
I do have some concerns with this approach : though I’ve not tested it yet I can see some issues with storing persistent settings due to the varying location of each instance of the application on the users system. In use, it also appeared to not clear the temp folders it created, though it did delete their contents.
I’m also not convinced that it would work all that well with our “copy protection” format, though I have yet to try, test and analyse it.
April 9th, 2008
Categories: Uncategorized | Author: Jim | Comments: 4 Comments |
It was with great interest that I noticed a new release of the zinc 3 flash projector tool, and looking at the features it would appear to do everything I need.
To explain, the products I work on are distributed and sold in 2 ways, DVD and web access. Previously, we’d had 3 separate distributions in effect : a disk based executable for windows, one for mac, and a server based one for the web site. All did (and still do) use the same assets, but nevertheless it’s a big effort to keep all 3 in sync and up to date (This, of course, costs us money).
So, the promise of one common code base (or, at least, much much further towards one), and a pretty well identical and consistent interface on both DVD and web was a big plus for me to sell to the company when I suggested a flex version.
So far it’s gone extremely well (As I’d hoped). I’ve abstracted out the database access (initially, prior to Apollo/AIR I used a socket server built in haxe for the local version, but now it’s remoting using flourine for web and AIR’s native sqlite access for the disk), and apart from that and a couple of file save classes I do have 2 projects that share 95% of their code. Cross platform. Sorted.
Except for one thing, which is that it’s a requirement to have some sort of copy protection on the disks we sell.
AIR , being a runtime, makes that a little less straightforward to implement than a standalone executable, and I’ve yet to decide quite how I’m going to do it.
The system we use at the moment (Sony’s Securom) relies on the executable being encrypted against the DVD, but of course I can’t do that on the AIR runtime…
I have some thoughts involving protecting the “stub” exe that air seems to use, using a web based licensing system (we tried that before, with no great success), or maybe having a socket based “licence server” that runs off the disk itself. To be honest I don’t much like any of them, being the one who has to answer the support calls when all these schemes fail (Copy protection problems are 90% + of our support calls, and I hate it …).
At this point it’s worth reading this interesting article “AIR is not a projector tool”, which makes the points of difference between AIR and projector tools rather better than I can (and uses zinc 3 as an example).
All of which brings me back to Zinc 3, and in particular it’s sqlite access (since that’s the deal breaker for my application).
I will, however, start with the AIR sqlite implementation, which I have to say is extremely good.
Apart from some early hiccups in the first betas (writing blob binary data and a bug with some null results) plus the decision to remove full text search (quite understandable but a disappointment for me) it’s really well thought out and very usable indeed.
Data comes out typed and ready to use in flex, it deals with binary results, hell you can even specify an object to use as a result format or access fields by their name in the sql query. There’s also a nice api for analysing the database schema now rather than the rather clunky sqlite native method (Yes, I’ve used that and had to parse the results ). And it works, in that I can rely on it 100%, based on experience.
Now, compare and contrast with that in zinc 3, the documentation is here. It’s short (Drill down to Database > Objects > Database.SQLite) …
The first thing that I noticed is that there is no code completion for it in flex builder (I believe it uses some sort of run time resolver package instead, not thought that one through as yet though). Fair enough, I can live with that, so I ignored it and built a quick test using a production database and some very simple queries.
Next it seems that you have to make a call then check if there was a database error rather than a try/catch that one can use in AIR.
Fine, I guess that’s an abstraction that helps make the sqlite access compatable with the 3 others (mySql, Access, and ADO) that zinc supports.
However, I don’t particularly like it.
Next, all your results come back as a multi dimensional array. Of strings. Mnnnnn. So that’s quite a few parseInt()s then and whilst I’ve not tried it I suppose blob data is out of the question. Not only that, but you’re forced to use index based access to the results, which is not especially readable.
I did try the getXML() method thinking that it might bring back field names in the xml but I’m afraid that for me it just returns nothing at all. oops.
There doesn’t seem to be any way to use parameters in the query, rather you have to build the whole query as a string.
Now in this app I’m not concerned about sql injection and having to escape input, but I’d rather it was there for the time that I do.
Still, it did seem to work well enough to consider giving it a go for real, so that what I did. The service in question is roughly 2000 lines of actionscript, I guess it took me about 3 hours to port it over, which was tedious but not too bad.
It didn’t actually work first time (Yes, I expected that!) but fortunately it’s fairly easy with zinc to build a debugging projector and use the flex debugger.
Turns out that it doesn’t do what I can do with AIR, which is to just keep open a database connection for the life of the application (I’m relaxed about doing that in AIR, particularly since I can open the database read only, which I can’t in zinc as far as I can tell).
Instead you need to keep opening and closing the connection to the local file, and once I’d worked that out my results stopped having zero length (Hey, why no error raised? That would make more sense. If I’m stupid enough to query a closed connection then I should be told) and I could correct my own errors (unusually for me, I didn’t have all that many).
I’m happy to say that it does all work as expected in zinc now (which is good, this was not a trivial test, the db gets a lot of use), with a couple of important caveats.
Firstly, it’s slow. Noticeably so. Mainly in deference to the web version, I do regularly pull a large amount of data out of the db that gets sent to the client, which cuts down on the latency of making many smaller queries much more often. In c# or AIR, these big operations occur so fast as to be acceptable in actual use (probably in the order of a second at most, even on our minimum spec clients). In zinc, I’d say about 5 or 6 seconds on the fastest, which you do notice.
I’ve yet to profile it, it’s possible there are improvements to be made (though I did optimise the database open/close as much as I could).
However, the app itself is sluggish compared to both flash player and AIR, even when I’m not touching the database (memory usage appears to be fine, mind you).
The worst point is that the projector will crash, and I just cannot publish an application that does that at all regularly (AIR, by comparison, will not do so).
It may only have been a couple of times in a day or so, but that’s too many to get through QA and I’ve not been able to pin it down on any one particular point.
I think I’ll be waiting a while before giving zinc 3 another go.
Whilst this little review may seem harsh, I appreciate that it’s not easy at all to produce rock solid software at version .0 and I’m actually really glad to see zinc still out there and improving. I’m hoping it improves to the point where I feel that I can use, and quite confident that it will.
There are other features that whilst not must have will help us a great deal, so I’d like to be able to use them.
I should say that (perhaps unfairly) I’ve not contacted multdmedia with any of my concerns before writing this, and I’m very happy to stand corrected if anything is inaccurate, misleading or unfair.

February 11th, 2008
Categories: AIR | Author: Jim | Comments: 3 Comments |
I just returned from this, and as I hoped it has raised my interest in the framework.
This was, apparently, Paddy’s first public speaking experience and I have to say that he did a very good job.
The slides and structure were well thought out and prepared, and delivered spot on time, in fairly conversational but to the point manner.
We spoke for 5 minutes afterwards, he’s a very nice guy and it was good to see that we were in general agreement even if using different frameworks.
He started out with pureMVC at about the same time I did with Cairngorm and it would seem that our experiences and the benefits we’ve received are pretty well matched.
One of the points of his presentation was to recommend use of a framework (any one of the many alternatives) in general and held pureMVC up as being a good example.
That said, it’s got to be an almost impossible task to cover this sort of topic in one hour, to a mixed audience. I also saw Neil Webb’s cairngorm presentation last year, although that was probably rather easier for me to follow since I was already using it at the time. I think that knowledge helped me a lot with this one as well, but even so it’s hard to follow once someone goes through the code in even a small demo app. I’m unsure about how useful running through code actually is in this sort of limited timescale, though I guess it depends on the context.
I expect it did go over a few peoples heads, even when I knew roughly where it was going (as I said, I’ve looked at it before, read the docs a few times even if it was a while ago) it was still a little hard to follow so much in a short time.
I had a brief conversation with Tink about his flash on the beach 2007 presentation, where he asked if there was enough detail on the actual code. In his case, I think he got it pretty well spot on for that one. It was a fairly quick and sparse overview, pointing out the important points and things to look out for. For me, that’s ideal, since I can look over the code later, check out those points and see how they fit in. However, that was for a rather smaller example of flex skinning, and to go over a whole framework in 20-30 minutes is ambitious, maybe too much so.
No easy answer to that one, really, except to take more time perhaps!
In any case, I was after an overview, and I got a good one, so many thanks for your effort Paddy!
I also enjoyed Rob Bateman’s talk on the history and current state of 3D on the web, I was unsure if I was going to stay for that, but glad I did.
It was nicely delivered,and free from any hype (I’m slightly cautious about the current enthusiasm for 3d, and Rob seemed to understand very well the point that it’s great *where appropriate and well done*)
February 1st, 2008
Categories: Flash | Author: Jim | Comments: 2 Comments |

OK, I’m interested enough in Aral Balkan’s “Singularity” to post about it here, on the offer of an early peek this weekend.
I have no idea what it is, or might be, but I’m confident that coming from Aral it will be interesting.
For those of you that have not seen him present (and he does seem to at many conferences), he’s an enthusiastic presenter who never fails to entertain and enthuse. I caught his “rediscovering fun” presentation about “swx” this time last year at LFPUG, and enjoyed it greatly.
Now, swx is not for me at the moment (I’m hoping to stick with AS3, and quite happy with AMF thanks), but never the less it’s a nice project and I can see the value in certain circumstances (Flash lite would be the one that I’d personally pick as being a great match).
What I did find rather impressive was catching Aral doing a twenty minute presentation at Flash on the Beach last year, during which he put together a simple but usable demo application in about ten! Since last January, he’s evidently put together some nice tools to complement the format itself, aimed at making it easy to get started.
Well, time will tell what “Singularity” brings…
January 30th, 2008
Categories: Other | Author: Jim | Comments: 1 Comment |
I’m looking forward to catching Paddy Keane presenting a session on pureMVC tomorrow night in london.
I’ve gained quite a lot from Cairngorm in my work flex app, and I’d like to have a little experience of the alternatives (especially if I end up in flash environment without flex’s built in binding again) . I’d given pureMVC serious consideration before going down the cairngorm route and I’m still interested. I’m hoping it will enthuse me enough to rip apart one of my smaller private projects and see how it hangs in a framework rather than my ad-hoc plumbing. Rather better, I suspect!
Thoughout 2007 I made some effort to get to the LFPUG meets, which at least are only a few minutes down the road from me.
For those thinking of it, I’d recommend turning up. They’re a nice bunch , and the organiser (Tink) is a welcoming kind of guy with a keen sense of wit.
There’s also a regular raffle (Not that I’ve won anything yet ), this months is for two tickets to FITC in Amsterdam. I’m hoping …
January 30th, 2008
Categories: ActionScript | Author: Jim | Comments: No Comments |
It was with pleasure that I received an email early this morning to inform me that this blog has been accepted for the MXNA news aggregator.
I hope to continue to post on subjects that interest me in regard of Flex, Flash, AIR and any related matters.
The prospect of having a potentially large audience out there encourages me, I can only hope that it is of occasional interest or use to others.
Comments are welcome (in fact encouraged), especially those that are constructive enough to correct any mistakes or inaccuracies I may post!
In the meantime, here is an irrelevance :

January 30th, 2008
Categories: Other | Author: Jim | Comments: No Comments |
the main view in my application changes it’s data quite frequently, and uses a comboBox as one of it’s controls. Initially I’d just left the comboBox’s width unset and it was happy to resize itself according to the data presented.
However, our “designer” was not happy…
It was decreed that the comboBox should remain at a fixed width (In it’s collapsed state).
The data labels it presents varies considerably in length, and to set it’s fixed width to the longest would a) look stupid in most cases b) require measuring all possible labels before deciding on the width.
So, I decided that I would make the dropdown size itself to the widest label for any one current dataset in it’s dataProvider, at the point at which the dataProvider changes.
I also thought about a dataTip function in a similar fashion to that of the tree control, but decided that the dropdownwidth made more sense since generally speaking labels for our data are of a similar length in any set, so it’s a different question than just the odd one long label in the dropdown.
This proved to be fairly straightforward to get working, though it was new to me and I have to admit I still lack confidence that I’ve done it in entirely the correct way.
However, it does seem to work OK so thats probably good enough until I learn a little more.
Measuring the labels widths was easy (dropdown.measureWidthOfItems) , and initially it seemed to make sense to do that and alter dropdownWidth when the dataprovider changed (in collectionChangeHandler() ) .
However, that lead to a dropdown that was less than the width of the base which was ugly, so I thought I’d “just” do dropdownWidth = Math.max(newDropDownWidth, this.width).
That, of course, turned out to be a problem if the dropDown was visible at the time (since that affects the measured width of the component), and lead to some rather strange results 
Some experimentation showed that the “FlexEvent.VALUE_COMMIT” event occurred at a nice time to measure and set what the new dropdownwidth should be (The dropdown is hidden before the event occurs, so you get the “base” width at that point. On reflection, it may have been worth getting the width of the “base” element somehow, but I didn’t look for that.
The other thing that needed attention was that “measureWidthOfItems” does not take into account the presence or otherwise of the scrollbar on the dropdown.
I detected it’s presence by using dropdown.maxVerticalScrollPosition > 0, but initially hardcoded it’s width to the standard 16 pixels(it was late at the time).
There’s no direct way to get the scrollbars width (it’s protected within the standard dropdown), instead you must extend your dropdown with a method that reads and returns it.
Though I’d not done that before, it proved to be easy enough. However, I’m slightly suspicious that the fact I use a cast to get my extended dropdown class before being able to call the scrollbar width method means that I’ve not quite got it 100% right. But considering it only gets called when the dataProvider changes, I’m not too bothered in practice…
All that remains is to make an example featuring cute puppies…



View example (right click example for source)
January 14th, 2008
Categories: Flex | Author: Jim | Comments: 2 Comments |
This one came as a bit of an unwelcome surprise, in a kind of “didn’t work as I expected” kind of way.
Consider, say, a config class that needs to prompt the user for two files to use (in the case where I came across this, a database file and a directory of images, for instance) .
I’ve found (at least with flexbuilder beta3 and windows XP), that you cannot chain one file browse operation from the select event of another. (BrowseForOpen or browseForFolder are the only ones I’ve tested so far) .
The example mxml text below should demonstrate this …
The trick to fix it is to call the second browse operation via callLater() (if your class extends UIComponent) or a timer (if not).
so to fix the below you would change “checkConfig() ;” in the function file1Selected() to : this.callLater(checkConfig);
I also noticed that in that file the first file browse dialogue appears *beneath* the main application widow, which is a bit rubbish really.
That does make sense, in that the application window may not be rendered to screen in AIR on creationComplete, therefore the browse widow is placed on screen before the window of the AIR application (which appears above it since it was rendered to screen later on).
So … Change the call to checkConfig to the “windowComplete” event of WindowedApplication.
(I first tried the “applicationActivate” event, but seems that gets called whenever the AIR window is focused (or something), so caused some chaos and , confusingly, the same error. Because, of course, checkConfig() was called whenever the file browser was closed and the AIR window activated. DOH!)
As I said, this didn’t work as I’d expected. Is it *wrong*? I don’t know. I’d prefer it didn’t work this way, but so long as I know it does, I can work with it.
Which is really the point of writing about it, hope it helps anyone else lost on the same thing.
View the final, error free mxml file here.
=> this is the original example (that didn’t work…)
<mx:WindowedApplication xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”absolute” width=”674″ height=”343″ creationComplete=”checkConfig()”>
<mx:Script>
<![CDATA[
[Bindable]
private var filePath1:String;
[Bindable]
private var filePath2:String;
private function checkConfig():void
{
if(filePath1 == null)
{
var file1:File = new File();
file1.addEventListener(Event.SELECT,file1Selected);
file1.browseForOpen(”pick a file, any file”);
return;
}
if(filePath2 == null)
{
var file2:File = new File();
file2.addEventListener(Event.SELECT,file2Selected);
file2.browseForOpen(”pick a file, any file”);
return;
}
}
private function file1Selected(event:Event):void
{
var f:File = event.target as File;
f.removeEventListener(Event.SELECT,file1Selected)
filePath1 = f.nativePath;
checkConfig();
}
private function file2Selected(event:Event):void
{
var f:File = event.target as File;
f.removeEventListener(Event.SELECT,file2Selected)
filePath2 = f.nativePath;
}
]]>
</mx:Script>
<mx:Text x=”142″ y=”33″ width=”492″ id=”txtFilePath1″ text=”{filePath1}”/>
<mx:Text x=”142″ y=”83″ width=”492″ id=”txtFilePath2″ text=”{filePath2}”/>
<mx:Label x=”10″ y=”83″ text=”config file 2″/>
<mx:Label x=”10″ y=”33″ text=”config file 1″/>
</mx:WindowedApplication>
January 14th, 2008
Categories: AIR | Author: Jim | Comments: No Comments |
For a long while now, I’ve been building (if you like) the model of my application, using a very basic gui about whose looks and usability I’ve not cared about too much…
As the application has neared it’s desired functionality and it’s quirks and bugs have decreased, I’ve been able to devote more time to how it’s gui looks and works.
I ended up using cairngorm as a framework (though nearly went for pureMVC, which I’ll probably try on another smaller project in due course for interests sake), so in theory I ought to be able to mess about with my presentation without breaking anything too badly. It’s worked out well so far, on the face of it a little verbose in terms of writing events, commands, delegates etc for everything I do, but the payback has been worth it. I no longer have to remember what particular hack I pulled or worry so much about changing that hack breaking something else since there is a clear path for everything. Changes have been easier, what at first seemed as if it could be restricting is in fact liberating.
I’m actually looking forward to the gui side of things, it’s a whole new area that I’ve not been able to devote much time to since I took up flex.
As I start writing the odd custom component and delving into the framework source I’m getting a better understanding, learning some new stuff and generally getting interested again.
Yes, I have a few books on as3 and flex, but I always find that there is no better way to learn than to do.
The first thing of any interest was the tree thumbnails example I posted, at the moment I’m attempting to bend comboBox to my will. I’ll post on that when it’s done to my satisfaction (There being a couple of things that are still troubling me) and I feel I’ve understood what I’m doing a little better.
January 8th, 2008
Categories: Flex | Author: Jim | Comments: No Comments |
This is one thing that I am, and have been, really really missing in flash. My clients, of course, fail to understand why text handling in flash is basic, from their point of view it’s all text that appears on the interweb and why can’t flash “just” display the same kind of text???
Standing in their shoes, I do understand it, but in mine I appreciate the flash player difficulties (A bit, anyway I wish it were not so, nevertheless ).
Well, I’ve heard a whisper of flash player 10 having the ability to display tables in text fields, which would be nice, but then I guess we’ll see in good time.
In any case, just as the issue reared it’s head properly at work, I get not one but two little rays of sun shiney hope poking through the clouds, literally in the past couple of days.
Firstly, Alex Harui posted some code and what I consider some quite insightful comments on his blog here.
It is by his own words incomplete and possibly buggy, but that’s fair enough and I’m still very pleased to see it.
As he rightly supposes, in my case I only really need a small subset, and having code to start from will be an enormous leg up compared to starting from scratch.
So far I’ve only had 3/4 hour to play with that one, but it looks promising.
Secondly, another library (htmlwrapper) comes along, open sourced on google code (via FlashcodersNY). Not had much of chance to play with this one either, but reading the docs html tables are yet to come (a shame, since the text I have to render is table based, and unfortunately a little haphazard). Still, I’m looking forward to checking this out as well.
Again, just having something to start with and code to play with should be a great thing.
Many thanks to the respective authors for releasing the code, it’s so much appreciated.
January 3rd, 2008
Categories: Flash, Flex | Author: Jim | Comments: 1 Comment |
Next Page »
|