| JezUK Ltd - The Coffee Grounds - November 2003 |
| << October 2003 | December 2003 >> |
Trundled down to the Register Office to fill out Harry's forms on Tuesday. It's a reassuringly British and bureaucratic process. As you arrive at the Register Office, a board instructs which window to present yourself for births, deaths, marriages or just rootling around in your genealogical tree.
On presenting yourself at the births window, a functionary takes your details and looks them up in a big looseleaf binder, confirming that your bundle of joy really did arrive at the time and place given. Quite what happens here if the two don't agree, I fortunately didn't have to find out. You're given a docket, and told to wait until called.
Because I'd turned up mid-Tuesday morning, it wasn't busy and the wait was only for two or three minutes. Up those stairs there, to the office on your card, Mr Higgins. Accurate to a fault, but also rather redundant because there is only a single flight of stairs. Up I ran.
The stairs turn onto a long corridor, windows on one side, doors on the other. The door nearest has a little sign sticking out above it marked 'A', the next 'B' and so on into the distance at somewhere around 'P' or 'Q'. My docket said 'B', so that's the door I chose. Anything else would just have caused confusion and delay. The door opened onto a rectangular room, furnished only with a desk, a couple of chairs and a little trolley with hanging files. The Registrar sat on the far side of the desk, and invited me to sit opposite. Again for the avoidance of confusion and delay, I did.
In a brief acknowledgement of changing times the registration form filling is done on a computer. Once you've confirmed the baby's name, your name, occupation and so forth though, the Registrar opens a desk draw and pulls out a big, apparently leatherbound, book, the Register indeed. Using a dip pen the details are transcribed from screen to page, given a good going over with a piece of blotting paper, and presented to you for signing. You're given a little certificate, which the Registers signs and blots, and that's that.
Don't forget to hand the docket back in on your way out.
One of the many strange things about MS Agent is that it runs in a single-threaded apartment when underneath it's plainly multi-threaded. Pop it and watch it perform little animations all of his own accord. Call a method on it, and it'll return immediately and do the action asynchronously.
You might actually be interested in when those asynchronous actions finish (so you can, say, hide your custom speech balloon when the Agent's stopped speaking), and in a rare display of common sense the Agent will tell you. Your code needs to implement the AgentServerObjects.IAgentNotifySinkEx interface and register the implementation with the Agent.
public class AgentWrapper : IAgentNotifySinkEx
{
private static IAgentEx srvEx_;
public static AgentWrapper LoadAgent(string CharacterFile) { ... }
private int notifyId_;
private IAgentCharacterEx characterEx_;
private int speakReqId_;
// ...
private AgentWrapper()
{
// set up stuff
srvEx_.Register(this, out notifyId_);
}
public ~AgentWrapper()
{
srvEx_.Unregister(notifyId_);
}
public void Say(string thingsToSay)
{
characterEx_.Speak(thingsToSay, null, out speakReqId_);
}
// ... more stuff ...
////////////////////////////
// IAgentNotifyEx implementation
public virtual void RequestStart(Int32 dwRequestID) { }
public virtual void RequestComplete(Int32 dwRequestID,
Int32 hrStatus)
{
if(dwRequestID == speakReqId_)
balloon_.Hide();
}
public virtual void ActivateInputState(Int32 dwCharID,
Int32 bActivated) { }
public virtual void ActiveClientChange(Int32 dwCharID,
Int32 lStatus) { }
public virtual void AgentPropertyChange() { }
// ... and so and so on
}
The fact that you register your class with the AgentServer object rather than the AgentCharacter is just one more piece of API strangeness.
Anyway, so you're all registered up and happily receiving callbacks from the Agent. You might even be doing other things as a result. What you absolutely can't do is call a method on the Agent during a callback.
...
public virtual void Click(Int32 dwCharID, Int16 fwKeys,
Int32 x, Int32 y)
{
// x and y are click positions
if((fwKeys & AgentCharacter.MK_LBUTTON) != 0)
{
int ax, ay;
characterEx.GetPosition(out ax, out ay); // throws InvalidCastException
balloon_.PopupAt(ax, ay);
}
}
...
If you need information about the Agent, its position or size for instance, during a callback then you have to cache it ahead of time.
I suspect that the reason you can't call the Agent is because the callback is made by the multithreaded AgentServer, while the Agent is in a single-threaded apartment. The callback is therefore in a different thread to the Agent, and the marshalling breaks down. The Googling around I've done states that all this cross-apartment stuff should work automagically, but it plainly doesn't.
Update - The right search term combo turned up this bug report: InvalidCastException When You Call a COM Component That Is Marked as STA
Prompted by an enquiry about Wheres Kal, I did a tiny bit of work on him over the weekend and he's back online as wheres_kal on YahooIM or whereskal@myjabber.net. He'll do basic eliza chat with you, or try google something or ask wheres jez.
As an aside, the Jabber-Yahoo gateway on the backend seems to have been improved over the last few months and is now much more reliable. Time was it seemed to timeout after some period, but he's been running all weekend and he's still there.
.NET promises all things to all men. To some, it points to a happy future where contented programmers snap together besoke applications which delighted customers swoon over. For me, it offers a quick way to do the way to the easy stuff, and a whole load of pain for everything else.
Bootstrapping the Agent into .NET is straightforward. The Agent is driven through a set of COM interfaces, so generate wrapper assemblies with AxImp. Reference those assembly DLLs and off you go.
For simple stuff, you don't need anything else. However, for anything slightly more exciting, it gets a little awkward because the Agent runs in a single-threaded apartment. This is, in my experience, pretty unusual. If you haven't worked directly with COM stuff before, you probably won't have encountered it. This is why my workchums spent a week swearing and cursing trying to get this to work, before I had one of those strange slow-treacly-brain-churning only-realise-you-knew-it-once-you'd-worked-it-out revelations.
In my application I have something over there (possibly on another machine) talking back and forth to the Agent over here. The over here bit is multithreaded, which means the Agent won't work unless it's hived off into a seperate thread. More than that, it has to be completely isolated in that thread. For .NET Forms you can cheat by launching a Form in one thread, then calling methods on it from other threads[1]. Try that with the Agent, and it'll throw a System.InvalidCastException - "QueryInterface for interface AgentServerObjects.IAgentCharacterEx failed" - all over the place. To get the multithreaded part of your program working with the Agent you need to get friendly with Threads and Events. Here's the outline of what I've done -
public class AgentFace
{
static private string toSpeak_;
static private AutoResetEvent pleaseSpeak_ =
new AutoResetEvent(false);
static private AutoResetEvent pleaseHide_ =
new AutoResetEvent(false);
static AgentFace()
{ // class constructor to kick things off on demand
// isolate the Agent in its own little place
Thread nt = new Thread(new ThreadStart(AgentLoop));
nt.ApartmentState = ApartmentState.STA;
nt.Start();
} // AgentFace
static void AgentLoop()
{
WaitHandle[] handles = {pleaseSpeak_, pleaseHide_};
AgentWrapper = AgentWrapper.LoadAgent("merlin.acs");
while(true)
{
int h = WaitHandle.WaitAny(handles);
switch(h)
{
case 0:
agent.Speak(toSpeak_);
break;
case 1:
agent.Hide();
break;
} // switch ...
} // while(true)
} // AgentLoop
// here's the multithread facing part
public AgentFace() { }
public ~AgentFace() { }
public void Speak(string sayThis)
{
toSpeak_ = sayThis;
pleaseSpeak_.Set();
} // Speak
public void Hide()
{
pleaseHide_.Set();
} // Hide
} // class AgentFace
...
some_other_part_of_the_program_Class
{
private AgentFace agent_ = new AgentFace();
...
public something()
{
startSomeExternalHardware();
while(askTheHardwareIfItsFinished() == false)
Thread.Sleep(500);
shutdownExternalHardware();
agent_.Speak("Your coffee is ready.");
} // something
}
To expose more of the Agent functionality with this approach needs progressively more and more events, and correspondingly more and more cases on the switch. That isn't especially pretty or maintainable, so a nicer approach might be use a single AutoResetEvent together with a little family of command objects.
[1] Like this, for instance
public class BouncyBalloonWrapper
{
ServerForm.Dialogs.BouncyBalloon balloon_ = null;
Thread worker_;
public BouncyBalloonWrapper()
{
worker_ = new Thread(new ThreadStart(this.FormWorker));
worker_.Start();
} // BouncyBalloonWrapper
~BouncyBalloonWrapper()
{
balloon_.Close();
} // ~BouncyBalloonWrapper
private void FormWorker()
{
balloon_ = new ServerForm.Dialogs.BouncyBalloon();
Application.Run(balloon_);
} // FormWorker
public void Display(string str, Point pos, Size ext)
{
balloon_.Display(str, new Rectangle(pos, ext));
} // Display
public void BounceAround(string str, Point pos, Size ext)
{
balloon_.Bounce(new Rectangle(pos, ext));
} // BounceAround
}

At least in this house anyway. They've got the best national anthem by far, and not only that all their players seemed to know the words. Ok, given the political significance of rugby in South Africa there's probably a bit of pressure to brush up your Xhosa, but it was by far the most rousing of the various anthems on display over the weekend. I'd put Le Marseilles a distant second, with an honourable mention for Ireland's Call. Bottom of the pile is the absolutely rotton God Defend New Zealand, although I'm prepared to listen to arguments that since the band was Australian they played it badly on purpose.
After the matches, France look pretty super and everyone else still needs work. My guess is the winner of the France-England semi-final will win the final, which is a bit of shame. I'd like France to actually make the final this time, instead of finishing third again, but I'd have prefered it if they didn't have to beat England to do it.
[Add a comment]
From: Mail Delivery SystemTo: evknguz@jezuk.co.uk Subject: Mail delivery failed: returning message to sender Message-Id: Date: Sun, 09 Nov 2003 15:52:53 +0000 This message was created automatically by mail delivery software. A message that you sent could not be delivered to one or more of its recipients. This is a permanent error. The following address(es) failed: stvn342003@aol.com SMTP error from remote mailer after initial connection: host mailin-03.mx.aol.com [64.12.138.120]: 554- (RTR:SC) The information presently available to AOL indicates 554- that this server has been repeatedly used to transmit unsolicited 554- bulk e-mail to AOL. Based on AOL's e-mail policies at 554- http://postmaster.info.aol.com/standards.html, AOL cannot accept 554- further e-mail transactions from this server for an extended 554- period of time. Please have your ISP/ASP contact AOL to resolve 55 ------ This is a copy of the message, including all the headers. ------ Return-path: Received: from user-1864.bbd15tcl.dsl.pol.co.uk ([81.77.183.72]) by tmailt1.svr.pol.co.uk with smtp (Exim 4.14) id 1AIrrw-0003NF-Hp; Sun, 09 Nov 2003 15:52:52 +0000 Received: from 172.3.67.45 by 81.77.183.72; Mon, 10 Nov 2003 02:48:01 -0200 Message-ID: From: "Dee Corbin" Reply-To: "Dee Corbin" To: touchgoddess007@aol.com Subject: Brand name meds at 70% off. 38881223232 Date: Mon, 10 Nov 2003 03:51:01 -0100 X-Mailer: Internet Mail Service (5.5.2650.21) MIME-Version: 1.0 Content-Type: multipart/alternative; boundary="--02141612184245916473" X-Priority: 3 X-MSMail-Priority: Normal X-IP: 172.40.88.91 Blah-de-blah some normal spam rubbish
AOL refuse an email on the grounds that it's spam coming from an address that's sent them spam in the past. Congrats AOL. So why send the email saying so to a blatently forged return address?
pol.co.uk is Energis, so we can see this mail was routed through an Energis broadband subscriber, who probably doesn't even realise they are running an open mail relay. 172.3.67.45 is an unassigned address, so that's probably been hijacked.
Forwarding to abuse@energis, for all the good it'll do anyone.
It needs bit more shiny-graphical super-shinyness, but it's the right size, it's in the right place, and it's yer actual HTML. The bubble is a little window with an embedded Internet Explorer web browser control. The web browser is rendering HTML written directly to it, has been resized to fit the text and also had it's scroll bar supressed. Microsoft's support for embedding IE is best described as grudging and when I set off this morning, I really didn't know if any of this was actually going to be possible and for extra pain its all done for .NET using C#. So I'm feeling a bit pleased, frankly. Techno-how-to-bollo to follow some time on Monday, fans!
The reason for all this blather about MS Agent, by the way, is so maybe Google can help prevent some other poor sod going through the same hassle.
Refried Beans! Big, hearty yummy food that's quick and easy to make. If you've got a bit of time you can go for the whole hog Mexican spread with beans, flour tortillas, guacamole, salsa, cheese, heuvos, and so and so on. Less time, or can't be arsed you can have them by themselves, or just spread on a bit of toast, or just hit some happy mid-point.
You'll need
So here we go
While pinto beans are traditional, you can use red or black kidney beans instead, or a combination. Pinto beans give a very smooth bean mash, while kidney beans are a bit heavier and more mealy. I've also used borlotti beans, which give quite a sweet end result. I use 3 or 4 tins of beans, which feeds three or four depending on your level of gutbagness.
You can also boil up dried beans instead of using tins, but that takes a while so I tend to do that at the weekend rather than in the week. Put the dry beans in a pot with a quatered onion, and cover with water. Bring to the boil. If you're using kidney beans let them boil hard for ten minutes. Knock back to a simmer. When the beans start to wrinkle, add a bit of oil. Keep on simmering, topping up the water as needed, until the beans are nice and soft right through. Stir in a good teaspoon or so of salt. Depending on the age and type of your beans, this could take anywhere between 90 minutes and three hours or more. You can eat them as they are, if you don't want to wait any more. They also freeze well, if you can wait longer.
Don't worry if you make too much, because refried beans keep and reheat really well. I believe this might be why the beans are called refried - today's beans were made by adding more beans to yesterday's leftovers.
MSDN. Loathe it or, well, just loathe it I guess. It's filled to the brim with information, a lot of it either out of date or not yet useful [1], is massively disorganised, fragments related information in lots of different places, is frequently just plain wrong, and it has a crappy search facility.
MSDN is quite clear that an Agent character with a speech balloon has a speech balloon for good, and there's nothing you can do about it.
This turns out to be not quite true. You can embed tags in the text to be spoken which modify the Agent behaviour.
Most interesting is the map tag which lets you display one thing in the balloon but have something else spoken.
agent.Speak("Those Chapman brothers? What a pair of \\map=\"twats\"=\"c*nts\"\\");The tags are \ delimited, so you have to double up in C like languages.
Where it get really interesting is if you map all of the text to be spoken to nothing -
\map="all"=""\No balloon! The little bloke speaks and lip-syncs along, but no speech balloon is displayed. Yes!
agent.Speak("\\map=\"A lot of talk but no balloon\"=\"\"\\");
Now the speech bubble is not displayed, it gives a lot more room to throw up your own. Of course, you can't know animate it in the same way as the built-in one, but it's a small price to pay for the ability to put up something a bit prettier.
[1] - Right now for instance the front page barks Longhorn! Longhorn! Longhorn! Longhorn is the next version of Windows, and is due to ship sometime in 2006 or thereabouts. So yeah, loads of articles all starting This document supports a preliminary release of a software product that may be changed substantially prior to final commercial release are really, really useful right now.
[added 6th Nov 2003]
And how about formatting strings in baloon?
IMHO there is no any tool (like tag) witch allow
to force "\n" in baloon text, where it needed. [added 28th Apr 2006]
Went down in to the city centre on Saturday to have a look at toys that the Bean might like for Crimbo, thus ensuring that grandparents (or at least a grandparent in particular who just can't manage by himself) are properly primed. After hoofing from the Early Learning Centre to The Entertainer (jolly good shop this) via the Lego Shop, Nattle took Harry away home and the Bean and I went off to get a pizza. Happily all the Pizza Huts along New Street had enormous queues so we lugged ourselves all the way up to Canal Basin and went to Pizza Express, which is much classier.
Attuned as I now am to Bean-fun stuff, I noticed as we walked several kiddiess with helium balloons. I didn't really pay attention to what was printed on them until one kind of swirled in front of me - an alien face and the words XEMU loves you. I wasn't quite sure I'd clocked it properly - the XE and MU were either side of the face - so I started to pay more attention. Yes, definitely - XEMU loves you. Well that rocks, but it was slightly perplexing. Did it mean there was some kind of anti-Scientology demo going on. Unfortunately, we missed whoever it was because I would have loved to parade around with a XEMU balloon.
A bit of image-Googling this morning confirms there was a protest outside the Scientology org. We must have just missed them. While I'm sure I read XEMU, the ballons said XENU loves you, but hey, not even L Ron Hubbard was consistent.
Lots more on the wackiness, if not outright criminality, of the Church of Scientology at Operation Clambake.
mailgate.focus-solutions.co.uk - Gary?
sync34.avantgo.com - Pete?
neuron.neuron1.com - hello
cache2.ubswarburg.com - Mr Foxon, I presume
relay2.parliament.uk - Ali, I guess?
195.11.208.1 - looks like you Mr Singleton
66.151.128.23 (Bloglines) - Hi!
cltea-proxy1.sun.com - TV's Dr Marc, that must be you
and a special hello to my most dedicated readers, Googlebot, msnbot and the Altavista Scooter.
Yes? No? Then reveal yourself ...
Mike, your old mucker from Taylor Hobson. [added 4th Nov 2003]
Mike, your old mucker from Taylor Hobson. [added 4th Nov 2003]
You should get out more :) [added 7th Nov 2003]
My current client lurrves MS Agent, so for the next few days I'm going to be wrestling with its limited and utterly crappy programming interface in an attempt to make it mildly less annoying and a tad more useful.
If you're lucky enough never have seen MS Agent, it's a little animated blokey (or parrot or robot) that pops up and talks to you. It's like that irritating paperclip you get in MS Word, but even less useful than that, if you can imaging such a thing. Up comes this word balloon, and it reads out the text in a croaky computer voice. Wow!
It turns out that underneath the Agent is quite a cool text-to-speech engine. You give it some text, and as it's spoken the engine tells you the phoneme its saying, the word the phoneme comes from and the accompanying mouth shape. As much as it might pain me to say, it's really pretty cool.
The Agent slaps a series of pre-canned animations (point-left, look up, etc, etc) over the top of the speech engine, and completely takes all the good bits away from you. It'll only tell you when the entire text has been spoken, so you can't do cool things like sync video to the speech. The word balloon is ugly and you can't put formatted text (not even line breaks) or input widgets on it. You can't hack around and subclass it (maybe you could if you escalate your security privs, but that's not going to be a general solution). If you grab the window handle and draw your own stuff on it, the speech stops. If you move it around, it gets move back.
I've come to the conclusion that the whole MS Agent is some demo that an MS drone stuck together that escaped somehow and became a "product". It doesn't give the useful stuff because it was just meant to get a few laughs at conferences. Unfortunately someone else thought hey cool, the kids'll love that as much as MS Bob, and out it went into the wide world.
Thought it would be good to start with something of my own devising. It's a bit of a mediterreanian mish-mash, because at the time I first slung it together that's the kind of thing I had in the fridge, but its no less yummy for that. It's pretty typical of what I cook during the week because it doesn't take that long to prepare, only needs one or two pans, doesn't really require any measuring of ingredients, and reheats well for your lunch the next day.
So then, you'll need
Let's go
You can make this at anytime of year. In summer, if you're feeling keen you can skin some fresh tomatoes to make the sauce. In the winter, use tinned tomatoes and passata and let it simmer down for a bit longer to get a richer, heavier sauce.
None of the quantities are really critical, and you can vary them really quite wildly. Less tomato and more cheese gives quite a nice sticky finish to the pasta, while more tomato and less pasta makes a bit more soupy and leaves lots of dribbly bits you can wipe up with a bit of bread. The timings arn't really critical either, other than ensuring the pasta isn't overdone. In fact, it might be best to slightly undercook the pasta, because it'll cook on a bit more when you mix it into the sauce.
| << October 2003 | December 2003 >> |