Skip to content

Windows

Scheduling tasks on win32 with PHP

The other product of last night was another win32 only extension, this time for manipulating scheduled tasks. Why do you need an extension? The reason is that Microsoft chose to expose the "mstask" API as non-automation compatible COM APIs; that means that you can't simply use the PHP COM extension (which is really "PHP-OLE") to work with it.

The win32scheduler extension provides 5 functions:

  • win32_scheduler_enum_tasks() - returns an array containing the names of the scheduled tasks that are present on the system.
  • win32_scheduler_run(string taskname) - requests that the system run a given task immediately. It does not wait for the task to complete.
  • win32_scheduler_get_task_info(string taskname) - returns information about a given task.
  • win32_scheduler_set_task_info(string taskname, array info [, bool new]) - updates or creates a new task.
  • win32_scheduler_delete_task(string taskname) - deletes a named task.

It's fairly self explanatory, although the details for the task info are a little weird. I'll document those for PHP at a later date, but if you're interested in working with it now, you'd do well to read the MSDN docs on the Task Scheduler; the structures and unions docs will help you to figure out the task info format. If you want to create a task, you'll find it helpful to var_dump() the info returned from an existing task; the set_task_info() function uses the same data format. Top tip: you need to supply a "Password" field and set it to the password for the user account you set in "RunAs".

This extension should also be showing up on the PHP 5 PECL snaps page in the next couple of hours.

Enjoy!

WinXP Desktop Shell Replacement

A little while back I mentioned some annoyances with virtual desktop software under windows. It occurred to me that the only sane way to implement virtual desktops is to do away with the explorer shell and use something else.

This is not a new idea: GeoShell, LiteStep and BB4win all try to do this kind of thing, with varying degrees of success. I tried to use each of these and found that they all have their rough edges that just made them unusable for me.

So I was frustrated. Motivated by frustration and curiosity, I wrote my own shell replacement for WinXP. It features low resource consumption and a low-clutter interface that I suppose is somewhat similar to the OSX dock, even though that wasn't my original intention.

screenshot full-size.

My shell, dubbed "WezDesk" for want of a better name (EvilDesk!?), implements the following features:

  • Quicklaunch icon bar
  • System Notification area (aka "Tray"), including tray bubbles
  • Clock with day/date
  • No classic taskbar, but "flashing" windows have their icons bounced to get your attention
  • Light-weight icon-free start menu
  • Kludge-free Workspace management; use up to 4 virtual desktops
  • icon-free desktop

If you feel like a change from explorer will also do you some good, then feel free to download and test-drive my latest offering.

Disclaimer : the software is provided as-is in binary form only with no warranty. I am not liable for any bad mojo, be it mental, physical or meta-physical, that arises from its use. By downloading it, you assert that you will not decompile or reverse engineer the software in any way, and that you will not re-distribute it to any third party in any form without my express consent. All rights reserved; All liabilities disclaimed.

Caveat Emptor : when switching desktops, some naughty apps (notably gaim and the MSDN help browser) occasionally get "warped". Restarting the app is one solution, not switching desktops is another. (Running miranda instead of gaim saved several MB of RAM at runtime, runs faster and has no weird interactions that I know of).

Caveat Emptor 2 : tray "bubbles" sometimes miss updates to their text content.

Despite these niggles, I've been happily using the current build for over a month, >15 hours per day and it's been a pleasure to use.

Download wezdesk.msi

Installation

Double click the .msi file to install. Note that installation is non-destructive; it simply installs the app into your start menu. You can then go into:

Start | Programs | Evil, as in Dr | Shell | Set Shell

to register it as your shell. You need to log out and log back in again to use it.

If you want to go back to explorer.exe, call up the start menu (right click on the desktop):

All Programs | Programs | Evil, as in Dr | Shell | Reset Shell

and log out and log back in again.

You'll also find the online docs available in the start menu; in there you'll find out about the various hotkeys available.

nVidia nView virtual desktop manager

I've been using this for a while and been annoyed by two things it does:

  • Won't allow you to assign ALT-F4 to switch to desktop 4
  • It's unable to manage win32 console windows

The latter doesn't seem fixable, but I can live with it. The Alt-F4 thing can be solved with a bit of registry hacking. First, turn off nView, so that it saves its state into the registry. Then open up regedit and create the key shown in the picture below (the full registry path can be seen in the status bar). Once you've added it, turn on nView again.

Alt-F4 as a hotkey

I did try VirtuaWin for a while, but it has particularly crappy interaction with the cygwin X server: switching desktops causes most of your xterm windows go into attention grabbing flashing mode. Very annoying.

.Net annoyances

I've spent some hacking time this weekend improving the COM/.Net extension for PHP. I am both happy and annoyed with the results. In order to convey why I'm annoyed, you'll need a bit of background.

COM is all about interfaces. PHP uses the OLE Automation interfaces that allow run time binding of classes and methods; the key here is IDispatch. There are also a set of interfaces known as Connection Points that effectively allow an object to call you back, via another object that you supply, to be able to handle events. The guts of Connection Points are hidden away from you in VB via the WithEvents keyword. PHP also supports Connection Points and also hides the details from your script via the com_event_sink() function.

Now, .Net is similar, but not. It has a number of COM-ish concepts and even ships with the "interop" layer that will automagically map COM into .Net and vice versa. PHP uses the interop layer to bind to .Net at runtime; this is cool because it means that PHP doesn't then require the .Net runtime to be installed on the target machine.

As part of my quest for a better offline blogging tool, I thought about using .Net from PHP to get at the System.Windows.Forms classes. It quickly became apparent that there are some shortcomings with the .Net interop stuff. The theory is that .Net will map its classes to IDispatch'able interfaces via CCW's (COM Compatible Wrappers). In practice it does that, but doesn't explicitly implement the interfaces; instead, IDispatch is present implicitly in the System.Object interface. The effect of this is that the PHP stuff breaks when it sees these objects coming back out from COM, because it can't see IDispatch: .Net is being naughty.

The fix for this is simply to import the interface definition for System.Object and explicitly check for that interface in each of the places where we are looking for IDispatch. This is fine, except that it isn't simple. System.Object is defined in the mscorlib assembly. There is no mscorlib.h header file, and there is no mscorlib.idl to generate a C header file either. In order to get at this stuff I had to load up the binary type library file and export it as IDL. I then needed to hand-edit the 20k lines of IDL so that it could be compiled by MIDL (the order of many declarations was wrong). This process resulted in a 3MB mscorlib.h file (115k lines).

Armed with the header, I added support for System.Object and was able to instantiate the System.Windows.Form::Form class from PHP, set its caption and size and see it on-screen. Yay!

The next step was events, because a form that doesn't react to user input is not very useful.

Events in .Net are based on delegates. Delegates is a posh way of saying "callbacks"; your delegate is essentially a structure containing a pointer to a function or method and a pointer to the object to which the method belongs. Hooking up the event is simply a matter of telling the source object (say, a button on a form) where your delegate structure lives.

At the lower level, the procedure for hooking up a 'Click' event for a button is something like this:

  • $type = $button->get_Type();
  • $event = $type->GetEvent('Click');
  • $event->AddHandler($button, new Delegate('my_click_handler'));

With the help of our 3MB header file, we can execute this code. We need to implement the Delegate interface on our own stub, because there is no standard Delegate class that we can set up with a generic C callback function.

And this is where things fall down: .Net doesn't accept our Delegate instance because it is not managed code.

The way to get this stuff to work is to build the PHP com extension as managed code, and re-implement chunks to use the Microsoft specific extensions to C++. This poses 3 problems:

  • Not everyone has a CLR capable compiler, so our build system needs to either detect this or provide a switch for the user to turn it on.
  • /CLR mode is not compatible with the majority of regular CFLAGS we use (like turning on debug info), so it requires some hackery in the build system to even get it to build.
  • Building with the /CLR switch will force a hard dependency on the .Net runtime, so we would need to:
    • make COM a shared extension (php_com_dotnet.dll)
    • build one version with and one version without .Net support (more build hackery)

Big ouch.

A really nice clean solution would appear to be to separate the .Net stuff out in its own extension; that would solve a couple of problems, but introduce others: there are a few places that need to handle .net specific cases, and those introduce linkage dependencies on the .Net extension.

This is fixable, but we're now into the realms of work-exceeds-fun-returns, so I'm probably just going to leave things as they are. Sorry folks.

If you want events in the mean time, all the .Net docs suggest that you should instead make your own wrappers for the objects, using a .Net compatible language, and implement them as Connection Point style interfaces. The interop layer can then perform behind the scenes voodoo for the CCW's, and you should then be able to use com_event_sink() to hook up your events.

VMware on win32 tweaks

My laptop has a dual boot xp/linux install.

For a little while now, I've been running linux 98% of the time, but I've started to miss my music. In the last couple of days I've tried a number of Ogg/MP3 players on linux and have been a bit annoyed by the memory and cpu usage. I suspect a combination of bad audio and firewire drivers as being responsible for most of CPU usage. Anyway, it was making the system unusable, and I need my music.

So, I've switched back to the XP side of things for now, using vmware to get at the linux install (using a native persistent disk to provide access to the real linux partition). One of the things that annoyed me about vmware was that NAT networking sucked; it would cause my wifi to drop out and not come back, sometimes, if I rebooted the virtual machine. I also didn't like the fact that I had to run dhcpd and nat daemons. In a flash of inspiration today, I made the following adjustments to my config:

  • remove (by unchecking the box) the vmware bridge protocol from all of my network connections, including the vmnet connections.
  • for vmnet8 (the default NAT net), disabled NAT and DHCP and set the subnet to 192.168.0.0 in the VMWare "Virtual Network Settings"
  • in the Service MMC console, stop the 3 VMWare services (auth, nat and dhcpd). Set auth and nat to manual startup and disable the dhcpd.
  • edit your virtual machine config: remove your NAT NIC, then add a new NIC; choose custom networking and set it to use vmnet8
  • right click on your internet facing NIC (under "Network Connections") and turn on internet connection sharing; select vmnet8 as the private network to share.

The net effect (pun not intended ;-) is to have windows perform the nat and dhcpd for vmnet8 natively. By disconnecting the VMWare Bridge Protocol, vmware leaves your NICs well alone. I've noticed better stability and less latency using this approach today.

On the linux side, I run RHEL; for vmware I boot into run level 3 instead of the usual 5. I've turned off most services in run level 3: acpid, bluetooth, autofs, sshd and all that stuff, leaving just syslog, gpm, vmware-guestd and a couple of mingetty and tweaked rc.local to stop hotplug and unload additional modules, all to reduce the memory footprint of the vm.

Back on windows, I run the Cygwin X server and use that to serve xterms up from the virtual machine (don't forget to use xhost under cygwin to allow the vm access to the X server).

Should have known better

I installed Norton Antivirus 2004 Pro edition on my laptop recently (I actually paid for Pro so that I could give my younger sister a copy to run on my old laptop; you get two licenses for less than the cost of two copies of the regular version).

Well, I was surprised to find that NAV keeps Windows Messenger open in the background, so that it can screen it for bad things happening. That wouldn't be so bad except that it eats 20MB.

I was also surprised just now when Outlook Express (a neccessary evil) decided to reply to a message that came in over news:// using Outlook. An ill-fated sequence of events led to this disaster. Having now corrected the configuration of the beast, I'm considering giving Outlook 2003 a second chance.

I just noticed the memory usage:

Outlook.exe 31MB
msmsgs.exe 22MB
winword.exe 23MB

Ouch. I don't even (officially) have Word or msmsgs (messenger) open; they are secretly loaded by other apps.

Surely you don't need much more than a couple of MB to check your email??