Are you thankful?

Fri, December 3, 2004, 02:59 PM under dotNET

Threading blah blah

Thu, December 2, 2004, 03:00 PM under Events
Just came back from the local VBUG meeting session, where Benjamin Mitchell presented an introduction to .NET threading. It went down well but, every time I see someone presenting threading topics (and I have seen a few), I always think this stuff is hard to explain to others (it's hard enough knowing how to use it, but teaching it takes the biscuit).

The second thought that crossed my mind (besides that I should finally volunteer to run a session or two at VBUG), was how lucky the desktop guys are with all those resources in the Threading namespace (I cannot use them in my full Fx projects, as virtually all my desktop code runs on the CF as well).

He mentioned in passing how Whidbey makes life easier and that got me thinking: I have blogged about threading topics before but how much easier would it be to explain the usage of BackgroundWorker? I guess that has to be the subject next time.

Satellite Assemblies

Wed, December 1, 2004, 12:48 PM under MobileAndEmbedded
So we looked at how you change the language in a Compact Framework scenario and how we can read the current locale. The most popular reason for which users change language, is to read the text of your UI in their own language (profound statement or what? :-)

The .NET way of doing that is using RESX files and resource dlls (a.k.a. satellite assemblies). The process is very similar to the desktop scenario, but the file formats are incompatible (and that will be the case for CF 2.0 too). There is a lot of info on using RESX files, so I won't go into any detail here. (In a nutshell, in the designer you can set the Localizable property of your forms to true and then change the strings once per language; this creates a RESX file per form per language and, on compilation, all RESX of a language are rolled into a resources dll. When deploying your app, place each .resources dll in its own folder under your app's directory, naming each directory as per the two-letter language code. At runtime, the CLR picks up automatically the system's locale and hence which resources dll to load and life is goodness).

Note that the SatelliteContractVersion attribute is supported at compile time but does not work at runtime so don't use it. Essentially you must deploy all your satellite dlls with your exe every time you roll the version (unless the only change is the revision in which case they are considered compatible as per the CF rules).

Also note that, if you need additional strings for translation (e.g. MessageBox strings), they have to reside in their own resx file (that you create manually and must include as an embedded resource). To retrieve these you need to explicitly use the ResourceManager (i.e. like this).

Finally, you may find the CFResgen tool useful. It allows for conversion between txt/resx/resources files (tip: save your txt file as utf-8).

So I leave you with my approach:
I do not use the RESX files created by the forms, and in fact I leave Form.Localizable to false. Instead, I maintain one text file of name/value pairs and whenever I need a string in my app I add it to the txt file. The implication is that I set the Text for all controls outside the InitializeComponent method (ctor or OnLoad as appropriate). I then run cfresgen on the text file to get a single RESX file, which is the one included in my project as a resource. I send it to the translators and they send me back a translated RESX file that I also add to my project; from those, the compiler spits out the satellite assemblies. Note that our translators use a localisation tool that works great with RESX files, but we had one who did not have access to the tool, so sending them the txt file and then performing the conversion to RESX on our end worked out great. The attraction to the aforementioned method is the simplicity of dealing with a single txt file from which everything else stems.

CultureInfo

Tue, November 30, 2004, 11:45 AM under MobileAndEmbedded
One of the FAQs on the CF newsgroup is regarding changing the Thread.CurrentThread.CurrentCulture. The answer is that it is not supported. Instead, the user must change the language on the device and restart your application for changes to take effect [Not restarting the application is possible by reloading ALL resources but the approach is dirty and is as good as restarting the app anyway, so there is no point going through the hassle to do it - trust me].

If your app is running in kiosk mode (or for whatever other reason you wish that the user changes the language through your UI), it is easy to set a dialog up for the said purpose. Just list the languages in a combobox associating with each comboxitem a LCID; when the user selects the language and confirms their selection, you have to change the system language via the SetUserDefaultLCID API (and restart your app). Some devices support MUI, so in those cases you also have to call the SetUserDefaultUILanguage API (and reset/restart the unit).

To finish the story on language change, on our platform I don't go through the APIs but instead apply the changes via the registry (the API is the way to go, but I am not doing that for our own historical reasons - not relevant here). In case it helps someone, these are the registry entries (shouldn't be too different to others):
-HKLM\nls\DefaultLCID
-HKLM\nls\overrides\LCID
-HKLM\SOFTWARE\Microsoft\International\LCID
-HKLM\MUI\SysLang
-HKCU\MUI\CurLang
As you'd expect, I set the LCID in each one of these DWORD values, flush the registry (RegFlushKey) and perform a soft reset (KernelIOControl\IOCTL_HAL_REBOOT).

After your app restarts you may want to know what the locale is. You may do this through CultureInfo.CurrentCultre (and CultureInfo.CurrentUICultre).

Note that sometimes you just need to perform some formatting based on a locale other than the system-wide one e.g. the French decimal point is comma "," and you may want to parse a string containing doubles that come from a network where dot "." is the decimal point. In those cases you have to use overloads of the framework's methods that can accept a CultureInfo (or any other IFormatProvider object) as a parameter, e.g. Double.Parse or DateTime.Parse

Next we look at resources in satellite assemblies.

QFE 4.2 - CF update

Mon, November 29, 2004, 12:52 PM under MobileAndEmbedded
This just appeared:
http://www.microsoft.com/downloads/details.aspx?familyid=e0e66c77-dee2-4aba-9623-a3bfff434b5c&displaylang=en

CF version number appears to be: 1.0.4292.0

Fixes made (from the readme):
This update addresses the following issues:
a.. Transitions between managed and native code may cause memory leaks on ARM platforms.
b.. A NullReferenceException may occur when a Web Method returns an empty array using the xsi:Nil attribute.
c.. Modifying the SoapClientMessage.ContentType property does not modify the Http requests ContentType header.
d.. Stack corruption may occur on SHx, MIPS and x86 platforms when local variables are created but never used.
e.. Invoking a multicase delegate from a catch handler may cause a MissingMethodException on SHx, MIPS and x86 platforms.
f.. Command line arguments containing double byte characters may get truncated to a single byte.
g.. An ObjectDisposedException may occur when a asynchronous web request is aborted before the response is received.
h.. Invoke on a disposed control may hang the application.
i.. An array containing one or more elements may be sent to the Web Service incorrectly.
j.. An application may hang when invoking a Web Method that uses multiple XmlElementAttributes on a single argument, member or property.
k.. Memory corruption may occur on devices that have the native security model enabled and both .NET CF V1 SP3.
l.. Deadlocks may occur when running under severe resource constraints.
m.. The issue of the Tool Bars loosing their image on Windows Mobile 2003 SE when removed from the form is addressed by this update.
n.. An ObjectDisposedException may occur when the server closes the socket connection.
o.. Setting the Minimum and Maximum properties of a progress bar may crash the application.
p.. An exception may occur when adding an image to an imagelist on an Hx4700 and Hx4705.
q.. Data misalignment may occur on decimal fields in MIPSIV devices.

Blog link of the week 48

Sun, November 28, 2004, 12:15 PM under Links
Giagnocavo cracks code!

Jared Parsons mixes Generics and Singleton. I wonder how many patterns can be rewritten using generics... anyone working on that?

Over at Saurabh Verma's blog there is a pretty picture showing a memory representation of a reference type holding a value type. Hopefully we'll get more pretty pictures of more complex scenarios.

Surprise Delivery

Fri, November 26, 2004, 03:03 AM under Random
Got a nice surprise through the post today:
.NET Compact Framework Programming with VB.NET

As I said on my Amazon review of it (the C# version), I had read the draft but didn't actually own the released version.

Further surprise followed when seeing my name in the acknowledgments (page XXXVII) and even more so with my quote on the very first page (3rd one down)!

Someone said "fame at last", but I already said that when I was first (knowingly) acknowledged in the VB6/VB.NET Design Patterns book :-)

mouse_event

Thu, November 25, 2004, 02:46 AM under MobileAndEmbedded
Assuming you read ToolBarButton BUG and ContextMenu.Show...

Let's look at the real solution. The title gives it away really: PInvoke mouse_event. The API is pretty straightforward, but you must RTFM and in particular the bit where it says "...dx and dy contain normalized absolute coordinates between 0 and 65,535".

The DllImport is very easy (all parameters are Int32) and if you STFNG you'll find this. So here is a short sample for performing mouse clicks from code, using mouse_event. Create a winforms smart device project with a Form, 2 buttons and a MainMenu. The code should be self-explanatory and easily translatable to C#.
		

' Wrapper for mouse_event, performing click action on coordinates given
Public Shared Sub PerformMouseClick(ByVal aX As Int32, ByVal aY As Int32, ByVal aForm As Control)
Dim p As Point = aForm.PointToScreen(New Point(aX, aY))
Dim m1 As Int32 = (65535 \ Screen.PrimaryScreen.Bounds.Width)
Dim m2 As Int32 = (65535 \ Screen.PrimaryScreen.Bounds.Height)
Dim x As Int32 = m1 * p.X
Dim y As Int32 = m2 * p.Y
Win32Api.mouse_event(2 Or &H8000, x, y, 0, 0)
Win32Api.mouse_event(4 Or &H8000, x, y, 0, 0)
End Sub

' Button1 event handler. Simulates button click OR opening a main menu. Same principle applies for ToolBarButton
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
PerformMouseClick(Button2.Left + 1, Button2.Top + 1, Me) 'performs click on other button
'PerformMouseClick(5, 5, Me) 'opens main menu on full screen WinCE app
'PerformMouseClick(Me.Left + 5, Me.Height + 5, Me) 'opens main menu on PPC app
End Sub

Finishing off, here are a few tips for playing with mouse coordinates and the above:
1. Use the static method Control.MousePosition to determine where the mouse pointer is (or where you have sent it). E.g. run a timer and when it ticks update a listview with the coordinates.
2. Even better than 1, but only applicable if you are using Platform Builder, add the cursor to your image [it is under Shell_and_UI -> UI -> Mouse]. Now you can see where you are sending the mouse and where you exactly last tapped :-)
3. When passing the coordinates to mouse_event be sure to use the instance method Control.PointToScreen as in the example given above (otherwise e.g. you'll be aiming for the toolbar and hitting the title bar)

Let me know if it (or anything else on this blog) doesn't work for you.

STFNG

Thu, November 25, 2004, 02:37 AM under MobileAndEmbedded
Regular readers of this blog (but also anybody that has been developing with the CF for more than a week), know that there is a one-stop shop for all your CF answers, and it is of course the CF newsgroup. Anybody hanging out there for more than a month will know that the same questions get asked over and over and over and over again (trust me, there are not enough "overs" in this sentence).

Sometimes I wish I could just ask people to STFNGTM

Search The Freaking NewsGroup

The FAQ is also a good place to start.

ContextMenu.Show

Wed, November 24, 2004, 11:07 AM under MobileAndEmbedded
Here we will look at a solution for the bug reported previously. In a nutshell, we have a situation where we would like a menu to automatically appear, but instead the onus is passed onto our code. The solution is more interesting than the problem, and in fact in this case applies to other problems as well, so without further ado...

The initial (obvious) solution is to manually show a ContextMenu, which is easy given the Show method. This takes two arguments (the control and position) and is easy to call. The approach does indeed solve the problem and can be adequate for some scenarios, but is not without its problems. They all stem from the fact that when putting up a ContextMenu you are blocking: "This method does not return until the menu is dismissed".

Consider for example what happens if you are performing asynchronous operations (e.g. network requests) that are Control.Invoked to the GUI (e.g. populate a listview) and suddenly the user taps the ToolBarButton to get a menu and you use ContextMenu_Show: all those threads/messages are blocked in a queue only to be released (all at once!) when the menu is dismissed - I don't fancy that myself.

As a workaround to the aforementioned condition (and also a standalone requirement for some scenarios), you will want to stop other activities before showing the ContextMenu, and restarting them after it is dismissed. But wait, how do you know that they must be restarted when it is dismissed? The conditions/scenarios are different if it was dismissed due to a menu item being clicked (which as a result may have now navigated the user to different forms, for example) and different if the user tapped somewhere else, hence dismissing the ContextMenu. A solution to the new problem is to use a global flag tracking if a menu item was clicked or if the ContextMenu.Show call returned without a menu being clicked (and based on the state of the flag make the decision). As you can see, it gets messy.

Other differences compared to a plain menu include the following scenario: Form A shows form B; form B shows a ContextMenu; Form A decides (e.g. based on a Forms.Timer event) to close form B. The result is you are now looking at Form A while the ContextMenu from form B is still up! The workaround to this is to place a this.Capture = false in the Closing event of the Form.

Talking about the problems of manually showing ContextMenus, it is worth capturing (for the record) a known issue with showing them from the AfterSelect event of a TreeView. Rather than describe it here, I point you to the ng archives here and here [my posts there also include a workaround].

From my previous four paragraphs I hope you can see that ContextMenu.Show is useful and can solve some simple scenarios, but is definitelly not a one-to-one replacement for the user tapping on the arrow part of a ToolBarButton and having the menu auto-displayed for them (where none of the problems above apply).

Next time we look at a much nicer workaround to the original problem.