Category Archives: mISV

Last updated by at .

Dual SHA256 / SHA1 Windows App Code Signing

I wrote about digitally signing programs for Windows a few years ago. Microsoft announced last year that windows would no longer trust files signed with the SHA-1 algorithm after 1 January 2017. This causes some problems with older operating systems (like XP SP2 and Vista) as they do not support the SHA256 algorithm for certificates used to sign programs/apps. To maintain compatibility with ealier versions Microsoft suggests dual signing with both the SHA1 and SHA256 certificates. It turns out my certificate (from Comodo and issued in mid-2015) supports both the SHA1 and SHA256 algorithm so it’s not a big hassle for me. However, some older certificates (that have not expired) may need to be re-issued by the issuing authority, some certificate issuers such as K-Software are issuing replacements for free.

Here’s what I had to do to sign my EXE files with both SHA1 and SHA256 versions of my certificate.

1. Download an up-to-date version of the signtool.exe file from Microsoft. Such as this one from the Windows 8.1 SDK.
2. My certificate was installed automatically by Comodo when I purchased it so it needed to be exported to a PFX file. You’ll need to know what the password for your certificate was when you purchased it. There’s a decent tutorial covering this process here.
3. Work out the new commands to dual sign your EXE files. In my case they look something like this.

//Code sign with SHA1
signtool.exe sign /f "c:\path\to\pfx-file\my-pfx-file.pfx" /p mypfxpassword /t http://timestamp.comodoca.com /v c:\Path\To\File\somefile.exe

//Code sign with SHA256
signtool.exe sign /f "c:\path\to\pfx-file\my-pfx-file.pfx" /p mypfxpassword /fd sha256 /tr http://timestamp.comodoca.com/?td=sha256 /td sha256 /as /v c:\Path\To\File\somefile.exe

It’s important to use the /fd and /td switches on the second call to ensure both the file and date/time stamp are SHA256 signed. I actually wrote a little batch file to automate the dual signing of files. I call the file from my various build scripts to sign both my program executables and installers. Here’s that file:

@echo off
echo ************************************************ 
echo Running %0
IF %1=="" GOTO InvalidParameter
IF not exist %1 goto InvalidFile  
echo Signing %1
echo Signing with sha1

"d:\code signing 256\signtool.exe" sign /f "c:\path\to\pfx-file\my-pfx-file.pfx" /p mypfxpassword /t http://timestamp.comodoca.com /v %1

echo Signing with sha256
"d:\code signing 256\signtool.exe" sign /f "c:\path\to\pfx-file\my-pfx-file.pfx" /p mypfxpassword /fd sha256 /tr http://timestamp.comodoca.com/?td=sha256 /td sha256 /as /v %1
echo Signing completed Successfully
goto eof

:InvalidParameter
echo You must pass this script a file to sign
goto eof

:InvalidFile
echo %1 does not exist
goto eof

:eof
echo ************************************************ 

You can easily check if you’ve dual signed correctly from within Windows 10 by right clicking on your signed EXE file, selecting Properties and then clicking the Digital Signatures tab. If it’s done right it should look something like the screen below. Note that both the SHA1 and SHA256 certificates are present.

Dual Code Signed EXE File

Dual Code Signed EXE File

Case Study – How Much Time are Employees Spending Working on Jobs?

Recently I’ve been working with a local truck re-finishing company. They work for their customers on a job-by-job basis, providing fixed priced quotes for each job. Obviously their profitability relies on them controlling both the time and materials spent on those jobs. Their billing, invoicing and payroll is controlled with MYOB, while employee time and attendance and tracking time spent on jobs is done via wall mounted Fingertec Timeline 100 time clock and Fingertec’s TCMSV2 time and attendance software. The time clock only allows for tracking of two digit job numbers which means job numbers must be re-used. There is no link between the TCMS software and MYOB.

The Client's Fingertec Timeline 100 Time Clock

The Client’s Fingertec Timeline 100 Time Clock

The Problem

The owner of the business has little or no idea of how much time employees have spent on jobs until the job is complete. This has lead to losses being made on jobs where better control could have resulted in less time being spent by employees. It has also lead to problems in quoting as it has been difficult for the owner to access historical cost information for jobs of a similar type. The reasons for the delay in reporting can be pinned down to the following root causes:

1. The employee responsible for monitoring time and attendance and generating job reports had little understanding of the TCMS software and was not willing to learn more. As a result reporting was largely a process of printing hard copy reports and manual calculations that were either wrong or severely delayed.

2. The TCMS limitation of two digit job numbers meant that the historical information stored by the system was useless. For example, job 89 in May could easily be a completely different job in December. The client controlled what each job code represented via a descriptor the TCMS software could store with each code. When a number was re-used the descriptor would be changed. However, the TCMS software only discriminated by job number.

3. Delays in reporting meant the owner was loathe to ask for reports as they took so long to generate and (appeared) to require a lot of work to generate.

The Goal

The goal of the project was to provide the owner of the business with a simple tool that could report on the time his employees spent working on customer jobs. The tool should be easy to use by admin staff and be able to generate reports in minutes rather than hours or days.

The Solution

I spent several hours familiarising myself with the TCMS software. The reason for this was two-fold. First to understand what data integration was possible, and secondly to investigate the TCMS software for automation opportunities to speed up the time and attendance reporting and payroll processing procedures. The second task lead to immediate cost savings within the business via the following:

1. Configuring TCMS to automatically round times to the nearest 15 minutes (previously done manually)
2. Set up overtime within the software to automatically determine daily overtime on week days and penalty overtime for employees working on weekends.

These two changes have immediately reduced the time taken to process payroll by 2-3 hours per week.

The TCMS reporting system can produce reports in a variety of formats. I was hoping the software could generate reports directly to MS Excel as this would have made my job a lot simpler. However, the Excel reporting system in TCMS is severely flawed resulting in a single report taking more than an hour to generate (I suspect due to not setting the Calculation property of Excel to xlCalculateManual). Further digging around indicated that CSV reports could be generated. In particular the TCMS “Job Cost Analysis” report could be generated in CSV format. The format was a simple flat file that included a record for each time punch pair, the employee, the date, and the recorded job number and job descriptor tag.

The Excel Job Reporter

I developed an Excel spreadsheet that could import the Job Cost Analysis CSV file from TCMS and then present the data to the business owner in a useful format. The spreadsheet allows the construction of a master list of current jobs so that monitoring of current spend of jobs is quick and easy. You can see what this job sheet looks like below. This report is generated daily and made available to staff so that they can see how much budget is available on their current jobs.

Excel Job Tracker - Current Job List

Excel Job Tracker – Current Job List

The system also allows the owner to easily see the time spent on any historical job by any employee. Incredibly useful for quoting of new jobs. You can see that report below.

Excel Job Tracker - Detailed Job Report

Excel Job Tracker – Detailed Job Report

A number of other reports are also available allowing the owner to see who has been spending time on what in the preceding day, week, or month.

The Benefits

The entire system was developed from start to finish in under 20 hours. The cost of the project is going to be recouped in lower payroll processing costs alone in about 3 months. The opportunities for better job cost control are likely to give multiples of project cost in savings and better quoting each month. All of this with some simple business process analysis, better use of the time and attendance software the client already owned, and making use of Excel.

No Time. No Time. The Prequel.

Here’s my original post on Reddit that spawned what I wrote here.

I’m a 40 something guy who has been doing “try before you buy” software for more than 10 years. I have several products, but one has been my bread and butter for several years. I’ve also got a SAAS product in the same field. Now, my “business” is essentially me, my better half helps me out with bits and pieces but, essentially it’s me doing everything. Development, support, marketing, SEO, website maintenance, blogging, planning. My products do ok, with many thousands of sales and 200 or so new customers each month and monthly sales still good (low $xx,xxx each month) but not what they could have been if I’d kept up my growth curve. Recurring income is not what it should be because when I got started recurring income wasn’t the done thing in online software sales.

I’ve been trying to remove myself from certain aspects of the business and “out-source” work. Namely, blogging, social media, and some SEO. I’ve been trying for 2 years and have burned $30,000 on either free lancers or businesses who have either completely failed to deliver, required so much direction that I may as well have done the work myself, or delivered work of such poor quality that it was valueless.. My most recent experience is with a large company whose principal is well known in web marketing circles. I spent $6,000 in three months for NOTHING despite there being a well defined plan of action in place. Honestly, I have no idea how businesses can behave so poorly. It’s very demoralizing.

Anyway, I work long hours at tech support and product development and it continually eats away at me at the work that isn’t being done. The things that need doing every week, blogging for my users, doing keyword research for new articles, posting to FB and LinkedIn and Google+. Plus I am overloaded with ideas for new tools and link bait for my sites, nothing that requires much expertise but work that takes time. I know the type of person I need, and I only need them for a day a week. But I am out of places to look and perhaps I don’t know the right questions to ask to find the person. I’ve tried various online forums, freelancer sites, and local VA’s. The people I’ve found have been poor at best and require so much management that it’s not worth the money.

I am sick of wasting time on this, and wasting money and watching my online ventures fade away because there’s only one of me. Help. What do I do NEXT!!!!

No Time. No Time.

I’ve been struggling to find someone to do some work for me so that I’m not doing everything myself. I posted up something on Reddit about it a few weeks back and got a message today. I thought my reply was worth posting here.

Hi Tom, No, didn’t find anyone. To be honest I don’t really understand how I can really out-source much of my work without building a strong on-going relationship with someone, and that’s really only ever going to happen with an employee. Right now I can’t afford an employee.

I’ll give you an example. I post weekly tips for using my products better to Facebook. I can’t outsource the selection of the tips, it’s something that someone who does tech support for my product needs to do. So, that means I do it. So I spend a couple of hours every month writing tips. Then I could outsource the actual posting to FB and Google+. But you know what, that takes about 2 minutes. So no real point. Another example is writing user guides. I’ve tried outsourcing this multiple times and the outcomes are just rubbish, it needs someone intimately familiar with my products for it to work. And that’s only going to happen with an employee that spends multiple hours working the products and supporting them. I’d love to find someone to create flash demos for my products too, but I have exactly the same problems.

I’ll run a task past you and see if it’s something you think that fits within your skillset. My products are HR/time tracking/payroll based. B2B 100%. I have multiple free resources available on the websites that are designed to bring quality traffic to my site. Generally they are “inspired” by other free resources I find around the internet. For example, right now I am building a simple employee shift schedule tool in Excel. As I basis I use other templates I find around the internet, improve on them, put some smart formulas in Excel to automate some of it, and hey presto free resource. I am careful that they are “inspired” by other free resources rather that slavishly copied. I then write a 500-1000 word keyword targeted article on the tool, publish in WordPress, link to it from a few relevant internal pages on my website, post an update to FB and Google+ and job done. All up about 4-6 hours work.

If I provided you with a link to another tool along those lines that I need done would you take a look and let me know if it’s something that would fit in your skillset? Right now I’ve got three more of these tools I need developing and I’d happily pay someone to do so. However, what I am not happy doing is spending 10 hours explaining what I need done and then 10 hours doing it again because it’s rubbish. And that’s been my experience to date. Here’s a tool I’d like created:

*link removed*

Take a look and let me know what you think.

Thanks for taking the time for reading all of this. I’d like to hear your thoughts.
By the way, I take a long time to respond to things on Reddit as I’ve locked myself out of accessing it during office hours (it’s a terrible yet compelling time-waster). Also, your PATH link isn’t going where it should I think. I am not sure at all what PATH is and would appreciate some more information.

Mark

Redirecting Adwords by Operating System

My desktop software products run on Windows. I spend a significant amount each month on Google Adwords to drive traffic to my websites, which is problematic as Adwords does not allow you to filter traffic by operating system. It does allow you to reduce bids on Mobile traffic but it bundles Desktop/Full Sized Tablet with Browser traffic into one segment. In the last few years the amount of non Windows OS traffic I receive via Adwords has grown steadily and now makes up slightly more than 50%. I’ve been struggling for a while to work out what to do with this traffic as clearly users from non Windows devices cannot use my software. Yesterday at 4AM I hit on a possible solution and spent the day implementing it. Basically I’m now intercepting the clicks on my “Download Now” buttons/links, checking the client operating system, and if it’s not a version of MS Windows then redirecting them to a page targeted at my online SAAS products. Those products WILL work on their non Windows devices. Sure, it’s an obvious thing to do but I work in isolation and sometimes it takes me a while (years) to reach the “obvious” solution to a problem.

Here’s how I did it:

1) Installed the PGWBrowser plugin for jQuery. This plugin allows you to detect the OS, browser and viewport of web clients.
2) Enabled the plugin in WordPress by enqueuing the JS file:

wp_enqueue_script('pgwbrowser',get_stylesheet_directory_uri().'/js/pgwbrowser.min.js',array(),false,true);

For non WordPress pages (my Adwords landing pages) I used the following (making sure it came after loading jQuery):

<script type="text/javascript" src="/wp-content/themes/Divi-child/js/pgwbrowser.min.js"></script>

3) Now I adjusted the onclick call on my download buttons. I know that using the onclick event like this is archaic but I’ve been doing it this way since 2004 and it works nicely so I’m not changing it! I use this event to redirect users who download the software to a “Download Complete” page that allows me to count downloads. Here’s what the HTML/JS looks like now:

<a class="button_class button_icon_download" href="/downloads/some_file_name.exe" onclick="SetUpRedirect('some_file_name.exe',event)">

You can see I’ve passed the file name to be downloaded to the JavaScript function as well as the event (click) object.

4) The final step was to adjust the SetUpRedirect JS function to accomplish my goals. Here’s what that looks like now:

function SetUpRedirect(destination,event_object)
{
		var pgwBrowser = jQuery.pgwBrowser();
		var os=pgwBrowser["os"];
		var os_name=os["name"];
		os_name=os_name.toLowerCase();

		if (os_name.indexOf("windows")>-1)
		{
			setTimeout(function(){window.location='http://'+location.hostname+'/download-file/?file='+destination;},3000);
			return true;		
		}
		else
		{
			event_object.preventDefault();
			location.href="http://"+location.hostname+"/some-landing-page/?file="+destination+"";
		}
	}

That’s all pretty self explanatory. We’re making use of the PGWBrowser plugin to get the Operating System of clients and checking if it contains “Windows”. If it does those users get redirected to the download success page as usual. However, if they are not Windows users we use the jQuery preventDefault method on the event object to stop those users downloading the trial version of my software. They are then redirected to another landing page that says something like “hey we noticed you’re using a non Windows device, why not try out our spiffy web-based product instead?” Neat.

Debugging a Visual Basic 6 Application Crash

In late 2012 one of my software products started failing on start up on certain computers.  The product is programmed in VB6 and uses a number of 3rd party controls. The concerning thing about it was that the product would run for months or even years on a computer and then one day, without warning, the software would start up, and display the dreaded “The Application has stopped working” message.  I must confess that for a long time I believed the issue was a minor one, and probably caused by a conflict with an anti virus program or similar.  I tended to (rather poorly) recommend rolling back Windows to an earlier point.  As the months rolled by I saw the issue on Windows Vista, Windows 7 (32 and 64 bit) and Windows 8 and 8.1.  Every 10 or 12 weeks I’d spend a day digging around on Google trying to get to the bottom of it with no luck.  For three reasons, firstly I had no real idea what I was looking for so my searching was rather aimless.  Secondly, I could never EVER ever reproduce the problem on any of the half dozen computers in my office, nor on innumerable VirtualBox machines.  And finally, I never really paid the problem the attention it deserved, which is entirely my fault.

The Dreaded Application Has Stopped Working Message

However, in the last month the issue seems to have become more common, with the occasional person reporting it in my software uninstall surveys, and long term users reporting the sudden failure of the software on their computers.  Of course, if it was failing for trial users it was costing me money, but I couldn’t measure the loss so I ignored it.  That changed in the last month when I had to give a number of refunds to users who bought software that suddenly stopped working.  Ouch.  So the bug was really starting to concern me and to be honest, I’d started losing sleep over it because I just couldn’t work out what was going on.  I was even considering re-coding the entire product in C#, not a small task with 75,000+ LOC in the Visual Basic code.  I’d be looking at 6 to 12 months to completion and then at least 6 months of quick release cycles to get the bugs ironed out.  Not an exciting prospect and I’d still have the bug gnawing away at my sales while I was beavering away on the re-write.  And I’ll be honest I didn’t want to move the application to .NET.  I’ve ported a VB6 application to C# before, and while the coding structure was much nicer, the actual speed of the application was FAR worse than the VB6 app and I’ve had to resort to all sorts of caching chicanery to get the performance to what is a barely acceptable level.

Matters Come to a Head

Things came to a head yesterday, with two new people informing me of the error and it was looking like I’d have to refund both of them for purchases made in the last few months.  I spent some time poking around on Google again yesterday and finally hit on something useful, how to create application crash logs.  It turns out getting an application to create a crash dump is as simple as adding one new Windows registry key:

HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindowsWindows Error ReportingLocalDumps

When the application fails it creates a dump file here:

%LOCALAPPDATA%CrashDumps

I tested this on my local dev machine by triggering an application failure and creating a dump file, which I then could open in VS 2010. I couldn’t make much sense of it though. All I needed now was a machine that was experiencing the crash to create a dump file for me and perhaps I could get to the bottom of things.

Along Comes a White Knight

I had the first glimmers of the possibility of getting to the bottom of this vexing issue last night, when a long time user of my product reported that he was experiencing the issue. Recently my usual approach has been to ask if I can get a short time to access the problem computer remotely using my remote access tool of choice, Teamviewer. Sometimes people say yes, sometimes they so no. In the case of this particular customer, he said sure you can have access, and I’ll leave it on ALL NIGHT for you so you can take as long as you like. HUZZAH!

Grabbing the Crash Dump

I logged into the customer’s PC remotely and ran my software. Sure enough, it crashed on startup. Interestingly when it crashed I saw a flash of the main application window before the troublesome error box appeared. I then tried starting up the software with the application window minimized, and hey presto it ran. But as soon as I activated the window the software crashed again. This confirmed what I’d already suspected, that the crash had something to do with the first redraw/paint of the application window. Examining the log files my software creates showed what it usually did, the software crashes somewhere AFTER the first form resize event. Anyway, I’d confirmed that the crash I was seeing was the same one other people had seen. Time to create the Windows Registry key I mentioned earlier to have the software create a crash dump. I did that, ran the software to crash, and looked in the %LOCALAPPDATA%CrashDumps folder and there was my crash dump. Transferring the file to my own computer I wondered what to do next.

WinDBG is King

I’d already opened up a crash log in VS2010 and to be honest it meant nothing to me. So I tooled about for a bit on Google and it appeared that the tool of choice was “WinDBG”. I found an installer for it on windbg.org. Once installed it fired up and I loaded up my crash log and was shown this:

Crash Dump in WinDBG

Crash Dump in WinDBG

All very interesting but it doesn’t tell me much and the reason is on the 5th line of text “Symbol search path: *** Invalid ***”. Now, time for full disclosure, I am no computer scientist and my programming coursework was confined to PASCAL and I have little idea of computer architecture and a computer stack to me, is just a pile of computers. So, when I saw this message I had no idea what a “Symbol” was and had even less idea how WinDBG was going to help me. 10 minutes of research later I’d worked out that Symbols are used to decode the crash dump (which is essentially a memory dump) and allow WinDBG to display various function calls and function sources associated with the dump. Giving the debugger a symbol path is simple, just run the following command in WinDBG:

.sympath SRV*f:localsymbols*http://msdl.microsoft.com/download/symbols

Once that was done I could analyze the dump file again with:

!analyze -v

The analysis took a minute or so and the first thing of interest that popped up was:

*** ERROR: Symbol file could not be found.  Defaulted to export symbols for ciaXPLabel30.ocx - 
***** OS symbols are WRONG. Please fix symbols to do analysis.

ciaXPLabel30.ocx is a third party text label control used on the main form of the application. The same form that would partially re-draw before the fatal crash.

The actual call stack was many lines long, but basically was a repetitive loop that looked like this:

0009750c 72a09a7b msvbvm60!BASIC_DISPINTERFACE_GetTypeInfo+0x2aa
0009754c 72a09c2c msvbvm60!EVENT_SINK_Invoke+0x50
0009757c 758c370d oleaut32!CFontEventsCP::Notify+0x9f
000975d4 7589c30e oleaut32!CFont::DiscardFont+0x5d
000975e8 758c41e5 oleaut32!CFont::put_Size+0x76
000975fc 729932c4 msvbvm60!IID_IVbaHost+0x24c84
00097630 72973db1 msvbvm60!IID_IVbaHost+0x5771
00097684 729c1e19 msvbvm60!IID_IVbaHost+0x537d9
000976c0 729acdb0 msvbvm60!IID_IVbaHost+0x3e770
000976fc 729ad0a1 msvbvm60!IID_IVbaHost+0x3ea61
00097730 72980eed msvbvm60!IID_IVbaHost+0x128ad
00097990 4fbadfdb ciaxplabel30!DllCanUnloadNow+0x10efd
00097a28 4fbaa41a ciaxplabel30!DllCanUnloadNow+0xd33c
00097a64 75873e75 oleaut32!DispCallFunc+0x165
00097a80 72a16ef5 msvbvm60!_vbaAptOffset+0x68b

You’ll note in there that the ciaxplabel30 control has raised it’s head again. To my mind this was enough evidence to remove the label controls of that type from my form and replace them with vanilla Visual Basic 6 text labels. That took me about 20 minutes, I ran my automated build script and transferred the new executable file to my helpful user’s computer. And ran the application. And it ran. AND IT DIDN’T CRASH. So, two years of frustration and fruitless work followed by half a morning of focused effort and I’d fixed up my problem.

Where I Went Wrong

My main failing in this whole sorry saga was not paying the bug the attention it deserved. I dithered and procrastinated with it, and it wasn’t until it was costing me real money that I became truly motivated to solve it. Don’t get me wrong, I’d spent time looking into it but it was rather half-hearted. Shame on me.

Why I Went Wrong

There’s two main reasons outside of my control that contributed to the time taken to solve this problem. First, I work in a rather isolated fashion with no other programmers that I can bounce ideas off of. Add that to the fact that my technical understanding of crash dumps and dump analysis borders on non-existent and you have a situation that isn’t going to result in quick fixes. To remedy this in the future I’ll resolve to make better use of online resources like StackOverflow. If I’d asked a question there I am sure someone would have pointed me to the right path in short order. The second contributing factor was my inability to replicate the issue on one of my own dev computers. If could have done that I would have solved the problem through trial and error. Ipso facto, removing all the controls from the form one at a time until the application stopped crashing and solving the problem by trial and error.

Where I Went Right

I didn’t do any of this very well. I guess I did bumble onto the solution in the end but it took a long time and I was heavily reliant on a generous user of my software to do so. Without that help I wonder now if I would have ever gotten to the bottom of it all. Part of me doubts it.

Where to from Here

There’s an important new tool in my debugging arsenal now, and the next time (and I’m sure there’ll be a next time) I know were to start with application crashes. If I get stuck I will make better use of online resources and forums. And I will not sit on these problems and hope they resolve themselves.

Uninstall Surveys

There was a post on the almost dead Business of Software forum last week from a poster who had recently implemented an ‘uninstall survey’ in his try-before-you-buy software product. An uninstall survey is triggered when a software product is uninstalled from the host operating system. It usually takes one of two forms, the first is a small program that asks some questions and then emails the results or posts them to a website. The second (and far more common) type of survey is triggered by popping up a webpage on the software product website.

But, I digress, the main point of the post was that the number of responses the poster was receiving was small and the number of useful responses was even smaller. I’ve included an uninstall survey with my product Time Clock MTS for several years and this mirrors my experience. I’m lucky to see a 10% response rate (10% of total downloads) and of those, not more than 1 in 10 or 20 actually contains useful information. At first glance it seems to be hardly worth the effort but the hour or two it took to implement has probably seen an ROI measured in thousands of percent. It has allowed me to save the occasional sale by responding to a user who complained of missing features in my product that were not missing at all. It has allowed me to find holes in my documentation and fill them, it has highlighted certain software features I was missing and that respondents found useful, and it has helped me fix up problems with the process flow in the software.

My experience certainly pours water on the comments of one poster on the BoS forums who suggested:

Indirectly related, the existence of an uninstaller often implies a poor architecture, such as apps that stuff random files and overwrite libraries in many places.

This is an insanely negative attitude. Knowing that a product is not perfect and can be improved is the ONLY way of being able to build a better product. And the best way to build a better product is to establish channels of communication with your customers. The software uninstall survey is one channel that is virtually free to establish and provides a means of communication that, in the case of downloadable trial software, would otherwise be impossible. Consider that a trial version of my software can be downloaded, installed, and trialed all without the user having to email me, ask me for a registration key, or even make me aware that they are using the software. Giving the user the opportunity to communicate with me once they have decided to uninstall (and presumably not purchase) my software has proven to be invaluable.

If you’re wanting to implement an uninstall survey, you sell Windows software and you’re using the very nifty (and free) Inno Setup then it’s dead simple. Just add an URL to the [INI] section of your Inno Setup script like this:

[INI]
Filename: {app}UninstallSurvey.url; Section: InternetShortcut; Key: URL; String: http://www.some-domain.com/uninstall-survey.htm

And then add an [UninstallRun] section to your script that opens the URL.

[UninstallRun]
Filename: {app}UninstallSurvey.url; Verb: open; Flags: shellexec

In my case I open an URL to present the user with questions. But there’s no reason why the [UninstallRun] section couldn’t run a small executable file that pops a window up to the user asking them the same questions. Those results could either then be emailed or POST’ed to your website. For your reference here’s the Time Clock MTS Uninstall Survey. It used to have a lot more questions but I’ve simplified it greatly based on the response of one poster in the BoS thread I referenced in the first paragraph. Respondents simply fill in the details and I receive an email contain the responses and a couple of other bits of information such as the users IP address and user-agent. The user-agent is particularly useful so that I know what version of Windows the respondent is using.

There you have it, uninstall surveys. Quick and easy to implement, zero on-going costs, and they provide you with a communication channel to software users that is unlikely to exist otherwise. If you’re selling try-before-you-buy software you’d be crazy to not be using them.

C# Formless Application with Multi Threading

I’ve been having a bit of fun in the last few weeks developing the next major release of Time Clock MTS. Part of this release includes a system that sends out emails based on certain events occurring within the software. To accomplish this I could have included some POP email fields and asked users to enter their mail account details. Or I could have used MAPI email. But both of these approaches are ugly as they require input from the user and don’t work for web mail.

So I decided on an approach that goes something like this:

1. The existing application generates an XML file when an event email is required and saves it into the appropriate application data folder.
2. A process monitors the data folder and uploads the files to a web server as it finds them. Each upload process is executed on its’ own thread.
3. The web server processes the XML file and sends emails as required.

Step 1 was easy enough even in Visual Basic 6 (which Time Clock MTS is developed in). However step 2 was always going to be tough so I decided to build a small application in c# to perform this role. The VB6 application would start the C# app on startup and the C# app would shut itself down when the VB6 application closed. Step 3 would be accomplished easily enough with PHP. The key advantage (as I see it) with this approach is that the only requirement for a client computer is an internet connection. It doesn’t require an email client to be installed (like MAPI does) and doesn’t require the user to know and enter some arcane email server settings when they first install the program.

I won’t bore you with Step 1 or 3 but Step 2 had some interesting code that I thought I might share. The C# app is a standard Windows Forms app but I hide the form (except when the debugger is running). I could have made this a windows service but that seemed to introduce a level of complexity (and difficulty in debugging) that was unwarranted. Here’s how I hide the form in the form constructor:

            InitializeComponent();
            if (!Debugger.IsAttached)
            {
                // Prevent the window from showing up in the task bar AND when Alt-tabbing
                ShowInTaskbar = false;
                FormBorderStyle = FormBorderStyle.FixedToolWindow;

                // Move it off-screen
                StartPosition = FormStartPosition.Manual;
                Location = new Point(SystemInformation.VirtualScreen.Right + 10, SystemInformation.VirtualScreen.Bottom + 10);
                Size = new System.Drawing.Size(1, 1);
            }

The work that the form does is handled by a WebExtensionsService class that includes a couple of timers. The class is initialized in the form constructor and the timers are started at the same time. The first timer checks for the existence the parent VB6 program process. I’m sure there’s better ways of doing this but the method I used below works fairly well with little or no CPU overhead and raises an event.

        private void timCheckForClose_Elapsed(object sender, ElapsedEventArgs e)
        {
            //writeLog("timCheckForClose update");
            Process[] adminProcesses = Process.GetProcessesByName("appname");

            if (adminProcesses.Count() == 0)
            {
                if (_bAppRunning == true)
                {
                    _bAppRunning = false;
                    OnAppNotRunning(EventArgs.Empty);
                }
            }
            else
            {
                if (!_bAppRunning )
                {
                    _bAppRunning = true;
                    OnAppNotRunning(EventArgs.Empty);

                }

            }
            

        }

Then I could consume this event in the C# form with this:


        private void WebExtensionsService_AppStopped(object sender, EventArgs e)
        {
            Application.Exit();
        }

I used the Command Line Parser Library to read in a range of command line options that were passed to the C# application when it was started by the parent VB6 application. These settings were used by the second timer to do the real work of this C# forms app. Namely scanning a folder looking for XML files and then uploading them to a server. The second timer in the WebExtensionsService class does this:


        private void timCheckFiles_Elapsed(object sender, ElapsedEventArgs e)
        {
            this.checkForXMLFiles();
        }

        public void checkForXMLFiles()
        {
            Classes.Uploader uploader;

            if (uploaderThreads.Count < this.MaxThreads)
            {
                if (bDirectoryExists(this.XMLFolder))
                {
                    DirectoryInfo info = new DirectoryInfo(this.XMLFolder);
                    FileInfo[] files = info.GetFiles().OrderBy(p => p.CreationTime).ToArray();
                    writeLog("checkForXMLFile:: " + files.Count().ToString() + " files found");
                    foreach (FileInfo file in files)
                    {
                        if (!uploaderThreads.ContainsKey(file.Name))
                        {
                            uploader = new Classes.Uploader();
                            uploader.Filename = file.Name;
                            Thread workerThread = new Thread(uploader.DoWork);
                            uploaderThreads.Add(file.Name, workerThread);
                            workerThread.Start();
                            writeLog("Starting uploader thread " + uploaderThreads.Count + " of " + this.MaxThreads);
                        }
                        if (uploaderThreads.Count >= this.MaxThreads)
                        {
                            writeLog("checkForXMLFile:: MaxThreads " + this.MaxThreads.ToString() + " are busy");
                            break;
                        }
                    }

                }
                else
                {
                    writeLog("checkForXMLFile::"+this.XMLFolder+" doesn't exist");
                }
            }
            else
            {
                writeLog("checkForXMLFile:: MaxThreads " + this.MaxThreads.ToString() + " are busy");
            }
        }

The Uploader class is my worker class that is used to upload a file in it’s own thread. Note that I have a MaxThreads setting and maintain a collection of UploadedThreads to make sure I don’t try to upload the same file twice.

And for interests sake here’s the method from the Uploader class that does the actual work. It’s worth noting that the WebExtensionsService class is a singleton and contains all of the global settings I need. I’m sure this isn’t the most elegant way of doing things but it’s always worked extremely well for me in the past.

        private bool uploadFile()
        {
            bool bReturn = false;
            string sResponse = "No Response";
            WebClient wClient = new WebClient();


            try
            {
                byte[] byte_response = wClient.UploadFile(WebExtensionsService.UploadLocation, "POST", WebExtensionsService.XMLFolder + "\\" + this.Filename);
                if (byte_response != null)
                {
                    sResponse = System.Text.ASCIIEncoding.ASCII.GetString(byte_response);
                    WebExtensionsService.writeLog("Upload " + this.Filename + " Reponse :: " + sResponse);
                    if (sResponse == "1")
                    {
                        bReturn = true;
                    }
                }

            }
            catch (Exception e)
            {
                WebExtensionsService.writeError(e);
            }
            if (bReturn)
            {
                sResult += " UPLOAD SUCCESSFUL";
            }
            else
            {
                sResult += " UPLOAD FAILED RESPONSE " + sResponse;
            }
            

            return bReturn;

        }

Right now the uploadFile method waits for the correct response from the web server. I am thinking about not just uploading the file but also uploading a checksum of the file contents so that the web server can check that the file has not been corrupted in the upload process. But that’s something for another day.

The final step in the application is how to close it out cleanly. I showed earlier how the Application.exit() method was called when the parent VB6 program process was no longer present. Here’s the code that makes sure that all the uploader threads are closed out before the application exits.

        private static void OnApplicationExit(object sender, EventArgs e)
        {
            Classes.WebExtensionsService WebExtensionsService;
            WebExtensionsService = Classes.WebExtensionsService.Instance;
            WebExtensionsService.writeLog("Closing TimeClockMTSWebExtensions");

            foreach (KeyValuePair<string, System.Threading.Thread> entry in WebExtensionsService.UploaderThreads) //wait for uploaded threads to finish
            {
                System.Threading.Thread thread = entry.Value;
                thread.Join();
                WebExtensionsService.writeLog("Waiting for " + entry.Key + " uploader thread to finish");
            }
            WebExtensionsService.writeLog("Closed TimeClockMTSWebExtensions");
        }

The key here is the thread.Join() call. This blocks the main C# program thread until the child threads (stored in my UploaderThreads collection) have completed their work.

Doing this work in C# is far easier than trying to do the same thing in VB6. The C# network code is about a million times easier and more robust. Part of me would LOVE to port the entire VB6 application to .NET but there’s three things stopping me. Firstly, the sheer amount of work. We’re talking about 80,000 lines of code that need porting. Not a trivial task. Second, ADO code in .NET simply isn’t as good as it is in VB6. It’s slower, handles connection pooling very poorly in comparison with VB6, and file sharing of a simple database like MS Access just doesn’t work as well. In the past I’ve had to perform tricky pre-caching of ADO data in .NET to get it performing even remotely as quickly as VB6 code. And finally, it scares me witless mainly because of all the reasons discussed in this great article by Joel Spolsky.

That being said stepping back to VB6 to do development work instead of using C# is like owning a 2013 model car and being forced to drive a leaky 1969 Volkswagen Beetle every second day. It works but it’s not fun. As a result I’m trying to build new features and move older non database reliant features (such as web camera image capture and network time checking code) into COM visible .NET code. However, because of the problems with database handling in .NET I don’t think I’ll ever get rid of VB6 altogether unless Windows drops compatibility for it.

Take a Deep Breath

Sometimes being the place where all bucks end up stopping can be a little wearisome. I’ve been tail up working on a major release for one of my products for a few weeks now and this morning I was keen to get a large chunk of a new feature polished off. However, as I am responsible for both technical support and new product development (and everything else in my mISV) I try to get support issues resolved before I start development for the day. Usually this takes an hour or two and leaves me the majority of the day to work on development but this morning I just couldn’t seem to get (what I thought) was a fairly basic point across to a user of one of my products. The email chain went something like this:

To: Support
From: User

My employee can’t clock in.

From: Support
To: User

What product are you using? What version? What browser (and browser version) are you using. Are you getting any error messages when your employee tries to clock in?

To: Support
From: User

It tells them they are not authorized to log in from that computer and I have to manually enter their times.

From: Support
To: User

Oh, you’re using ProductNameRemoved. You’ve set up IP login restrictions to stop people from logging in from un-authorized devices (such as their cell phone). Just log into ProductNameRemoved as the administrator and go to this screen (URL REMOVED) and you’ll be able to adjust the allowed computer IP addresses for your account.

To: Support
From: User

We never set that up.

From: Support
To: User

I’ve just logged into your account and taken a look and you’ve restricted logging into your account to the following IP addresses (ADDRESSES REMOVED)

To: Support
From: User

Oh. Yes I did set that up. My employee can’t clock in.

From: Support
To: User

Why don’t you check what the IP address is on the computer the employee is trying to clock in from. Perhaps it has changed. Once you work that out you can adjust your restriction rules.

To: Support
From: User

Why did you change the computer IP address?

From: Support
To: User

We didn’t. Perhaps your internet service provider has changed your IP address? Unless you have a contract that specifies a fixed IP they can certainly change.

To: Support
From: User

I will check into that. I will give them a call. Thank you for your help!

By this time I’d wasted nearly an hour with this back and forth and (in the words of Homer Simpson) my urge to kill was rising. Thankfully my partner told me to take a deep breath and step away from the computer and go and do something else. So I ducked out, got some sushi, calmed down and have returned for an afternoon that is being entirely more productive.

Dipping my Toes in the ODesk Pond

I’ve known for a while that the main limiting resource in the on-going growth of my software business is me. I’ve had real issues being willing to farm out work to others and it hasn’t helped that when I have reached out and found others to do work for me that results have mostly been awful. There’s been one good experience on 99Designs a few years back and about six months ago I found a Virtual Assistant in Canberra who has been doing good work for me. But other than those two I’d say quite cheerfully that I’ve wasted my money paying people to do work for me.

My problem right now is that I want to build some Explainer Videos for one of my products and need some inspiration in terms of the tone, and content of the scripts for those videos. The videos will be used on Ad Words landing pages. I want them to be short (two minutes or so) and to quickly establish empathy with the potential users (we understand your problem) and then present them with a solution (my software). I’d like the videos to be light-hearted and, if possible, amusing. After all, my products are pretty boring so any humour that can be injected can only be a good thing. I’ve written one script myself but really want a few so I can get 10-15 minutes of voice talent recorded with the eventual aim of getting several videos made and A/B testing them. Now, I know that I should write the scripts myself but to be perfectly honest I don’t have the time or the talent to write multiple scripts from a blank page. So I was hoping that I could find some people with some experience creating explainer video scripts and they could write some scripts for me. Even if I don’t use the scripts at least they’d provide me with some inspiration so I could develop a few more myself.

ODesk seems to be the freelancer site of choice these days. So I’ve toddled off and set up an ad for 2 x 2 minute explainer video scripts. After a week or so I’d got 6 applications and a few hours ago I chose two of the applicants to interview via Skype. I’ve since conducted those interviews and hired both applicants. My expectations are not high but for the cost a few hundred dollars I might get one script I can use and I’m fairly certain I’ll get some useful ideas I can work on myself.