Tuesday, July 29, 2008

 

Tip #1 – datatips on comments
One of the coolest debugger features introduced in VS2005 was DataTips (grey editable, navigable tooltips on steroids that also become transparent when you hold down the Ctrl key). Did you know that you can get data tips for commented code? At first this made me raise an eyebrow, but I can see how for some piece of code I may always need to inspect some other variables quickly and do not want to have to enter them in the watch window manually or navigate to the required area from existing variables that do exist in code. Here is a screenshot that shows this feature in action:

Notice how the datatip appears after highlighting a variable that is in a commented area of the editor (my highlight is set to yellow).

Tip #2 – save output window
In Visual Studio many times we need to transfer the text from the Output window to an actual txt file. Dunno about you, but I always selected all (Ctrl+A), copied (Ctrl+C) and then pasted (Ctrl+V) into a notepad instance that I always have running (Alt+TAB). It turns out that there is a direct way of saving that output to a file. Simply hit Ctrl+S (or invoke it from the menu item) once you've given focus to the Output window – nice!

What cool debugger features do you like in Visual Studio (or would like to see)?

Labels:


Tuesday, May 27, 2008

 

A cool class that was introduced in .NET Framework v2.0 is the BackgroundWorker. If you do any kind of UI development I encourage you to learn about this class (e.g. by following the links below), which makes it easy to execute long running operations on a separate thread, supporting cancellation, progress reporting and marshalling results back to the UI thread.


Back in 2004 I described what the class looks like (inc. links to MSDN) when I implemented it for the .NET Compact Framework v1.0/v2.0 (and for .NET Framework v1.1): BackgroundWorker. I also provided sample code to demonstrate the usage.

An added incentive to learn about it is that Silverlight 2 includes a fully interface-compatible version of BackgroundWorker. A good way to learn about it is by porting to Silverlight the desktop PI sample from my previous blog post and follow what the code does... have fun!

Labels: ,


Monday, May 19, 2008

 

Since you are reading my blog, chances are that you are a .NET developer. Do you realise that eXtensible Application Markup Language (XAML) is now part of .NET? Are you familiar with it? How would you describe XAML (pronounced "zammel") to someone that is not familiar with it if you only had 10-20 minutes? Below is my take.

Background
XAML was introduced as part of Windows Presentation Foundation (WPF) which was released in November 2006 as part of .NET Framework v3.0. In addition, XAML is at the core of Silverlight, v2 of which will be released this year. Whilst XAML itself is independent of those two technologies, I am a practical person so I associate XAML with those two presentation technologies (WPF for the Windows desktop and Silverlight for the cross-platform browser) and have no qualms in intermixing those terms in this blog post. The XAML that Silverlight 2 will support is a subset of the XAML that WPF supports but the core principles and most capabilities are the same.

XAML is an XML language for describing a hierarchy of objects and their properties; in our concrete examples, it describes a hierarchy of visual objects that make up a Graphical User Interface (GUI).

Relationship to managed code
An important fact about the XAML elements (more precisely the WPF/Silverlight graphical system and controls) is that they are fully composable. For example, the following screenshot shows some crazy XAML that places a TextBox inside a button as its content (and in the bottom right corner the result):

It is important to note that anything you can do with XAML, you can also do in code (C#, VB etc). For the previous example, the alternative code required is a single statement in the ctor:

Regardless of that important fact, it is our preference to use XAML for the (naturally) declarative part of describing a GUI. We then use code, of course, to programmatically react to user interaction with the GUI elements that were declared in XAML, i.e. we use code to capture the behaviour.

The obvious question that arises is how we connect the XAML bits to our managed code. In brief, the technique of partial classes ensures that the XAML bits and the managed code bits end up making a single class. XAML properties assign names to controls so we can reference them in the code-behind and, finally, XAML properties connect events in XAML to event method handlers in code. The following screenshot shows a GUI where clicking on the button, updates the time in the textbox.


Take a look at the combination of XAML and C# to achieve this.


Tool Support and the Designer-Developer Workflow
Visual Studio 2008 has a designer tool that allows you to type XAML (with full intellisense) and instantly see the results. For WPF projects it allows you to also drag and drop controls onto the surface and have the XAML generated for you. Have a look at this VS2008 screenshot of Silverlight designer.

For Silverlight 2, the VS2008 is just a read-only designer so you have to type things in the XAML pane or use an external tool to generate the XAML such as Expression Blend.

Expression Blend allows your designer friends to work on the same project/solution and more importantly on the same XAML that you, the developers, are working on.


Have a look at the screenshot of the exact same project you saw just now, but this time in Blend.

That is one of the reasons XAML is an XML-based language: XML is very toolable. Because of that we have a nice workflow between designer people in Blend and developer folk in VS2008, where there is no exchange of bitmaps or binaries but they are both truly working on the same asset: XAML files. The designer-developer interaction for WPF with XAML is described in this paper.

In my relevant demo, I use Blend to style the Button, add a storyboard for animating it and changing the background color to the LayoutRoot to a gradient color. You can see glimpses of that in both of the above screenshots of VS2008 and Blend, so revisit them paying attention to the XAML parts.

Concepts Worthy of Further Exploration (MSDN links)
If you are a seasoned .NET developer, you'll find that XAML/WPF/Silverlight introduce some new concepts. Below I list MSDN links to the top 5 areas I recommend you investigate – just follow the hyperlinks.

- The Layout System. Note that from the list of layout panels, Silverlight 2 only supports Canvas, StackPanel and Grid. Explore all 3 by changing the LayoutRoot type and dragging various controls on it. This is a good time to grok Attached Properties.

- Dependency Properties. These are important to understand because they enable the following 3 equally important areas:

- Animations (in my session I make a button fly around a bit when clicked).

- Databinding (in my session I bind the results of a WCF call to a list).

- Control Styling/Templating (in my session I change a button to look like a happy face)

Conclusion – XAML levels of competency
If you have been ignoring XAML so far, I don't think you can afford to do so any longer. IMHO there are 4 levels of XAML competency:

Level 100 – truly understand all of the above.

Level 200 – confidently read XAML (e.g. as spat out by VS2008 or Blend).

Level 300 – be able to type XAML yourself with an aim to create a structure of a bland GUI (setting basic properties and hooking events etc).

Level 400 – create styles, templates, animations and set gradient colours by hand.

Labels: ,


Tuesday, May 06, 2008

 

Recently I was playing with some LINQ to XML for a demo I was preparing and was having trouble retrieving the expected values from what was a very straightforward query.

Have a look at the XML file that looks like this (the results of programmatically calling this Amazon service).

Each Item element represents a book and I wanted to retrieve the Title. How would you form that query with LINQ to XML?

I went for the obvious:
  var res =
from ia in XElement.Parse(e.Result).DescendantsAndSelf("ItemAttributes")
select ia.Element("Title").Value;
When that did not produce the expected results I scratched my "tired" head at the time and pinged MikeT who came up with the correct way of doing this (you still have time to work it out on your own).

The clue (and at the same time further "excuse") is that all my previous experiments with LINQ to XML involved using my own demo XML files that never had namespaces inside so I forgot all about them (haven't paid the tax in a while). I find quite ugly what you have to do to incorporate namespaces in a LINQ to XML query, but there seems to be no nicer alternative to the following (thanks Mike):
void SomeMethod()
{
var res =
from ia in XElement.Parse(e.Result).DescendantsAndSelf(n("ItemAttributes"))
select ia.Element(n("Title")).Value;

// TODO use res
}

static XName n(string name)
{
return XNamespace.Get("http://webservices.amazon.com/AWSECommerceService/2005-10-05") + name;
}
I still didn't like this solution for the simple demo I wanted to use it for. So, I recalled VB's superior support for XML and I converted the project to VB and used the following instead which needs no extra method and is all round more elegant:
  Dim res = _
From ia In XElement.Parse(e.Result)...<n:ItemAttributes> _
Select ia...<n:Title>.Value()
...and if you are wondering where the n comes from, that is at the top of the VB file:
Imports <xmlns:n="http://webservices.amazon.com/AWSECommerceService/2005-10-05">
...another aspect of VB's beauty and another thing I had the opportunity to mention in my demo ;-)

Labels: ,


Friday, February 29, 2008

 

A while back I recorded some screencasts on this hot topic, and I see now that the popular VSJ developer magazine has published my article on Parallel Extensions to the .NET Framework.

For those of you that can't get a hard/physical copy, they have kindly made it available online for your reading pleasure.

Labels: ,


Thursday, December 20, 2007

 

When the Windows Mobile Managed APIs were introduced they made use of a new underlying Windows CE 5 feature (that WM 5.0 is based on): volatile registry keys (see dwOptions). Volatile keys are also available on normal/desktop/server Windows. In a nutshell, volatile registry keys do not survive an OS reboot so anything you write there is not persisted the next time you restart. The obvious question at this point is: Does the managed registry API support volatile keys? The answer is "no" for both the full .NET Framework and the .NET Compact Framework. Let's explore an UNSUPPORTED solution for both platforms.

On the surface without too much digging it appears that the native RegCreateKeyEx is wrapped by RegistryKey.CreateSubKey. So for the BCL team to add support for volatile keys to the managed API, one way would be to add a new overload to CreateSubKey that accepts our desire for volatileness and at the point it calls RegCreateKeyEx it passes in REG_OPTION_VOLATILE instead of 0 which is what the implementation does now (i.e. a change of one line of code).

But since we are not the BCL team and we cannot edit the library's code, we can create a helper static method to do that job for us. Maybe that method would live in the static Program class with a signature something like this:
public static RegistryKey CreateSubKey(this RegistryKey rk, string subkey, bool isVolatile)
{
if (!isVolatile)
{
return rk.CreateSubKey(subkey); // i.e. no change
}
else
{
// call our own method since we cannot change the framework's
return MothCreateVolatileSubKey(rk, subkey, RegistryKeyPermissionCheck.Default);
}
}
Note on the above:
1. I've made the method an extension method which means that we can call it in either of these two ways (the first being more natural):
RegistryKey rk1 = Registry.CurrentUser.CreateSubKey("my vol", true);

RegistryKey rk2 = Program.CreateSubKey(Registry.CurrentUser, "my vol", true);
2. As you can imagine the implementation of MothCreateVolatileSubKey is a simple duplication of the implementation of the existing framework method with 2 differences:
a) Where the framework's implementation passes 0 as the 4th argument to the native RegCreateKeyEx, we will pass REG_OPTION_VOLATILE.
b) Wherever the framework's implementation calls private/internal methods we will have to use reflection to access them (unless you fancy duplicating all of that too;)).

Yes, I know the above is very ugly, very unsupported and very much a hack. I have created a VS2008 solution with the above that you can use to test it out at your own risk. It contains two projects, one for NETCF to run on your Windows Mobile and one for the desktop.

It is quite possible that by running the code from the project your machine will be wiped clean and that your monitor will go up in flames after transferring all your money out of your bank account. You have been warned!

So, at your own risk, get the VS2008 projects here.

Labels: ,


Tuesday, December 18, 2007

 

At the same time as .NET Framework 3.5 we shipped Service Pack 1 for .NET Framework v2.0. There is a standalone update for v2.0 SP1 for Windows XP and Windows Server 2003 and it ships out of the box with Windows Server 2008. For Windows Vista, it is included with Vista SP1. Of course, Fx v3. 5 depends on v2.0 SP1 so the installer for v3.5 will also install v2.0 SP1 if it is not on the machine already.

So what is in Fx v2.0 Service Pack 1?

1. Tons of bug fixes. See the list here.

2. Performance Improvements.
+ NGEN boosts.

3. Some new public APIs:
+ New members on the GC class.
+ DateTimeOffset class (this has also propagated to many other APIs that in addition to DateTime now use this new type e.g. XmlConvert.ToDateTimeOffset()).
+ Addition of the ShieldIcon for drawing.
+ IME additions.
+ File Dialog Enhancements.

4. Internal changes:
+ Increase of ThreadPool limit.

Some devs are aiming at using VS2008 to target Fx v2.0. Please note that you are really targeting v2.0 SP1. I blogged a pointer before on potentially addressing this goal, but it is important to stress again that the supported framework for VS2008 is v2.0 SP1 and not v2.0 RTM.

By the way, at the same time, the .NET Framework v3.0 SP1 was also released. Everything I wrote in the first paragraph of this blog post for v2.0 SP1 applies to v3.0 SP1 too. The list of fixes for v3.0 SP1 is in this KB article.

Labels:


Monday, December 17, 2007

 

In Windows Forms, the OpenFileDialog and SaveFileDialog inherit from the abstract FileDialog class. If you work with these classes you should know that there have been a few additions in this area as highlighted by the following class diagram.


There are broadly 3 enhancements:

1. The OpenFileDialog has a new property (SafeFileName) that returns just the name and extension of the selected file without the path information. Because of that it is accessible to applications running with limited trust. Should the selected files be more than one, you can use the corresponding SafeFileNames property that returns an array of strings.

2. There is a new property for both Save- and OpenFileDialog that is only applicable on Windows Vista (it gets ignored on XP): AutoUpgradeEnabled. The default is true, and it means that the dialogs will have the Vista look and feel when run on Vista.

3. If you have not opted out from auto upgrading for Vista just discussed, then another property of the 2 dialogs may be useful: CustomPlaces (of type FileDialogCustomPlacesCollection). You can use this to add strings (or Known Folder GUIDs) that represent locations on your disc that are then easily accessible to the user of the dialog you are showing via the "Favorite Links" section.

To see the Vista dialog in action with custom places, watch my screencast.

Labels:


Sunday, December 16, 2007

 

It should be a known fact that Windows Forms controls support Input Method Editors out of the box, so you don't have to do anything special.

However, if you are writing your own control (e.g. UserControl) then with v2.0 of the Framework you may run into issues. This is a fairly niche scenario but if you are facing it then you should obtain hotfix 934197 that solves the issue by providing a property you can override in your control: CanEnableIme.

Also read the MSDN docs for the protected property.

Hopefully the details on the links above make it clear, but I also found this connect feedback item that makes it crystal clear with a repro. As per the MSFT response there, this property is now available in v2.0 Service Pack 1 on all controls. Maybe now that it is in an SP this angry guy here won't have such an issue distributing the fix...

Bonus IME topic: Also in v2.0 SP1, relevant to Chinese IMEs only, you will find a new member for the ImeMode enumeration for HalfShape: OnHalf.

Labels:


Saturday, December 15, 2007

 

Seasoned Windows Forms developers will be familiar with the System.Drawing.SystemIcons class that has a bunch of properties returning standard system icons. For example, if you throw on a form a picturebox and a button with its event handler the following code shows how you can take advantage of SystemIcons:
void button1_Click(System.Object sender, System.EventArgs e)
{
// use Error
pictureBox1.Image = Bitmap.FromHicon(SystemIcons.Error.Handle);

// use Warning
this.Icon = SystemIcons.Warning;

// use Information
int h = button1.ClientSize.Height / 2;
Icon ico = new Icon(SystemIcons.Information, h, h);
Bitmap bitmap1 = Bitmap.FromHicon(ico.Handle);
button1.Image = bitmap1;
button1.ImageAlign = ContentAlignment.MiddleLeft;
}
With Service Pack 1 of .NET Framework v2.0 we get a new member of that class: SystemIcons.Shield. So, if you are on Windows Vista and you are working with User Account Control, you may find it a useful icon to use (for example on a menu).

Below is a screenshot of what the code above looks like side by side, before clicking the button and after. The screenshot at the bottom is after replacing the 3 icons with shield:

Labels: , ,


Friday, December 14, 2007

 

Most .NET developers have at some point in their career called into a native library (whether it was C\C++ DLL part of the Windows OS or one of their own or one from a 3rd party is irrelevant). To call native functions form .NET you use the DLLImport attribute and even though it has various overloads, the main thing we do is point it to the native DLL name (the Declare statement in VB does the same thing although I use DllImport in VB projects as well).

The subtle point is that there is no convention for how we specify the name so, for example, all of the following are valid and will work at runtime:
    [DllImport("kernel32.dll")]
static extern ...;
[DllImport("kernel32")]
static extern ...;
[DllImport("Kernel32.DLL")]
static extern ...;
[DllImport("KERNEL32")]
static extern ...;
[DllImport("KeRnEl32.DlL")]
static extern ...;
Now, I know that there is no convention, but if you used the above this is what you would see in ILDASM (and indeed in reflector too):


You may think: "So what, isn't just one kernel32 loaded in my process at the end of the day?". Well, yes, but there is a slight performance penalty because the loader treats each separate string as a separate module!

Some of you are thinking that "for that performance reason alone" you should standardize on a convention e.g. always use lowercase and include the ".dll" extension... or something like that. Others are probably thinking that "the performance gains are negligible" for you to make a rule for this.

In my opinion (and it is my blog so don't be shocked that these are my opinions here!), you are both wrong and right at the same time. For me, this is an issue of hygiene. Once I learnt about this fact (many moons ago :-), I couldn't live with myself if in my assemblies I didn't define a standard for pinvoking. Nothing to do with performance – it just feels dirty to me not to use the same DLL name throughout my assembly. That's just the kind of person I am...

So I sighed when I discovered that System.Core.dll is the first and only assembly so far in v2.0/3.0/3.5 of the framework not to follow the "all lower case with .dll appended" convention, as viewing it through a disassembler proves :-(

Labels:


Wednesday, December 12, 2007

 

In this 4th video in my series on the ParallelFX, I explore what it is like to program against the lowest level API in the Task Parallel Library: the Task and TaskManager. Watch it or download it from here.

Labels:


Sunday, December 09, 2007

 

In my videos on Parallel FX, I showed some class diagrams. I thought that you may want to see those independently of the video. Click on the links below to open the respective class diagram png image (or "save as"):

+ Parallel LINQ.
+ Parallel class and friends.
+ TaskManager, Task, Future and friends.

Labels:


Friday, November 30, 2007

 

The Parallel Extensions to .NET Framework 3.5 is available! Download and play with the first ever public drop - the December CTP.

If you are then ready to dig into it, I have three 20' screencasts (plus more cooking):
1. Tour of the Samples. A tour of what gets installed to get you started.
2. Declarative data parallelism. This is about PLINQ.
3. Imperative data parallelism. This is about the static Parallel class.

After watching the above, visit the relevant MSDN dev centre. For any questions please use the dedicated online forums. For feedback please use the connect site. If you want to congratulate or blame the product team, visit their blog.

Labels: ,


Thursday, November 29, 2007

 

If you watched my videos here, please note the following clarifications:

First, in all videos I am using the old internal sort-of-a codename "PFX". This is no longer used internally or externally. Instead just refer to it as Parallel Extensions.

Second, in the PLINQ video, when I very briefly mention what pipelining does, my description is outdated; for an up to date description of pipelining please see this. Also in the same video I do not make it clear that the ForAll option will *not* allow you to preserve ordering.

Third, in the Parallel video, where I use return sum and imply that it is the same as return 0, I am obviously wrong: The issue is that it’s possible for the first thread to complete so fast that the second thread doesn’t have any work to process. The first thread will store its partial result back into the total sum, but since the thread local selector delegate has captured that total sum variable, the second thread will be initializing its partial sum with the total sum that already includes the first sum’s partial. Thus, you could end up counting values twice so you should use return 0 instead. In the same video, if you really wanted to simulate break, in the parallel version you would have to add a return statement after the ps.Stop() statement that I used.

Labels:


Wednesday, November 28, 2007

 

I was in Joe's session at Tech Ed when he mentioned in passing (almost as a throwaway comment) that there are some changes in the System.Threading.ThreadPool in v3.5. I guessed that what he really meant was changes in v2.0 Service Pack 1 for the ThreadPool class (which I confirmed myself by testing it on my machine). It goes to show you that you cannot rely on API surfacing to discover all the changes in SP1 and somebody should come up with a master list... Anyway, you are probably more interested in what the change is, right?

Apparently, the ThreadPool now defaults to having a max of 250 threads instead of 25. I found this ironic because I recall that the NETCF ThreadPool had 256 in v1.0 and changed to 25 to match the desktop behaviour in v2.0 and I wondered if they caught this change in NETCF v3.5 so they can be in sync again. Seems not :-)

Regardless, remember that starting with v2.0 you can change the max yourself not just through hosting APIs but through the managed SetMaxThreads method. In fact, to validate all of the above, I used the counterpart method, GetMaxThreads that you can try yourself:
  int a, b;
ThreadPool.GetMaxThreads(out a, out b);
MessageBox.Show(a.ToString());
:-)

Labels:


Wednesday, October 03, 2007

 

Huge announcement today: Microsoft is releasing the source code for parts of the .NET Framework (inc. BCL, WinForms, WPF, ASP.NET) under the Ms-RL license which effectively, by my interpretation, means that you can read but you cannot copy or modify.

The cool bit is not that you can just read the framework code in your favourite text editor once you download and accept the license; no, the real goodness is that when you debug your applications with Visual Studio 2008 you will have the option to debug right down into the Framework code (with an autodownload feature from an MSDN server)! Are you still debating the move to VS2008? ;-)

For more details read ScottGu's blog post or listen to ScottHa's podcast or watch ch9 interview with the man that made it happen.

Labels:


Monday, October 01, 2007

 

I have no reservations in describing the move from Visual Studio 2005 to Visual Studio 2008 as a "no-brainer upgrade". The latter is a superset of the former and given framework multi-targeting and the layered approach of Framework 3.0 and 3.5, it makes perfect sense to move your development environment over and, when you are ready to change your deployment, seamlessly take advantage of a new .NET Framework 3.x

But how about those people still using VS.NET 2003 (shocking I know :) who wish to take advantage of the VS2008 enhancements and move closer to our latest platform? That's a different kettle of fish. The issue there is that: you are on a different CLR version (1.1) which has some breaking changes compared to the CLR 2.0 (which is used by Fx 2.0/3.0/3.5). You are also on a different framework version (1.1) which has some breaking changes compared to Framework v2.0 (which is also an essential part of Fx 3.0/3.5). There is no reason for you to go to VS2005 as an intermediate step, so by all means go from the IDE of 2003 to 2008, but be prepared to face all the issues people faced when they moved from Fx 1.x to Fx 2.0.

I thought I'd resurface below some links to documents that help people move from v1.x to v2.0, the first two being the most important ones IMO:
- Design time Breaking Changes in .NET Framework 2.0
- Runtime Breaking Changes in .NET Framework 2.0
- Microsoft .NET Framework 1.1 and 2.0 Compatibility
- Compatibility Testing Scenarios

Also note that if you search you'll find plenty of issues in particular for web projects since the project model fundamentally changed from VS.NET 2003 to VS2005. It was later amended via separate downloads for VS2005 but since you are moving to VS2008 you don't have to worry about that (both web site projects and web application projects are included out of the box with VS2008). So, just one extra link for you pre-ASP.NET v2.0 devs:
- Feature changes in ASP.NET 2.0

A much older blog post on the same topic may also be of use to you.

Labels:


Friday, September 21, 2007

 

Angelos found an interesting article via the UK MSDN Flash. If you did as well, check out more about that on the author's blog (one more reason to be sorry PDC was cancelled).

Anyway...

In a not so unrelated area, on a list that I am a member of, a question came up about affinitising a managed thread to a specific CPU. Jeffrey Richter came to the rescue by pointing out the System.Diagnostics.ProcessThread and its ProcessorAffinity property but then the harder question came along of how to associate that class with the System.Threading.Thread class. Below is the answer in C# from Mr Richter again:
      // Call this passing in 0
public static ProcessThread GetProcessThreadFromWin32ThreadId(Int32 threadId) {
if (threadId == 0) threadId = ThreadUtility.GetCurrentWin32ThreadId();
foreach (Process process in Process.GetProcesses()) {
foreach (ProcessThread processThread in process.Threads) {
if (processThread.Id == threadId) return processThread;
}
}
throw new InvalidOperationException("No thread matching specified thread Id was found.");
}
...where ThreadUtility is a class containing at least:
      [DllImport("Kernel32", EntryPoint = "GetCurrentThreadId", ExactSpelling = true)]
public static extern Int32 GetCurrentWin32ThreadId();

The code (and much more) is part the Power Threading Library which is available on the Wintellect site.

Labels: ,


Wednesday, May 16, 2007

 

One of the things you will hear/see a lot in Orcas talks/articles is that the CLR remains at the same version: v2.0.50727. What is meant by that is that a "service pack type" upgrade will be made which is guaranteed not to break any existing code: just bug fixes and maybe some tiny enhancements that can have no negative side effects and in most cases have to be explicitly taken advantage of from newly written code.

So, on your system navigate to your %windir%/Microsoft.NET/Framework/v2.0.50727 directory. Pick any of the DLLs starting with "mscor", e.g. mscorjit.dll, and look at its full version information. Note that the presence of Fx v3.0 has no effect on the scenarios below or to this post as a whole.
1. If you have installed NetFx v2.0 on XP or lower, the revision will be 42 i.e. full version is v2.0.50727.42
2. If you have installed Orcas Beta 1 on your machine the revision is actually 1318.
3. If you are on Vista (which comes with NetFx 2.0 by default) and have *not* installed Orcas, then your revision is 312 (that's right, it is "the same version" as RTM)

So there isn't much point to this post, other than to highlight/reinforce that when we say it is the same version, we do not count the Revision part – just the Major.Minor.Build parts.

Labels:


Sunday, February 18, 2007

 

In VB you are always advised to declare all your variables since due to its heritage, VB allowed you not to declare a variable type, which is bad thing:
Sub Main
myVariableName = 6
myVariableName = myVariableNmae + 1

Debug.WriteLine(myVariableName)
End Sub
In the code above you were expecting myVariableName to be 7 when actually it is 1, the reason being that you misspelled your variable. Because you don't have to declare the variable, a new one was created for you when you misspelled it and of course it starts with a value of zero. Crazy world! Luckily, in VB6 and before, you could specify Option Explicit and that would not allow you to use variables without declaring them... phew. In .NET land with VB 7.0 by default Option Explicit On is specified for all projects but if people want to be stupid they can turn it off.

Another bad thing you could do in VB6 still remains today and that is the ability to declare untyped variables, for example:
Sub Main
Dim myVariableName = 6
myVariableName = myVariableName + 1
myVariableName = "hello"
Debug.WriteLine(myVariableName)
'
Dim o
o = New Collection
o.FeelingLucky
End Sub
Effectively, myVariableName is a Variant (or Object in .NET land) which means it can take any value you want and you never get a compile time error, instead you get a logical error. Similarly for variable o where the compiler cannot tell you that FeelingLucky is not a method of the Collection class so you get a runtime error/exception. So how can you escape this crazy world? A new option in VB 7.0 and beyond is Option Strict. Unfortunately, to this very day, you have to manually turn this option On yourself - it is not the default. If you haven't already, do it now for all your projects. You will prevent logical errors at runtime, runtime exceptions *and* you will make your code faster since method calls will be early bound rather than late bound.

Now, there are some niche scenarios where Option Strict Off may be convenient, namely when doing COM Interop with old automation models that are constructed in a certain way or if you have large codebase that already works and you do not want to touch it (hence you do not want to deal with the compile time errors that you will get when you turn option Strict On). The point here is that you can turn this option on/off at the file level. So I suggest that you turn it On at the project level and turn it off for the files that you think could do without the compile time checking.

Before I close this blog post, note that C# does not have either of these options which basically can be seen as that they are both On and you cannot turn them Off. While Option Explicit is useless, the convenience of doing easy late binding in VB with Option Strict Off can be cool. In c#, you have to do explicit late binding using reflection (which is what the VB compiler does for you in the VB case).

For much more on this topic use your favourite search engine i.e. like this or like that.

Labels:


Monday, January 01, 2007

 

Exactly two years ago on New Year's day, I wrote the Best of "The Moth" 2004 blog enrty where I picked my favorite blog entries out of 96 posts. Exactly one year ago I had to choose from 151 posts to find the ones I thought were the best in terms of content and the result was the Best of "The Moth" 2005.

The year of 2006 I made 142 blog entries and below are a select few. Happy New Year!

01. I didn't have a chance to play with it as much as I wanted to, but with very little public info available, this blog served it well: .NET Micro Framework, its product sheet and other NETMF links.

02. Recognising an idiom of the using statement.

03. A cute desktop feature implemented for the Windows Mobile/WinCE platform in a reusable NETCF control: TextBox cue banner.

04. A picture is worth a 100 words and a video is... a whole bunch of pictures! Check mine out following the instructions here for my nuggets.

05. A comprehensive collection of links for Windows Workflow Foundation (WF).

06. I collected the links to my 9 blog posts on sharing assets between desktop and mobile platforms in one place. Follow the numbered links.

07. The most controversial feature of Windows Vista is something every developer must understand: User Account Control.

08. One of Vista's features is becoming my obsession and that is SideShow. My series of SideShow gadgets blog posts will continue in 2007 and so far you can read parts one, two, three, four and five.

09. I spent 6 months last year focusing almost entirely on Vista developer features that are new and that are *not* part of NetFx3. I have catalogued my blogging & screencasting efforts in a large collection of links to content that supports my speaking engagements on Vista. IMO this blog post alone could have been the best of "The Moth" this year:
Vista-only features for the managed developer.
Stay tuned in 2007 via one of the subscribe options on the left :-)

Labels: , , ,


Monday, December 11, 2006

 

A few months ago when I was looking at what new Vista native APIs to play with from managed code, I was advised internally not to touch anything that is loaded in arbitrary processes. The short answer was that this has versioning implications.

Let's drill into it. If you write a shell extension of any sort with managed code, you have introduced a dependency onto the version of the CLR that you are using. The classic example is to think of all the managed applications on a computer that open a file dialog. Every time they do that, your .NET shell extension code gets injected into their process and bad things can happen (not just to the shell) but more importantly to those applications *IF* the version used in your managed extension, is not the same as the version used by that application. So the real root of this issue is the fact that each process can only load one version of the CLR combined with the fact that a version of the CLR can side by side on the same machine with other versions of the CLRs.

It sounds like we are pretty serious about this advice. An article written in 2004 demonstrating how to write namespace extensions with managed code was edited this June 2006 to include the following warning in red at the top of the article:
[ Editor's Update - 6/23/2006: Because shell extensions are loaded into arbitrary processes and because managed code built against one version of the runtime may not run in a process running an earlier version of the runtime, Microsoft recommends against writing managed shell extensions and does not consider them a supported scenario.]

Now at this point, some of you will have arguments and workarounds of how to avoid this limitation. I doubt they haven’t been raised already, but if you think you have something new to offer, go contribute to this extremely interesting discussion on the msdn forums (inc. contributions from Raymond Chen).

Labels:


Thursday, October 05, 2006

 

The problem
When I was trying to call the WerRegisterFile API as described previously, I run into an interesting situation: Calling the API without specifying any CharSet should have attempted first the WerRegisterFile. If I specify CharSet.Auto, it should first attempt WerRegisterFileW and if it cannot find that then go back to try WerRegisterFile. Either way, you would have thought that this would succeed or blow. As it turns out, leaving the CharSet off, silently fails (no exception, return value is 0, Marshal.GetLastWin32Error is 0 etc) but specifying CharSet.Auto works as expected! For the record, CharSet.Unicode behaves like CharSet.Auto and CharSet.Ansi fails like when we omit altogether CharSet from the DllImport declaration.

Download a repro VS2005 project here (obviously requires Vista).


Solution
Looking at kernel32.dll (which exports this function) shows that there is only one entry for the API (screenshot of dumpbin). This seems to be a pattern with Vista, according to my colleague MikeT.

Well it seems that after a series of chats with Mike, he updated his post with useful thoughts. Read his 4th thought in the updated section and come back... I’ll wait...

[...time passes...]

So to prove the thought, I looked at another Vista API that takes strings: RegisterApplicationRestart. It too has no trailing A or W (screenshot). So calling this API without CharSet also gives no errors, but the string you get passed back to the application at the command line is definitely not the one I passed to it (something Chinese-looking on my system when I gave it something English).

That rests the case. I should have realised earlier that CharSet is not just about finding the correct entry point but also about how the string will be marshalled.

As an excuse I should state that, having mostly coded for WinCE, I think I have been spoilt since that is all Unicode and I can’t remember using CharSet with any of NETCF pinvokes and they all worked fine :)

Final thoughts
1. Whoever thought the default of CharSet should be Ansi has a lot to answer for. Note that it is a C# decision as the CLR defaults to Auto!
2. I wonder if the Vista change (that threw me off track) is going to bite others or if it just muppets like me that fell for it :S
3. Why is WerRegiserFile in kernel32.dll when all the other WER APIs are in wer.dll?!
4. WerRegisterFile should be giving some indication to the developer that it failed. At the end of the day, you give it a path (string), it thinks it is a different string (path), so it cannot find the file and then it silently fails! WTF?
5. I detest silent failures. Make an explosion next time. A big loud one or a quiet one, but not a silent one. Silent ones are the worst!

Labels:


 

This post just serves as a background URL that I can link to from my next post later :)

We all know that to call unmanaged code from managed code you have to pinvoke (i.e. use the DllImport attribute). One of the enumerations that becomes relevant when calling native methods that accept strings is CharSet.

It is also relevant for native structures with strings but I am explicitly ignoring that in this post. I am also ignoring the CharSet.None enumeration as it means the same as CharSet.Ansi and is there for C++ legacy reasons, I think.

CharSet is useful so you can instruct the runtime to invoke the "A" version of an API or the "W" version (Ansi or Unicode) e.g. MessageBoxA or MessageBoxW. If you select CharSet.Ansi the CLR attempts to invoke an entry point with an appended "A" if the entry point specified by the signature doesn't exist. If you select Auto, then on modern OSs it will default to Unicode. This means the CLR will attempt to invoke an entry point with an appended "W" before attempting to invoke the entry point specified by the signature. If you miss the CharSet from your DllImport declaration, the default is Ansi with the behaviour described above.

Labels:


Wednesday, October 04, 2006

 

In my Vista sessions I focus on accessing native APIs form managed code. A few times now people ask me if there is an API browser of some sort... you know, like the API Text Viewer from the VB6 times... I found this replacement on the web but haven’t personally tried it. Frankly, I don’t know of a Microsoft one so if there is a replacement hiding somewhere and you know about it please share a pointer!

So the alternative I offer is... the alternative I have always offered! The title gives it away: pinvoke.net
If you read my older entry then you’ll know where to go to get the interop book.

There is also a Visual Studio 2005 add-in for the pinvoke wiki site, but I haven’t personally tried it. Get it from gotdotnet.

I plan to update the wiki with Vista API definitions at some point (the power API I mentioned previously is already there). I’ve added this to my TODO list (item # 88345602), but if you feel you’d like to take on the task, please go for it (Vista dllimports all over my blog entries since May this year)!

Labels:


Thursday, September 28, 2006

 

I've talked about the new button style in Vista before: CommandLink (and Vista uses this everywhere).

While looking on the web for something irrelevant, I came accross an old post by Geoff Appleby that offers a custom implementation of CommandLink for XP. Check it out!

Labels:


Monday, July 03, 2006

 

Previously we described the using statement. After showing a quick code example and describing the intent of the construct, we proceeded to a more pragmatic description of what it does which I repeat here:
"The using statement accepts as an argument an object that implements IDisposable; it defines a coding block where the ctor of the said object is called at the start and the Dispose method is called in the end."

Taking the definition above, a design pattern is formed (or to be more precise in this case, an idiom):

Sometimes you have a piece of code that has to be enclosed between two code blocks A and B. Code blocks A and B are repeated in multiple places in your project(s). When you recognise that, you can extract a new class with a ctor containing code block A and the Dispose method containing code block B. Replace all client side occurrences with a using block passing in the newly extracted class.

As an example, instead of:
void SomeMethod()
{
Cursor.Current = Cursors.WaitCursor;
statusBar1.Text = "Busy...";
button1.Enabled = false;

// do some real work here

button1.Enabled = true;
statusBar1.Text = "Ready";
Cursor.Current = Cursors.Default;
}
we write:
void SomeMethod()
{
using (BusyTaskRegion bt = new BusyTaskRegion(statusBar1, button1))
{
// do some real work here
}
}
...after extracting the following class:
class BusyTaskRegion : IDisposable
{
private Control _c1;
private Control _c2;

public BusyTaskRegion(Control c1, Control c2)
{
this._c1 = c1;
this._c2 = c2;
Cursor.Current = Cursors.WaitCursor;
c1.Text = "Busy...";
c2.Enabled = false;
}

public void Dispose()
{
this._c1.Enabled = true;
this._c1.Text = "Ready";
Cursor.Current = Cursors.Default;
}
}
Another example would be measuring performance of some code (i.e. extract the timer setup and tear down into a class and then use it via using). I was trying to find the pattern formally documented online but I couldn't. However, I did find examples of its usage here, here and here (much better than my fictitious example above!).

When I first saw this (at a previous project for some impersonation code), it felt wrong on two levels. First, it felt like using was being misused in scenarios that it wasn't originally intended for (e.g. when people read the client side code they have to take an extra mental step to digest that no memory management stuff takes place). Second, it felt wrong to define classes that have just a constructor and a Dispose method with nothing else in-between.

What do you think?

Labels:


 

The using statement has been in C# for ever and was newly added to VB8 as well (Using). I hope everybody is familiar with it by now (not to be confused with the using directive typically appearing at the top of code files - Imports in VB). If I asked you to tell me about the using statement, I bet that most of you would use a code example , maybe something like the following.

Given a class like this:
class SomeType : IDisposable{
public SomeType(){
// do some ctor stuff
}

public void DoSomething(){
// do some useful stuff
}

public void Dispose(){
// clean up unmanaged resources
}
}
We know that the client code should use it like this:
SomeType st = new SomeType();
try
{
st.DoSomething();
}
finally
{
st.Dispose();
}
The using statement makes that cleaner, easier to remember and easier to enforce like this:
using (SomeType st = new SomeType())
{
st.DoSomething();
}
So, the easy way to describe the purpose of using (without a code example) is this:
The using statement offers an elegant way of ensuring that the developer does not forget to call Dispose on an object that (directly or indirectly) holds unmanaged resources. (My links to msdn at the top pretty much say the same thing)

Effectively, we all associate the using statement (or at least I always did) with not leaking. However, if we look at the using statement not from an intent point of view but instead from a practical point of view, we would probably come up with this:
The using statement accepts as an argument an object that implements IDisposable; it defines a coding block where the ctor of the said object is called at the start and the Dispose method is called in the end.

Next, we'll see how to take that last statement further to describe a design pattern.

Labels:


Sunday, November 13, 2005

 

We are all familiar with the Type class of the framework and how useful it is in many scenarios. To obtain the Type of a type we can call GetType() on the instance, the static Type.GetType passing in the full name, or use C#'s typeOf operator (or GetType operator in VB).

We are also familiar with Generics (supported on NETCF as well).

At first there isn't much relation between the two. There are some cases, though, when you design an API (that depends on a type to be passed to it) where either of the two can satisfy your goal: A generic class or a class with a constructor accepting a Type. So in that case, which approach should you choose and why?

1. Blah<T>{...} //type declaration
2. Blah(Type t); //ctor

Just to be ultra-clear, the calling code for each occasion looks something like this:
1. new Blah<SomeType>();
2. new Blah(typeOf(SomeType));

Just FYI, and don't allow this to affect your thoughts, the indigo (or WCF if you prefer) team chose the 1st approach originally and then, after Beta 1, changed to the second. I am referring, of course, to the ServiceHost class. Why did they do that?

Since this blog doesn't have comments, reply on your own blog (or if you don't have one, feel free to email me your reply).

UPDATE:
1) .NET design guidelines guru provides comments.
2) Ayende describes his preferences
3) Apparently, there is no mystery on the Indigo change:
"The ServiceHost just needs to know the type of the object being hosted - none of the methods or properties were accepting or returning the so the generic mechanism wasn't really being using per se."

Labels:


Sunday, July 31, 2005

 

If I am interviewing you for a dotnet position and I ask you about boxing I expect you to be able to quickly describe what it is, why one should care and give me a quick example. If I see you struggle, I'll help you by asking "If I add an integer to an ArrayList, does boxing occur?". If you hesitate answering that then... [this really happened recently when I interviewed someone for a senior .NET position].

So it was with pleasure that I saw a post on boxing this week. If you are struggling with the concept go read Raymond Lewallen's Boxing and Unboxing for Beginners.

I don't really have anything to add to that post other than a couple of comments on the Intermediate Language:
1. Note the IL instruction box. That is where boxing gets its name (should be obvious but thought I'd spell it out :-)
2. Raymond's example uses VB and unfortunately has Option Strict Off. If it had Option Strict On, you'd see that when you retrieve the result you have to cast it to the integer
i.e. b = DirectCast(a(0), Int32)

If you make that modification, then you see the other magic IL instruction: unbox (rather than the *really* ugly VB-compiler-injected-statement:
[Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.IntegerType::FromObject(object)).

Here is the IL with that small modification for VB in VS.NET 2003 (note that it is compiled in Release mode so we get cleaner IL):
.method public instance void  Method1() cil managed
{
// Code size 39 (0x27)
.maxstack 2
.locals init (class [mscorlib]System.Collections.ArrayList V_0,
int32 V_1)
IL_0000: newobj instance void [mscorlib]System.Collections.ArrayList::.ctor()
IL_0005: stloc.0
IL_0006: ldloc.0
IL_0007: ldc.i4.s 10
IL_0009: box [mscorlib]System.Int32
IL_000e: callvirt instance int32 [mscorlib]System.Collections.ArrayList::Add(object)
IL_0013: pop
IL_0014: ldloc.0
IL_0015: ldc.i4.0
IL_0016: callvirt instance object [mscorlib]System.Collections.ArrayList::get_Item(int32)
IL_001b: unbox [mscorlib]System.Int32
IL_0020: ldobj [mscorlib]System.Int32
IL_0025: stloc.1
IL_0026: ret
} // end of method Form1::Method1
3. Just so you can see the IL for a C# version compiled under Visual Studio 2005 Beta 2, here is some code:
private ArrayList ar = new ArrayList();
public void Box() {
ar.Add(5);
}
public void Unbox() {
int i = (int)ar[0];
}
...and equivalent IL:
.method public hidebysig instance void  Box() cil managed
{
// Code size 19 (0x13)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldfld class [mscorlib]System.Collections.ArrayList BoxUnbox.Form1::ar
IL_0006: ldc.i4.5
IL_0007: box [mscorlib]System.Int32
IL_000c: callvirt instance int32 [mscorlib]System.Collections.ArrayList::Add(object)
IL_0011: pop
IL_0012: ret
} // end of method Form1::Box

.method public hidebysig instance void Unbox() cil managed
{
// Code size 19 (0x13)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldfld class [mscorlib]System.Collections.ArrayList BoxUnbox.Form1::ar
IL_0006: ldc.i4.0
IL_0007: callvirt instance object [mscorlib]System.Collections.ArrayList::get_Item(int32)
IL_000c: unbox.any [mscorlib]System.Int32
IL_0011: pop
IL_0012: ret
} // end of method Form1::Unbox
By the way, a great follow up to the above is to show how Generics eliminate the need for boxing in such a scenario pointing out that
a) No boxing/unboxing penalty takes place
b) No cast is required
c) Compiler prevents anyone adding objects of other types to our List

Labels:


Monday, May 30, 2005

 

As you know, in VB variables do not need to be initialised, as they always get the default value for the variable type in question, e.g.
Dim b As Boolean 'b is already initialised to False

In C-based languages (inc. C#) this does not apply; you must initialise the variable before you use it, e.g.
bool b = false;

The above is the reason we cannot have ByOut in VB (only ByVal and ByRef) whereas C# has both ref and out (ByVal is implied by omission).

I have seen VB code that follows the C-style and there are two common reasons for doing so; one is to be explicit and the other is because of a C-based upbringing. The question is what IL will the VB compiler generate, faced with code that differs only by explicit assignment (we know it is pretty crap at optimising the simplest of scenarios, so we don't go in with our hopes high).

Given these two methods (and assuming a private method GetNumber):
    Public Sub VBStyle()
Dim i As Int32
i = GetANumber()
End Sub

Public Sub CsStyle()
Dim i As Int32 = 0
i = GetANumber()
End Sub
I can tell you that the VB compiler generates slower code for the second scenario (I have to say if you have a bottleneck in your project, cases like this will not be it, but it is still good to know :-).

Here is the corresponding IL:
.method public instance void  VBStyle() cil managed
{
// Code size 8 (0x8)
.maxstack 1
.locals init ([0] int32 i)
IL_0000: ldarg.0
IL_0001: callvirt instance int32 VBWinApp.Class3::GetANumber()
IL_0006: stloc.0
IL_0007: ret
} // end of method Class3::VBStyle

.method public instance void CsStyle() cil managed
{
// Code size 10 (0xa)
.maxstack 1
.locals init ([0] int32 i)
IL_0000: ldc.i4.0
IL_0001: stloc.0
IL_0002: ldarg.0
IL_0003: callvirt instance int32 VBWinApp.Class3::GetANumber()
IL_0008: stloc.0
IL_0009: ret
} // end of method Class3::CsStyle
Needless to say that the C# version closely reflects the first version (it is even better, since it uses a call rather than a callvirt - but that is a different story...).

Labels:


Tuesday, May 10, 2005

 

Can you think about the amount of information that is part of your knowledge and account for it in terms of where you found out and how you consider it as common knowledge? Well, it turns out there is some stuff in my head that I thought everyone knew but it is not true. So I might as well capture it here on my blog.

What does mscorlib stand for? Stop reading! Don’t look for the answer, just try and work out the answer. What does the main dll of .the NET Framework stand for? What was the codename or first candidate name if you like, for C#? Well here are my answers (this is just my understanding which I cannot track back to a reference!):

C# and the design of this new .NET language ws COOL! More importantly, mscorlib has 3 parts to its name: ms cor lib.
ms: Magnificent Software (ok, it is Microsoft :-)
lib: as you expect this means library
cor: Before .NET was chosen as the name, this new platform was a successor to COM so it was codenamed COM 3.0 and then the name chosen was… Common Object Runtime (cor) and that is where mscorlib derives its name from (and that stuck regardless of the fact that .NET was the final name)!

So, now you know ;-) And if you knew already now you have something to point to!

Labels:


Wednesday, March 16, 2005

 

On his blog, as part of a useful OOP series, Raymond Lewallen defines polymorphism. He merges 3 concepts in one post. We've had the discussion on the topic previously, but I don't think we reached total agreement. In my opinion, to say that there is such a thing as "true" polymorphism and to claim that it applies only when inheritance is involved, is plain wrong.

If you are reading that blog entry to understand polymorphism, please read my (complementary) take on the topic below.

First I would ignore overloading. This has nothing to do with polymorphism and I would not bring it up in any discussions on the topic. Just for info, overloading is the ability to define two or more methods on the same class with the same name but different argument lists. Nothing more, nothing less.

Second, do not directly associate polymorphism with inheritance (as you can probably tell this is my pet peeve :-). Polymorphism directly depends on subtyping. Unfortunately, many people confuse subtyping with implementation inheritance. Inheritance is *a* way to achieve subtyping, but not the only one (another is interface implementation). If you ask the question in reverse: Is inheritance the same as polymorphism? The answer is no. Inheritance offers a way to achieve subtyping *and* a way to reuse code.

So if someone asks you to explain polymorphism with a VB.NET example using inheritance, you can do something simple like this:
Public Class EntryPoint
Shared Sub Main()
Dim m As New Mouse
Dim s As New Shark
DoSomethingWith(m)
DoSomethingWith(s)
End Sub

' Client code
Public Shared Sub DoSomethingWith(ByVal a As Animal)
a.Bite()
End Sub
End Class

Public MustInherit Class Animal
Public MustOverride Sub Bite()
End Class

Public Class Mouse
Inherits Animal

Public Overrides Sub Bite()
MsgBox("Disease")
End Sub
End Class

Public Class Shark
Inherits Animal

Public Overrides Sub Bite()
MsgBox("ouch!")
End Sub
End Class
The client code uses the type Animal. It does not know what objects it will be given (i.e. instances of what class) but the fact that they support the programmatic interface of the Animal type is enough for it to use them: polymorphism in action.

If someone asks you to explain polymorphism with a VB6 example, you can do something like this:
In a Standard EXE project, change the startup Object in project properties to Sub Main.

Add a module1 with this code:
Sub Main()
Dim m As New Mouse
Dim s As New Shark
DoSomethingWith m
DoSomethingWith s
End Sub

' Client code
Sub DoSomethingWith(ByVal a As Animal)
a.Bite
End Sub
Add a Animal class module:

Public Sub Bite()
End Sub
Add a Mouse class module:
Implements Animal
Private Sub Animal_Bite()
MsgBox "Disease"
End Sub
Add a Shark class module:
Implements Animal
Private Sub Animal_Bite()
MsgBox "ouch!"
End Sub

That's it. Polymorphism in VB6: nothing fake or incomplete about it.

Naturally you can mix many concepts in your designs including polymorphism, inheritance and overloading etc. If you want to see a design combining these topics, then Raymond's post has a nice VB.NET example.

Labels:


Friday, March 04, 2005

 

Here is another question that appears in various guises.
"Event X from control Y gets fired twice" or "I want to stop this event from firing while I am waiting for Z"

For example assume that you do stuff in the event handler of the selectedindexchanged event of a list; you now need to select an item in a list programmatically but do not want to run your event handling in that case (only when it is done by the user). Here is a simple example:

Given a form with a listbox and a button let's assume your event handler looks something like this:
Private Sub ListBox1_SelectedIndexChanged(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles ListBox1.SelectedIndexChanged
MessageBox.Show(ListBox1.SelectedItem.ToString())
End Sub
And let's assume as part of a button click you are doing something like this:
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
ListBox1.SelectedIndex = 0
End Sub
When you click the button you don't want to see a messagebox.

As you'd expect the solution is simple:
1) Use a boolean flag and check it in your event before processing
or
2) Dynamically remove the event handler

1) Define a boolean in your form code like this:
Private mDontRun As Boolean
Change your button click event handler body to be like this:
mDontRun = True
ListBox1.SelectedIndex = 0
mDontRun = False
And add to the selectedindexchanged event handler the following as the first line:
If mDontRun = True Then Return
2) The alternative is not to declare a boolean, not to change the selectedindexchanged event handler and simply change the button click method body like this:
RemoveHandler ListBox1.SelectedIndexChanged, _
AddressOf ListBox1_SelectedIndexChanged
ListBox1.SelectedIndex = 0
AddHandler ListBox1.SelectedIndexChanged, _
AddressOf ListBox1_SelectedIndexChanged
Simple really... and one more entry I can point to from the ng...

Labels:


Monday, February 28, 2005

 

It is amazing how many requests in the newsgroups are something like
"...I have this C# code I need to translate to VB..."
Occasionally we get requests for translating VB to C# and the same principle applies.

If you really need to translate the code, *please* learn how to at least read the other language: it will make you a better .NET developer, guaranteed. There are many sites that list the differences and there are many converters for VB-to-C# or for C#-to-VB or both.

However, if you just picked up a C# file and you need to use it from your VB project you can do just that by adding it to a dll first. Given that the advice leads to more "how to" questions (believe me it does!), here are the steps needed to achieve just that:

1. Create New C# Smart Device project (not for Full Fx)
2. From the wizard select the target and then Class Library. Choose a suitable name and location.
3. Delete the default file Class1.cs
4. Project->Add Existing Item (from the menu)
5. Navigate to the folder where you have saved/downloaded your C# file. Open the file (which makes a copy to the project's directory) or Link to it (which allows you to use it in the project but only keeps one copy on disc)
6. Double-click in solution explorer to view the file. Note the namespace declaration at the top (e.g. namespace OpenNETCF.IO). Optionally change it to the name of your project.
7. Repeat steps 5-6 for any other files
8. Build->Rebuild Solution
9. You now have a dll in the bin\Debug directory under the project you just created

From your exe/application project select from the menu Project->Add Reference and browse to the dll; Select it and OK.

Labels:


Wednesday, February 16, 2005

 

Dim i As Int32
Dim k As Int32 = mCol.Count
For i = 0 To mCol.Count - 1
Me.MethodThat_May_ResultInRemovalFromCol()

If k > mCol.Count Then
i -= 1
k = mCol.Count
End If
If k <= i + 1 Then
Exit For
End If
Next i
Years ago I upgraded some nasty VB6 code that, amongst other things, used the Collection class. The VB.NET Collection object allows removals from it while iterating. If you upgrade it to ArrayList, though, (apart from making changes to reflect the 0 index base), you soon find that ArrayList forbids removing items while iterating. At the time I came up with the above smelly hack. Due to recent refactorings the design/code has been modified, so I am capturing the hack here for posterity...

PS I upgraded Collection to ArrayList for performance reasons
PS2 Locking the collection is irrelevant here
PS3 I am aware of Synchronized - I don't like it
PS4 The real solution is not to remove while iterating; it is bad design.

Labels: