Friday, March 31, 2006

Yesterday afternoon, I posted on ShellExecuteEx and executing files with unknown file extensions. I was really confused, though, because my feed just wouldn't show up in my aggregator (RSS Bandit). I knew it wasn't my reader, though, because I had navigated to my feed's address (http://feeds.feedburner.com/DavidMohundro) and it wasn't displaying my added post. I threw a few test posts out there but nothing would show up. I'll admit, I couldn't help but wonder if something had happened with FeedBurner. I shouldn't have doubted, though. It was indirectly my fault.

You see, FeedBurner has a nice report called FeedMedic (located under their Troubleshootize tab). I hadn't found that report until this afternoon. It informed me that my source feed was too large.

Wha???

Apparently, my last post on file extensions was too big. I ended up editing the last post and taking out a lot of the unnecessary HTML that came out of my code to HTML addin. I ended up using Jeff Atwood's great FormatToHtml VS2005 macro. The HTML it generates is a lot simpler, but also a lot cleaner. That solution alone dropped my post under the "large" size. Now everyone can happily see my posts again!

posted on Friday, March 31, 2006 5:04:26 PM (Central Standard Time, UTC-06:00)  #    Comments [0]

Recently, I was adding the ability to open files from an application at work. Luckily, I had run into this situation before, so I knew that you could run a Process.Start on the file in question and have Explorer open it for you. What I didn't know was that it would throw a Win32Exception if the file didn't have an associated program to open it. My next step was what any good developer would do: Google it.

Fairly early on in my searching, I came across a post by Ned Batchelder that described the EXACT same scenario I was working on! Ned wanted to open a file with an unknown extension from a managed application just like me. He details his Google search and explains how he discovered information about the API functions, ShellExecute and ShellExecuteEx. Basically, you can call ShellExecuteEx with an "open" verb and Explorer will attempt to open the file. If it fails with an SE_ERR_NOASSOC error, then call ShellExecuteEx again but using an "openas" verb. Doesn't sound too bad, though some code snippets still would've sped the process up for me :-) I did learn it better this way, though.

Anyway, here's where I started. First, I pulled in the definition for the SHELLEXECUTEINFO structure.

Friend Structure SHELLEXECUTEINFO
    
Public cbSize As Integer
    Public fMask As SEE_MASK
    
Public hwnd As IntPtr
    <MarshalAs(UnmanagedType.LPTStr)> _
    
Public lpVerb As String
    <MarshalAs(UnmanagedType.LPTStr)> _
    
Public lpFile As String
    <MarshalAs(UnmanagedType.LPTStr)> _
    
Public lpParameters As String
    <MarshalAs(UnmanagedType.LPTStr)> _
    
Public lpDirectory As String
    Dim nShow As Integer
    Dim hInstApp As SE_ERR
    
Dim lpIDList As IntPtr
    <MarshalAs(UnmanagedType.LPTStr)> _
    
Public lpClass As String
    Public hkeyClass As IntPtr
    
Public dwHotKey As Integer
    Public hIcon As IntPtr
    
Public hProcess As IntPtr
End Structure

Next, I created definitions for the SW, SEE_MASK, and SE_ERR constants.

#Region " SW Constants "
    Friend Enum SW As Integer
        HIDE = 0
        SHOWNORMAL = 1
        NORMAL = 1
        SHOWMINIMIZED = 2
        SHOWMAXIMIZED = 3
        MAXIMIZE = 3
        SHOWNOACTIVATE = 4
        SHOW = 5
        MINIMIZE = 6
        SHOWMINNOACTIVE = 7
        SHOWNA = 8
        RESTORE = 9
        SHOWDEFAULT = 10
        FORCEMINIMIZE = 11
        MAX = 11
    End Enum
#End Region

#Region " SEE_MASK Constants "
    Friend Enum SEE_MASK As Integer
        CLASSNAME = &H1
        CLASSKEY = &H3
        IDLIST = &H4
        INVOKEIDLIST = &HC
        ICON = &H10
        HOTKEY = &H20
        NOCLOSEPROCESS = &H40
        CONNECTNETDRV = &H80
        FLAG_DDEWAIT = &H100
        DOENVSUBST = &H200
        FLAG_NO_UI = &H400
        UNICODE = &H4000
        NO_CONSOLE = &H8000
        ASYNCOK = &H100000
        HMONITOR = &H200000
        NOZONECHECKS = &H800000
        NOQUERYCLASSSTORE = &H1000000
        WAITFORINPUTIDLE = &H2000000
        FLAG_LOG_USAGE = &H4000000
    End Enum
#End Region

#Region " SE_ERR Constants "
    Friend Enum SE_ERR As Integer
        SE_ERR_FNF = 2              ' file not found
        SE_ERR_PNF = 3              ' path not found
        SE_ERR_ACCESSDENIED = 5     ' access denied
        SE_ERR_OOM = 8              ' out of memory
        SE_ERR_DLLNOTFOUND = 32
        SE_ERR_SHARE = 26
        SE_ERR_ASSOCINCOMPLETE = 27
        SE_ERR_DDETIMEOUT = 28
        SE_ERR_DDEFAIL = 29
        SE_ERR_DDEBUSY = 30
        SE_ERR_NOASSOC = 31
    End Enum
#End Region

Finally, I created my definition for the ShellExecuteEx function.

<DllImport("shell32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
Friend Shared Function ShellExecuteEx( _
        
ByRef lpExecInfo As SHELLEXECUTEINFO) As Boolean
End Function

I stuck all of this into a NativeMethods class and tried the code below:

Dim info As New NativeMethods.SHELLEXECUTEINFO
info.cbSize = Marshal.SizeOf(info)
info.lpDirectory = Path.GetDirectoryName(fileToStart)
info.lpFile = Path.GetFileName(fileToStart)
info.nShow = NativeMethods.SW.SHOWDEFAULT
info.lpVerb =
"open"
info.fMask = NativeMethods.SEE_MASK.FLAG_NO_UI Or NativeMethods.SEE_MASK.FLAG_DDEWAIT

If Not NativeMethods.ShellExecuteEx(info) Then
    If info.hInstApp = NativeMethods.SE_ERR.SE_ERR_NOASSOC Then
        Dim sinfo As New NativeMethods.SHELLEXECUTEINFO
        sinfo.cbSize = Marshal.SizeOf(info)
        sinfo.lpVerb =
"openas"
        sinfo.lpDirectory = Path.GetDirectoryName(fileToStart)
        sinfo.lpFile = Path.GetFileName(fileToStart)
        sinfo.nShow = NativeMethods.SW.SHOWDEFAULT
        NativeMethods.ShellExecuteEx(sinfo)
    
End If
End If

UPDATED (9/12/2006): Many thanks to Michael and his comment regarding using the SEE_MASK.FLAG_DDEWAIT. That fixed all of the problems I was running into regarding the above code. (see the usage on info.fMask)

A quick note about the code: Ned mentioned in his post that he got the ERROR_NO_ASSOCATION error instead of SE_ERR_NOASSOC. Well, the ERROR_NO_ASSOCATION is what is returned in the Win32 error (Marshal.LastWin32Error). The SE_ERR_NOASSOC is returned in the hInstApp (see MSDN documentation here).

See any problems with that? I certainly didn't (and still don't). It works like a charm for files with associations... however, it would only work one time for files without any associated program. Afterwards, it wouldn't give me anything... no errors, nada, zilch. After a few tries, an AccessViolationException would get thrown. Why? I have no idea. I tried various things to see if I should be cleaning up memory somewhere but I couldn't find anything. I did find out that if I just called ShellExecuteEx with the "openas" verb the first time, I wouldn't get any problems at all. What in the world???

As a result of the strange behavior, I changed my code slightly to look like this:

Try
    Using p As New Process
        p.StartInfo.FileName = fileToStart
        p.StartInfo.UseShellExecute =
True
        p.Start()
   
End Using
Catch win32Ex As Win32Exception
   
Dim sinfo As New NativeMethods.SHELLEXECUTEINFO
    sinfo.cbSize = Marshal.SizeOf(sinfo)
    sinfo.lpVerb =
"openas"
    sinfo.lpDirectory = Path.GetDirectoryName(fileToStart)
    sinfo.lpFile = Path.GetFileName(fileToStart)
    sinfo.nShow = NativeMethods.SW.SHOWDEFAULT

   
If Not NativeMethods.ShellExecuteEx(sinfo) Then
        Throw New Win32Exception
   
End If
End Try

The above code is working like a charm. I still have no idea why my first example won't work for me. If anyone has any ideas or suggestions, please let me know. I haven't worked with Interop between managed and unmanaged code very much. My experience up to this point has primarily been an entirely managed project or an entirely unmanaged project (and that only in college).

NOTES: Here are some resources I found while researching this:

[http://www.nedbatchelder.com/blog/20050318T070512.html]
[http://www.pinvoke.net/default.aspx/shell32/ShellExecuteEx.html]
[http://www.pinvoke.net/default.aspx/Constants/SW.html]
[http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/shell/reference/structures/shellexecuteinfo.asp]

Also, be sure to look at the ShellAPI.h header file!

posted on Friday, March 31, 2006 8:08:06 AM (Central Standard Time, UTC-06:00)  #    Comments [2]
 Wednesday, March 29, 2006

If Hollywood had its way with history, this might be what it would like.

It is basically the history of the world if every movie actually happened.

Nice.

(via Woot)

posted on Wednesday, March 29, 2006 8:10:03 AM (Central Standard Time, UTC-06:00)  #    Comments [0]
 Friday, March 24, 2006

Yesterday, after I had been at work for about an hour, Outlook started giving me some weird errors (something about cyclic redundancy checks). I googled the error and found a KB article that suggested that I try chkdsk. I tried that and it found some bad sectors on my drive. Afterwards, my computer booted back up and from then on... utter mayhem. Explorer would die with "Unknown hard errors" all over the place.

Below is a screenshot of my eventvwr after my disk was ghosted to a new, working drive. As you can see, the all-wise eventvwr knew there were problems before I did.

posted on Friday, March 24, 2006 5:09:06 PM (Central Standard Time, UTC-06:00)  #    Comments [0]
I've been using the System.Collections.Generic namespace an awful lot lately. I really like it, too. I use the List<T> collection all the time. It is so much better than inheriting from the CollectionBase to get a type-safe collection. I won't go into the reasons here, though. This post is about Predicates. If you have a List<T> collection, you'll notice it exposes a Find method that takes a Predicate as an argument. A Predicate is just a delegate that takes an object of type T and returns a boolean. When you call the Find method, it will call your Predicate for each object in its collection until your Predicate returns true. Looks like a nice, clean way to search collections, right?

Well, if you're using C# anyway.

Compare the following VB.NET code and C# code:

VB.NET :

    1 Module Module1

    2     Sub Main()

    3         Dim tests As New List(Of Test)

    4         tests.Add(New Test("test"))

    5         tests.Add(New Test("test2"))

    6         tests.Add(New Test("test3"))

    7 

    8         toSearchFor = "test2"

    9         Console.WriteLine(tests.Find(AddressOf FindTest).ID)

   10     End Sub

   11 

   12     Private toSearchFor As String = ""

   13     Private Function FindTest(ByVal val As Test) As Boolean

   14         Return val.ID = toSearchFor

   15     End Function

   16 

   17     Private Class Test

   18         Public ID As String

   19         Public Sub New(ByVal id As String)

   20             Me.ID = id

   21         End Sub

   22     End Class

   23 End Module


C# :

    1 using System;

    2 using System.Collections.Generic;

    3 using System.Text;

    4 

    5 namespace CSPredicateExample

    6 {

    7     class Program

    8     {

    9         static void Main(string[] args)

   10         {

   11             List<Test> tests = new List<Test>();

   12             tests.Add(new Test("test"));

   13             tests.Add(new Test("test2"));

   14             tests.Add(new Test("test3"));

   15 

   16             string toSearchFor = "test2";

   17             Console.WriteLine(tests.Find(delegate(Test val) { return val.ID == toSearchFor; }).ID);

   18         }

   19 

   20         class Test

   21         {

   22             public string ID;

   23             public Test(string id)

   24             {

   25                 this.ID = id;

   26             }

   27         }

   28     }

   29 }


I've bolded the important part. In case you missed it, it is the toSearchFor variable. The only way for a predicate to find a value is to compare it to another value; however, there is no way to pass this value to the predicate outside of an external variable. In C#, this isn't a problem because of closures. The anonymous delegate (my predicate) is a closure, which means it can access variables in the outside scope. VB.NET does not have this feature (yet!). Because of this limitation in VB.NET, you have to have a class-level variable or global variable to effectively use predicates so that your predicate has something to compare to! It seems to me that the lack of closures effectively cripples the use of predicates in VB.

Because of features like this, I am really looking forward to the next release of VB. Of course, with LINQ, will I even care about predicates then?

I would be very interested to know if anyone has any other suggestions on ways to effectively use the predicate model in VB.

UPDATE: Wow, go Paul Stovell! Instead of complaining like I did, Paul actually did something to get around this limitation in the current version of VB.NET and created an implementation of "Almost-anonymous" methods! His method also shows how to use the Widening overloaded operator in VB.NET, which I haven't seen much of. Cool!
posted on Friday, March 24, 2006 4:58:16 PM (Central Standard Time, UTC-06:00)  #    Comments [0]
 Wednesday, March 22, 2006
I've blogged about FileHelpers before. I like it a lot.

Since then, Marcos has added even more great features to it including support for .NET 2.0 Generics and a new Wizard application that really increases the speed in developing new records.

Marcos has an article up on CodeProject as well as a FileHelpers website with examples and a support forum.

Seriously, if you're still using String.Substring calls to parse out flat files, give the library a try.
posted on Wednesday, March 22, 2006 1:02:17 PM (Central Standard Time, UTC-06:00)  #    Comments [0]
At work, we're slowly moving from a classic ASP Intranet environment to a smart client environment. It has been very interesting watching (and being a part of) this movement. The typical intranet application is usually a form that consists of grabbing user input and doing something with it. Validating that user input can be a major headache. Typically, good applications would have both client-side (to improve usability and let users know about invalid data early on) and server-side (to ensure that nothing weird happened and to guarantee that the data is valid).

Where am I going with this? Well, I've seen more and more of my coworkers jumping at the chance to use masked textboxes. And you know why? I think it is because masked textboxes offer "free" validation. If you're unfamiliar with masked textboxes, you provide a mask (i.e. (999) 000-0000 for phone numbers) and the textbox will only accept input that matches the mask. It is basically a guarantee that the data from the textbox will be in the format you specify. If you only want 5 numbers, a dash, and 4 numbers (standard U.S. zip code), that's what you'll get. The problem is that it isn't particularly user friendly. There isn't any notification to the user when they type something invalid that it didn't take their input. It just sits there. It also lets them put their cursor in the middle of the mask, so they can start typing anywhere.

I really think that Jeff Atwood hit the nail on the head when he said that we should "we let the user enter the number however they like, and accept lots of formats" (see his post here). I think that the masked textbox idea could work if the mask it displays were gray and showed the user an example of what the input might look like; however, I don't think it should hinder a user's input. As soon as the user focuses, it could clear so they could type freely. Outlook has a similar feature in the search contact box at the top of the window (see image below).



I'd be curious if anyone has any really successful uses of masked textboxes. And I'm talking about cases where you know the user likes the functionality, not where you like it because it saved you a little bit of validation time :-)
posted on Wednesday, March 22, 2006 8:48:12 AM (Central Standard Time, UTC-06:00)  #    Comments [0]
I remember the first time I ran into an ASP.NET error talking about event validation (specifically while playing with CommunityServer). The solution I discovered at the time was to just disable event validation by setting enableEventValidation to false in the web.config file.

Well, thanks to a couple of posts from K. Scott Allen, I now have a much better understanding of event validation and why it is a really good thing. Check out his posts here and here.

Thanks Scott!
posted on Wednesday, March 22, 2006 7:35:30 AM (Central Standard Time, UTC-06:00)  #    Comments [0]
 Monday, March 20, 2006

It looks like the build from MIX06 has been released.

Check out the post from the IEBlog here!

posted on Monday, March 20, 2006 1:00:53 PM (Central Standard Time, UTC-06:00)  #    Comments [0]

Some of you may be aware that I'm currently pursuing my MBA. (I would've preferred to get my master's degree in CS or something like that, but there weren't any good options nearby that would allow me to get my degree part-time.)

One of the things that I've often heard in my classes is how a system of reviews is needed. One popular form of reviews that is mentioned often is anonymous 360 degree reviews. Basically, you not only get the standard review from your supervisor, but you also give anonymous reviews back to your supervisor as well as to the others in your department. With this system, peers can share feedback and supervisors get to hear how the area thinks they're doing.

Personally, I really like this idea. It gives everyone the chance to share ideas and feedback on how everyone is doing without worrying about hurt feelings. Not only that, but employees don't have to worry about the pointy-haired boss not liking them anymore because they suggested a different way of doing things.

In a programming environment, it would be a good way to suggest to management things like employing unit tests or to let a peer know that they really need to work on their documentation in their projects. It seems like a simple and cost-effective means of spreading knowledge and getting people involved.

What do you think? Am I off base here or do you think this really works? Does anyone actually have any experience with this, particularly in a software environment? I mean, even Dilbert has 360 degree reviews!

(I wonder if I should have a Business category for posts like this...)

posted on Monday, March 20, 2006 12:57:21 PM (Central Standard Time, UTC-06:00)  #    Comments [0]
 Saturday, March 18, 2006
As some of you may have noticed, I changed my blog's theme ever so slightly to encorporate the cool, orange feed icon:

 

It is quickly becoming the standard way to indicate to users that a feed is available for aggregation. By way of FeedBurner, I found the Feed Icons website, which is striving to help establish the icon as a standard. There, you can download the icon in different colors so it better fits your existing theme. If you've got a site that has a feed, you might consider updating your site to use the new icons. I believe that FireFox was the first to use the icon, but now Internet Explorer has decided to use the icon as well. Standardization is a great thing, eh?

posted on Saturday, March 18, 2006 5:36:23 PM (Central Standard Time, UTC-06:00)  #    Comments [0]
 Friday, March 17, 2006
First thing - jfo's coding is a great blog. I regularly find her posts very helpful and relevant to what I'm working on.  If you're not already reading her blog, you should check it out.

Second, as she says, tracepoints ARE awesome. I had used a few of the new breakpoint features in VS2005 such as Condition and Hit Count, but I seen the "When Hit..." option yet. All I can say is, you can say goodbye to Debug.WriteLine'ing (aka "the poor man's debugger"*).

* If you're unfamiliar with "the poor man's debugger," it is basically using output statements to determine values and flow of code. If used correctly, it can actually be very useful. It just isn't fun to remove all of that code after you're done debugging (particularly the Response.Write variety in webpages... ouch).
posted on Friday, March 17, 2006 9:09:17 AM (Central Standard Time, UTC-06:00)  #    Comments [0]
 Thursday, March 16, 2006

A few days ago, I was working in Visual Studio 2005 and I was getting awful performance out of it. I decided to pull up Process Explorer to see if I could determine where the problem was. I right-clicked on devenv.exe and pulled up its properties and went to the .NET tab. You can then use the combo box to select different .NET performance measures. Here's what I found:

As you can see with my hastily scribbled red circle and arrow, I'm attempting to draw your attention to the 65.94 number. It is telling me that devenv.exe was spending about 66% of its time in doing garbage collection.

WHAT?!? 66%? That explains why my process was barely responding to me, but it doesn't tell me why it was doing that. I will say that I had a code file up that was about 7000 lines long, but I would still expect it to be able to handle that. Maybe I'm wrong, though. Since then, I've significantly cut that file down in size and I haven't run into that issue again, but it was pretty weird.

posted on Thursday, March 16, 2006 7:48:50 AM (Central Standard Time, UTC-06:00)  #    Comments [0]
 Saturday, March 11, 2006

According to Gadgetopia, a nine-year old in North Dakota made a bet with his older brother that he could print the entire Internet.

You know, if the story stopped there, it might just be a cute story about childhood innocence or something like that. Unfortunately, it doesn't stop there. The bet is for $50 and he has 6 months to do it. It gets better. His hometown is HELPING him. They apparently have no idea just how big the Internet is.

As the post says,

At one time, Google claimed to have 8 billion pages in its index. This kid is printing eight pages to a sheet of paper, but he still has to print one billion sheets of paper.

Let's hope the environmentalists don't get their hands on this. This kid is going to need a few rainforests to get all the paper he'll need. On top of that, there is a whole lot of stuff on the Internet that is completely inappropriate for a nine-year old (much less a 25-year old like myself).

Maybe if his brother is nice, he can get a deadline.

posted on Saturday, March 11, 2006 11:33:03 PM (Central Standard Time, UTC-06:00)  #    Comments [0]
 Friday, March 10, 2006

The majority of the time I spend in front of computers is at work where a proxy has been set up. I don't have a problem with proxies, but it does really annoy me when I try out applications that have very limited support for them. It doesn't help any that the configuration for the proxy at work is through a configuration script (in Internet Explorer, see Tools -> Internet Options -> Connections -> LAN settings -> Use automatic configuration script).

In using configuration scripts, we avoid having to hardcode proxy addresses, ports, userids and passwords. Instead, we are just prompted for our userid and password every time we start up a program that connects to the internet (annoying on its own)... so long as that program supports configuration scripts. The only Internet-capable applications I currently use that support these scripts (as far as I know) are Internet Explorer and Firefox. Period. Take RSS Bandit, which I use every day. I've got a hard-coded proxy address, port, userid and password in there just so that it will work. Every time I change my network password, I have to go in there and change it there as well. Annoying. The second example is PostXING. I've really, really been looking forward to the new release of this application. I never was able to get the prior version to work with proxies. Well, yesterday the beta of the new release was released... and I can't get it to work through proxies. I've used PostXING at home many times and I really like it, but I just can't use it at work... where I spend most of my time.

Now, I have no idea what it takes to program support for proxies and configuration scripts. For all I know, configuration scripts may not even be used mainstream, even in corporate environments. Maybe I should research that. Anyway, my short rant has gotten longer than I intended.

NOTE: Don't take my comments as negative towards RSS Bandit or PostXING. I highly recommend them. I just wish they supported my corporate setup a little better :-)

posted on Friday, March 10, 2006 7:23:43 AM (Central Standard Time, UTC-06:00)  #    Comments [0]
 Thursday, March 09, 2006

Words can't describe this. You've got to see it for yourself.

(Can you imagine programming at 10240x3072?)

(via Digg)

posted on Thursday, March 09, 2006 7:25:13 AM (Central Standard Time, UTC-06:00)  #    Comments [0]
 Wednesday, March 08, 2006

Live.com received a makeover recently, and their search engine has been revamped. I've played around with Live.com before, but never really used it. The changes they've made are definitely an improvement, but after adding a few of my own feeds to it, I'm still seeing some unexpected behavior such as sections overlapping each other. I'm using the IE7 beta 2 preview.

The search engine has some pretty nice features, like smart scrolling, which allows you to scroll all of the results instead of having to go between pages. It is interesting at the very least. A cool idea is the ability to search within pages directly from the search results page. It is worth having a look. I did run into a few issues with it such as getting some permission denied JavaScript errors after a couple of searches.

I would consider using it more after some of the edges are cleaned up.

UPDATE: I tried the search feature again... it is really pretty cool. I haven't used it enough to get a feel for how accurate the search results are, but I really do like the way it handles scrolling. We'll see how continued usage goes.

posted on Wednesday, March 08, 2006 12:46:12 PM (Central Standard Time, UTC-06:00)  #    Comments [0]

I've blogged about the FlowLayoutPanel in the past. It is a highly useful control in certain circumstances, but it doesn't behave as expected a lot of the time. In trying to better explain how it works, I created a small test program to show what it is doing. Here is what it looks like:

It primarily demonstrates how FlowDirection and WrapContents affect controls in the FlowLayoutPanel, particularly dynamically added controls. If you click one of the dynamically added buttons, it will pull up that buttons PropertyGrid, which will allow you to edit its properties. This is useful to examine the effects of sizing, anchoring, and docking.

Here's the link to the VS2005 solution if you're interested in playing around with it. It's in C#. I wanted to play around with anonymous delegates :-)

posted on Wednesday, March 08, 2006 8:33:27 AM (Central Standard Time, UTC-06:00)  #    Comments [0]