Friday, July 11, 2008

Tech's Next Global Challenge

In the early 1960's, Japan began to re-industrialize. Eventually what was once viewed as a nation who produced cheap and sub-standard electronics became the world leader in the manufacturing of automobiles and technology. The same is happening in developing nations such as India and China in regards to the IT industry. Today we outsource much of our development to these countries but in the near future, these countries could take over the IT industry altogether, once again changing the face of the global economy forever.


We can choose to ignore this change or embrace it. We as an industry can live or die by it. Let the games begin!

Read: Tech's Next Global Challenge

Labels: ,

Tuesday, June 24, 2008

Indie Video Game Designers Finally Catch a Break

Here's a great article on how game console companies like Nintendo, Microsoft and even Sony are doing things to help independent game developers make their mark. I'm so glad I'm an applications developer and not a game developer. You guys have it pretty rough.

Indie video game designers break through - CNN.com

Labels: ,

Monday, June 23, 2008

Making a class private to only your assembly

Sometimes in .NET you'll want to make a class that is only accessible/exposed within your assembly. This happened to me, and for the life of me I couldn't remember how to get the thing to work, but it's actually quite simple:


In VB.Net:
Friend Class MyClass
...
End Class




In C#:
internal class Widget
{
...
}


Now, it's important to note that this will limit your class's accessibility to the assembly, but it will still be visible/accessible between namespaces within the same assembly. I'm not sure how to make a class only accessible to the namespace. If you do, how about you tell us in the comments!

Labels: , ,

Friday, June 13, 2008

HOPE is Dead


...or at least it will be soon. The last HOPE (Hackers On Planet Earth) conference will be held aptly at the Hotel Penn, NYC the third weekend in July. Registration is only $75 so be sure and make this historic event if you can.

As for me, the wife and I just moved into the new place, so I can't drop the funds for the plane ticket. So it goes.

The Last HOPE - July 18-20, 2008 - Hotel Pennsylvania - New York City

Labels: ,

Wednesday, June 11, 2008

"The file you are trying to open, '[filename]', is in a different format" Excel Error

On a recent project, the client asked me to allow some reports to be exported to Excel (XLS.) I've had such a request 1000 times over the years and quickly went to work rendering the grid as HTML, sending its HTML as a response and setting the Response.Header to the Excel MIME type... Simple.

Or so I thought. Turns out that Office 2007 doesn't like that much. When you open such a spreadsheet in Excel 2007 you get an error like:
"The file you are trying to open, '[filename]', is in a different format than specified by the file extension. Verify that the file is not corrupted and is from a trusted source before opening the file. Do you want to open the file now?"
(Yes | No | Help)
After searching for hours, I finally ran into this document from MS that essentially says this is a "feature" of the new Excel and no matter how much everyone hates it, they won't fix it. Here's an excerpt from that document:
"The current design does not allow you to open HTML content from a web site in Excel... So ASP pages that return HTML and set the MIME type to something like XLS to try to force the HTML to open in Excel instead of the web browser (as expected) will always get the security alert... If you use an HTML MIME type, then the web browser will open the content instead of Excel. So there is no good workaround for this case because of the lack of a special MIME type for HTML/MHTML that is Excel specific. You can add your own MIME type if you control both the web server and the client desktops that need access to it, but otherwise the best option is to use a different file format or alert your users of the warning and tell them to select Yes to the dialog." [Emphasis added]
In other words, give up because there's no good solution. Here's some bad solutions you could try, though:

  • Display a message that says something like, "If you are using Office 2007, please select "Yes" from the resulting dialog."
  • Include a registry script that the user can optionally run to change their HKCU\Software\Microsoft\Office\12.0\Excel\Security\ExtensionHardening DWord to 0, disabling this useless prompt (more details.)
  • Only export to CSV, not Excel. Sure, it's not as pretty but it'll work.
  • Instead of using this much simpler spreadsheet generation method, instead open a template spreadsheet on the server as a data-source, write to it (using SQL), and save it with a unique file name for the user to download. Of course you'll then have to do stuff like ensuring your saved files have unique file names (perhaps using GUID) and deleting the old spreadsheets from the File System.

VSOfficeDeveloper: Known Problems, Bugs, and Fixes : Excel 2007 Extension Warning On Opening Excel Workbook from a Web Site

Labels: ,

Monday, March 10, 2008

Daily Link Roundup of Bloxes and Codes


Padded Lamposts Cause Fuss in London - For GPS using people who never look up from their cell.
May Day Boycott Looming, EBay Revises Impact of 'Bug' - DO NOT USE EBAY ON MAY 1ST!
British Security Camera Can See Through Clothes - Brits rights are quickly disapearring.
1.15: Code Security - This is how crappy code started.
How to save money running a startup (17 really good tips) - Good tips for any business really.
Bloxes Cardboard Modular Building Blocks - These would be awesome to use in a loft.
Gmail Assistant: Keep Tabs on Your Gmail Accounts - Does what it says.
Porcupine Flashlight Weaponized For Your Pleasure - Blind and mame your foes.
Elgan: Making the phone-PC connection - We all want a cell that works seamlessly with our PC.
Savvy Circle Monitors Your Wishlist for Price Drops - Yet another way to get the best price.
Review: Ultimate Ears iPhone Earbuds Let You Talk Pretty, Today - Because earbuds suck.

Labels: , , , , ,

Tuesday, February 19, 2008

Windows Services in C#: Part 3: Getting Your Installer to Start Your Service

In Part 2 we discussed how to add an installer for the Windows Service we wrote in Part 1. Today, we'll learn how we can tell the Installer to start our service after it has been installed.

This article is one in a five-part series covering the following topics:

  1. Programming a Windows Service in C#
  2. Adding an Installer for Your Windows Service
  3. Getting Your Installer to Start Your Service
  4. Some Options for Debugging Your Windows Services
  5. Adding an Uninstaller for Your Windows Service

The examples in this series are written in C#, but this should help anyone out there wanting to do this on the .NET 2.0 Framework no matter if they're using C#, VB.Net, or any other language. Additionally, the code in this article can be used in any context to start a Windows Service programmatically - not just from an installer.

If you're just jumping in at this article in the series, we've already written our service and added an installer to it. If you'd like a copy of our sample code up until this point (instead of having to go through steps 1 & 2 to recreate it), you can download it here.

On to the code!

Getting Your Installer to Start Your Service
You may have notice that when the installer installs your Windows Service, it doesn't start it automatically, even if the StartType for the service is set to Automatic. Today, we'll learn how we can tell the Installer to start our service after it has been installed.

1. Add the System.ServiceProcess Namespace.
We'll be using the System.ServiceProcess.ServiceController class for this portion of the project, so be sure to add a using statement for System.ServiceProcess to the top of your code:

using System.ServiceProcess;

2. Add an Override for the Install function.
Go into Code View of your Project Installer class (ProjectInstaller.cs in our example.) Add an override for the Install function to your class. All you have to type is "public override void Inst" and hit tab, and Visual Studio's auto-complete should add the class for you, complete with the base.Install line seen below.

Here is the complete code for my example class, with the new bits highlighted:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration.Install;
using System.ServiceProcess;

namespace SuperService
{
[RunInstaller(true)]
public partial class ProjectInstaller : Installer
{
public ProjectInstaller()
{
InitializeComponent();
}

public override void Install(System.Collections.IDictionary stateSaver)
{
base.Install(stateSaver);
}

}
}
3. Start your Service using the ServiceController.
Since we want our Windows Service to start after the install has finished (we can't exactly start it before it's installed!) we'll add our code to start the service after our base.Install is called. (Note: Optionally, you could have instead done this by adding an event handler for this.AfterInstall and putting your code there. As far as I know, it doesn't make any major difference which way you do it.)

      public override void Install(System.Collections.IDictionary stateSaver)
{
base.Install(stateSaver);
ServiceController controller = new ServiceController("Logger");
controller.Start();
}
Let's break this down for ya'. As I mentioned before, we add our code to start our service after the base.Install line.

We start by instantiating a new ServiceController object, passing the name of our Windows Service as the only parameter. We specified this name earlier in the ServiceName property of our ServiceInstaller object. (Go to Design View of your Project Installer class and click on serviceInstaller1 to view this property.) If this name doesn't match your ServiceName property, you'll get an error (or possibly start the wrong service!)

Finally, we run the Start function of our ServiceController to start our service up. That's about it. You might want to add some code to handle any errors this step may throw so your install doesn't fail if your service fails to start. For example, the code below will trap the error and record it to the event log. If you use it, remember to also add a reference to the System.Diagnostics namespace to the top of your code.

       public override void Install(System.Collections.IDictionary stateSaver)
{
base.Install(stateSaver);
ServiceController controller = new ServiceController("Logger");
try
{
controller.Start();
}
catch (Exception ex)
{
String source = "SuperService Installer";
String log = "Application";
if (!EventLog.SourceExists(source))
{
EventLog.CreateEventSource(source, log);
}

EventLog eLog = new EventLog();
eLog.Source = source;

eLog.WriteEntry(@"The service could not be started. Please start the service manually. Error: " + ex.Message, EventLogEntryType.Error);

}
}

That's all there is to it! Next time, join us to learn a few of your options for debugging your newly-installed service. Until then, feel free to check out some of these other great articles that I used as sources for this series:

Labels:

Thursday, February 14, 2008

DataGrid Edit Column Does Nothing

Recently I was working on a project in .NET 1.1 and had to use a DataGrid. It had been a while since I used the old thing. (Anymore I use mostly third-party grids.) I was adding an EditCommandColumn to it, to allow the user to edit any of the rows. For some reason, I'd hit the Edit button and none of the editable columns would turn into textboxes.

I had to kick myself since I knew I had experienced this same issue before and since it's such a novice issue, but I couldn't remember how to fix it! It finally dawned on me (it was really a stupid thing) so I figured I'd post about it for anyone else out there kicking themselves for being stuck on the same problem.

In the subroutine you have your DataGrid's EditCommand event bound to, you need to set the grid's EditItemIndex to e.Item.ItemIndex, and rebind the contol! Example:


private void Page_Load(object sender, System.EventArgs e)
{
dgDealers.EditCommand += new DataGridCommandEventHandler(dgDealers_EditCommand);
if(!this.IsPostBack)
{
LoadGridData();
}
}

private void LoadGridData()
{
// Get the data from the database.
SqlConnection conn = db.NewOpenConn();
SqlDataAdapter da = new SqlDataAdapter("EXECUTE SelectDealers",conn);
DataTable dt = new DataTable("dealers");

//Fill it into the DataGrid.
da.Fill(dt);
dgDealers.DataSource = dt;
dgDealers.DataBind();
ViewState["gridData"] = dt;

//Cleanup.
conn.Close();
da.Dispose();
conn.Dispose();
}

private void RebindGrid()
{
//Set the DataSource to the DataTable previously saved in the ViewState
dgDealers.DataSource = ViewState["gridData"];
dgDealers.DataBind();

}

private void dgDealers_EditCommand(object source, DataGridCommandEventArgs e)
{
dgDealers.EditItemIndex = e.Item.ItemIndex;
RebindGrid();
}

In my example, when I make my call to the Database I store the resulting DataTable in the ViewState. Later, in the EditCommand event for my grid (highlighted) I set the grid's EditItemIndex to e.Item.ItemIndex and call a function (RebindGrid) that re-retrieves the grid's data from the ViewState and re-executes DataBind on the DataGrid.

Now, you may want to instead cache your data in a Session variable, Caching object, or not at all (and retrieve the data again from your original DataSource) - depending on your data and how much you have. Also, as far as I know, this is the same whether you're using the either .NET Framework 1.1 or 2.0. You also need to make sure EnableViewState is set to true for your DataGrid.

If anyone knows a better way to do this (without having to rebind the DataSource), please let us all know in the comments. It's ridiculous that you need to do such a thing, especially since the data is essentially already being stored in your ViewState for the DataGrid.

Labels:

Tuesday, February 05, 2008

Search for Stored Procedure Containing Text

Today I was working on some inherited code and found myself wondering what a particular database field meant. I tried searching the application's code, but being a good little programmer the previous developer had used stored procedures for all the database calls. So off I went to internetland to find how to find a stored procedure containing specific text. Here's what I found:

SELECT ROUTINE_NAME, ROUTINE_DEFINITION
FROM INFORMATION_SCHEMA.ROUTINES
WHERE ROUTINE_DEFINITION LIKE '%myField%'
AND ROUTINE_TYPE='PROCEDURE'

Just replace myField with whatever your field is called, or (to be more specific) whatever text you're trying to find in the stored procedure. I'm using this on SQL Server 2005, but judging from the query it should be fairly universal.

Labels: , ,

Monday, February 04, 2008

Windows Services in C#: Part 2: Adding an Installer for Your Windows Service

In Part 1 we discussed how to write a simple Windows Service that used a timer to write to to the Event Log every 5 minutes. Although we can use installutil to manually add the service to Server Explorer, this can be cumbersome when deploying our application. This is especially true when we need to deploy to multiple machines or the Windows Service is being deployed directly by our customer. To simplify this process, we'll learn how to add an installer for our solution and tell that installer to add the service for us.

This article is one in a five-part series covering the following topics:

  1. Programming a Windows Service in C#
  2. Adding an Installer for Your Windows Service
  3. Getting Your Installer to Start Your Service
  4. Some Options for Debugging Your Windows Services
  5. Adding an Uninstaller for Your Windows Service

The examples in this series are written in C#, but this should help anyone out there wanting to do this on the .NET 2.0 Framework no matter if they're using C#, VB.Net, or any other language. This is particularly true with this subject since most of the work is actually done in the Visual Studio 2005 GUI and not in the code.

Enough with the small talk, let's build this thing!

Adding an Installer for Your Windows Service
Making a Windows Service installer is about the easiest thing to do, once you know how to do it.


1. Select Add > New Project... under the File menu.
This will (as you may have suspected) add a new project to your Solution.




2. Select a Setup Project .

Under Other Project Types > Setup and Deployment, select Setup Project. At this point you should also give your project a name. For my examples my project will simply be called Setup. Click Ok when you're done.




3. Add Project Output to Application Folder.
When you select your Setup Project in the Solution Explorer you're presented with the File System Editor for that project. From here you can specify what files are added to the user's machine and where. This allows you to designate things like what files from the solution should be added to the application's directory and what desktop and start menu items should be created, if any.

To tell your installer that you want the output (executable and support files) from your Windows Service project to be installed on the hard-drive, right-click on Application Folder and select Add > Project Output...



Select your Windows Service Project from the resulting dialog (shown below; mine is SuperService) and select "Primary output" from the selection box. As you can see from the Description, this is the "DLL or EXE built by the project", but will also include the support files for that executable. Click Ok when you're done.



At this point we have our Setup project that will generate an installer that adds our project's files to a folder on our computer. However, the installer won't actually install the service... yet.

This is where the real Visual Studio 2005 magic begins. We're going to get our Setup Project to fully install our service, fully customized and configured, without touching a single line of code.

4. Add a service installer for your Windows Service project.
Open your Windows Service in design view. Right-click anywhere in the blank space and select Add Installer from the right-click menu.



A new class called ProjectInstaller.cs will be added to your project. Open that new class in Design View. You'll see two controls, serviceProcessInstaller1 and serviceInstaller1.



Click on serviceProcessInstaller1 and observe the Properties, particularly the Account property. This lets you specify which user or system account to run the service under. What you select here really depends on what your application does, but for our purposes, we'll set it to LocalSystem.

Next, select serviceInstaller1 and observe its Properties. Several of them are note-worthy:
  • Description: In the Server Explorer (Control Panel > Administrative Tools > Services) each service has an optional Description next to its name. This property allows you to assign that description. For example, I've set mine to "Writes to the Event Log."
  • DisplayName: If you set this property, the service will be displayed in the Server Explorer as this name instead of the service's real name (dictated by the ServiceName property.)
  • ServiceName: The name of the service. It will appear under this name when windows records events for this service (such as when it errors.) This of course does not include the events we're programmatically logging to the Event Log as detailed in Part 1.
  • StartType: Here, you have three options:
    • Automatic: Start the service when Windows starts. For some reason this does not mean the service will automatically start as soon as it's installed. That's why we'll handle how to start your service after install in our next article.
    • Manual: The user (or another program) must manually start the service.
    • Disabled:Well... Disabled.
5. Connect that service installer to your Setup Project.
Once you're done playing with your properties, you have to let the Setup Project know you want to use this Project Installer during the installation process. Save your property changes, then click on your Setup Project in the Solution Explorer. You may have noticed that when the Setup Project was selected, the buttons across the top of the Solution Explorer changed. The second-to-last button (to which my cursor is pointing in the image below) is for the Custom Actions Editor.



Click on that fella and the Custom Actions Editor will open. Right-click on Install and select Add Custom Action... from the right-click menu.



From the resulting dialog double-click on Application Folder, then "Primary output from [Your service] (Active)". It should be easy to find since it will probably be the only option. Click Ok when you're done.

You can then rename the thing if you want, or keep the default label.

6. Build and install.
All you have to do now is build your project and test the installer. However, by default the Setup Project is set to not build. This is because the MSI file can take a little while to compile and you probably wouldn't want to have to wait for it every time you built your solution.

When you're ready to test your installer, go to Build > Configuration Manager and check the Build checkbox next to your Setup Project. Then when you build it will compile as well.

To actually run the installer, you don't have to go dig up the MSI file on your hard-drive and run it. All you need to do is go to Project > Install.

I realize being able to install your service but not uninstall it is kinda lame. The "uninstall" portion of this series isn't completed yet, so I'll clue you in on a little secret: It's really really easy. Remember back in step 5 where we added the Custom Install Action? Well, do the exact same thing but for the Uninstall folder, instead. Yeah, that's all there is to it. You don't even need to make anther ProjectInstaller.cs file, or anything.

In the next segment I'll show you how you can get your installer to start the service immediately after installing. Why MS didn't just make this a property option on the installer, I don't know. Perhaps they just wanted to give me the opportunity to write this article!


Continue to Part 3: Getting Your Installer to Start Your Service

Labels:

Monday, January 28, 2008

Daily Link Roundup of Crazy Guns and Blu-Ray Wins

Not a lot going on today in the Blogosphere.

Today's Daily Links go out in memory of those great minds lost in the Challenger disaster on this day in 1986.



Would You Read a Book On Your Cell Phone? - You probably already do if you're in Japan.
Snickers Charged Infused with Caffeine and Taurine
Monitor Your Home From Afar With AlertMe
ScrapeRite Plastic Razor Blades - Giving me another one of those "why didn't I think of that" moments.
MacBook AirCraft Has Boatload of Features - But who the hell needs 5 firewire ports? I never use my 1. Maybe it's a Mac thing.
Robovie Finds Lost Shoppers, Eats Their Souls
Worst USB Gadget Yet: Mouse With Speaker, Mic
Amazon Says International MP3 Downloads Coming in 2008
What happens to blog-posts after you click Publish
"Brugo" Mug Cools Coffee One Sip at a Time
Wakerupper: Free Wake-up Calls from the Web
Researchers See Blu-ray Win by End of Year
Windows 7: The Anti-Vista?
Chevrolet Equinox Fuel Cell: A blueprint for eco-friendly cars of the future? - Over 100 of these hydrogen powered cars are being produced for consumer sale as part of the world’s biggest fuel cell test fleet.
C# Code Format - This tool allows you to format your C#, VB, HTML, XML, T-SQL or MSH code for publishing on a web site or in a blog.
Pistol ring and other unusual guns - A gallery of odd guns.

Labels: , , , , ,

Windows Services in C#: Part 1: Programming a Windows Service in C#

Part of a project I recently completed involved developing a custom email queue Windows Service in C# on the .NET 2.0 Framework. The application itself was quite simple. Unfortunately it had been some time since I wrote a Windows Service, and I hadn't written one on the 2.0 Framework and couldn't really remember much about it from the 1.1 days.

To the internet I went. I found lots of helpful articles on how to write the service, but not so many on how to start it after install, how to debug it, or how to add it to the uninstaller. However with a little imaginative searching and guesswork I managed to overcome. It sure would have helped me to have all of these topics in one place, so I figured I'd do a collection of articles for anyone out there struggling with the same issues.

This article is the first in a five-part series covering the following topics:

  1. Programming a Windows Service in C#
  2. Adding an Installer for Your Windows Service
  3. Getting Your Installer to Start Your Service
  4. Some Options for Debugging Your Windows Services
  5. Adding an Uninstaller for Your Windows Service
The examples in this series are written in C#, but this should help anyone out there wanting to do this on the .NET 2.0 Framework no matter if they're using C#, VB.Net, or any other language. This is particularly true with this subject since most of the work is actually done in the Visual Studio 2005 GUI and not in the code.

Let's get started!

Programming a Windows Service in C#

The obvious first step is to program our Windows Service. Our project is going to write a message to the Application Log when the service starts, stops and at every 5 minute interval while running. We'll do this with the help of the Timer object, so you might also learn a thing or two about the Timer object in the process. Plus, we will write a slick little helper function for writing to the Application Event Log.

1. Select New Project... under the File menu.
This will add a new project to your Solution. But, if you didn't know that already we're in trouble!




2. Select a Windows Service Project.
Under Visual C# > Windows, select Windows Service. At this point you should also give your project a name. For my examples my project will be called SuperService. Click Ok when you're done.



Since I'm not adding this project to a solution, a new solution of the same name will also be added for my project to reside in.

3. Delete Service1.cs and add your own.
It's rather unlikely that you want your service to be called "Service1", so let's delete Service1.cs and add our own Service. After deleting Service1.cs, right-click on your project in the Solution Explorer and select Add > New Item... From the menu.



In the resulting window, select Windows Service and give your service a name (mine is Logger.cs), as seen in the image below. Click Ok and your new service will be added to your project.

Now that you have the Windows service you want, you need to indicate that it is to be run. Open Program.cs in code view and find the line similar to the one below:


ServicesToRun = new ServiceBase[] { new Service1() };


Replace Service1 with whatever your new service is called.

4. Add your Timer to the Windows Service.
Drag and drop a Timer object from your Toolbox into the design view of your new service. It will probably be found under the Components category.



5. Set up your service and Timer's properties and events.
Click the timer you just added (probably displayed as timer1) and set its interval property to 300000 (that's 5 x 60 x 1000 for 5 minutes in milliseconds.)

Go into code view mode for your service.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.ServiceProcess;
using System.Text;

namespace SuperService
{
partial class Logger : ServiceBase
{
public Logger()
{
InitializeComponent();
}

protected override void OnStart(string[] args)
{
// TODO: Add code here to start your service.
}

protected override void OnStop()
{
// TODO: Add code here to perform any tear-down necessary to stop your service.
}
}
}

In there you'll see that two overrides for your service have already been added, OnStart and OnStop. Naturally, OnStart runs when when you first start your service and the OnStop runs when your service is stopped - Be it by a user, the system or by another program.

Each time your service is started all of the code in your OnStart event must be executed. When starting a Windows Service, Windows only gives a fairly small amount of time for the service to begin before it times out. If your application can take a while to run, you will want the smallest possible footprint for your OnStart event - Both to save the user from having to wait for the service to start and to ensure it doesn't time out.

One of the great things about using a Timer with a Windows Service is that its counter runs in a separate thread. So, in your OnStart event only assign your EventHandlers and start your Timer. Perform the actual functionality of your service from the Timer's tick event. This will allow for a quick startup of your service.

For our example, our project is just going to write an event to the Event Log when the service starts and stops and when the timer iterates. I wrote a basic helper function to simplify the actual writing to the Event Log. You can copy the code below and paste it into your class:

static void LogEvent(String Message, EventLogEntryType type)
{
String source = "Logger";
String log = "Application";
if (!EventLog.SourceExists(source))
{
EventLog.CreateEventSource(source, log);
}

EventLog eLog = new EventLog();
eLog.Source = source;

eLog.WriteEntry(Message, type);
}
Logging to the Event Log is a bit out of the scope of this article, but I do want to point out one line:
String source = "Logger";
This string specifies what you want displayed for the Source column in the Event Log.

Now, let's update our service's OnStart and OnStop events to write to the Event Log:

protected override void OnStart(string[] args)
{
LogEvent("This SuperService has started!", EventLogEntryType.Information);
}

protected override void OnStop()
{
LogEvent("This SuperService has stopped.", EventLogEntryType.Information);
}
Now we'll have entries added to the Event Log when the service starts and stops, but what about our timer?

namespace SuperService
{
partial class Logger : ServiceBase
{
public Logger()
{
InitializeComponent();
}

void timer1_Tick(object sender, EventArgs e)
{
LogEvent("This Timer has been ticked!", EventLogEntryType.Information);
}

protected override void OnStart(string[] args)
{
timer1.Tick += new EventHandler(timer1_Tick);
timer1.Start();
LogEvent("This SuperService has started!", EventLogEntryType.Information);
}

protected override void OnStop()
{
LogEvent("This SuperService has stopped.", EventLogEntryType.Information);
}

protected override void OnPause()
{
base.OnPause();
timer1.Stop();
}

protected override void OnContinue()
{
base.OnContinue();
timer1.Start();
}

static void LogEvent(String Message, EventLogEntryType type)
{
String source = "Logger";
String log = "Application";
if (!EventLog.SourceExists(source))
{
EventLog.CreateEventSource(source, log);
}

EventLog eLog = new EventLog();
eLog.Source = source;

eLog.WriteEntry(Message, type);
}
}
}

For this we add an event handler for the tick event of our Timer to the OnStart function of our Service. You might say to yourself, "But what if the service is started and stopped multiple times? Won't the event be excessively re-hooked?" In short, no. When you stop the service its thread closes, effectively killing off the hook. This is also why we don't need to stop the timer in the service's OnStop function. It will be killed when the service stops.

Additionally, we added a Timer1.Start() call to the OnStart function, to get our Timer rolling. We also added OnPause and OnContinue overrides, with Timer1.Stop() and Timer1.Start() calls, respectively.

That sums up our introduction to programming a Windows Service. In Part 2 we discuss
how to add an installer that will install our Windows Service. This gives us a chance to test out our code and simplifies the process of adding the service to other machines.

Continue to Part 2: Adding an Installer for Your Windows Service

Labels:

Friday, January 25, 2008

Mirror: 101 CSS Techniques of All Time - Part 1

This awesome article's site went down when it hit the front page of Digg. Here's the original link. Be sure and check it out if it's back up, and Digg it.


CSS has fundamentally changed web design, it has provided designers with a set of properties that can be tweaked to make various techniques to make your pages just look right.

Today we are presenting a round-up of 101 CSS techniques designers use all the time. Definitely worth taking a very close look at! This is just the first series , the second part will be coming soon, stay tuned and Enjoy!

Update:

You can check Part2 here.

CSS Sprites

CSS sprites save HTTP requests by using CSS positioning to selectively display composite background images. To maximize accessibility and usability, CSS sprites are best used for icons or decorative effects.

CSS Sprites


CSS Rounded Corners

Rounded corners is one of the most popular and frequently requested CSS techniques. There lots of ways to create rounded corners with CSS, but they always require lots of complex HTML and CSS. Here are easy ways to achieve this effect.

  • Even More Rounded Corners With CSS- Single-image, PNG-based, fluid rounded corner dialogs with support for borders, alpha transparency throughout, gradients, patterns and whatever else you could want.
    Demo :
    rounded corners


  • Rounded corners in CSS- Simple bordered div which contained four divs that each had a background-image and that were positioned in each of the corners.
  • Liquid rounded corners- For liquid design and transparent scrolling - tutorial and stylesheet
  • Mountaintop Corners- easier way for creating decent rounded corners.
    rounded corners

Image Replacements Technique

: Image Placement vs. Image Replacement (FIR)

This technique is mostly for headlines by using CSS to replace normal HTML text, with a background image in order to achieve a particular look.Several different image replacement methods have been proposed, each with their pros and cons.

when you need image replacement you can check the Gilder/Levin Method as described by Dave Shea or, if the replaced text is linked and CSS support for IE/Mac is required, the Gilder Levin Ryznar Jacoubsen IR method.

image replacement


Sliding Doors

Sliding Doors of CSS introduced a new technique for creating visually stunning interface elements with simple, text-based, semantic markup.Beautifully crafted, truly flexible interface components which expand and contract with the size of the text can be created if we use two separate background images.

sliding doors


Sliding Doors" Box– Rounded Corners for All- The goal of this technique was to create rounded-corner boxes with visual flare and the absolute minimal amount of semantically correct markup. While making sure they could resize while keeping their backgrounds intact.

sliding doors


Image Text Wrap Technique

How many times do you have an image floated left in a block of content, but want to keep that content from wrapping around your image?

This technique allows you to wrap around image text flow control to emulate magazine style page layouts.

Image Text Wrap


Equal Height Technique

One of the somewhat frustrating properties of CSS is the fact that elements only stretch vertically as far as they need to. So how can we make all columns appear to be the same height? Several techniques was introduced to solve this issue.

  • Faux Columns- The simple secret is to use a vertically tiled background image to create the illusion of colored columns.
  • Equal Height Columns - revisited- A method to make all columns appear to be the same height but without the need for faux column style background images.
  • Equal height boxes with CSS- The trick is to use the CSS properties display:table, display:table-row and display:table-cell to make containers (in this case div elements) behave like table cells. The basic XHTML structure looks like this:
        

    Here is the CSS used to make this structure behave like a table:

         .equal {            display:table;    }    .row {            display:table-row;    }    .row div {            display:table-cell;    }    

Turning A List Into A Navigation bar

Why use a list? Because a navigation bar, or menu, is a list of links. The most semantic way of marking up a list of links is to use a list element. Using a list also has the benefit of providing structure even if CSS is disabled.


Making Headlines With CSS

Headers in Web pages–marked up with h1, h2, h3, h4, h5, or h6 elements–help the reader determine the purpose of sections in content. If your header is visually stimulating, the odds are better that the section will capture your reader’s eye.

heading


  • Heading Style Gallery- Want something a little more stylish for your content headings (h1,h2,…) than a different font or color? Try one of the heading styles listed here to spruce up your content.
  • Typography for Headlines- Improve the typography in your headlines by being more creative, give them more ‘pop’, that sort of thing.
  • Making Headlines With CSS- With a dash of design, we can utilize CSS to stylize those Web page headers to catch the reader’s eye and encourage them to read on.

CSS Shadows Techniques

A technique to build flexible CSS drop shadows that can be applied to arbitrary block elements that can expand as the content of the block changes shape.

  • -Build flexible CSS drop shadows that can be applied to arbitrary block elements that can expand as the content of the block changes shape.

    CSS Shadows


  • - Most of the existing techniques use negative margins, while this one is a really simple version wich uses relative positioning.
  • - This set of tests are based on an article found on A List Apart’s technique, but with less CSS coding.
  • CSS Drop Shadows II: Fuzzy Shadows- Picking up where Part I left off, in Part II designer Sergio Villarreal takes his standards-compliant drop-shadow to the next level by producing warm and fuzzy shadows.

    CSS Shadows


  • An improved CSS shadow technique- A very robust and easy-to-use technique for applying snazzy looking shadows using only Web technology and a few little image elements prepared beforehand.

CSS Transparency

One of the trickiest things to control, in a CSS-driven design, is the transparency of the interaction between foreground and background content.Below is a list of the best examples of the differing transparency approaches possible with CSS.

  • Partial Opacity- Placing text over an image can sometimes make it difficult to read, but with Stu Nicholls’s methods the background for the text is made ‘opaque’ using various methods of opacity (including css3) and the black text is then quite readable.

    CSS Transparency


  • Cross-Browser Variable Opacity with PNG- How to overcome flaky browser support for PNG so you can take advantage of this graphic format’s lossless compression, alpha transparency, and variable opacity.
  • Two Techniques for CSS Transparency

Various Link Techniques

  • Showing Hyperlink Cues with CSS- The CSS Guy shows us how to get the little icons next to hyperlinks that signify if that link will take you offsite, open a popup, or link to a file (as opposed to another html page). Here’s how to do it in a way that’s supported in IE7, Firefox, and Safari.
  • The ways to style visited Links- CSS offers various possibilities to make links more usable and preserve text readability at the same time. We need to differentiate visited and unvisited links, but we must keep text scannable and readable.
  • Link Thumbnail- Shows users that are about to leave your site exactly where they’re going. When that curious mouse pointer hovers over a link pointing to somewhere outside of your site, the script displays a small image of the destination page.
  • Iconize Textlinks with CSS- If you’re looking for more icons to implement, Alex provides a nice start.


Labels: ,

Wednesday, January 02, 2008

Add More File Types to Windows Thumbnail Preview

It's always frustrated me that Windows Explorer only supports a few different file types for it's thumbnail preview of your files. Recently I ran into a thing called ThumbView that will add a butt-load of other file types to Explorer's list of thumbable files, including cut, dcx, dds, mdl, mng, pcd, pcx, pic, pix, png, pnm/pbm/pgm/ppm, psd/pdd, psp, pxr, sgi/rgb, tif/tiff, tga/vda/icb/vst, wal and xpm images.

The best thing about it is that it doesn't install any software that runs in the background. The app is actually just a shell extension that only gets called when a preview needs to be generated. Clever programmers out there can even write their own "Thumbnail Extractors" to add support to additional file types.


Download ThumbView Here.

Labels: , ,

Tuesday, November 27, 2007

XML Goes Binary with EXI

A new standard from the W3C promises to allow web servers to talk to each other super-fast.

When it comes to bandwidth usage, binary beats text any day. The same is true when it comes to CPU processing of data. That's why programs are compiled and why most databases don't simply store data in giant text files. So, it doesn't make much sense for XML, a metalanguage who's primary purpose is the interchange of data on the Web, to take the form of plain text.

To address this issue the W3C has recently been developing a standard called EXI (Efficient XML Interchange) that represents XML data in a binary form. This should mark a significant improvement over both data compression and commercial XML hardware-accelerators available on the market today. "It is unlike data compression, which has overhead associated with it", explained John Schneider, co-editor of the EXI working draft, "There are people out there that are buying XML accelerators and hardware to speed up XML processing... but it doesn't do anything for bandwidth."

Representing XML as binary will help solve both issues because it will not only be the most minimal possible size representation of the XML (which is good for bandwidth), but the data can be stored and processed directly in its EXI form. So not only will you not have the added overhead associated with data compressors, but processing will actually be significantly faster in this new binary form than in it's plain-text XML representation. According to Schneider, "on average, 12 to 14 times faster than processing normal XML." The way I see it, even if EXI in the real world doesn't even come close to their estimates, it'll still be hella-fast.

The best part about this whole thing is that, chances are, us programmers won't have to do a thing to take advantage of EXI. John says it will be embedded at the lowest level of the XML stack, in the parser or serializer, so your Web server will do all the work for you.

Read: W3C
Read: XML Developer

Labels: , ,

Monday, November 05, 2007

Google's Android vs Apple's iPhone

The web is abuzz with talk of Google's new mobile OS, Android. What I find interesting is the stark contrast between Apple's tyrannical stance against people developing their own apps for the iPhone, and Google's stance that outside apps can only lead to the melioration of their product. Apple has blocked users from developing apps for the iPhone, going as far as causing all outside apps that were developed for the iPhone to be bricked. Google has done quite the opposite. Their new Android mobile operating system not only is designed specifically to allow programmers to produce apps for it easily and is not tied to any specific phone or carrier, but the entire operating system will be made open-source sometime next year!

For those of you guys that missed it, Google today announced that they are in fact not making a cellphone or mobile device, but instead were actually developing an open-source operating system for cellphones and mobile devices. The software is known as Android. C'mon, are you really surprised that a software company developed software and not a device? Here's the scoop on Android:



  • It's wide-open:
    • An SDK will be available November 12th.
    • The OS is free for mobile providers (or anyone else) to put it on their phones. Google plans to make their money off ads displayed to the users of the phone (not sure if it's on the phone itself or simply when browsing Google's pages.)
    • The OS itself will go open-source sometime next year.
  • It has been suggested that the design of the phones may be similar to the iPhone (with touch screens and what-not) but I'm guessing the OS's interface will not be tied to one particular navigation or input system.
  • Google will continue developing mobile versions of its web-based applications such as Google Maps and GMail for those of us with non-Android phones to continue to use.
  • Google has also announced the Open Handset Alliance: A group of developers, manufacturers and providers to "commercially deploy handsets and services using the Android Platform."
  • Android-powered phones are not expected to be available until the second half of 2008.

Again, all of this is such a contrast to how Apple has handled outside development on their iPhone. Jobs was quoted as saying, "These are devices that need to work, and you can't do that if you load any software on them." And that, "'Cingular doesn't want to see their West Coast network go down because some application messed up." People figured out a way to get their own apps on their (own) iPhones, but instead of voiding their warranty (or thanking them for making the iPhone waaaay better), Apple blocked their apps with their latest patch. It has taken a huge public outcry and several lawsuits to get Apple to finally open the iPhone a bit. They've now announced that they'll be releasing a developer kit this February.


As you can see, I'm a little pissed at Apple for locking down the iPhone. It would be a glorious device to develop on, but right now isn't worth the risk of Apple killing any apps one would develop on it. As for me, I'm not holding my breath for iPhone's developer kit release, either. As a developer, why should I settle for semi-open-source when the real thing is right around the corner?

Labels: , , , ,

Wednesday, October 31, 2007

QUICKY: Using TortoiseSVN with Total Commander

One of the best things about my favorite repository tool TortoiseSVN is how it displays the little overlay icons in over files and folders in Windows Explorer to indicate their status in your Subversion repository. Unfortunately I couldn't figure out how to get these little icons to show up in my favorite file browser Total Commander (formerly Windows Commander.) That is, until now.

In Windows Commander go to Configuration>Options>Display and check the "Show overlay icons, e.g. for links" checkbox, then click Ok. It's the last option under "File display" as shown in the image above. Now you'll see those pretty little icons in any folders you have Checked Out from your repository using TortoiseSVN.

Labels: ,

Monday, October 29, 2007

Simple Work Timer

Einstein once said, "Make everything as simple as possible, but not simpler." To help me keep track of the time I spend working on a particular project, I wrote a simple little app called Simple Work Timer (or SWT.) It is the simplest of devices but a piece of software that I use more than just about any other program.

Click to Download
All it is is a little window with 4 buttons. The top one displays the hours, minutes and seconds since the timer was started. The second button displays the amount of time that has passed in hours (for example this button would show 1.5 after 1 and a half hours.) The third button starts and pauses the timer. The last button resets the timer to zero.

Clicking either of the top two buttons will copy that button's text to your clipboard. I use this most often with the second "hours" button. This allows me to paste the exact time I've spent on a project into Quickbooks or a spreadsheet for tracking and billing my time.

As the timer runs, the title of the program will display the hours and minutes since the timer was started. So, a simple glance down at your taskbar is all you need to take a peek at your current hours. This will also tell you if you forgot to unpause the timer when you went out for your smoke break, since SWT will also append the text "Paused" to it's program title when paused.

Perhaps you occasionally need to have multiple timers running at once. All you have to do is run multiple instances of the program and it will add multiple, independent copies of itself to your desktop. To help me keep track of which timer is which, I always place the timer in the same location on my screen for a particular client. For example, for my (fictional) client Joe's Widgets I always put the timer in the lower-right-hand corner of my screen. That way if I start another timer for another task, I'll always be able to distinguish between it and Joe's timer. I've found that SWT works very well coupled with Launchy, especially when launching multiple timers.

I've provided two links at the bottom of this post. The first link is to download Simple Work Timer. SWT is Windows only and requires version 2.0 of the .NET Framework. If you don't know what that means, don't worry - You probably already have it. If not, follow this link to download and install the Framework. SWT itself doesn't require any installation. It's just one executable file that you can put anywhere on your hard drive and run it.

The second link is to download the source code of SWT. It was originally programmed in VB.NET 1.1 but I've up-converted it to 2.0 for you and translated it to C# as well. Both the VB.Net version and C# version are contained in the solution in their respective folders. Obviously this program is very basic, but it's (fairly) well commented and could be a good learning tool for new developers. Here's some of the things you can learn from this code:

  • A comparison of VB.NET code to C#.
  • How to add an icon to your application.
  • How to modify the title of your window.
  • Using the timer component.
  • Copying text to the clipboard.
  • Casting and type conversion.

Without further ado, here's the downloads:

Labels: , , , ,

Tuesday, October 16, 2007

Minor Redesign

If you're a regular subscriber to the Grinn Productions Projects Blog, you've probably noticed the recent changes to the site. These were designed to make it easier to read, to help popularize the blog, and to make our ads more appealing. Here's what we've done:

  • Increased the width of the contents of the blog by about 40%.
  • Rearranged our advertisements to more readily accessible positions.
  • Changed the color scheme of our ads to be more noticeable (but hopefully not a nuisance.)
  • Added Digg buttons to the top of each post.
  • Added the "Subscribe to this blog" link to the right of the post.
  • Added an "Email this Post" link to the footer of each blog entry.
There are a few more minor changes scheduled for later this week, including subscribe buttons for Google Reader, Bloglines, netvibes, and subscribing via email.

What tricks have you used to help promote your blog and increase your ad clicks?

Labels: ,