Running Code on a Self-Contained Timer 2020

[Note: Updated RunAfter to support the OnLostFocus event]

A year without a post? Hrm. There is a post dated 2019 but it's in the Draft state; never got around to finishing it... oops.

So here's a little post on running code on a timer; I don't know if C# can do this more elegantly, but I don't think VB can. Hence: this.

There have been far too many times I've wanted to run code that only displays something for a couple of seconds and then hides it. While simple enough, I really don't want to have to create a Timer component, instantiate it, run the code, and then tear it all down again. Just display a thing and then go away - that's all I want!

I couldn't even have a half-solution by instantiating and executing the Timer without having an explicit method for the Timer to call into when the time elapses, such as [note that all code formatting in this post is wonky; a lot of it isn't really fixable]:

Dim timer As New Timer

timer.Start(2000, Sub()
			Console.WriteLine("Runs after 2 seconds!")
		End Sub)

I may make a specific extension method for this sort of thing but that's only tangibly why we're here and so will have to wait for another time.

So, today, I decided to try and get this sorted out. While it would probably have to be a Task, I wasn't entirely sure the best way of going about achomlishing accomplishing this and so I began; after various experiments, here's the result.

#Region " Public: RunAfter "
	''' <summary>Runs the specified <paramref name="action"/> on the UI thread after the specified number of <paramref name="runAfterDuration"/> milliseconds has expired.</summary>
	''' <param name="ctrl">The <see cref="Control"/> thread to run on. If using additional actions (such as <paramref name="additionalEventAction"/>), then any hooked events will be hooked into <c>ctrl</c>.</param>
	''' <param name="runAfterDuration">The number of milliseconds (1000 = 1 second) to wait before running the <paramref name="action"/>.</param>
	''' <param name="action">The code to run.</param>
	''' <param name="additionalHookEvent">Specifies that an event should be hooked for the duration of the task; specify the action associated with the hooked event via <paramref name="additionalEventAction"/>.</param>
	''' <param name="additionalEventAction">The action to perform once the <paramref name="additionalHookEvent"/> has been fired.</param>
	''' <example>
	'''   Me.RunAfter(1000, Sub()
	'''				Me.Text = "Runs on the UI thread"
	'''			End Sub)
	''' </example>
	Public Sub RunAfter(ctrl As Control, runAfterDuration As Integer, action As Action(Of Task), Optional additionalHookEvent As RunAfterEvent = RunAfterEvent.None, Optional additionalEventAction As Action(Of Task) = Nothing)
		If ctrl Is Nothing Then Throw New ArgumentNullException(NameOf(ctrl), $"{NameOf(ctrl)} cannot be null (Nothing in Visual Basic).")
		If ctrl.IsDisposed Then Throw New ObjectDisposedException(NameOf(ctrl), $"{NameOf(ctrl)} cannot be disposed.")
		If action Is Nothing Then Throw New ArgumentNullException(NameOf(action), $"{NameOf(action)} cannot be null (Nothing in Visual Basic) and must be a method.")
		If Not additionalHookEvent = RunAfterAdditionalEvent.None AndAlso additionalEventAction Is Nothing Then Throw New ArgumentNullException(NameOf(additionalEventAction), $"An {NameOf(additionalHookEvent)} is specified but the {NameOf(additionalEventAction)} is null (Nothing in Visual Basic).")

		' Ensure the duration is within an appropriate integer range.
		If runAfterDuration < 0 Then runAfterDuration = 0
		If runAfterDuration > Integer.MaxValue Then runAfterDuration = Integer.MaxValue

		' This allows us to marshal the invocation to the UI thread
		' so we don't perform a cross-thread execution.
		Dim ts As TaskScheduler = DirectCast(ctrl.Invoke(Function()
						Return TaskScheduler.FromCurrentSynchronizationContext
					End Function), TaskScheduler)

		Dim task = Threading.Tasks.Task.Delay(runAfterDuration)

		' The action will be executed after the initial runAfterDuration delay above.
		Dim continuedTask = task.ContinueWith(action, ts)

		Select Case additionalHookEvent
			Case RunAfterEvent.OnClick
				' Add the additional specified action to the Control's client event.
				' This won't ever be executed if the user never clicks on the Control.
				AddHandler ctrl.Click, Sub(sender As Object, e As EventArgs)
								ctrl.Invoke(additionalEventAction, continuedTask)
						   End Sub

			Case RunAfterEvent.OnLostFocus
				AddHandler ctrl.LostFocus, Sub(sender As Object, e As EventArgs)
								ctrl.Invoke(additionalEventAction, continuedTask)
						   End Sub
		End Select
	End Sub
#End Region

And the enumeration:

#Region " Enum: RunAfterEvent "
	''' <summary>Specifies an event to be hooked.</summary>
	Public Enum RunAfterEvent As Integer

		''' <summary>No event is hooked.</summary>

		''' <summary>The <see cref="Control.Click"/> event is hooked.</summary>

		''' <summary>The <see cref="Control.LostFocus"/> event is hooked.</summary>

	End Enum
#End Region

The formatting is completely off in this post, so it doesn't look like that in actuality. I also have extension methods for certain things (eg. ensuring a value is appropriately clamped between a range) but I've replaced them with "pure code" versions above.

The above allows you to run code after a specific duration has elapsed, and optionally run additional code based on an event that occurs - all without having to create a separate Timer control that needs to be manually instantiated, hooked, and torn down. Again, this appears to be true for VB.

While not posted here, I have another version that uses a TimeSpan instead of a duration (meh; not a major thing) as it's "more appropriate", and a version that has the initial ctrl As Control parameter as item as ToolStripItem to be used with anything that inherits from said ToolStripItem, such as a ToolStripButton.

Use item.GetCurrentParent() with a ToolStripItem as only the parent ToolStrip has the required Invoke() method.

Oh, an example? Right - yep, this is the basic version that only specifies the code that should be run after a duration has elapsed. This hides the (eg.) ToolStripLabel Verified OK! indicator after two seconds and is used to let the user know a thing they validated/verified/whatever is fine.

Me.tbrMain_VerifiedOk.Visible = True

' Hides the success indicator after two seconds.
Me.tbrMain_VerifiedOk.RunAfter(2000, Sub()
						Me.tbrMain_VerifiedOk.Visible = False
				End Sub)

And another version that does the same as the above but also adds code that hides the visual indicator if the user clicks on it (this is a simplified version of code being used in Twitter Delitter).

Me.tbrMain_VerifiedOk.Visible = True

' Hides the success indicator after two seconds, or
' when the user clicks on the indicator itself.
Me.tbrMain_VerifiedOk.RunAfter(2000, Sub()
						Me.tbrMain_VerifiedOk.Visible = False
				End Sub,
						Me.tbrMain_VerifiedOk.Visible = False
				End Sub)

And that's about it. While countless changes could be made, it would make this post unwieldy; I have a particular loathing for code examples that have extraneous fluff that does nothing but obfuscates what is being shown.

Use this code at your own risk, etc - I (mainly) use VB .NET so you probably shouldn't listen to me in the first place...


Installing Java on Windows XP 2018

This blog seems to be turning into a problem-solving database thingy. Still, it's better than it getting no updates at all, right?

This particular post is about trying to install horrible Java on horrible Windows XP. Namely - you can't. Or, at least, I couldn't.

Double-clicking on the Java installer (either the online or offline versions; doesn't matter) results in a flurry of disk activity and then a whole lotta nothing. Looking in the system's %TEMP% location shows a directory, and a log file named jusched.log. Digging through the file shows that there was an exception - a crash - during the initialisation of the installer.

ERROR: Exception with message 'Resources.cpp(65) at Resource::getPtr(): cannot find resource (name='#1605', type='#6'). System error [1813] the specified resource cannot be found in the image file

According to some of the stuff I read, the version of the installer those Oracle/Sun idiots use doesn't run on Windows XP despite them telling you it, uh, does. The Java runtime might, but the installer used to install it... doesn't. It doesn't surprise me that Java is as shit as it is.

Grab an earlier version of the Java runtime and install that, instead. Here's the one I used (note the installer is still signed by Oracle - important!):

View Page: Java Runtime Environment 8.0 build 152

Why not use an older one directly from Oracle themselves? Because those morons want you to create an account to just download it! Hahaha-ha-ha... ha. No.

When running the older installer, ignore the warning that says it may not work in Windows XP - it's just saying that because even Oracle knows Windows XP is a huge, outdated crock of shit.

I'm running Windows XP in a virtual machine to run a Java JAR file, otherwise, I have no use for Windows XP nor Java.

I'm glad Java is practically dead.

And Windows XP.


Windows 10 Start Menu not Working? 2017

This problem is extremely annoying: sometimes, the Start menu in Windows 10 will outright stop working. It won't appear when the Start button is clicked or the Windows Key is pressed. In addition, various Windows 10-style windows (such as the system's volume control that appears in the top-left of the screen when the volume is changed) will also not appear. Right-clicking on the Start button will then cause any 10-style windows to then draw... it's all very weird.

After many months, it seems I may have stumbled upon a fix for the issue. Every time I tried restarting or shutting down the system, Windows always complained about there being a task that wouldn't exit. The string given is:

Microsoft\windows\plug and play\device install reboot required

Microsoft introduced a "feature" that allows Windows to finish installing device drivers after an update or restart. Awesomely - it doesn't work. This broken feature has been the cause of months of (random) frustration and re-installations.

Anyway - whatever, onto the fix. Hopefully it's the fix; I haven't ran into the Start issue since.

  1. Go into the Settings window in Windows.

To do this using the Start menu (eg. you're performing this as a preventative measure), just type Settings into the Start menu and open the item that appears.

If you have a broken Start menu, you can either click on this link right here or press Windows Key+R to open the Run window and then type the following, followed by pressing the Enter key.


Include the trailing colon character otherwise you'll get an error.

  1. In the Settings window that appears, click on Accounts (the little round guy; second row in the second column in my particular version).

  2. On the left, click on Sign-in Options.

  3. Scroll down in the view on the right and under the Privacy heading, uncheck the Use my sign-in info to automatically finish setting up my device after an update or restart.

That should be it.


Accounts Site Revisit 2017

No updates for nearly two months? Like I mentioned before, that's mainly because I can't really think of anything to post. But, today, I am posting.


If you're a registered user of my software, then expect to see an update of the Accounts site in the near future. I've just upgraded all of the backend libraries and such, and the (local development) site is completely broken. It appears things changed in Laravel, Apache, and PHP - so that shizz got busted.

I had to struggle with working out why Apache kept giving me 404 errors in regards to virtual hosts. Here's what I finally came up with that seems to work in Apache 2.4.x.

<VirtualHost *:80>
    ServerAdmin [email protected]
    DocumentRoot "P:\website_path"
    ServerName mysite.l
    ErrorLog "logs/mysite.l-error.log"
    CustomLog "logs/mysite.l-access.log" combined
	<Directory P:\website_path>
		AllowOverride All
		Require all granted
		Satisfy Any

Just that took a few hours (as I followed numerous false trails), but it's done now and I can just apply it to all of my other virtual hosts.

Next-up is fixing the Laravel errors, which in all honesty, don't really make much sense. Thankfully the Whoops! error-handler is back so I'm not just scrabbling around in the dark.

Still no idea what the problem is, though.


Hot Electrolytes Drink 2017

Here's a little electrolytes drink I've come up with to supplement sodium, potassium, and any other essential nutrients/chemicals that you may have the ingredients laying around for.

I won't get into the specifics of why I'm making or having this drink other than to mention the following for search engine purposes: zero carb, low carb, keto, ketogenic, LCHF. The listed eating regimes are diuretic in nature, so extra supplementation is required; the consumption of incidental salts is very low due to not eating processed foods. In fact, adults should aim for 5g - 6g of sodium chloride per day (table salt is only 50% chloride). Research is out there, if you're interested.

This recipe - if you want to even call it a recipe - isn't zero carbohydrates. It's extremely low, but it's not outright zero. You can make substitutions to remove the (already low) count if you prefer, by, say, substituting the cube for bone broth.



Crumble the bouillon cube into a cup and fill it up with hot water. Add in a small-ish quantity of the rest of the ingredients, mix, and taste.

If you can handle more of the salts, add more of them in. Mix, again, and taste. Keep doing this until you determine how much of the ingredients you can have without it being too salty.

The aim is to get as many of the electrolytes in as possible while keeping it palatable. The more there is, the better.

I use vinegar and Worcestershire sauce to give it a much stronger flavour; I love acidity, so both of these work brilliantly. I specifically chose Henderson's Relish as its carb count (7g per 100g/ml) is much lower than, say, Lea and Perrins which is way higher at 21g. Sainsbury's own brand is 30g! Hell naw, son.

You can add calcium and such, but they float in the liquid and so aren't fun to consume. Horrible and bitter; better off taking it in tablet form if you require supplementation.

Don't forget: If you're doing zero or low carb, or ketogenic, your mineral and vitamin requirements are greatly reduced. No competing with glucose for cellular uptake or any of that crap. Do note that vegetables will interfere with absorption, so zero carb has even lower requirements than the other LCHF diets.

Supplement Vitamin D3 and K2. Everyone should.


Sucralose 2017

Still can't think of much to post about, but checking out the viewer stats shows that there really isn't much point. The visitor count tanked years ago when I neglected the site.

Anyway, today I'm going to post about the Sucralose sweetener as it may save people a lot of money.

Alright, a bit of background: I do zero carb. Originally I did a ketogenic (keto) diet but that shizz ain't good enough for a guy like me, so I now almost (almost!) exclusively eat meat and cheese. I don't need to be that strict - but I am. I plan on bodybuilding after this massive cut I'm through so I need to get this shit right.

I use sweeteners quite a lot and so I've been buying Stevia / Sucralose / Erythritol / Saccharin (delete as applicable). Except, the other day I got annoyed with how much Stevia sweetener I had to use as compared to previously.

Then, somehow, I stumbled upon 100% pure Sucralose. This is what manufacturers dilute (typically along with a sugar - which is idiotic considering the point of sweeteners in the first place!) to create the final product.

100% pure Sucralose is 600 times sweeter than sugar. It is completely insane how potent the stuff is when it hasn't been diluted down to near-uselessness (hello, homoeopathy!). I put literally 30 milligrams in a cup of tea; this pure powder here is a 100g pack. I bought two packs.

I'll still have a lot left over after the heat death of the universe...

[No: sweeteners aren't bad for you, you colossal idiot]


A Bit Quiet... 2017

I haven't gotten lazy in writing new blog posts, it's just I honestly don't know what to write about.

I've done a lot of work on XBox Device Status, but not really anything I'd consider interesting. Hmm, actually - it could be interesting to a few people as I'm doing work with XInput and getting battery and state information. Someone might be interested in doing that from within C# / VB .NET?

I'm also considering doing a one-week fast that I could write about.

Who knows. Maybe I should resume work on that Assembler thing I started a while back? It's just the tedious parsing that's doing my head in and putting me off.


Sega, Sonic and the Fans that saved It 2017

I've been playing Sonic The Hedgehog 3 & Knuckles in little bursts over the last few days while waiting for Sonic Mania to arrive on the PC. I'm getting such feelings of nostalgia and am enjoying it so much that I also plan on completing Sonic 1, 2, and Sonic CD while I'm at it. I only ever completed Sonic 1 - this time I'm even getting all of the Chaos Emeralds; something that the child-me couldn't even get one of.

I entirely forgot that I was a major Sega and Sonic fan back in the day.

For the first time in a couple of decades, a new Sonic game has been released that is actually good. The reason for Sonic Mania going back to its roots and also being a great game is a simple one:

Sega had nothing to do with Sonic Mania

Sonic Mania is good. Obviously Sega didn't have a damn thing to do with it; it was due to Sonic fans that a good game finally came out.

If there's one constant in this universe, it's that Sega is a bunch of clueless dip-shits that have no idea how to handle their properties. They will fuck things up.

They released brilliant games back in the day; such as Streets of Rage, Revenge of Shinobi, Sonic, Golden Axe, Comix Zone, and so many more.

Sega has brought out multiple new Sonic games over the years and they have all been consistent bags of shit. Sega clearly doesn't know how to do Sonic - at least, they don't know how to do it well. Or even okay; they have an uncanny ability to give the absolute opposite of what the fans want.

A werehog? A fucking WEREHOG!?

What about their other games? Well, what about them? They don't appear to exist as far as Sega are concerned. As far as I'm concerned, that Golden Axe game doesn't exist. Fuck sake, Sega.

Finally, after all these years, a great Sonic game has been released. They - or rather, their fans - got it right, so can't we just be happy? Did you forget this is Sega? Of course, we can't be happy!

They fucked it up in typical Sega fashion.

Sonic Mania has Denuvo DRM

Sega said they needed two weeks to polish-up Mania for the PC after the release of the console versions, but it appears they were busy just adding in the most-hated DRM of all time.

Classic Sega.

I decided not to purchase Sonic Mania.

If Denuvo is removed? A guaranteed buy.

Until then...

Fuck off, Sega.


XBox Device Status 1.00 2017

A new project is out: XBox Device Status.

For some reason, Microsoft haven't provided a way to display the battery levels of any connected Xbox pads on Windows PCs. There is a Windows Store app specifically for configuring the Xbox Elite Controller (US version here) that sort of shows it, but it's horrendous and not really designed for that.

In steps XBox Device Status. It's an application (by me, of course) specifically designed for showing the battery levels of any attached Xbox pads and headsets. Version 1.01 will add support for events.

There was quite a delay in releasing XBDS as I ended-up making numerous changes to my Metro library, changing existing controls and adding a new one. This library is why XBDS looks the way it does; it's my implementation of how Metro should've looked before even Microsoft themselves released their original version all those years ago. A form with large empty areas with a mid-grey background is just terrible, regardless of what idiocy you use to try and justify the design decision.

XBox Device Status: Battery Full

The Free Edition does what most people want, showing the battery level of a connected controller. The Home/Enterprise Edition provides extra features such as headset levels, system tray support, and the aforementioned events. More Home/Enterprise features will be continually added.

Additional screenshots are available here.


Porky's Norovirus: The Revenge 2017

It's been days since I last posted and I refuse to let this blog go the same way as all of the previous ones, so here's a post about pork. Yes, bloody pork.

There was supposed to be a post a couple of days ago as I was on the verge of making a new software release - an entirely new product, in fact. But I got mired in reworking one of the (user-)controls that it uses and then made changes to the Metro-style interface. The following day: I got Norovirus. Never heard of other countries getting it but I assume it goes under a different name; the UK gets it yearly, and I get it really, really bad.

I got Norovirus once back in 2001. I've never had it before, I never heard of it before (it didn't even have a name AFAICT), and the doctor didn't even seem to know what it was. Speaking of the doctor, a quick, cringey tale:

When he arrived on the house call and he saw me riddled with this Noro-thing, I repeatedly said to him that it's so bad that I frequently wished I was dead. While he tried to remain professional, I could tell he was mad and irritated.

The following day I found out his dad died that very same day.

It lasted for a week and was probably the worst thing I've ever been through (barring personal tragedies and the like, of course). It was a week, but it honestly felt like it lasted for multiple as in my mind time slowed down to such a degree that I was physically - literally - moving in slow motion. Bonus Fact: I got a PayPal charge-back during that week as I was unable to process an order someone made for my first-ever (PC) shareware app.

That was the first time that I ever got the Norovirus, and thankfully the last time I experienced the slowing effect.

It was hell. Possibly literal to some degree as my spastic-mind imprisoned me in my very own bullet-time nightmare. The day it ended, my mum made me fried-spam-in-toast sandwiches. I got a craving for them; guess that's what happens without food for a week.

When my niece and nephew appeared years later, the Norovirus became a regular feature of the year - sometimes even twice a year as they were getting it at different times. I may seemingly be partially-immune to getting the cold or flu (when I do get them, it lasts for around 10 minutes - a really un-fun 10 minutes), but Norovirus? It's my jam.

It always starts the same way and it always ends the same way.

I'll get a sore throat. I never get a sore throat for any other reason. Once that happens, I know the following six days are going to be horrible. The night of that day I get woken up to a pounding headache, dizziness, weak and sore joints and muscles, and a weird feeling like my head is continually expanding; a form of headache, I assume. The rest of the night I'll wake-up and fall asleep around 20-50 times, often within a minute or two between each "event". I'll actually sleep for an hour when it starts to get light, presumably because of how exhausted I am.

The following day I'm a shambling zombie; everything hurts and my brain is wrapped in cotton wool. The day is completely wasted... until I discovered something.

If I carry on through the day as though nothing is wrong, the symptoms of the Norovirus essentially disappear for that day.

I didn't do that this time and consequently I wasted the day on the settee somewhat watching TV. I haven't done that in a long time, and I kinda enjoyed it. I had to start watching stuff on the HTPC because live TV appears to just be full of boring, tedious shite.

The following day - today - I'm sat here writing this. I want to at least be somewhat productive. I'll try and release that new app, too. I've got two more projects to update and release, too, and a lot of website stuff to get done. In one of those rare times, I'm actually busy.

What? Oh - oh, right: pork.

I mentioned pork at the start of this post. Yes.

Over the last few days there's been reports in the British (British? I remember when we used to say English - when did that change?) press about imported pork being infected with Hepatitis E.

It's only today that I've finally been able to find out which supermarket may be responsible: Tesco.

I do most of my shopping at Tesco simply because the one near me is massive and I love their reduced section because I exclusively eat meat and dairy and the reduced bit has an abundance of those (from their deli) at half price.

Now it turns out their own-brand - assuming Tesco is Supermarket X - pork sausages and ham may have Hepatitis E. The ones that aren't labelled British; as in, imported from the EU.

I bring this up because the symptoms of Hep-E appears to be the same as Norovirus.

When I got Norovirus yesterday, something was different: I didn't get the initial sore throat the day before. I got plunged directly into the middle of the second day with the weakness and all of it.

Just before starting this post it appears this whole Hep-E thing happened 2014 - 2016, so maybe it's unrelated. Still, in all these years I've never once not had the sore throat signalling that the end-times are coming.