I've had a few more chances to play with Office 2007 since installing it from MSDN a couple of weeks ago. Although I'm still getting used to it, I like the mouse UI that the new Ribbon interface presents. What I'm having a lot more trouble working with is the keyboard interface.
I'll confine my comments here to Word 2007, which aside from Outlook (which doesn't use the Ribbon in its main window) is the Office 2007 app I've had the chance to use most so far. I'm a big fan of using keyboard shortcuts to execute frequently-used commands in applications, so in my work with Word 2007 so far, I've tried to continue and use the keyboard shortcuts that I'm familiar with from Word 2003. However, although Office 2007 is advertised to support Office 2003 keyboard accelerator sequences, I have run across a couple of instances already where the Word 2003 keyboard accelerators are altered or broken in Word 2007.
Office 2007 also doesn't provide any "forward breadcrumbing" for Office 2003 keyboard accelerator sequences. For example, if I want to do the Office 2003 command "Table | Insert | Row Above", and I know that the 2003 accelerator sequence starts with Alt+a (Table), I (Insert), but then I'm not sure which letter of "Row Above" is the final step of the sequence ("r"? "a"? something else?) then I'm out of luck in Office 2007; Office 2007 shows the keystrokes of the sequence I've typed so far, but provides no feedback regarding the available keystrokes to continue the sequence.
Using a Word 2003 keyboard accelerator sequence in Word 2007
(In Office 2003, the menu would have opened along with my typing so that I'd be able to glance up and see the underlined letter in "Row Above" which is the correct final keystroke for my command. It turns out to be "a" in this case, in case you were wondering.)
So today, I tried to start using (and memorizing) the new keyboard accelerators built into Office 2007. 2007 does provide nice "forward breadcrumbing" for its new keyboard accelerators; when the Alt key is first pressed, the available shortcuts corresponding to each visible Ribbon option are displayed, and new available options continue to be displayed as you progress through the keystroke sequence.
My problem with the new 2007 accelerator sequences is that in general, they don't seem to lend themselves to memorization very well at all. In Office 2007, the accelerator sequence for "Insert Table Row Above" is alt+j, l, a. (Alt+j, l brings up the Layout ribbon section; a does the Insert Above.) There doesn't seem to be an "obvious" mnemonic for this sequence. (I found myself thinking "Justice League America!" for this particular sequence, but I'd hate to try and extend that paradigm to all 2007 accelerator sequences...)
Most 2003 sequences corresponded fairly well to the command you were executing, making them easy to remember. (Alt+e, f for Edit | Find, for example.) Even the 2003 sequence for Table | Insert | Row Above (alt+a, i, a) was pretty good when you remember that alt+a was the accelerator for the Table menu (alt+t being used for the Tools menu).
The "Insert Table Row Above" is actually not too bad as 2007 accelerator sequences go, either. The 2007 accelerator sequence for the basic "Find" operation is Alt+h, z, n, f, d, f -- 6 keystrokes! And not terribly intuitive, the most questionable sequence being "z, n" to open the "Editing" item on the "Home" ribbon.
The Word 2007 Ribbon after pressing Alt+h. "z, n" is the accelerator key sequence for the "Editing" item.
(You can, of course, alternatively use the Ctrl+F keyboard shortcut to bring up the Find dialog, but many commands don't have a simple Ctrl-key shortcut that can be used for quick access.)
Another point about Office 2007 keyboard accelerator sequences is that they will switch the active (visible) section of the Ribbon; so if you were working with the Home section of the Ribbon, and then type the 2007 "Insert Table Row Above" accelerator sequence, the Ribbon switches to make the Layout section visible, and you need to subsequently manually switch the ribbon back to the Home section (either by clicking it with the mouse or with the accelerator sequence alt+h, Esc, Esc) to make the Home icons visible once again.
My (early) overall impression is that the Office 2007 Ribbon is a nice step forward in UI design for novice/intermediate users (who will normally be activating commands by using the mouse, as opposed to the keyboard) and for the activation of seldom-used commands, but it is in some ways a step backwards for "power users" who prefer to activate frequently-used commands via the keyboard.
I just updated this blog to use a new template. The main reason for the update was to get the content column of the blog (this column) to be variable-width. Thus, if you increase the size of your browser window, the text in this column should rearrange itself to take advantage of all of the available width.
Previously, the content column was fixed-width. In addition to not taking advantage of the full available width of large browser windows, the previous template also limited me to posting no wide non-wrapping items (in particular, images and code snippets); if such items were too wide, the template would break (and break rather spectacularly on IE!).
It took me a while to come up with HTML/CSS code that would work for what I wanted to do, given that IE (at least through version 6) does not support the min-width CSS style property. Eventually I came across this excellent page by Stu Nicholls which demonstrates a method of getting IE to emulate the min-width style property. (More details on the technique used available here.)
I took advantage of Stu's technique to put together this template, which (seems to!) behave properly in both IE 6 and Firefox 2: The right column is always a constant 270px, and the left column resizes dynamically to take advantage of the remaining available width of the browser window, but never shrinks below 400px or the width of the largest non-wrapping item in the column (whichever is greater). Thanks Stu!
C# events are typically used when a class instance that needs to notify other class instances that reference it that something has taken place.
In some cases, it is useful for an event handler to respond to the event by sending data back to the instance that fired the event. For example, in response to an event fired to give notification that a particular operation is starting, an event handler for the event could respond by returning a boolean value to indicate that the operation should either proceed normally or be aborted.
There are a couple of ways in which returning data in response to handling an event in this manner can be set up.
One is for the event handler method to set a parameter on one of the event method arguments. The System.ComponentModel.CancelEventArgs class is a good example of this. The CancelEventArgs class has a public Boolean property, Cancel, that the event handler method can set to True to indicate that the operation associated with the fired event should be aborted.
A second way for an event handler method to return a value back to the sender is for the delegate that the event is based on to include just that -- a return value. Instead of being declared to return void, the delegate on which the event is based can be defined to return a type such as bool or int.
What happens, though, if there is more than one handler for the event, both of which set the return value (either in a property of one of the event method arguments, or in the return value itself)?
The answer (at least in a single-threaded scenario) is “last-one-in-wins” -- that is, the last event handler method to run is the one whose return value will be received by the code that fired the event. (Event handlers run in the order in which they were registered. A good example of this which uses just plain delegates (not events) can be found in section 15.3 of the C# Language Specification.)
For example, consider this code, which wires up 3 separate event handlers to the event fired by the EventSource class, each of which set the value of the Data parameter of the event’s argument:
class Program
{
public static void Main(string[] args)
{
EventSource eventSource = new EventSource();
Handler handler1 = new Handler(1, eventSource);
Handler handler2 = new Handler(2, eventSource);
Handler handler3 = new Handler(3, eventSource);
eventSource.FireEvent();
}
}
///
/// The IntEventArgs class extends the basic EventArgs class
/// to add a public parameter of type int named Data.
///
class IntEventArgs : EventArgs
{
public int Data = -1;
}
///
/// The EventSource class fires an event that passes an
/// IntEventArgs as a parameter.
///
class EventSource
{
public delegate void ReturnIntHandler
(object sender, IntEventArgs eventArgs);
public event ReturnIntHandler ReturnIntEvent;
public void FireEvent()
{
Console.Out.WriteLine("[EventSource] Firing event...");
IntEventArgs eventArgs = new IntEventArgs();
ReturnIntHandler e = this.ReturnIntEvent;
if (e != null)
{
e(this, eventArgs);
}
Console.Out.WriteLine("[EventSource] Finished firing event. "
+ "eventArgs.Data: " + eventArgs.Data);
}
}
///
/// The Handler class gets assigned an ID value when it is created
/// (to distinguish it from other instances of the Handler class),
/// and handles events of type ReturnIntEvent.
///
class Handler
{
private int _id;
public Handler(int id, EventSource eventSource)
{
this._id = id;
eventSource.ReturnIntEvent +=
new EventSource.ReturnIntHandler(eventSource_ReturnIntEvent);
}
private void eventSource_ReturnIntEvent
(object sender, IntEventArgs eventArgs)
{
Console.Out.WriteLine("[Handler (" + this._id + ")] "
+ "Handling event, setting Data to " + this._id + ".");
eventArgs.Data = this._id;
}
}
This code produces the following output. Note that the final value of the eventArgs.Data parameter (3) matches the value set by the last event handler method to handle the event.
[EventSource] Firing event...
[Handler (1)] Handling event, setting data to 1.
[Handler (2)] Handling event, setting data to 2.
[Handler (3)] Handling event, setting data to 3.
[EventSource] Finished firing event. eventArgs.Data: 3
At lunch today, I installed the Office 2007 release (obtained through my company's MSDN subscription), replacing my previous Office 2003 installation.
Per the online documentation and also a nice Jensen Harris (Microsoft) blog post, Office 2003 menu shortcut keys (e.g. Alt+F, S for File menu | Save) are supposed to work in Office 2007 as well.
When I fired up Word 2007 and opened a document, it put me in the "Print Layout" view. I'm accustomed to working in what was called "Normal" view in Word 2003, so I did Alt+V, N to try to switch to Normal view.
So far so good after the Alt+V -- Word 2007 even put up a nice little tooltip letting me know I had started typing a Word 2003 shortcut -- but subsequently pressing the N key had no effect.
I opened up the new "View" ribbon, and saw in the "Document Views" section that "Normal" was no longer present, but a new "Draft" view was available. I clicked that, and it put me in the equivalent of what "Normal" view was in Word 2003.
So "Normal" is apparently renamed to "Draft" in 2007... I switched back to "Print Layout" view (Alt+V, P did work), and then tried Alt+V, D to switch to "Draft" view -- it worked.
I fired up Word 2003 on my test machine to verify that Alt+V, D didn't work on that version. I expected it to have no effect... but Alt+V, D actually has a different function in Word 2003: hide/show the Document Map!
Consequently, the Alt+V, D shortcut for "hide/show Document Map" in Word 2003 is essentially broken in Word 2007 as well (since it now has a different function than before). I couldn't find any alternative "Alt+V" shortcut in Word 2007 for Document Map, although the "new" Alt+W (View ribbon), V, M shortcut does work for "hide/show Document Map" in Word 2007.
I recently had a problem where in Firefox, the Downloads window (the "Download Manager") wouldn't appear when I started to download a file. The Downloads window previously had been working fine.
The behavior I saw was that I would begin download of a file (typically something with a .zip extension), but the Downloads window would never appear, and I wouldn't get any notification that the download was in progress or that the download had finished.
I did verify that the "Show the Downloads window when downloading a file" checkbox in the Options dialog was checked. Checking and unchecking the checkbox and restarting Firefox didn't do the trick, either.
I also tried upgrading from Firefox 1.5 to 2.0, but the problem was still present after the upgrade.
A Google search didn't turn up any relevant information. On a suggestion from my brother Jeremy, I got more specific and searched the Mozilla Bugzilla database (the Firefox public bug-tracking system) for the issue, and came up with a similar issue, describing a problem where the Download Manager window did appear, but wouldn't show downloads in progress.
Ria Klaassen left a comment on that bug record with a suggestion to try deleting the downloads.rdf file in the Firefox profile folder, as it can sometimes get corrupted and cause problems.
I tried deleting my own downloads.rdf file and restarting Firefox, and the fix worked like a charm! My own Download Manager window is working perfectly once again. Thanks Ria!
In Firefox 2.0, a new top-level menu, "History," was introduced, with an accelerator keystroke of Alt+S. Unfortunately, this conflicts with the Alt+S keystroke used to access the Google Toolbar for Firefox. In Firefox 2.0, a press of Alt+S activates the History menu instead of setting the focus to the Google Toolbar.
A somewhat extreme, but effective, workaround for this is just to disable (remove) the History menu. This can be done by adding the following code to your userChrome.css file:
/* For now, hide the new Firefox 2.0 History
menu because Alt+S collides with Google toolbar
search field */
menu[label="History"] {
display: none !important;
}
Help on editing your userChrome.css file is available here:
http://www.mozilla.org/support/firefox/edit
Google has indicated (in this Google Groups thread) that a better solution should be forthcoming. Hopefully it won't be too long! Until then, I can live with using the Ctrl+H History sidebar and the back button dropdown menu session history to duplicate most of the History menu's functionality.
Update 6/8/2007: The keyboard shortcut Alt+G now works in the latest version of the Google Toolbar for Firefox 2.0 to set the focus to the Google Toolbar's search field. This isn't quite as handy to reach with the left hand on a standard keyboard (compared to Alt+S), but it does bring the Google Toolbar for Firefox into step with the Toolbar for IE, which has always used the Alt+G shortcut to set the focus to the Search field.
(Alt+G wasn't used in older versions of the Google Toolbar for Firefox because it conflicted with the shortcut for the Firefox 1.x Go menu.)
I just ran across the following weird behavior in Internet Explorer 7:
- Open a new IE7 window.
- Fairly quickly (before the homepage begins loading), paste a valid URL into the address bar and press enter.
- An error message appears: Caption "Address bar", text "[Url] is currently unavailable."
The error doesn't occur if I wait a second or two after the windows opens before navigating to the new URL.
This may seem like a fairly contrived set of steps to take, but I wasn't hunting for IE7 bugs when I ran across this; I was just using the browser normally. It's a series of steps I do fairly frequently to quickly bring up a URL in a new IE window via keyboard: copy an URL to the clipboard, open the new IE window (Winkey | down arrow | Enter -- IE is the top item in my Windows XP Start menu), Alt+d to set the focus to the address bar in the new IE7 window, Ctrl+v to paste the URL, enter to navigate to the URL.
I have to think this behavior of IE7 is a bug -- at least, it doesn't seem like something that should be classified as "working as intended" or "by design"! And I never saw this behavior in several years of using IE6.
I just spent a frustrating few minutes trying to figure out what looked to me like some unexpected behavior in the code (C# 2.0) for a drag/drop operation in a Windows Forms component. In the handler for a DragOver event, I was setting the Effect property of the DragEventArgs argument to DragDropEffects.Link, but then subsequently in my GiveFeedback event handler, the Effect property of the GiveFeedbackEventArgs was coming back as None.
In other words, I was essentially doing this in my DragOver event handler:
private void HandleDragOver(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Link;
}
And then seeing this result in my GiveFeedback event handler:
private void HandleGiveFeedback(object sender,
GiveFeedbackEventArgs e)
{
if (e.Effect == DragDropEffects.Link)
{
// (Execution not making it here -- value of
// e.Effect is DragDropEffects.None!)
}
}
I figured out that the problem was that I was using this line of code to start the drag operation:
DoDragDrop(dragData, DragDropEffects.All);
See the problem? I didn't, at first.
The DragDropEffects enumeration is a FlagsAttribute enum with the following members: All, Copy, Link, Move, Scroll, and None. At the time that I coded the DoDragDrop line of code above, I had made the assumption that the All value of the enumeration was defined as:
Copy | Link | Move | Scroll
That is, a bitwise OR combination of all of the members of the enum aside from the None member.
However, looking that the value of System.Windows.Forms.DragDropEffects.All in the Visual Studio debugger, the value is actually defined as:
Copy | Move | Scroll
The Link member is not present! This is why my GiveFeeback event handler was failing to recognize DragDropEffects values of Link -- Link was not actually being specified in the allowedEffects parameter (argument 2) of my DoDragDrop call.
Since what I really wanted to accomplish was to support Move and Link operations, I changed my DoDragDrop call to:
DoDragDrop(dragData, DragDropEffects.Move | DragDropEffects.Link);
This did have the desired effect of getting my GiveFeedback event handler method to support DragDropEffects values of Link.
The fact that DragDropEffects.All does not include the value for Link isn't clearly spelled out on the MSDN help page for DragDropEffects. The description for the All member does say "The data is copied, removed from the drag source, and scrolled in the drop target," but you need to be paying pretty close attention to notice that "Link" isn't mentioned, even assuming that you check the help before using the member in the first place! Similarly, the example code given on the help page for DoDragDrop does use DragDropEffects.All | DragDropEffects.Link in its call to DoDragDrop, but the use of the Link member there isn't called out or noted with a comment.
Due to its counter-intuitive nature, I would go so far as to call the omission of the DragDropEffects.Link bit from the DragDropEffects.All value non-standard... if the DragDropEffects enum wasn't a part of the standard .Net Framework library! :-) Perhaps there is a good historical (legacy support) reason for the exclusion, but if so, it isn't at all clear from just looking at the documentation.
I'll first get right to the main point of this post; the full story behind how I encountered this issue is included at the end.
Issue and Workaround
When running the installer for the ATI Rage driver for Windows XP from the ATI site (from archive file wxp-ragexl-5-10-2600-6009.exe, unpacked by default to (C:\ATI\support\wxp-ragexl-5-10-2600-6009\setup.exe), an ATI logo and an InstallShield dialog with a progress meter appears. After the progress meter reaches 100%, the installer application crashed. Clicking the "Debug" button on the crash dialog reported an error in file _ins5176._mp.
Setup still crashed when I ran it with my virus scanner and other non-critical background processes temporarily disabled.
I was able to work around this issue an install the driver through the following procedure:
- Open Device Manager. (Start | Control Panel | System | Hardware tab | Device Manager button)
- In the Device Manager dialog, expand the Display Adapters tree item.
- Under Display Adapters, right-click on the ATI adapter item (this may vary; in my case, "XPERT 98 RXL AGP 2X") and select Properties from the context menu to open the Properties dialog.
- In the Properties dialog, on the Driver tab, click Update Driver to open the Hardware Update Wizard. In the Hardware Update Wizard dialog:
- In Step 1, the wizard asks "Can Windows connect to Windows Update to search for software?" Select the "No, not this time" radio button and click Next.
- In Step 2 ("What do you want the wizard to do?"), select the "Install from a list or specific location (Advanced)" radio button, click Next.
- In Step 3 ("Please choose your search and installation options"), select the Browse button.
- In the Browse For Folder dialog that appears, navigate to C:\ATI\support\wxp-ragexl-5-10-2600-6009\ATIDrive. Click OK. Back on the Hardware Update Wizard dialog, click Next.
- Proceed from that point to install the ATI drivers and finish the Hardware Update Wizard.
Background
The CPU case of my development machine at work had started making a moderate-volume loud humming/buzzing sound, coming from the power supply fan in the back of the case. (Normally the machine runs near-silently.) I opened up the case to see if I could fix the problem.
I did manage to get the fan fixed such that it stopped making noise, but when I came into work the next morning and powered on my machine, only one of my two monitors was working. The secondary monitor, which is connected to a cheap older PCI video card (an ATI Rage) that I had added to the machine to get dual monitor support, was not functioning; it just showed a blank screen, and the status light on the monitor showed yellow (sleep/inactive) instead of green (active). The ATI Rage display adapter was missing in both Device Manager and the Settings tab of the Display Properties dialog (where all available monitors are normally shown).
I think the cause of the problem was that I must have slightly jostled the PCI video card in its slot when I had the case open to fix the fan problem, such that the card was no longer tightly seated, and the computer could no longer "see" that the card was present.
I tried (with the computer powered off, of course) tightly reseating the video card in its slot; no luck, the computer still did see the video card.
At this point, I uninstalled the ATI driver, with the intention of updating it with the latest version. However, as it turns out, there was no newer version available (next time I'll check this before I uninstall!); further, when I tried to reinstall the driver, I encountered the setup.exe crash problem that I detailed at the top of this post, so I couldn't get the ATI driver to reinstall.
I then tried moving the card to a different PCI slot (which I have had some luck with in the past with a similar problem with a Soundblaster Live sound card not being detected by the computer in one of my home machines). This did get the card to show up in the Display Adapters section of Device Manager once again, but the card still refused to appear in the Display Properties dialog where multiple monitors are configured. I think the problem now was that the card was now running using the Microsoft-provided driver, which apparently did not include multiple monitor support for this particular model of video card.
Using the workaround I found, I did get the manage ATI drivers back in place. Even after a reboot, though, the ATI Rage card still wouldn't show up in Display Properties, and the monitor connected to the card continued to stubbornly refuse to activate or show anything.
Finally, I swapped the video card back to its original PCI slot, and everything started working again! It was a relief to get the second monitor back on; having enjoyed the luxury of working with dual monitors at work for a while now, I don't ever want to go back to doing development work on a single-monitor machine.
All in all this was not exactly the most productive hour or two that I've ever spent at the office! At least I did manage to find a workaround for that Windows XP ATI Rage display driver setup issue, which hopefully might help others out there experiencing the same issue.
My son, Elijah, is now 16 months old. I have reflected once or twice on how I might go about introducing him to computer programming (in a few years, once he's older).
I myself first learned to program circa 1983, on an original IBM PC that my dad brought home from work. Like other PCs of the time, this machine did not have a hard disk; if booted with no floppy disk in the A: drive, a BASIC programming environment would come up. I remember writing programs that would do math problems, as well as some simple games, eventually including a Zork-like text-based adventure game.
It was a pretty nice "sandbox" environment to be able to experiment and learn in. The programming environment was easy to access (just turn on the machine!), and since the machine didn't even have a hard drive, and no floppy in the drive, I couldn't do anything to damage any of my dad's data.
So when Eli is ready to try out some programming for the first time, I've wondered, given today's much-changed environment from the one in which I learned to code back in the early 80s, how might I go about it? This afternoon, I came across a really nice series of blog posts by David Bau about how he went about first helping his (at the time) 6-year-old son learn to program.
David and his son Anthony start out writing just a text-based program, just like I used to do on the old IBM PC (and, as David writes, that he used to do on an old Atari machine). Despite the standard of today's kids' software of having rich visuals and sound, Anthony was pretty excited just to be able to create a text-based "guess the number" game (featuring a vampire!). And once Anthony was ready to want to add some simple graphics and sound to his game, David was able to figure out how to get it done with the help of some brief research via Google and some free libraries.
As a programmer and the parent of a young child, I found David's story to be an informative (as well as enjoyable and heartwarming) read. I now have a better idea of how I can go about first helping Eli learn to program when the time comes!
In the Visual Studio 2005 Watch window (where values of variables or expressions can be monitored during a debugging session), while editing a value in the Name field (where the variable/expression to be monitored is defined), support for the Ctrl+LeftArrow and Ctrl+RightArrow keyboard shortcuts isn't implemented properly.
In text-editing applications, the keyboard shortcuts Ctrl+RightArrow and Ctrl+LeftArrow commonly have the functionality of moving the caret (insertion point) to the beginning of the current word or the end of the next word. In the VS 2005 main code window, there is very good support for these keyboard shortcuts: in an identifier with several parts separated by period characters (such as System.Drawing.Rectangle.Empty), the insertion point is moved to before and after each period character on successive uses of the shortcut, making it easy to quickly navigate to either the beginning or the end of each component of the identifier. The insertion point also stops at other common code "punctuation", such as the [ and ] characters.
However, while editing a value in the Watch window, the Ctrl+LeftArrow and Ctrl+RightArrow shortcut behaviors follow a different set of rules. When these shortcuts are used in the Watch window, the insertion point bypasses most common code punctuation characters, and instead stops only at space characters. (A bit of additional testing I did shows that certain punctuation characters such as ? and ! are also treated as stopping points in the Watch window.)
Interestingly, this behavior appears to be a regression from Visual Studio 2003; in VS 2003, a quick test shows that when doing a Ctrl+LeftArrow / Ctrl+RightArrow in the Watch window, the caret does indeed treat a "." character as a stopping point. Possibly this is a result of a rewrite of the Watch window to implement one of the very nice new features introduced in VS 2005: Intellisense being enabled and usable when editing a value in the Watch window.
This morning, I noticed an issue with a particular popup browser window that my development team's browser-based application opens. With the application running in an Internet Explorer instance in the right monitor on my dual-monitor dev system, when the popup window was opened, it appeared in the center of the left monitor.
I decided to change this behavior to have the new popup window appear on top of the parent window, to make the appearance of the new window more obvious. I knew that the location of the new window could be specified relative to the screen in parameter 3 of the Javascript window.open(URL, windowName, windowFeatures) call in IE using the top and left attributes, like:
window.open('http://localhost/', 'newWindow', 'top=200,left=300')
What I wanted to do was supply top and left attribute values based on an offset of the screen position of the parent browser window. However, it wasn't clear to me how to get the parent window position.
Google searches such as javascript window screen position weren't much help -- no obvious solutions to my question among the first several pages returned. Searching on "location" instead of "position" was even less help, since the results included a lot of keyword collisions with the location (URL) property of the window object.
Eventually I did come up with a quirksmode.org page with the answer I was looking for by searching on "screenX", the attribute used with window.open on Navigator/Mozilla/Firefox to specify the horizontal position of a new window. The solution: In IE, the screenLeft and screenTop properties give you the horizontal and vertical positions of the browser window relative to the screen, respectively. (screenX and screenY are apparentely the equivalent properties in Mozilla, although I haven't tested this myself.)
On a page with frames, self.screenLeft will return the horizontal position of the current frame relative to the screen, whereas self.screenleft will give you the position of the browser window relative to the screen.
Interestingly, support for the screenLeft and screenTop properties apparently was first introduced in Internet Explorer 5. (This is why my copy of The Flamingo was no help in finding the solution -- my old copy is only current through IE 4.) This seems to indicate that in IE 4, you could set the position of a window (either initially in the window.open() call, or later via the window.moveTo() method), but there was no way to get the position of the window!
In any case, I was able to solve my original problem of the IE popup window appearing in the wrong place by getting the screen coordinates of the parent window using calls to top.screenLeft and top.screenTop, then adding a delta to each of those values and using the adjusted values in the call to window.open().
Firefox provides pretty nice hotkey support for the "Find Next" / "Find Previous" functionality of its incremental find/search feature. For the "Find Next" feature, several hotkeys are supported:
- Alt + n - Displayed on the Firefox Find Bar
- F3 - Standard in many Windows applications (Visual Studio, Word, notepad.exe, but not IE)
- Ctrl + g (Find again) - Displayed in the Firefox Edit menu; used by a few other apps
For "Find Previous", Alt + p, Shift + F3, and Ctrl + Shift + g are supported, which nicely mirror the "Find Next" shortcuts.
I like this broad hotkey support for these functions; I think the (arguable) inelegance of the redundant hotkeys is outweighted by the usability benefit of having whichever common Find Next hotkey combination that is intuitive to a particular user be supported.
The incremental find feature itself and its benefits have already been covered pretty well by Jeff Atwood -- head over there to read up if you aren't familiar with the feature.
I encountered a problem earlier today where when accessing a website set up to use Windows Integrated Authentication on my development machine across the LAN from my copy of Internet Explorer 7 (beta 3) on my test machine, Internet Explorer 7 was unexpectedly prompting me for credentials. I expected my credentials to be provided automatically via Integrated Authentication.
At first I thought that this might be an IE7-specific issue, but Googling for variations on "IE7 Integrated Authentication prompt" didn't turn up anything.
I then supposed that the issue might be a general Internet Explorer configuration issue, not specific to IE7. A Google search on "ie 'integrated authentication' prompt" turned up as its first hit a useful Microsoft Knowledge Base article, Internet Explorer May Prompt You for a Password.
Among other useful information, the article mentions that IE will prompt for credentials if it thinks the site being connected to is an Internet site (a site not located on the local LAN). This prompted me to check on my client machine IE's Tools menu | Internet Options | Security tab. In the Security tab, my web server machine was set as a member of the Trusted Sites zone.
I removed the web server machine from Trusted Sites and restarted IE. Somewhat ironically, Integrated Authentication then started working as expected; I could access the website on the server machine without being prompted for credentials. Apparently, at least in this case, IE interpreted the machine being present in the Trusted Sites zone as meaning that the machine was located out on the Internet (not internally on the LAN).
On another test machine running Windows 2003 Server that I used as a client to test the Integrated Authentication on my internal web site, I needed to not only remove my server machine from the Trusted Sites zone, but to explicitly add the machine to the Local Intranet zone before Integrated Authentication worked properly.
For both client machines, I had also verified that in the Internet Options dialog, in the Advanced tab, the "Enable Windows Integrated Authentication" checkbox was checked. (Apparently this checkbox is not checked by default on some Windows 2000 Server machines, which can cause Integrated Authentication to not work properly.)
I've posted an update, v2.0.1, to Bounds Test, my on-screen ruler utility. Updated in this release:
- The window Width and Height properties are now displayed in bold, and at the top of the properties list.
- Removed the display of window X and Y properties (as they were redundant with the Left and Top properties, which are also displayed).
- Windows XP themes/styles are now used by the application if they are supported by and enabled in the host operating system.
- Added a right-click context menu with an "About" dialog.
Get it from my utilities page, or just download it directly: BoundsTest 2.0.1 (9k)
As part of my investigation for an upcoming project at work, I've done some work to determine whether and how Windows Forms controls hosted in Internet Explorer can be made to use Windows XP Styles without having to use a .manifest file on the client machine. (We make an effort to minimize or eliminate client-based requirements for our application, so that clients can simply point their copy of IE to the app and have everything "just work.")
I found that including a call to Application.EnableVisualStyles() causes the control to be rendered using Visual Styles. For example, a simple Windows Forms 2.0 test control that I put together is normally rendered by IE 6 as show in the image on the left; with the call to Application.EnableVisualStyles() placed in the control's constructor method, the control renders like the image on the right:
XP Styles Disabled
|
XP Styles Enabled
|
This behavior may be different from previous .NET framework versions; my boss (the team's former technical lead -- a guy who knows his stuff) reported to me that he spent a fair amount of time back when we first migrated to using Windows Forms controls hosted in IE trying to get XP Styles enabled, when we were using the 1.1 .NET framework, without success.
The behavior is also directly contrary to the Microsoft documentation of the EnableVisualStyles method, which states:
This method will have no effect for controls hosted in Internet Explorer.
This makes me nervous about depending on this behavior, since Microsoft might decide to change it as a "bug fix" to make the behavior be in line with the documentation. (However, why making a change to create a difference in behavior between Windows Forms controls hosted in IE and WinForms controls hosted elsewhere would be a desirable change, I'm not sure, so perhaps no change is likely to be made.)
I also found that if the EnableVisualStyles() method is called for any particular control in a given IE window, it affects all controls on the page. (This does make sense, given that the method being called is a member of the Application object, with the application in this case being the IE instance itself.) Even controls which were rendered to the control calling the method are affected -- in the case I tested, a control which was already rendered in the IE window prior to the control calling EnableVisualStyles() repainted itself to use the XP styles, which it wasn't using when it was initially painted.
This isn't a major concern since my company's controls will be the only Windows Forms controls in any IE window where they appear, so inadvertently affecting any 3rd-party controls which might be present on the page shouldn't be an issue. (And generally, using the functionality of IE hosting WinForms controls at all seems to be a pretty rare practice!) We will need to be careful to coordinate the change among our own various controls, though, to make sure everything looks right when using the new styles.
Update 8/18/2006: This morning, I tested the same control using Internet Explorer 7 (beta 3), and it also does display the XP styles/themes. (I did hit a snag where at first, I thought that it wasn't working in IE7; however, this was because the test machine where IE7 was installed was set to have XP styles disabled OS-wide. When I enabled XP styles in the operating system display settings, the control as displayed in the IE7 window immediately updated to use the XP styles.)
While working on a simple Windows Forms control to determine how Windows XP styles are supported in Windows Forms 2.0 controls hosted in Internet Explorer -- more on that later -- I found that Internet Explorer 6 doesn't respect public properties of hosted/embedded controls that define a "set" but not a "get".
I had a property like this in my test control:
public bool UseRedBackgroundColor
{
set
{
if (value == true)
{
this.BackColor = Color.Red;
}
}
}
I had a param set in the object tag in my test HTML page to set the property to true, but the control didn't render with the red background:
<object id="XPTest" name="XPTest"
classid="http:/webtest/XPStylesTest.dll
#XPStylesTest.XPStylesTest"
width="158"
height="250">
<param name="UseRedBackgroundColor" value="True" />
</object>
I changed my public property to have a "get" section, and after a rebuild of the project and a reopen of IE, the control rendered with a red background as expected:
public bool UseRedBackgroundColor
{
get
{
return (this.BackColor == Color.Red);
}
set
{
if (value == true)
{
this.BackColor = Color.Red;
}
}
}
So if a public property of a Windows Forms control hosted in IE isn't being activated as expected, check to make sure that a "get" section has been defined for the property.
I recently authored a simple ASP.NET internal website at my office to act as a repository for proposed enhancements for an upcoming release of our product. (Visual Studio .NET 2005 made authoring the site really quick and easy, such that the small amount of dev time I needed to put in to create the site was worth the gain in usability over a solution like Excel or Access.)
Since only internal employees would be using the site, I decided to set it up to use the Windows Integrated Authentication provided by IIS 5, so users of the site would automatically be logged in using their domain credentials (rather than being prompted for a set of credentials).
This worked great when accessing the site in Internet Explorer. However, when using Firefox, I would get prompted by Firefox for a set of credentials when first accessing the site. Entering my domain username and password did allow me to access the site successfully.
I wanted to avoid the login prompt with Firefox, so I Googled for firefox integrated authentication prompt. This turned up a couple of good results that pointed me to the solution:
- In the Firefox Location bar, type about:config and press enter.
- In the Filter bar that appears, type Network.automatic-ntlm-auth.trusted-uris and press enter.
- Under "Preference Name", double-click on Network.automatic-ntlm-auth.trusted-uris, and enter the name of the web server machine in the dialog that appears.
Now users are able to access the site without being prompted for credentials -- Firefox uses Integrated Authentication and automatically uses the user's domain credentials to log in.
Earlier today, Aaron Sauve from Microsoft's Internet Explorer 7 team made an interesting post to the IE Blog covering the IE team's rationale for the various default behaviors in IE7 with respect to multiple tabs.
As of the latest IE7 beta (beta 3), the default behavior when using the Ctrl+Tab keystroke to switch to the next tab (or Ctrl+Shift+Tab to switch to the previous) tab is to define the "next tab" as the tab to the right of the current tab as the tabs are displayed along the top of the IE7 window.
An alternative behavior, available via a checkbox in the Advanced tab of IE7's Internet Options dialog, is to have the Ctrl+Tab switch to the most-recently-used tab instead of the "next" tab. This is how the Alt+Tab application-switch keystroke works in Windows: pressing Alt+Tab once switches you to the application you were using most recently (not the application to the right of the current application's button in the Taskbar).
As I've blogged previously, I feel pretty strongly that the "most recent" tab switching order should be the default behavior for Ctrl+Tab. It's more powerful than the "left to right" behavior, as it can be used to quickly toggle back and forth between any two open tabs.
The reasons that Aaron gives for the "left to right" order being the IE7 default are:
- We value predictability over the more focused compare scenario.
- We want to be consistent with other apps that use tabs throughout the system.
Regarding the predictability issue: Let's go ahead and assume for the moment that the "left to right" behavior is in fact more predictable. However, I would expect that the Ctrl+Tab keystroke would be used most frequently by more experienced users. Less experienced users would probably be most likely to activate a tab by clicking the desired tab with the mouse. Given that keystrokes such as Ctrl+Tab will most often be used by "power users," I would argue that the fact that the "most recent" switching order behavior taking a bit more time to "get the hang of" is a lesser concern.
Regarding the issue of being consistent with the behavior of other applications, I went ahead and briefly tested a few other Microsoft apps that I have on my system. Of the applications I tested, Microsoft Excel was the only application that has a "left to right"-like behavior on a Ctrl+Tab press, in this case to switch between open workbooks. (And even this example is questionable, as Excel displays open workbooks in multiple Taskbar buttons, not in multiple tabs.)
On the other hand, Microsoft Visual Studio 2005 provides an excellent implementation of "most recent" order switching on a Ctrl+Tab press, switching between open windows which are displayed in tabs near the top of the window. Visual Studio even provides an "Alt+Tab"-like window that appears while the operation is in progress showing all of the available open windows that can be switched to.
As I noted in my previous post, the most prominent app that I use currently that by default provides "left-to-right" Ctrl+Tab switching order is actually Firefox! Having a better Ctrl+Tab order by default could be nice differentiator for Microsoft.
Given that "most recent" Ctrl+Tab switching order provides more powerful behavior, and that it will be primarily "power users" using the Ctrl+Tab keystroke, I would encourage Microsoft to reexamine the decision of whether to have the "most recent" behavior checkbox under IE7's advanced options be enabled by default.
I've just posted a new version, 2.0, of my Bounds Test utility. This utility allows the size and position of on-screen elements to be gauged by positioning the transparent Bounds Test window over the object to be measured; the Bounds Test window then displays its size and position.
New in version 2.0:
- The window border has been removed, allowing better visibility of items under the Bounds Test window, particuarly along the top edge.
- The tic marks that appear along the right and bottom edges of the Bounds Test window are now labelled.
- The Bounds Test window can now be repositioned (moved) by clicking anywhere in the window (except on a button or along the edges) and dragging.
- The mouse wheel can now be scrolled to resize the window.
A mandate was recently handed down in my development organization at work that all .dll files included in a hotfix should be set with a distinct value in their File Version field, so that the file can easily be identified as a hotfix file when examined (on a customer server machine) in the future.
While preparing a hotfix this afternoon, I decided to take this a step further, and set the defect number as the 4th item in the version tuple (leaving the first three tuple values as the version of the product being hotfixed), instead of just selecting and choosing an arbitrary value for the 4th value.
The defect number in this case was 96375. In Visual C++ 6, in the project with the COM component I was building, I set the FILEVERSION value in the VS_VERSION_INFO section of the project's generated .rc file to 96375.
However, when I built the .dll and looked at the File Version value, it was set to 30839. I guessed that the value I used might have exceeded the maximum allowed value and either been set to a constant max value of 30839 (although that number didn't immediately have any meaning to me), or else "wrapped" after hitting the maximum value.
A Google search didn't reveal any information about a maximum value for FILEVERSION, so I did some brief experimentation. It didn't take long to determine that the field is apparently a 16-bit integer, with a maximum value of 65535. Assigned values of 65536 or greater just "wrap" back around to 0. So a value of 65537 yields 1, a value of 65538 yields 2... and my value of 96375 yielded the value of 96375 - 65536 = 30839.
I settled for assigning a value of 9999 to the 4th item in the FILEVERSION tuple, figuring that would be a suitable "red flag" for anyone inspecting the version number later. I also appended the text "HOTFIX 96375" to the FileDescription field's value, so that the defect number would still appear on the .dll file's Properties dialog.
I've put a new command-line utility that I developed, ccc.exe, up for download on my utilities page. ccc is short for "clipboard character count". This simple utility returns the number of characters currently present on the Windows clipboard. The utility supports an optional -all switch to specify that the utility should return the length of all forms of text from the System.Windows.Forms.TextDataFormat enum, instead of just returning the count of plain text characters.
While working in Visual Studio 2005, I noticed a cool Autoformat-like behavior provided by the IDE.
I was working in a class that had a private class constant declared that I wanted to change to have public instead of private visibility. The original code looked something like:
private const int MaxItems = 5;
I used the ctrl+rightArrow keyboard shortcut to select a nearby instance of the keyword public, and then hit ctrl+c (Clipboard Copy), so I ended up with "public " (including the trailing space) on the clipboard.
I then double-clicked the instance of the "private" keyword that I wanted to replace to select it, and pressed ctrl+v to do the paste.
After the paste, the result was:
public const int MaxItems = 5;
This struck me as odd for some reason. After thinking about it briefly, I realized that I should have been left with an extra space after "public", due to the trailing space that was present on the clipboard:
public const int MaxItems = 5;
However, Visual Studio recognized what I was doing, and automatically removed the extra space for me, saving me a press of the Delete key after the paste. I tried pressing ctrl+z (Undo), and Visual Studio restored the removed space; a second ctrl+z press rolled back the Paste operation.
A very minor feature, but also indicative of the great attention to detail that Microsoft has put into the Visual Studio IDE. Pretty nice!
Earlier today, I was attempting to copy a large VMWare virtual machine image file to my test machine at the office. I was able to copy the compressed .zip file, about 3 GB in size, to the machine with no trouble. However, when I tried to use Winzip to decompress the file, I got an error: "skipping: [filename] this file is not in the standard Zip 2.0 format":
I had previously set up the same virtual machine image from the same .zip archive on another machine. I tried copying the uncompressed image file (about 6 GB in size) from that machine, but got another error, this time from Windows Explorer: "there is not enough free disk space".
I checked, and I did have only about 5 GB free. At this point I figured that the Winzip error might have been a symptom of the insufficient free space on the drive, with the weird error text possibly being attributable to the large size of the file.
I cleared some large files off the drive -- mostly old SQL Server databases and old Virtual PC images -- and got up to over 20 GB of free space. However, when I retried the copy operation, I still got the same error from Explorer about insufficient free disk space.
A quick Google search on that error revealed that the problem might be that if the test machine was running the old FAT32 filesystem instead of the modern NTFS system, that error message could result, as FAT32 supports file sizes only up to 4 GB. I also noticed that Winzip was reporting the uncompressed size of the virtual machine image file as 4 GB instead of the correct 6 GB, which seemed to be a symptom consistent with the .zip archive file being stored on a FAT32 drive. The test machine was indeed running FAT32 (checked by opening My Computer from the desktop, right-clicking the C drive, and selecting Properties from the context menu) and I had no reason not to upgrade it, so I went ahead and ran convert.exe to do the upgrade to NTFS.
When the upgrade to NTFS eventually finished, I once again tried to copy the uncompressed machine image across the network to the test machine, but the network itself decided not to cooperate at this point, and the transfer was progressing extremely slowly (even given the 6 GB file size). So at that point I turned my attention back to uncompressing the image from the .zip archive, figuring that it would now work since the filesystem was now NTFS and the disk had sufficient free space.
To my surprise, I continued to get the same error about "Zip 2.0" format. I also noticed that Winzip was still reporting the uncompressed VM image file size as 4 GB instead of the correct 6 GB. Earlier I had ignored the www.winzip.com/zip20.htm URL mentioned in the Winzip error message, assuming it would be similarly unhelpful to the Microsoft site links that Windows error dialogs that come up when an app crashes occasionally provide, but at this point I figured it was worth a look.
The text on the linked page isn't terribly helpful, mostly text encouraging an upgrade to Winzip 10 (which isn't a free upgrade even for registered/paying users of Winzip 9 and previous versions). However, it does mention something about a particular compression method, "bzip2", requiring Winzip 9 or later.
At this, I checked the Winzip version numbers (on the app's Help | About menu) on both of my machines. On the machine where I had previously successfully uncompressed the large virtual machine image file, Winzip 9 was installed; on the test machine where I was having trouble, Winzip 8 was installed. Aha!
I upgraded the test machine to Winzip 9. Once that was done, I was able to uncompress the image file from the .zip archive with no trouble.
So, on the "this file is not in the standard Zip 2.0 format" error from Winzip, the thing to do is to upgrade to Winzip 9 if you're running an earlier version. It took me a while to get that figured out, but at least I had a reasonable excuse to spend a little time to get the disk on my test machine cleaned up and upgraded to NTFS along the way. :-)
On the Windows platform, there are several icons that can appear on the buttons at the upper-right corner of an application window that have a well-defined, standardized functionality. Examples include a horizontal line to minimize the window, a single rectangle to maximize the window, and, perhaps most commonly, an "x" to close the window.
In most cases, when then "x" button is clicked for the main window of an application, the expected behavior is that the button click closes that application. However, for some applications, a click on the application window's "x" button, instead of closing the application, instead performs a "minimize to system tray" operation. Applications that display an icon in the Windows system tray (or "notification area") often (but not always) have this behavior. Microsoft ActiveSync is an example of such an application:
In general, since the "x" button cannot be used to close (exit) such applications, users wishing to close the application must employ some other (non-standardized) method, such as a menu pick in the application window, or a menu pick in the right-click menu of the app's system tray icon. (Some system tray apps don't allow themselves to be closed at all short of manually killing their process, but that's a separate issue!)
It would be nice if a new standardized button icon were used in place of the "x" icon for the "minimize to system tray" functionality. I propose using a small horizontal line in the lower-right corner of the button; this would follow the convention of the left-aligned horizontal line that appears at the bottom of the standard "minimize" icon. Here's a quick sketch I put together in mspaint.exe:
When appropriate, application designers could use the new "minimize to system tray" icon alongside a standard "x" icon, allowing users to easily either minimize the app to the tray or exit it, as desired. This would be an improvement over the current state of affairs where designers must choose which of two standard behaviors -- "close" or "minimize to tray" -- to assign to the "x" button of the main application window.
Today I attended the Microsoft Security Summit 2006 conference in Novi, Michigan. It's a free conference that Microsoft has been putting on in various cities targeted at both developers and IT folks promoting good security practices.
Keynotes
Both keynote speakers were very good. The opening keynote by Bret Arsenault was information-packed. Bret gave an overview of the history of security challenges in the IT industry along with solid coverage of the challenges facing the industry today. The hour length of the keynote flew by quickly, a sure sign of an interesting session.
The closing keynote by Jesper Johansson, covering security improvements in Vista, was also (to invoke the cliche) entertaining and informative. I've been subscribed to Jesper's blog for a while now, so it was cool to hear him speak in person (particularly since he turned to out be a good in-person presenter as well as a good blogger). I didn't recognize Jesper by sight, as he wasn't wearing his scuba gear. :-) Jesper made a wry observation about why security can be a hard sell to corporate executives: It's a significant financial investment which, when finished -- if done properly -- will result in everything continuing to work as it has before!
Jesper also made a comment which makes good sense from a security perspective, but perhaps not from a "real-world" perspective. He asserted that any application which either requires administrative privileges to install and run, or writes per-user data to shared areas of the filesystem such as under Program Files or the Windows folder, is "broken." Jesper mentioned Adobe's Photoshop as an example of such an app.
I agree that such applications are certainly less than ideal, but for many software shops, the time and money to engineer apps to run well not just on all currently-available versions of Windows but on potential future Windows versions as well is a luxury that can't always be afforded. Not everyone has the cash in the bank to support the delay and re-delay of product releases to ensure that functionality that does not directly impact the user experience is perfect, like Microsoft is attempting with Vista. Still, the ideal of applications that are engineered superbly from both a features and security perspective is a worthy goal for developers.
Break-Out Sessions
One of the afternoon sessions focused on some types of attacks to which web applications are susceptible, such as SQL injection. I had heard variations of this session a couple of times before, including once at the 2003 PDC conference in Los Angeles. This session included one particularly nasty attack which was new to me, though, called a "one-click" attack. (The attack name brought to mind the infamous Amazon patent of the same name, although it turned out to be unrelated.) This attack involves coding a hidden form into a simple .html page which, when visited, invisibly auto-submits a request to another site to which the victim had recently logged in.
The example of the attack demonstrated was an employee visiting a (deceptively) simple "car for sale" web page on the internal corporate site, which modified the target account/routing number for the user's paycheck direct deposit via the recently-visited internal HR site. Dangerous stuff. (The solution involves setting up a key based on the session ID that is required to be submitted along with the change request, using an ASP.NET 1.1 ViewStateUserKey or other means.)
The second afternoon break-out session covered new features in Visual Studio .NET 2005 and SQL Server 2005 that support development of secure applications. One new SQL Server feature mentioned was EXECUTE AS, which uses impersonation to enable subsequent queries or commands to be run under the security context of another user. Impersonation is ended when a REVERT is run.
During the Q&A at the end, I asked whether nested EXECUTE AS operations were supported; in other words, if I do, in order:
EXECUTE AS 'Alice'
EXECUTE AS 'Bob'
REVERT
Then who am I running as at that point -- Alice, or the original user? The presenter wasn't sure. I looked into this briefly, and apparently (per this Bob Beauchemin blog post) nesting is indeed possible.
Also during the last afternoon break-out session, developers sitting in the first 5 rows (plus those who were willing to move up to the front from the back) got a free copy of the "Required reading at Microsoft" book Writing Secure Code, 2nd Edition. Happily, I was sitting in the 5th row. :-) I did partially read through a copy of this a couple of years ago, shortly after its release; now, with another couple of years of experience under my belt, I plan to give the book a more thorough read-through.
This past Thursday, moderately severe thunderstorms swept through much of the region, including here in Michigan. The power flickered on and off at least twice during the storm. My home web server machine was on at the time, but it seemed to weather the storm okay.
Until Friday, that is. Melissa went to use the machine, and although it appeared to be on (power light on the case on, internal fan audibly running), the monitor was showing its "no signal" graphic.
Power was definitely on to both the monitor and CPU, and the video cable was snugly connected. At first I suspected that the video card was fried, but when I tried rebooting the machine, there was no beep from the speaker, and Windows never seemed to boot as I couldn't contact the machine over the network in any way (via ping, http, or Terminal Services). I concluded that the motherboard had been damaged by the storm -- if the problem was with another component, I would expect to at least hear a series of error beeps after the machine powered on.
Fortunately, I was able to disconnect the hard drive and set it up as a slave on my other machine, and get all of the important files transferred onto the machine's primary hard drive. (I also had the important files backed up on an external USB drive, but this saved me the trouble of doing a restore.)
I've set up my remaining machine as the new web server. So, all you legions of jonschneider.com fans (read: the 8 or 10 unique visitors a month that I get coming in mostly from Yahoo! Search to look at my old Warcraft III maps page...) can celebrate -- the site is up and running again.
I also needed to re-register ASP.NET with IIS, set up MySQL as a WinNT service, and install MyODBC to set up a MySQL DSN to restore functionality of an internal web application that I have running on the server.
Melissa and I have decided to wait for now on getting the old server machine repaired or rebuilt. This seems like a good excuse to upgrade the motherboard and processor, but with our son Elijah (13 months old today!) around these days, we've found that we both want to be using a computer simultaneously far more seldom than we used to. And if we wait, when we do get a new motherboard and processor later, we'll be able to get more power for the money. Possibly we'll rebuild the server machine with Vista sometime after it is released.
At the office recently, I was asked to whip up a simple Windows Forms UserControl with a public method and run it embedded in an HTML page in Internet Explorer. "No problem," or so I thought at the time -- I had created similar simple controls in the past, and one of the components that I have primary responsibility for at work is an Excel-like Windows Forms spreadsheet control that is deployed embedded in web pages and run in Internet Explorer. (More information on running Windows Forms controls in IE in this manner is available at GotDotNet .)
I had the control up and running in a web page in just a couple of minutes:
- I opened Visual Studio 2005 and created a new C# Class Library project.
- Added a new User Control to the project, "SimpleUserControl".
- To ensure that the control would stand out on the web page, changed the background color property of the control to Blue, and added a label to the control with the text "SimpleUserControl".
- Added a simple public method to the control:
public string WhatTimeIsIt()
{
return DateTime.Now.ToString();
}
- Compiled the project, and copied the generated SimpleUserControl.dll to a folder under my IIS root directory.
- Quickly typed up a simple HTML page in the same folder to show the control and demonstrate a call to the public method:
<html>
<body>
<object id="SimpleUserControl"
classid="http:SimpleUserControl.dll
#SimpleUserControl.SimpleUserControl"
height="150" width="150" VIEWASTEXT>
</object>
<input type="button" value="What time is it?" onclick="DoWhatTimeIsIt();" />
</body>
<script language="javascript">
function DoWhatTimeIsIt()
{
alert(document.SimpleUserControl.WhatTimeIsIt());
}
</script>
</html>
I brought up the web page in IE and the SimpleUserControl displayed correctly, as expected. Unfortunately, clicking the button on the page, instead of returning a string with the current date and time, generated a Javascript error: "Object doesn't support this property or method".
I double-checked everything I'd done. (No, I hadn't misspelled the method name in the Javascript code... Yes, I really had declared the method to be public in the C# class...) Another one of the senior guys on the team plus my boss (who was the lead developer on the team prior to his promotion) also puzzled over this problem for a while without being able to figure it out.
Finally, my boss came up with the solution: In Visual Studio 2005, in the project properties, on the Application tab, in the Assembly Information dialog, the "Make assembly COM-Visible" checkbox (which was unchecked by default) needed to be checked. After doing this (and rebuilding the project, re-deploying the .dll file, and reopening the IE window), the button could successfully call the public method.
I was pretty surprised by not having run across this issue before. Apparently, though, in Visual Studio 2003 there is either no equivalent checkbox or it is checked by default (and thus similar controls which I had previously created, which worked fine, must have been created using VS 2003). Also, Visual Studio's migration wizard which upgrades VS 2003 projects to VS 2005 projects apparently checks the checkbox by default, which is why we didn't run into this issue after upgrading our product to use VS 2005.
After my development team at work switched to developing using Visual Studio 2005 and the 2.0 .NET Framework, we encountered a problem trying to debug older versions of our Windows Forms controls running embedded in web pages viewed with Internet Explorer with Visual 2003. We would open our project or solution in Visual Studio 2003 and start the debugger to launch Internet Explorer, but shortly after the page with the Windows Forms control would start loading, Internet Explorer would just close with no error message, and the debugging session would end.
Another symptom of the same problem occurred when we instead would try to first open an Internet Explorer window normally and navigate to the web page containing our Windows Forms control, which would load correctly. Then, when trying to attach Visual Studio 2003 to the iexplore.exe process for debugging, Visual Studio would just fail with the error message "Unable to attach to the process."
We eventually determined that the problem was because Visual Studio 2005 along with the 2.0 .NET framework was installed on the development machine, Internet Explorer would load the Windows Forms control using the 2.0 framework (instead of the 1.1 framework as it had previously). Then, when Visual Studio 2003 (which uses the 1.1 .NET framework) tried to attach to the managed code in the iexplore.exe instance for debugging, it would (apparently) get confused and just fail.
The solution we came up with was to use a special iexplore.exe.config file to force IE to load controls using the 1.1 version of the .NET framework. To use this solution, open up a new text editor window and paste in the following text:
<configuration>
<startup>
<requiredRuntime version="v1.1.4322"/>
<supportedRuntime version="v1.1.4322"/>
</startup>
</configuration>
Then, save the file as "iexplore.exe.config" in your Internet Explorer folder (typically C:\Program Files\Internet Explorer). Finally, close and reopen any open Internet Explorer windows.
This will force Internet Explorer to use the version of the .NET framework specified in the file (in this case, 1.1.4322, the version number of the commercial release of the 1.1 framework) for any Windows Forms controls it loads.
To have IE revert to the original behavior of using the default version of the framework to load Windows Forms controls, just delete or rename the iexplore.exe.config file. (On my dev machine, I leave a copy of the file in my IE folder named "iexplore.exe.config_forcev11" for easy access when I need it.)
Specifying Windows file paths in C# code can result in some messy-looking string literals, due to the need to specify "\\" as each backslash character to avoid having a single backslash character be interpreted as the beginning of an escape sequence. For example:
string myAppPath = "C:\\Program Files\\MyApp";
(For the purposes of this post, I'm ignoring the fact that assuming that "myApp" will always be located at "C:\Program Files\" may not be a good idea.)
A way to make this look cleaner (and possibly be less confusing) is to use a '@' character prior to the string literal. The '@' character makes C# skip looking for any escape characters in the string (with the exception of the \" escape sequence). This use of the '@' character before a string literal is called a verbatim string literal. The file path declaration above, revised to use a verbatim string literal, would be:
string myAppPath = @"C:\Program Files\MyApp";
For more details on verbatim string literals, see the string literals page in the C# Language Specification.
Also, the Visual Studio IDE supports displaying verbatim string literals in a different color than standard string literals, for further clarity. I have my Visual Studio installations configured to display standard string literals in off-white and verbatim string literals in red.
Microsoft's Visual SourceSafe (VSS) version control utility does not offer a built-in way to search the history of a file or project/folder. For example, you can't directly search on the check-in comments of a particular file for a bug number to find the file version where that bug was fixed.
However, it is possible to generate a report of all changes to a file or project, open that report in a text editor (such as Word or Notepad), and then perform the search in the text editor application.
To generate the report, first bring up the History dialog for the file or project to search (specifying date and user constraints if desired). Then, click the Report button on the History dialog to bring up the History Report dialog. (It doesn't matter what file(s) are selected in the left pane of the History dialog; the report will be generated based on all items present in the dialog.)
On the History Report dialog, you can select the items you want to appear in the report. I typically choose only "Include details", since the "Include differences" option can make the report take a long time to generate (even for a fairly small file/project history). I also typically use the "Clipboard" output option to make the output easy to paste into my text editor.
Pressing Alt+Space will for most windows activate the "top-left corner menu" for the window. There are a few useful tasks that can be performed from this menu using the keyboard that generally otherwise could only be performed by using the mouse.
In a command prompt window, Alt+Space, e, p -- Performs a clipboard paste. This is handy to perform a quick paste at the command prompt without needing to take your hands off the keyboard. (A Paste can also be performed in a command prompt window by right-clicking the window if "QuickEdit Mode" is enabled (in the Properties dialog), or by right-clicking and then selecting Paste from the context menu if QuickEdit is disabled).
Alt+Space, n -- Minimizes the current window. Also, Alt+Space, x will maximize the window, and Alt+Space, r will "restore" the window to its original size after it has been maximized.
Alt+Space, m -- Puts the current window into "move" mode. In this mode, you can move the position of the current window with the arrow keys. This can be useful for retrieving a window which is completely off the current screen (if you had multiple monitors connected and then disconnected the monitor on which the window was located, for example).
I've been continuing to have trouble with the wireless functionality of my home Linksys router, a model BEFW11S4. As I posted previously, I've never quite had the wireless connectivity with my PDA (a Dell Axim X30 PocketPC) working the way I wanted. Sometimes I activate the wireless connection on the PDA and it works perfectly; other times I try to connect, and although the PDA can see the router (with the correct SSID and excellent signal strength), it will never get a valid IP address. (I have the PDA set to get an IP via DHCP; apparently the router is just never assigning the PDA an IP address.)
I did end up upgrading the router firmware to the latest version (1.52.02). This seems to have resolved the problem with the router's DHCP server where it was never reusing IP addresses (and thus eventually running out), but now the router is just kind of acting kind of flaky; sometimes the PDA will get an IP address and be able to connect to the Internet immediately, sometimes the PDA will get an IP after a long delay (several minutes); and sometimes the PDA will just never get an IP. Since the PDA does occasionally connect successfully, that tells me that the issue is probably not with my security configuration.
Further, occasionally if the PDA is connected but then briefly loses contact with the router, it will lose its IP and its connection. This happens even after I've just been using the wireless connection for a short time (so the PDA's lease on the IP address should not yet have expired).
At least once, when the PDA has been unable to connect, if I cycle the power on the router (unplug it and then plug it back in), the PDA is then immediately able to connect with no problem.
My DVR was also unable to connect to the wireless network for a while yesterday -- I also resolved this by cycling the power on the router. This seems to indicate that the problem is with the router, not with the configuration or hardware of my PDA.
I finally decided to throw in the towel and forget about using DHCP with my PDA, and instead just assigned it a static IP address. This seems to be working fine; I haven't had any connectivity issues with the PDA since assigning the static address. Also, I didn't have to enter DNS server information on the PDA to be able to connect to the Internet successfully; just entering the static IP and the gateway (router) IP was sufficient. This is good, since it should keep the hassle of switching between a static IP (when I'm using the PDA at home) and a dynamic IP (when I'm anywhere else) to a minimum.
I poked around for a solution on Usenet briefly (searching for "BEFW11S4 DHCP" on Google Groups), and although I saw some posts about people having trouble with this model router and DHCP, I didn't see any good solutions. One person reported exchanging their BEFW11S4 for another copy of the same model, and they didn't have any problems after that, so maybe the DHCP functionality on these models is hit-or-miss.
In any event, I'm glad I have a decent workaround for the issue for now. Next time I go shopping for a wireless router, though, I'll probably look at brands other than Linksys.
Update 5/15/2006: Unfortunately, my workaround of using a static IP address on the PDA isn't as convenient as I thought. Initially, as noted above, I thought there was no need to enter DNS server information on the PDA; it had appeared that the PDA was picking this information up from the router. However, the PDA is reporting "Site Not Available" errors for any site I hadn't visited recently; appearently, DNS mappings for recently-visited sites were cached on the PDA, so things appeared to be working ok at first as I tested visiting sites that I had visited recently.
A further annoyance is information I manually enter when setting the PDA to use a static IP (the IP address, subnet mask, gateway IP address, and now primary and secondary DNS server IP addresses) gets cleared whenever I set the PDA to use a dynamic IP (when I'm online with the PDA away from home), so I have to re-enter all of that information whenever I return home. (This is the fault of the PDA software, not the router, but it does compound the problem with the router's DHCP service being so flaky.)
I may have to keep tinkering with the router to see if I can get the DHCP service to where it "just works".
Oftentimes, after installing some new software application that uses an InstallShield wizard (or similar tool) for its setup, a prompt to reboot the machine appears, stating that the new application can't be used until the reboot is performed. I've asked myself the question: Do I really need to reboot my machine to use the program?
I came across a utility that helps answer this question. At least in some cases, the reason that the install program wants to reboot the machine is that a file that it tried to write during the install couldn't be written because it was already in use by another process, so the installer sets up a task to write the file the next time the machine is rebooted (at which time the target file should be unlocked because the process that was locking it before was cleared during the reboot).
The PendMoves command-line utility from Sysinternals displays a list of these "pending move" operations. By running pendmoves and looking at the results, you can get more information to help decide whether a reboot is really necessary before running the app. For example, if the only file that is pending write is a help file, it's likely to be safe to run the application even without the latest version of that file installed yet.
I'm not sure how well this technique works with Windows updates from Microsoft, but it has worked well for me for standard software application installs.
I was looking for a way to expand and collapse Visual Studio .NET regions, and found one today on Visual Studio Hacks; Ctrl+M, Ctrl+M (chord key combination) expands or collapses the current region. This works on both user-defined regions (from #region and #endregion) and on regions that the IDE itself defines, such as class/method XML comments.
Another Visual Studio .NET keyboard shortcut/feature (also appearing on the linked Visual Studio Hacks page) which I use a lot is Ctrl+Shift+V. Like the standard Ctrl+V supported by most apps, this does a Clipboard Paste, but if you continue to hold down Ctrl+Shift and press V again, the text that was just pasted is replaced with the next-most-recently Copied text. This feature is known as the Clipboard Ring.
I found another bug with Notepad.exe earlier today. When Notepad has word wrap turned off, an entire long (longer than the window width) line of text is selected, and the window is scrolled all the way to the left, then when the Notepad window loses focus, it automatically (and unexpectedly) scrolls all the way to the right.
This means you can't have two Notepad windows on the screen, each with a long line of text selected, and each scrolled to the left, at the same time. This is inconvenient when trying to use side-by-side Notepad instances to compare a line of text in two similar files (with the line being compared selected in each window to make that line stand out).
To save myself from typing "otepad" a few times a day, I put together a simple batch file, "n.bat", that would just run notepad.exe with the specified command-line arguments (if any). The file "n.bat" consisted of just a single line:
@notepad.exe %1 %2 %3 %4 %5 %6 %7 %8 %9
(The leading "@" character just causes the command not to be echoed to the command prompt window, which makes things look nicer; the "%1" through "%9" are the environment variables for the first 9 command-line arguments, which should be more than enough for any scenario that I'd ever use Notepad for!)
This worked fine, except that the command prompt window would wait/hang until the Notepad instance exited. (When n.bat was run from the Start | Run dialog, an empty Command Prompt window would appear until Notepad exited.)
To resolve this problem, I used the start command:
@start notepad.exe %1 %2 %3 %4 %5 %6 %7 %8 %9
While looking at the MSDN documentation page on the CString::Find() method (C++ MFC), I noticed some information that appears to be incorrect.
The signature of the method is:
int Find( TCHAR ch, int nStart ) const;
The comment on the nStart parameter is:
The index of the character in the string to begin the search with, or 0 to start from the beginning. The character at nStart is excluded from the search if nStart is not equal to 0.
(Emphasis added by me.) If the bolded part of the statement is true, is it just me, or would it be impossible to use the Find method to search a string beginning with the character at index 1? :-)
I tested the behavior of the method in a simple C++ console app using Visual Studio 2005. The character at nStart is indeed always included in the search, even if nStart is nonzero.
I reported this issue to Microsoft via their web feedback form, and got a reply that the issue has been forwarded "to the appropriate Microsoft group for review." We'll see if any changes are forthcoming!
Earlier today, I installed the Google Toolbar v2.0 beta update for Firefox. I've noticed a couple of problems since installing it:
Alt+g
The Alt+g keyboard shortcut to focus the search field on the toolbar no longer works. I can see why this was missed during testing because for most Firefox users, Alt+g opens the Go menu. However, in my copy of Firefox, I have the Go menu disabled, which I did for the specific purpose of allowing Alt+g to work with the Google toolbar instead (as it does in the Internet Explorer version of the Google toolbar)!
I reported this issue to Google via their contact form for the Toolbar, so hopefully they'll fix it in a future update.
Update 4/7/2006 8:30pm: I got a response back from the Google team via email. They let me know that Alt+s works in Firefox to focus the search field. I hadn't previously been aware of this; it sounds good to me, since Alt+s is even easier to reach on the keyboard with my left hand than Alt+g! Now I'll just have to train myself to use it...
It is kind of unfortunate that the Firefox and IE versions of the Toolbar use different keyboard shortcuts; I wonder if IE could be made to use Alt+s as well for consistency.
Publishing Posts on Blogger
In my previous blog post that I made earlier today after installing the Toolbar update, the post wouldn't publish properly; when I clicked the Publish button, the browser just got stuck in a loop trying to publish the post; the post showed up in my list of posts in the Blogger "control panel", but it didn't actually show up in the blog itself.
I ended up opening up IE instead and publishing the post from there, and it worked with no problem.
I'm not sure yet whether the Google Toolbar update is somehow to blame for this problem. We'll see how publishing this post goes!
Update: Publishing this post from Firefox with the new Google Toolbar installed worked with no problem. Looks like maybe it was just a temporary issue with the Blogger site itself earlier today, as I haven't made any other changes on my machine since then.
I ran into an error when using caspol.exe (a command-line tool provided with the .Net Framework that can be used to establish trust with a particular URL, among other things) to try and establish trust to a specific IP address:
ERROR: Names must not begin with a number
At first, I thought that caspol just couldn't be used to establish trust to an IP adddress (as opposed to a named server). However, the problem turned out to be that I was using the -name command line option to try give the new code group the same name as the IP address I was establishing trust with -- and this is what the error message was complaining about.
So the resolution is to either use a name that doesn't start with a numeric character, or to just leave the -name parameter off the command line entirely (which will result in an unnamed code group).
The application that I develop at work runs in a client/server environment where the server is a Windows machine running IIS. I tend to reset the IIS service on the development server reasonably frequently, as it's a quick and easy way to clear any data that has been cached in memory on the server.
I used to start and stop the IIS service when necessary by going through Start | Control Panel | Administrative Tools | Services, selecting the IIS Admin service, and clicking the "start", "stop", or "restart" button on the toolbar as necessary. Eventually I decided that going through this multi-step procedure every time was too slow, and went looking for some ways to accomplish the same tasks from the command line. The shortcuts below work with IIS 5, and I believe they also work with IIS 6.
iisreset
This resets the IIS Admin service, including the World Wide Web Publishing service.
iisreset /timeout:0
This resets the IIS Admin service faster. According to the help information (iisreset /?), iisreset normally might wait up to 20 seconds to wait for a successful stop of internet services before the restart is performed. The timeout:0 parameter causes the reset to be performed immediately.
net stop iisadmin /y
This stops the IIS Admin service and dependent services (including the WWW service) without restarting them. The /y parameter skips the confirmation prompt that would otherwise be displayed.
net start w3svc
This starts the WWW and IIS Admin services after they were previously stopped.
On my Pocket PC, my text entry method of choice is the Block Recognizer -- a clone of the Grafitti text input method from Palm PDA devices. My first PDA was a Palm Pilot (the Pocket PC being my 2nd and current PDA), so I'm on familiar ground entering text with Block Recognizer.
One gripe I've had for a while is that I didn't know of an quick way to cancel the punctuation "shift" state if I put Block Recognizer into that state by accident. Block Recognizer has a input combination method of entering punctuation characters where you tap the text entry area once with the stylus, then do a pen stroke and the app will interpret the stroke as a punctuation character instead of a letter or number. (For example, a tap followed by a vertical top-to-bottom stroke is interpreted as a singlequote instead of the letter "i".) Grafitti allows you to cancel the "shift" state with a "backspace" pen stroke, but unfortunately, Block Recognizer interprets the "backspace" as a "-" character when in shift mode instead of cancelling the shift mode. (Tapping the screen once more doesn't work to cancel shift, either -- that's a "." character.)
I accidentally happened around a workaround for this earlier tonight. I noticed that when in shift mode, the dot that indicates shift mode is active appeares in only the "letters" or "numbers" portion of the input area, but not both. I found that if I want to cancel shift mode in the letters portion of the input area, I can tap in the numbers section -- this moves the "shift" dot from the letters aera to the numbers area -- and then proceed to write normally again in the letters area.
This will hopefully work out to be a better solution than my old method of cancelling shift than my old method of writing any stroke, backspacing it, and then continuing, which I found to be really disruptive to my flow of writing when in the middle of a sentence and trying to write quickly (such as when taking notes during a presentation or lecture).
Now I just need to figure out my other main problem with Block Recognizer -- my low accuracy rate, particularly when it comes to the software misinterpreting my entry of a standard a-z letter character as an "international" character. The "Å“" ("oe") character is one that comes up particularly often. I wish there were a way to disable recognition of all characters except the standard a-z and 0-9 when not in one of the shift modes.
In the past, I've run into a problem where when trying to debug a .Net Windows Forms application running embedded in an Internet Explorer window in Visual Studio, breakpoints will not be hit in the debugger. I've seen this issue myself in Visual Studio 2002 and 2003; a colleague just told me that the issue is apparently present in VS 2005 as well.
The solution is in the Project Properties dialog/pane, on the Debug tab, instead of checking the "Start browser with URL" radio button, the "Start external program" radio button needs to be checked instead. In the text field for "Start external program", enter c:\program files\internet Explorer\iexplore.exe (or wherever your local copy of iexplore.exe is located).
Optionally, you can enter the URL of the web page that you want to be initially loaded in the browser in the "Command line arguments" field.
Another thing to verify is that the <object> tab in your web page is pointing to the current debug version of your application's .dll file.
Once that is done, you should be able to successfully hit breakpoints when debugging the application running in the browser.
|
|