« January 2003 | Main | March 2003 »

February 24, 2003

Clipboard transfer to Excel is broken

Transferring information to Excel from the dotNET framework is mostly broken.

The comma separated values format doesn’t work. The metafile format doesn’t work. Even separating simple values by tabs and storing them on the clipboard as a string fails to handle multi-line values.

Resources:

http://support.microsoft.com/default.aspx?scid=kb;en-us;306023

http://support.microsoft.com/default.aspx?scid=kb;EN-US;307029

http://www.dotnet247.com/247reference/msgs/23/118514.aspx

 

February 11, 2003

HttpWebRequest POST Expect 100-continue

The only way the HttpWebRequest will generate a POST method request is using the HTTP 1.1 Expect header set to at least 100-continue. This has the effect of not including the posted data with the initial request, but rather waiting for the server to request the content. This is intended to allow the server to return an error before you ship it a ton of stuff it doesn’t want but is clearly slower for the 99% of traffic that knows what to post where.

Some servers (like www.jobsearch.org) don’t understand the Expect header and respond with a protocol error.

February 09, 2003

Parsing simple grammars using only a single Regex

This example parses the typical web search text grammars including support for search words, parentheses, and/or/not boolean operands and literals with embedded quotes:

 

                       Regex reTokens = new Regex

                        0     (@"(?six)

                  1     2     ((?<literal>""(?:[\w\s] | """")*"")

                        3     |(?<not>(?<=\s)not\s+ | -\s* | !\s*)

                        4     |(?<or>\s+or\s+ | \s*,\s* | \s*\|\s*)

                        5     |(?<and>\s+and\s+ | \s*&\s* | \s+)

                        6     |(?<lparen>\s*\(\s*)

                        7     |(?<rparen>\s*\)\s*)

                        8     |(?<word>[\w\*]+)

                              )*

                              ");

 

This regex parses the entire input into tokens. You end up with Group[1] containing a Capture for each token in order. Groups 2 through 8 correspond to each token type with a Capture for every token of that type. Drawback: Can’t iterate through Group[1] and easily determine token type. So build a captureMap as follows:

                        Hashtable captureMap = new Hashtable();

                        foreach (string tokenType in reTokens.GetGroupNames())

                              if (!Regex.IsMatch(tokenType, @"\d+")) // Groups 0 & 1 end up with names "0" and "1".

                                    foreach (Capture c in m.Groups[tokenType].Captures)

                                          captureMap.Add(c.Index, tokenType);

And then construct a vector of tokens:

                        Token[] tokens = new Token[m.Groups[1].Length];

                        int i = 0;

                        foreach (Capture c in m.Groups[1].Captures) {

                              string tokenType = (string) captureMap[c.Index];

                              if (tokenType == "literal" || tokenType == "word")

                                    tokens[i++] = new TokenValue(tokenType, c.Value);

                              else

                                    tokens[i++] = new Token(tokenType);

                        }

 

February 03, 2003

XML Serialization Reminders

Lessons from XML Serialization:

  1. - Tracking down errors from the exceptions & stack trace is hard.
  2. - Serializable classes need a public default constructor – obscure generic error message.
  3. - Non-polymorphic arrays can be elements to avoid nesting.
  4. - The XmlAnyElement allows a mix of parse & unparsed content.
  5. - Its easy and flexible to alter element & attribute names, shift things between elements, attributes, and text.
  6. - Match the value assigned to a field by the default constructor with DefaultValue attributes to keep optional stuff from appearing in the output.
  7. - Polymorphism works well and may be programmatically extended by using the second argument to the XmlSerializer constructor.

Timer Classes

There are three timer classes:

                1. The windows forms timer requires an event loop & GUI. And uses only the GUI thread.

                2. The System.Timers.Timer is a component, works with the Visual Studio designers.

                3. The System.Threading.Timer is not a component and has a cleaner interface than the SystemTimers.Timer.

Asynchronous callbacks, IAsyncResult, WaitHandles

- Making asynchronous callback delegates use member functions works well for associating state with the callback.

- The IAsyncResult returned by a Begin function is the same object (but at a different time) as the argument passed to the callback.

- See Ref. Waiting on the IAsyncResult’s WaitHandle will not suspend the thread until the callback completes. Probably just until the async operation completes. It is possible to write code that does the begin & end sequentially with an optional WaitOne in the middle and no callback at all. This makes sense if you have some other stuff you could finish sequentially before you need the result of the operation (otherwise you’d just use the synchronous call which is really just a begin-wait-end sandwitch (with the end doing an explicit wait internally). The Async Pattern.

Event Log custom categories & custom event logs.=

Lessons relating to EventLog:

  1. Creating custom categories isn’t supported by the framework.
  2. Creating a custom event log is simple.

February 02, 2003

Bug Report, HttpWebRequest.GetResponse()

The following example program hangs. If the commented line is restored, it exits, but without catching an exception.

In the context of a timer thread, in both console and service projects, execution on the current thread terminates without an exception.

Writing even an empty string is sufficient.

 

namespace Bug.HttpWebRequest.GetResponse

{

      using System;

      using System.Net;

      using System.IO;

 

      class Class1

      {

            [STAThread]

            static void Main(string[] args)

            {

                  try {

                        HttpWebRequest req = (HttpWebRequest)WebRequest.Create("http://www.doesntmatter.com");

                        req.CookieContainer = new CookieContainer();

                        req.Accept = "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*";

                        req.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 1.0.3705)";

                        req.Headers.Add("Cache-Control:no-cache");

                        req.Headers.Add("Accept-Encoding:gzip, deflate");

                        req.Headers.Add("Accept-Language:en-us");

                        req.Method = "POST";

                        req.ContentType = "application/x-www-form-urlencoded";

                        //StreamWriter sw = new StreamWriter(req.GetRequestStream()); sw.Write("a=1"); sw.Close();

                        req.GetResponse();

                  } catch (Exception ex) {

                        Console.WriteLine("Caught exception.");

                  }

                  Console.WriteLine("Exiting");

            }

      }

}