03 Mar 2010
Preventing the orphaned system tray icon in a Windows App
Windows applications often add an icon to the system tray to remove themselves from the task bar when minimized, and use that icon in the system tray for a context menu and/or changing image to represent application state. Unfortunately, it is common for the system tray icon to be “left behind” after the application terminates. It leaves the user with the false impression that the application is still running.
This can even happen when the application is gracefully shutdown: not just when the app is terminated by the task manager or another non-human-initiated event. The icon will appear in the system tray until the mouse hovers over the tray, at which time the orphaned icons are cleaned up. What exact event is triggered by hovering the mouse pointer there to remove it, I don’t know.
Strangely, even Microsoft’s Visual Studio IDE will display the same behavior when the internal web server runs to execute/debug a Web Application. If you select Debug / Terminate while the app is still running, the gear-in-a-page icon representing the internal web service will remain in the tray. Start the app again (with F5, etc), and another icon will appear. Start and Terminate the app enough times, and you’ll fill the tray with those icons. And hovering the mouse over the tray just once will clean out all the icons which were already terminated. Note: If you right click the tray icon while it is active, and select “Stop”, the internal web server will stop and properly remove the icon from the system tray.
The problem appears to be that the NotifyIcon object must have its Dispose() method called by the WinForm to terminate it and (get this) Windows will not properly remove the icon from the system tray unless an idle state occurs after the Dispose() method call, and before the application is closed.
If you’re confused, don’t worry. There is evidently one or more events not firing after the TrayNotificationIcon’s Dispose() method when the application is closing, but the event(s) do fire if the form returns to an idle state after the Dispose() method.
That being said, here is how to force an idle state to prevent orphaning the icon in the system tray.
This project on GitHub contains a single Windows form with a tray icon. That tray icon has a context menu associated to it, which you activate with a right-click. It contains options to restore the window when it is hidden, and an option to terminate the application. The application can also be closed by a button on the form, or the standard close button in the form’s upper right corner. To test it, you can also terminate the application from Task Manager. Note: I coded the closing event so that it will detect whether a user requested the form closure, and will prompt for confirmation if so. Anything else closing the form will not generate a user prompt: this includes Task Manager.
The Form has a timer object, which is used to actually close the application. The timer in combination with the module-level boolean variable Terminating (defaulting to false), is used in the FormClosing event to execute the Dispose() method, then return to the FormClosing event after the idle state occurs. See lines 29-76 of Form1.cs for the critical code which makes it work.
I set the timer for a 100ms delay before activating the shutdown. In reality, this delay could be lower. I just choose 1/10th of a second because users won’t really notice the delay.
I have seen other solutions that attempt to dive down into manually managing handles. In reality, a C# programmer should not need to go to this level except in very rare circumstances.
16 Feb 2010
Old rounding errors never die, they just fade into images…
My friend Matt Owen posted a snapshot of the stock results on his Facebook wall today, which showed some interesting results on NASDAQ.
It’s amusing to see little math slip ups appear from time to time like this on commercial web sites. Seeing this error reminded me of other experiences I have had in coding where numbers would throw wildly inaccurate answers every 3rd blue moon or so.
Since I’ve experienced this type of error before, I was thinking to myself, “Wow, that floating point rounding error is still around !” It’s probably not the problem with these invalid values, but you might be asking yourself “what’s a floating point rounding error?” Not everyone who has learned coding is aware of it, and it’s more likely to affect calculations requiring precise levels of resolution, rather than applications written for everyday use.
Nonetheless, it’s a good thing to be aware of. Back in my junior high school years, our math teacher used the following code in BASIC to demonstrate the problem:
1 2 3 4 5 | X = 0.0 For I = 1 To 1000 X = X + 0.1 Next I Print X |
The code’s purpose is simple: start at 0, and add 1/10th to it for 1,000 times. The expected result is the same as 0.1 * 1,000… or 100.
Since X is not type cast, the parser would find a suitable default floating-point type for X due to the decimal point in the number. Depending on the computer it ran on, it would produce slightly different results, but never the exact 100.00 which is the correct result. On an HP2000C mainframe, the answer would be 99.996, while on a Radio Shack TRS-80 the answer was 100.02.
So out of curiosity, I had to see what more modern languages like VBScript and C# would produce. Sure enough, neither produce the expected result of 100.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | x = cdbl (0.0) for i = 1 to 1000 x = x + cdbl(0.1) next WScript.Echo x y = csng (0.0) for i = 1 to 1000 y = y + csng(0.1) next WScript.Echo y z = 0.0 for i = 1 to 1000 z = z + 0.1 next WScript.Echo z |
The result is:
99.9999999999986
99.99905
99.9999999999986 (uses a default cast of double)
In CSharp, create a windows form with a label and a button, and insert this code at the button click event:
1 2 3 | double total = 0.0; for (int i = 1; i <= 1000; i++) total += 0.1; this.label1.Text = total.ToString(); |
The result is: 99.9999999999986
The problem is widely known and well-documented. Still, I like the way my high school math teacher put it. This is a card to put in your pocket, and play when you encounter the person who swears up and down that math is precise and computers are perfect. You can even use it on a person who laughs at the occasional “numbers gone wild” event in their portal… as in the stock result above. 🙂
11 Feb 2010
SQL Server, Integrated Authentication, and “To Tell the Truth”
Moderator: “Login number one, what is your identity?”
Number one: “I am known as jsmith”
Moderator: “Login number two, what is your identity?”
Number two: “I am known as jsmith”
… and after all the contestants would make the same claim, the moderator on “To Tell the Truth” would then give some background on the contestants, and iterate “one of these login’s is the real jsmith”.
Evidently, not in SQL Server 2005 Express. It just answers every contestant with, “Of course you are. Come on in.”
This behavior occurs under the following set of circumstances.
- On the SQL server and the client machine connecting to it, you have a local NT account which has the same name on both machines.
- Both accounts use the same password (so it’s not really any serious security threat in most cases).
- Your connection is using integrated security.
Apparently, SQL Server will compare the login as “.\jsmith” and not as “CLIENT\jsmith”. Unexpected behavior, but it has proven useful if it was intended. I’m only seeing the behavior in local NT access: I don’t see Domain NT access having this problem.
I haven’t seen any hits when doing a search on Google about this. I’m not sure if this is by design or an oversight. In my case, I’m am number one and number two.. so it has its advantages.
Follow-up: I found out (thanks Don Cross) that this is the behavior of Windows Workgroup mode, and is expected. It’s a neat little mode that I didn’t know about, and very useful for some things that I want to do.
28 Jan 2010
Weed Killer 1.0.1.0 Released (Source Forge)
Weed Killer Worker now has summary lines available through a new command-line switch “/c” (counts). After each server, and each configuration entry, it will display the number of files removed and the number of bytes recovered from the file deletions.
Note: The byte size is a minumum recovered size. It does not actually factor in bytes recovered from unused, allocated space in clusters, so the actual recovered size is almost always slightly higher.
See the download section for details.
28 Oct 2009
Time Change with the TimeZoneInfo class
It seems appropriate to talk about this class, with the switch from Daylight Savings Time to Standard Time occurring this coming weekend in the United States. I’ve done a bit of work with this class lately, and wanted to share a test application I wrote to demonstrate some conversions. It also shows the effects which occur during the actual time change, when converting between time zones. You will need the .NET Framework 3.5 installed to use this demo.
The console application shown here shows how to iterate the defined Time Zones, convert from UTC to the Eastern Time Zone, and Convert from Eastern Time to Pacific Time using UTC as a midpoint in the conversion. All this is done with the TimeZoneInfo class. Eastern and Pacific time zones (US) are arbitrary choices: any time zone can be used.
I designed the example times used in the demonstration to show the variances which occur between two times zones, during the actual day of a time change. It also shows the local time ambiguity which occurs during the two occurrences of 2AM in the fall, and the absence of 2AM in the spring.
While the demonstration provides practical examples of how to do these conversions, I hope you will see the value of storing time as UTC instead of its local value in critical applications. Any time a change occurs, there is a one hour period per year where an occurrence in the 2AM hour will be ambiguous when the date is localized: did it occur before the change or after the change. If the date recorded is UTC, the ambiguity does not exist.
While most applications can live with one hour of ambiguity in the time per year, especially since it’s a time when most people are asleep, it is an important design consideration in critical applications.
22 Sep 2009
The Single Application Instance Experience
While enhancing the Weed Killer application, I ran into an interesting problem trying to make Weed Killer a single instance application. There are several ways to do this: one is to use the semi-dreaded mutex. Surprisingly, the easiest way turned out to be implementing a class in the Microsoft.VisualBasic namespace from the .NET 2.0 framework: WindowsFormsApplicationBase.
But, at first, it may not seem like the easiest way. I had to make several attempts to get my inheriting class right. I ran into a lot of examples on the net which were not great fits for what I was trying to do. And some of the examples were right in principal, but the code didn’t work.
The class shown here can be used for your own code, by just replacing the form you instantiate in the startup event. ManagerMDI is the entry point for the application. Its method OpenFile() takes a single parameter: the file to open. It internally manages whether the file is already open in a child window, or if a new window needs to be open.
The value of inheriting from the WindowsFormsApplicationBase base class is obvious: all of the housekeeping for determining whether an instance is running or not is taken care of for the developer. The two startup cases are handled easily using specific events: Startup if you’re the first, or StartupNextInstance if you’re not the first.
One little note: The method SetForegroundWindow() from user32.dll is used to set focus on the application, at the end of the StartupNextInstance event. I also use it in the Startup event, in case the application was launched from an outside source (e.g. Windows Explorer context) though it probably isn’t needed there. The behavior of this method is dependent on a setting for limiting focus, which you can see in Powertoys for WinXP, etc (General / Focus). If “prevent applications from stealing focus” is enabled, the method will cause the application’s taskbar entry to flash. When disabled, the application will be restored and receive focus on the desktop.
21 Sep 2009
WeedKiller 1.0.0.5-Alpha released
This has some small changes to the configuration (Regular Expression versus Wildcards is now an enumeration), and the Manager is now a single-instance application.
Source code and binaries are available on the SourceForge.net project page.
11 Sep 2009
Don’t brick my iPhone: I might just throw it at you
Wow ! The mysterious and annoying RDC problem on my laptop took a dangerous turn yesterday. The problem has to do with the power setting properties sometimes mutating, when I bring my Windows XP Media Center laptop out of hibernation, and connect to it via RDC. The original problem is described in this post.
Apple released the new 3.1 O/S for the iPhone. Having a very busy week, I decided to start downloading the software and the firmware installation after I was setup at work. I was not going to stay up for the buzz of the new Apple release. So I setup everything in the morning, and I was able to launch the whole process via RDC on the laptop, switch back to my “work”-station, and be productive while the upgrade process ran automagically.
After 15-minutes, I got up to take a break and, while on it, had this sudden feeling of dread come over me. I had not verified that the standby and hibernation timeouts were still set to “never” after I connected via RDC. So I hurried back to my desk and checked.
Sure enough, the settings had mutated to 20 minute standby, and 30 minute hibernation for external power. D*!@mn 1t !! As I discovered this, the iPhone was displaying the Apple logo, with a progress bar under it just starting to fill. I obviously came back in time. Had the laptop gone into standby and hibernation, it would have screwed the firmware load, and I would have been on the phone with Apple support (at a minimum) to get this fixed.
I still have seen no information on this problem with mutating standby and hibernation settings, and I got a very ugly taste of what damage it can potentially do. Microsoft… find this and fix this !!!
09 Sep 2009
The lost art of break and continue
BREAK and CONTINUE are two forms of loop control, which I have seen under-used lately. I think it is time to revisit how these two statements can help you make more maintainable and readable code, with some basic comments where needed.
BREAK is used to prematurely exit a loop sequence, be it a WHILE loop, or a FOR loop. CONTINUE is used to force the next iteration of the loop. These two keywords exist verbatim in SQL and C/C#/Java, but all languages have them in some dialect.
A great way to understand easier maintenance is the venerable CURSOR in SQL. Generally, a cursor loop is written as such:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | DECLARE @nValue1 INT, @nValueA INT, @nValue42 INT DECLARE curSingleRow CURSOR FOR SELECT Value1, ValueA, Value42 FROM TABLE OPEN curSingleRow FETCH NEXT FROM curSingleRow INTO @nValue1, @nValueA, @nValue42 WHILE @@FETCH_STATUS = 0 BEGIN /* ... process the single row here ... */ /* ... get the next row to process ... */ FETCH NEXT FROM curSingleRow INTO @nValue1, @nValueA, @nValue42 END CLOSE curSingleRow DEALLOCATE curSingleRow |
This code is very familiar to SQL developers. The @@FETCH_STATUS value becomes non-zero if no further records are available, or some other error occurs. The problem with this code is that if a column in the select statement is added, dropped or changed, there are three points of maintenance: the declare statement, and two fetch statements. If the loop is rather large, the last fetch statement is going to be a long way down.
Consider the following change to the code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | DECLARE @nValue1 INT, @nValueA INT, @nValue42 INT DECLARE curSingleRow CURSOR FOR SELECT Value1, ValueA, Value42 FROM TABLE OPEN curSingleRow /* (1=1) means always true: intentional. Exit is forced within the loop itself */ WHILE (1=1) BEGIN /* ... get the first or next row to process ... */ FETCH NEXT FROM curSingleRow INTO @nValue1, @nValueA, @nValue42 IF @@FETCH_STATUS <> 0 BREAK /* ... process the single row here ... */ END CLOSE curSingleRow DEALLOCATE curSingleRow |
The change makes the WHILE loop appear infinite, but it simply guarantees that the execution will always enter the loop. Notice that I explicitly put a comment there to ensure that anyone maintaining the code understands the intent. Since the FETCH statement is identical for the first and successive records, that is what will occur with the same statement–on the initial entry and upon successive iterations. The IF statement checks for either no more records or an error condition, and will trigger the BREAK statement when that happens. Otherwise, the code below the IF statement continues to execute, and will iterate back to the WHILE statement. This scenario also covers the case of no rows returned by the SELECT statement. The BREAK statement immediately exits the WHILE loop, executing the CLOSE statement.
This now reduces maintenance to a DECLARE statement and a single FETCH statement. To me, the code is also more readable. My first instinct reading the original code is to question why the FETCH statement was repeated in the original code (it’s just because of the way my brain works).
BREAK and CONTINUE are extremely useful for creating short-circuiting logic, within a main logic condition. Example: Say you needed to parse a set of command arguments, where certain argument combinations would cause remaining arguments to be ignored, or cause the single argument to be ignored. The code could be written like this.
09 Sep 2009
Rethinking the Labor Day holiday
As Labor Day came and went, I began thinking about all that’s been happening in American politics, and some of the quandaries we have as a country. Our Labor Day holiday is based on celebrating the American workers and their accomplishments, by… well, I guess by giving them a break from their work.
Labor Day has always been a strange day for me, especially after living in Europe for over five years. The US holiday has been in existence since 1882, and has its origin during the industrial era of the Western World. It is very similar to the May Day holiday, which the Communist and Socialist World embraces. The biggest difference between the two holidays to me is that in America, we celebrate the time-off with our families and friends, having picnics and recreational getaways. May Day celebrations around the world seem to manifest as some galvanizing protest, with large amounts of energy spent on chanting and in many cases vandalism.
How we celebrate Labor Day versus other countries celebrating May Day is only one aspect. I could go on for hours about self-image, and about how a person identifies their self-worth with their work manifests in these celebrations.
But I have an important question. When are we, as a country, going to offically recognize our own economic system and create a holiday called Free Enterprise Day ?
Why is it in the United States, the world’s dominant superpower and sanctuary of capitalism, we have no official holiday celebrating business owners. That’s right.. ownership. I always chuckle when I hear about Government efforts to create “jobs.” It’s a buzz word that politicians use because they know that most people can relate to the steady paycheck, and work for someone else’s business. But in reality, jobs are created by passing a number of tax laws or grant programs to give enough incentive to people thinking about starting a business–so that they actually can do it. And it is those people who cross over the line, and many times burn bridges doing so, that end up creating those jobs that the politicians are so desperately promoting.
Did anyone ever take note that in the late 1970’s, before Soviet-style Communism was on the decline in the 1980’s, China began a shift to market-based economics and partial private ownership under Deng Xiaoping’s leadership. He sowed the seeds which make China’s economic presence so powerful today. When the Soviet bloc was crumbling, China was growing and beginning to prosper, and has moved into the position of the next Super Power. Granted, China does not have the extreme free-market style that the US has, but as you can see, a little Capitalism goes a long way.
So, as the next Labor Day holiday approaches, think about what you are celebrating. In this economy, it’s great to celebrate having a job. Just keep your thinking a little broader. Take the time to remember and thank those who dared to step over the line and create the business you work for.
After all, that’s what keeps the Labor force, blue or white collar, employed. And if you take the time to learn the demands of leadership and perseverance that business owners endure to make their vision survive and prosper, I think you will understand that a nationally recognized day for business ownership is long overdue.