Speeding Up Acrobat Reader's Launch

If you are a regular user of Acrobat Reader, then you are used to the delay associated with the initial launch of the application.  But if you follow this tip from John Roller, the start time can be significantly (and I mean on the order of 80-90%) faster.  John, thanks for adding minutes to every day I'm in front of the computer.

Opening a File by Association

For a simple application, which I hope to share with you shortly, I needed to be able to launch the application associated with a particular file type.  For example, if you had a file called 'test.log' and the '.log' extension was associated with UltraEdit32, I wanted to launch UltraEdit32 and load up test.log.  While I could have located the file association in the registry and launched the application, there is an easier way.  Use the Process class to execute “test.log” as a command and let Windows do the rest.

System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.EnableRaisingEvents = false;
proc.StartInfo.FileName = “test.log“;
proc.Start();

I can't get over how nicely the little things in the .NET Framework fit together.  It's almost like someone...what's the word...designed it ;)

Hiding the Startup Form

As part of my holiday weekend, I created an intray application aimed at tracking the build status in a continuous build environment.  To complete the aesthetics of the app, I wanted my initial form to be hidden immediately upon startup.  A quick Google of the problem found the following MSDN page.

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vbcon/html/vbtsksettingformtobeinvisibleatitsinception.asp

What concerned me is that it mentions that, without using Application.Run, there is the possibility that certain functionality wouldn't work.  I never liked wondering what *might* go wrong with my code (I have too much problem with what *does* go wrong).  With a little bit of experimentation, I found that the following variation gives me the desired result.

public static Main(string[] args)
{
   YourForm form = new YourForm();
   Application.Run();
}

Additionally, when you are exiting the form, it is important to remember that the message pump initiated by Application.Run needs to be shut down.  As a result, you need to include the statement 

Application.Exit();

as part of the form shutdown process.

Wrapping your Hands around the Throat of EnforceConstraints

Do enforced constraints have you down?  They did for me today.  I was getting the following error as I set EnforceConstraints on a freshly filled DataSet to true.

Failed to enable constraints. One or more rows contain values violating non-null, unique, or foreign-key constraints.

I found this to be annoying.  Actually, “annoying “is the polite word that I use when I find error messages to be useless in debugging.  This annoyance continued until I was pointed at this blog entry by my colleague Dave Lloyd.  Since it's his blog, it figures that he would know about it :).  It contains a code snippet can is quite handy in tracking down the source of this particular error message.  Very useful if you too need to tame the enforced constraints beast.

Getting the App Config Path

Continuing my weekend Internet cruising, I also came across a piece of information that I had wondered about, but never found.  How to get the path to the .config file being used by a .NET application.  Apparently, this information is available through the AppDomain. Specifically, the following method retrieves said path:

AppDomain.CurrentDomain.GetData("APP_CONFIG_FILE")

The things that are available on the Internet if you know where to find them.  Or, in my case, just randomly stumble across them.

The Danger of using

It is well known that the C# using statement is quite handy, especially when the object including in the using statement needs to be disposed.  However, there is a danger associated with having certain tasks done for you.  That is, that it is easy to forget exactly what is going on.

The scenario is that an HttpModule was being created by a client of ours to log incoming requests and outgoing responses to a web service.  I've done this before and it is not exceptionally difficult.  We created a handler for the BeginRequest event and accessed the Request.InputStream attribute. This stream was used as the basis for a StreamReader, as can be seen in the following code.

HttpApplication app = (HttpApplication)sender;

using (StreamReader sr = new StreamReader(app.Request.InputStream))
{
   
string content = sr.ReadToEnd();
    // Write content to a log file

}

app.Request.InputStream.Position = 0;

Seems innocuous enough.  But it's not.  The result of a call to a web service in which this HttpModule is installed results in an System.Xml.XmlException. The text of the message indicates that the root element of the XML document is missing. For those unfamiliar with streaming problems, this particular error message is frequently caused by an empty stream being processed by ASP.NET.

The root cause can be found in what the using statement does.  In particular, when the sr variable goes out of scope, it is disposed.  This is the non-obvious behavior I'm going about.  Not that it should be surprising that using causes the object to be disposed you understand.  That's the purpose of using after all.  But because you don't see an explicit Dispose or Close method, the solution to the problem is not obvious.  If you look at the definition of the Dispose method on the StreamReader, you will find that the underlying stream is closed. Not good, as it means that the remaining HttpModules and ASP.NET get nothing to work with.  The solution, for those who are interested (as pointed out to me by Marc Durand at Agfa during our debugging session), is to use the GetBytes method on the InputStream.  This keeps the stream open and the data flowing.

Assembly Signing Changes in VS2005

Just spending the evening playing with Visual Studio 2005 (which says a lot about my life, but that's another story). Actually, because a lot of my most recent has been with COM+, I've been poking and prodding in that area. As a result, I have been (by necessity) signing my assemblies and am happy to report an improvement in VS2005.

As a refresher, signing assemblies in VS2003 and earlier meant adding attributes to the AssemblyInfo file, something similar to the following.

[assembly: AssemblyKeyFile("..\\..\\ObjectSharp.snk")]
[assembly:
AssemblyDelaySign(false)]

But there are a couple of issues associated with these attributes.  First, the path to the key file is actually embedded in the assembly.  If you use a full path, it is possible that information about your machine (servers and directories, for example) can be easily extracted by using ildasm.  Not overtly dangerous, but not the best situation.

Also, providing a relative path to the key file is annoying.  I'm sure most of you have dealt with the trial and error of finding exactly where the .snk file is relative to the compilation directory. 

Finally, because compilation cleanliness is important to all of us, keeping the AssemblyKeyFile attribute in AssemblyInfo generates a warning message.  Not always critical, but annoying.

This is no longer necessary in VS2005.  Instead, the name of the .snk file is included as a property on the project.  Simply right-click on the project in the solution explorer and select Properties.  Then click on the Signing tab.  The path to the .snk file can be provided right there.  Providing this information updates the project file, which in turn is picked up by msbuild at compile time.

 

When Structs Go Bad

In tracking down the cause of a compilation error, I was once more reminded why I don't use structs that often.  The situation was as follows:

A simple struct was defined

public struct Demo
{
   public string value;
}

Variables of type Demo were now instantiated and placed into an ArrayList

ArrayList al = new ArrayList();
al.Add(new Demo);

Finally, a field within the struct was assigned a value through the ArrayList variable.

foreach (Demo d in al)
   d.value = “Test”;

Building this code resulted in the compilation error "The left-hand side of an assignment must be a variable, property or indexer."

For anything but a struct, the code just described would work quite nicely.  However, structs are special.  The intent of a struct is to be instantiated on the stack instead of on the heap.  There are (supposedly) speed reasons for doing this.  For example, if you were to create a large number of structs for a short period of time, creating them on the stack means they get disposed of as soon as they go out of scope (instead of at garbage collection time, as would be the case for a class). This would be fine if .NET weren't so nice to developers.

The problem in this situation is that an ArrayList is (basically) a collection of pointers to other objects.  But it is not possible to 'point' to a variable that has been instantiated on the stack.  So when the struct is added to the ArrayList, it is boxed.  That is, space on the heap is allocated and the values on the struct are copied.  Then the pointer in the ArrayList is quite happy.

So why the compilation message?  Because the C# compiler folk wanted the developer to be aware of what is happening under the covers in this particular situation.  If the d.value assignment were actually performed, the entire struct would have to be copied to the stack, the assignment performed and the resulting struct reboxed.  That has to potential to be a lot of work and, therefore, is something that should be explicitly requested. 

So ultimately, the compiler writers have decided that, in a situation such as this, I need protection from myself.  In principal, I don't mind.  As I started out with, the ease (and invisibility) with which structs are boxed usually means that any memory gains are lost on the performance side.  Unless you are very careful about how they are used.  By complaint is that the compiler message is cyptic.  There is nothing inately wrong with the assignment, other than the performance hit.  So tell me than instead of making me puzzle about why a public element in a struct can't be the target of an assignment.

Oracle, Unicode and NVARCHAR2, oh my

I'm the first to admit that I'm by no means an Oracle guru.  I'm familiar enough to connect to the database and use it, but don't ask me any design or configuration information that applies specifically ro Oracle (as opposed to SQL databases in general).  As a result, I wouldn't normally comment on anything associated with Oracle, for fear of looking plain silly.  Still, this little tidbit caught more than just me, so I feel a little safer.

I was trying to insert a string into an NVARCHAR2(64) database field.  The statement itself was pretty straightforward (and modified slightly, for simplicity).

INSERT INTO table (key, stringfield) VALUES (1, 'this is a string longer than 32 characters')

Since the code was coming from .NET, I wasn't surprised that the values being inserted were Unicode.  What did surprise me was the 'value too long' exception that was thrown when this statement was executed.  It seems that when you define an Oracle field as NVARCHAR2(64), you are really only allowing for a maximum of 32 Unicode characters.  In order words, the 64 is the number of bytes taken up by the field, not the number of Unicode characters that can be stored.  Talk about a mental disconnect.  I took about 15 minutes longer to address the problem then I might have while made sure I wasn't missing something obvious.  So consider yourself warned.

Modifying a cell in a DataSet

This post also falls under the “I need to remember this” category, because it's not the first time I've run into the situation.  I was running a quick test of something relatively unrelated (the functionality of the RowUpdating event) when I needed to modify a particular element in a DataSet. The line of code I created to do this was

ds.Tables[0].Rows[0].ItemArray[1] = “Test”;

Seems innocuous enough.  And if you're at all familiar with ADO.NET, this would seem to be a reasonable approach.  In fact, Microsoft even says this is the way to do it here (check out the first C# code...the VB.NET code works fine).  Unfortunately, both Microsoft and reason are mistaken. The correct way to update a cell is as follows

ds.Tables[0].Rows[0].Item[1] = “Test”;

or

ds.Tables[0].Rows[0][1] = “Test”;

The reason for fact that setting the element on ItemArray failing has to do with the actual definition of the ItemArray property.  According to the documentation, ItemArray “gets or sets all of the values for a row through an array”.  In other words, ItemArray is intended to be used an array at a time, not an element at a time. In fact, the getter on ItemArray is actually creating a copy of the values in the DataRow (if you need proof, use a tool like Reflector to check out the code).  So when you do the assignment, you're changing the value of a copy of the values in DataRow, not the underlying value itself.  Which, in turn, means that any DataAdapter.Update performed on the DataSet won't see the change.

Just something to keep in mind.