Blog.jmedved.com

If everything went as planned. You should read this blog on new address - blog.jmedved.com. Please do update links for RSS feeds.

POSIX Time

If you are working with Unix a lot, you get to be aware of weird time-stamps. That markup is usually called Unix time (although POSIX time is more precise term) and it represents number of seconds from January 1st 1970 (in UTC).

I was handling some timing issues in log and I needed to convert between local and Unix time. Since I got quite annoyed during day, at night I created small utility to handle this simple task.

Both application and full code is available.

Toolstrip and Case of Extra Line

Illustration

I needed up/down buttons for one program of mine and I decided to have them on ToolStrip control.

As soon as I figured how to make it vertical, I stumbled upon shade issues. In default RenderMode (ManagerRenderMode) background is just little bit of different background color. Usually this does not matter since ToolStrip is docked on form but in this situation it was quite visible. Solution was simple - switch RenderMode to System and control blends in perfectly.

However, using System render mode has one downfall. There is small light 3D line on bottom of control. Although it is not visible too much, it was enough to bother me.

Since System rendering is done via ToolStripSystemRenderer class, first idea was to extend it. And there it was - OnRenderToolStripBorder. It is enough to override this method and border is never created:

class ToolStripBorderlessSystemRenderer : ToolStripSystemRenderer {

    protected override void OnRenderToolStripBorder(ToolStripRenderEventArgs e) {
        //base.OnRenderToolStripBorder(e);
    }

}
[/sourcecode]


Only thing left is to actually telling ToolStrip to use our class for rendering:
```csharp
SettingsForm() {
    InitializeComponent();
    toolstripVhdOrder.Renderer = new ToolStripBorderlessSystemRenderer();
}

Order of Activation

In one of my programs I fixed a bug. I just forgot to restore TopMost state of form. Fix was very simple, just set this.TopMost = true in form’s constructor and everything is solved.

Like every simple fix, this one failed miserably. As soon as I “fixed it” I could not even start program. Even worse thing is that it failed where there was no error before. It didn’t take much to deduce that this change was somehow responsible for triggering error in completely different part of code. It looked like just fact that I have TopMost turned on changes how rest of program is loaded.

I tested program without setting TopMost and order of triggering form’s events was quite expected - first constructor, then Form_Load, then Form_Activated and Form_Shown. However, things do change when you set TopMost property.

It is a subtle change - contructor is still first one to complete, next one is Form_Activated, then Form_Load and Form_Shown is last once more.

If you have code in both Form_Load and Form_Activated you have a minefield in your code. Everything works as expected until you step on mine. In my case mine was TopMost property, but it is not only one that behaves like this.

Solution was dead simple - just move TopMost from constructor to Form_Load and everything works perfectly.

Sample for your testing can be found here.

Case for SysUInt

I was cleaning up one of my old programs and I got interesting warnings from code analysis:

CA1901 : Microsoft.Portability : As it is declared in your code, the return type of P/Invoke 'RichTextBoxEx.NativeMethods.SendMessage(IntPtr, Integer, Integer, Integer)' will be 4 bytes wide on 64-bit platforms. This is not correct, as the actual native declaration of this API indicates it should be 8 bytes wide on 64-bit platforms. Consult the MSDN Platform SDK documentation for help determining what data type should be used instead of 'Integer'.
CA1901 : Microsoft.Portability : As it is declared in your code, parameter 'wParam' of P/Invoke 'RichTextBoxEx.NativeMethods.SendMessage(IntPtr, Integer, Integer, Integer)' will be 4 bytes wide on 64-bit platforms. This is not correct, as the actual native declaration of this API indicates it should be 8 bytes wide on 64-bit platforms. Consult the MSDN Platform SDK documentation for help determining what data type should be used instead of 'Integer'.

I checked other code where I used that function and I noticed that I used IntPtr there. However, although this was technically correct, it didn’t sound right. SendMessage has many purposes and here I was using it to set tab stops. While I could wrap number in IntPtr and pass it like that, I wanted another solution. Solution that will allow me to use number and still have IntPtr-like behavior.

Of course, as usual in .Net P/Interop, solution seemed quite simple. Use whatever data type you wish and just set correct marshaling. I used signature that P/Invoke Interop Assistant generated:

Of course, as usual in .Net P/Interop, solution seemed quite simple. Use whatever data type you wish and just set correct marshaling. I used signature that P/Invoke Interop Assistant generated:

<DllImport("user32.dll", EntryPoint:="SendMessageW")> _
Public Shared Function SendMessageW(<InAttribute()> ByVal hWnd As IntPtr, ByVal Msg As UInteger, <MarshalAs(UnmanagedType.SysUInt)> ByVal wParam As UInteger, <MarshalAs(UnmanagedType.SysInt)> ByVal lParam As Integer) As <MarshalAs(UnmanagedType.SysInt)> Integer
End Function

On next run program greeted me with “Cannot marshal ‘parameter #3’: Invalid managed/unmanaged type combination (Int32/UInt32 must be paired with I4, U4, or Error).”. No matter what I did, it would not budge. It seems that this tool disappointed me once again.

To keep long story short, this is final function I decided to use:

<DllImport("user32.dll", EntryPoint:="SendMessageW", CharSet:=CharSet.Unicode)> _
Friend Shared Function SendMessageW(ByVal hwnd As IntPtr, ByVal Msg As UInteger, ByVal wParam As IntPtr, ByRef lParam As IntPtr) As IntPtr
End Function

Since my code still needed to pass integers I made wrapper to sort that out:

Public Shared Function SendMessage(ByVal hWnd As IntPtr, ByVal Msg As UInteger, ByVal wParam As Integer, ByVal lParam As Integer) As IntPtr
    Return SendMessageW(hWnd, Msg, New IntPtr(wParam), New IntPtr(lParam))
End Function

With that my work was done.

P.S. Yes, you are not mistaken, code is in VB.NET. It is program I wrote a while back and I have no intention to rewrite it in C# just to get single bug fixed.

P.P.S. I find it interesting that LRESULT is really IntPtr although name does not suggest so (at least not to me). HRESULT on other hand is not IntPtr although from name I would deduce it is handle (and thus IntPtr).