This wasn't intended but eventually becomes a followup on "Where is the caret?"
There are two directions to select some text in a rich edit: from left to right, or from right to left. The initial position of a selection is called an anchor point, and the ending position is called an active end. These are the names given in the EM_SETSEL documentation. The four other related messages and notification are: EM_GETSEL, EM_EXSETSEL, EM_EXSETSEL, and EN_SELCHANGE.
Since the caret marks the active position, to reshow or do something interesting with the caret, I need to know where the anchor and active positions are, given a selection range. As it turns out, all of above messages or notification (except EM_SETSEL) assume the start/min being anchor, and end/max being active.
Quite surprisingly, TOM can provide this information, through ITextDocument:GetSelection and ITextSelection:GetFlags (tomSelStartActive). I said surprising because TOM looks more like wrapper interfaces around Word and rich edit controls, but hey, what do I know?
Wednesday, September 15, 2010
Friday, February 5, 2010
What is RelatedImageListAttribute?
One use of RelatedImageListAttribute is to tell ImageIndexEditor where to get the images.
Imagine a class called ClassA with a property called ImageKey, and another property called PropertyB, which is a structure with an ImageList property.
Then, this is how ImageKey should be described:
Imagine a class called ClassA with a property called ImageKey, and another property called PropertyB, which is a structure with an ImageList property.
Then, this is how ImageKey should be described:
[Editor("System.Windows.Forms.Design.ImageIndexEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor))] [RefreshProperties(RefreshProperties.Repaint)] [RelatedImageList("PropertyB.ImageList")] [TypeConverterAttribute(typeof(ImageKeyConverter))] public string ImageKey { set; get; }
Wednesday, December 30, 2009
How to pick folder with OpenFileDialog?
OpenFileDialog allows selection of files, and FolderBrowserDialog allows selection of folders. For various legitimate reasons, I prefer to use OpenFileDialog for folders instead. The question is, how?
Overriding FileDialog.HookProc to obtain the dialog handle to change the dialog behavior is my first attempt, but it is immediately obvious that OpenFileDialog is sealed and cannot be inherited, and I don't feel like reimplementing everything with GetOpenFileName. So an alternative way to get the dialog handle is to cook up a dummy window (as in FileDialog > DummyOwner > RealOwner) and watch for WM_ACTIVATE in the overriden WndProc. As always, be extra careful when playing with window activation.
Now, with the dialog handle, and later the child notification dialog handle, I can do pretty much what I want. I can hide and rename various controls on the dialog, handle BN_CLICKED to accept folder, and more.
A few gotchas include having to do manual marshaling using COM task memory, but no big deal.
Next up is the Vista version.
Overriding FileDialog.HookProc to obtain the dialog handle to change the dialog behavior is my first attempt, but it is immediately obvious that OpenFileDialog is sealed and cannot be inherited, and I don't feel like reimplementing everything with GetOpenFileName. So an alternative way to get the dialog handle is to cook up a dummy window (as in FileDialog > DummyOwner > RealOwner) and watch for WM_ACTIVATE in the overriden WndProc. As always, be extra careful when playing with window activation.
Now, with the dialog handle, and later the child notification dialog handle, I can do pretty much what I want. I can hide and rename various controls on the dialog, handle BN_CLICKED to accept folder, and more.
A few gotchas include having to do manual marshaling using COM task memory, but no big deal.
Next up is the Vista version.
Friday, October 30, 2009
What good is new TTS voice if you are unable to select?
Some people purchase new Text-to-Speech voices to spice up their home automation or screen reader. Or they just want a divorce of Sam or Anna. Regardless, one of the problems when moving to 64-bit Windows is, those voices designed for 32-bit system can't be selected!
Loss of investment? Not yet! While it's true that 64-bit applications can't use those voices, and 64-bit Control Panel Speech Applet can't see them, the 32-bit applet can set one of those as the default voice for 32-bit applications. On Server 2008 R2, its at C:\Windows\SysWOW64\Speech\SpeechUX\sapi.cpl.
Sometimes Microsoft Anna may insist to stay with you. She will if you have used TTS in any 64-bit application.
Now, what if you use TTS in JScript or VBScript in Windows Script Host? Simply run the script in the 32-bit version of wscript.exe or cscript.exe in C:\Windows\SysWOW64.
Loss of investment? Not yet! While it's true that 64-bit applications can't use those voices, and 64-bit Control Panel Speech Applet can't see them, the 32-bit applet can set one of those as the default voice for 32-bit applications. On Server 2008 R2, its at C:\Windows\SysWOW64\Speech\SpeechUX\sapi.cpl.
Sometimes Microsoft Anna may insist to stay with you. She will if you have used TTS in any 64-bit application.
Now, what if you use TTS in JScript or VBScript in Windows Script Host? Simply run the script in the 32-bit version of wscript.exe or cscript.exe in C:\Windows\SysWOW64.
Monday, October 19, 2009
Where is Windows Calendar?
One of the useful applications removed in Windows 7/Server 2008 R2 is the Windows Calendar. I especially missed it because I never want any email/calendar bundle like Windows Live Mail.
Here is how to get it back:
- Copy the Windows Calendar program folder from an earlier Windows installation to your new Windows 7/Server 2008 R2
- Optionally import your existing iCalendar (.ics) file.
- Create a WindowsCalendar folder in C:\Windows\System32\Tasks\Microsoft\Windows (or whereever that is in your install)
- Set the security on that folder to NOT inherit but add existing parent permissions, and to allow Authenticated Users to create files/folders, write attributes/extended attributes, and read permissions.
- Optionally remove the permissions for Local Service and Network Service, and to change the owner to SYSTEM
Wednesday, September 9, 2009
What's up with the Server 2008 desktop graphics bugs?
When Desktop Experience is installed, Server 2008 provides similar visuals as Vista. One thing I noticed though, is that the desktop doesn't act the same as in Vista:
A more robust solution would create a message-only window in the background, listen for the TaskbarCreated message, and reapply the styles to the recreated SysListView32.
These would take care of 1 and 2, but not 3. May be a call to SetWindowTheme would work, but I haven't tried.
* Similar techniques can be used on XP to get the translucent selection rectangle, but will clear the wallpaper.
- The drop shadow on icon text may disappear on hover.
- The selection rectangle has dotted outline as in Windows XP* instead of translucent.
- The selected icons don't have translucent background.
A more robust solution would create a message-only window in the background, listen for the TaskbarCreated message, and reapply the styles to the recreated SysListView32.
These would take care of 1 and 2, but not 3. May be a call to SetWindowTheme would work, but I haven't tried.
* Similar techniques can be used on XP to get the translucent selection rectangle, but will clear the wallpaper.
Why doesn't WTSConnectSession work when I first tried it?
Because the MSDN documentation on WTSConnectSession is wrong.
If you follow it:
And call the function this way, your program will merely disconnect your current session with ERROR_NOT_CONNECTED, leaving you to wonder if the execution of the program stops in the middle of the call.
Once you find out that WTSConnectSession is just a wrapper for the undocumented WinStationConnect, which exists in earlier versions of Windows than its wrapper, you should realize that the first two parameters as documented are swapped.
If you follow it:
Parameters
- LogonId [in]
The logon ID of the current session, where the user of that session has permissions to connect to an existing session.
- TargetLogonId [in]
The logon ID of the session that you want to connect to.
And call the function this way, your program will merely disconnect your current session with ERROR_NOT_CONNECTED, leaving you to wonder if the execution of the program stops in the middle of the call.
Once you find out that WTSConnectSession is just a wrapper for the undocumented WinStationConnect, which exists in earlier versions of Windows than its wrapper, you should realize that the first two parameters as documented are swapped.
Monday, March 30, 2009
How to get the Security tab in XP Home?
Option 1:
How to add the Security tab to Explorer in XP Home (Download link of SCM)
Option 2:
Press F8 and boot to Safe Mode.
Option 3:
XP Home File System Hacks, See security tab and run with WFP disabled like in safe mode! or Security tab, ACLs in XP Home! Yeah!
Other Options:
Use the command line cacls or write your own GUI wrapper.
How to add the Security tab to Explorer in XP Home (Download link of SCM)
Option 2:
Press F8 and boot to Safe Mode.
Option 3:
XP Home File System Hacks, See security tab and run with WFP disabled like in safe mode! or Security tab, ACLs in XP Home! Yeah!
Other Options:
Use the command line cacls or write your own GUI wrapper.
Friday, February 27, 2009
What's the scope of window handles?
Someone stated (though I've yet to find any supporting MSDN documents) that window handles are global to all processes belonging to the same window station. Yet, if you somehow obtain a handle to a window belonging to another desktop, and try to send messages to it, SendMessage may fail with ERROR_INVALID_WINDOW_HANDLE. Why?
If window handles are truly global, then, say, thread 'a' on desktop 'A' should be able to use handles from thread 'b' on desktop 'B', assuming that both desktops belong to the same window station, right? It seems that some magic needs to happen for the handle table to be visible to other desktops, that is, thread 'a' needs to obtain a handle to desktop 'B' with read access.
It appears logical, but needs to be clearly documented!
If window handles are truly global, then, say, thread 'a' on desktop 'A' should be able to use handles from thread 'b' on desktop 'B', assuming that both desktops belong to the same window station, right? It seems that some magic needs to happen for the handle table to be visible to other desktops, that is, thread 'a' needs to obtain a handle to desktop 'B' with read access.
It appears logical, but needs to be clearly documented!
Tuesday, December 30, 2008
Where is the caret?
Carefully minded will spot at least one subtle difference between the Visual Studio editor, and the RichEdit control: the caret in selection. Why oh why I can get this cute blinking cursor when I select some text in Visual Studio, but not in WordPad or in RichTextBox?
Maybe it's hidden for some bizarre reasons, I'll just call ShowCaret to bring it back. Argh, doesn't work? OK, may be HideCaret is called multiple times, so I'll just call ShowCaret a zillion times to see what gives. What? The caret is actually destroyed, and I have to call CreateCaret?
How am I supposed to know the height of the caret? It's not exactly Font.Height, and I can't always get the position of two lines and take the difference.
Text Object Model (TOM) comes into rescue. It's implemented by RichEdit (not Edit, so no hope there, yet). Get an IRichEditOle with EM_GETOLEINTERFACE, then an ITextDocument, then an ITextRange from ITextDocument::Range, and finally the coordinates from ITextRange::GetPoint.
I'll leave it as an exercise to declare the TOM interfaces for .net. Yea this whole thing is purely artistic...
Maybe it's hidden for some bizarre reasons, I'll just call ShowCaret to bring it back. Argh, doesn't work? OK, may be HideCaret is called multiple times, so I'll just call ShowCaret a zillion times to see what gives. What? The caret is actually destroyed, and I have to call CreateCaret?
How am I supposed to know the height of the caret? It's not exactly Font.Height, and I can't always get the position of two lines and take the difference.
Text Object Model (TOM) comes into rescue. It's implemented by RichEdit (not Edit, so no hope there, yet). Get an IRichEditOle with EM_GETOLEINTERFACE, then an ITextDocument, then an ITextRange from ITextDocument::Range, and finally the coordinates from ITextRange::GetPoint.
I'll leave it as an exercise to declare the TOM interfaces for .net. Yea this whole thing is purely artistic...
Friday, June 27, 2008
MSDN Documentation. Follow or Not Follow?
When reading MSDN, often time there are sentences like "do not do XXX" or "you have to do YYY" without further explanation. This time it is on the OFNHookProc page:
It happened that I also came across the HookProc implementation in FileDialog, using the .Net Reflector:
May be the .net team know win32 in and out that outsider rules don't apply to them; or may be sometimes it is acceptable to ignore MSDN.
Do not call the EndDialog function from the hook procedure.
It happened that I also came across the HookProc implementation in FileDialog, using the .Net Reflector:
[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
protected override IntPtr HookProc(IntPtr hWnd, int msg, IntPtr wparam, IntPtr lparam) {
// ......
} catch {
if (this.dialogHWnd != IntPtr.Zero) {
UnsafeNativeMethods.EndDialog(new HandleRef(this, this.dialogHWnd), IntPtr.Zero);
}
throw;
}
// ......
}
May be the .net team know win32 in and out that outsider rules don't apply to them; or may be sometimes it is acceptable to ignore MSDN.
Wednesday, April 30, 2008
How to detect audio in DVR-MS files?
Just yesterday I was working on the audio detection in dvr-ms files in one software. Seeing it as a proprietary format, I didn't expect much information on dvr-ms from the ASF specification or from MSDN
. Some open source projects come to rescue, and the key GUID has been discovered.
Here's how I'm doing it:
When parsing ASF_Binary_Media, if the Major Media Type is 31178c9d-03e1-4528-b5823df9db22f503, and the Format Type is 05589f81-c356-11ce-bf0100aa0055595a or c4c4c4d1-0049-4e2b-98fb9537f6ce516d, then I can expect the Format Data to be WAVEFORMATEX
, padded up to Format Data Size.
Here's how I'm doing it:
When parsing ASF_Binary_Media, if the Major Media Type is 31178c9d-03e1-4528-b5823df9db22f503, and the Format Type is 05589f81-c356-11ce-bf0100aa0055595a or c4c4c4d1-0049-4e2b-98fb9537f6ce516d, then I can expect the Format Data to be WAVEFORMATEX
Tuesday, April 29, 2008
Why Visual Studio Limits Me to a Specific C Runtime?
A while ago I wrote a Windows application with Visual Studio 2008. Everything went fine when tested on the development platform (Server 2003), but when I deployed it on Windows XP, the system complained that the application needed to be reinstalled.
What?
I scratched my head. My application was simple enough that it had only one executable, required no fancy installation, and I double checked that it didn't depend on anything specific to Server 2003.
In fact it did, sort of. It was linked to a version of VC runtime that doesn't ship with Windows XP, according to Dependency Walker.
How did that happen? Turned out that Visual Studio 2008 comes with this latest and greatest msvcr90.dll, and my application happened to use some C library functions. With the "right" compiler settings, msvcrt.lib (version 9) was linked in, and so I had no choice but needed this CRT to run a simple Windows application on XP.
What a great idea. An additional dll that's several times bigger than my application.
Or did I have a choice?
I could statically link the new CRT, thus only need to ship one executable file. Nah, that doesn't solve the size problem.
I could copy the old msvcrt.lib to tell VS that the application needs the old msvcrt.dll, which ships with XP, and thus eliminate the need of msvcr90.dll.
Or so I thought. Or so depends.exe said. What happened was, to use themed common controls, I told VS to include the proper manifest; and to avoid distributing yet another file, I told VS to embed (hide) the manifest in the executable. Little did I know that this manifest also included a dependency on Microsoft.VC90.CRT...
Now I could use Resource Hacker to edit that out. It would have been easier if VS allowed me to choose the version of CRT to begin with.
Of course, in the end, I started from this article and dropped the C runtime functions altogether.
What?
I scratched my head. My application was simple enough that it had only one executable, required no fancy installation, and I double checked that it didn't depend on anything specific to Server 2003.
In fact it did, sort of. It was linked to a version of VC runtime that doesn't ship with Windows XP, according to Dependency Walker.
How did that happen? Turned out that Visual Studio 2008 comes with this latest and greatest msvcr90.dll, and my application happened to use some C library functions. With the "right" compiler settings, msvcrt.lib (version 9) was linked in, and so I had no choice but needed this CRT to run a simple Windows application on XP.
What a great idea. An additional dll that's several times bigger than my application.
Or did I have a choice?
I could statically link the new CRT, thus only need to ship one executable file. Nah, that doesn't solve the size problem.
I could copy the old msvcrt.lib to tell VS that the application needs the old msvcrt.dll, which ships with XP, and thus eliminate the need of msvcr90.dll.
Or so I thought. Or so depends.exe said. What happened was, to use themed common controls, I told VS to include the proper manifest; and to avoid distributing yet another file, I told VS to embed (hide) the manifest in the executable. Little did I know that this manifest also included a dependency on Microsoft.VC90.CRT...
Now I could use Resource Hacker to edit that out. It would have been easier if VS allowed me to choose the version of CRT to begin with.
Of course, in the end, I started from this article and dropped the C runtime functions altogether.
Thursday, February 7, 2008
How to modify PropertyGrid's internal controls?
PropertyGrid is a wonderful control. But this post is not to praise it; more to solve a practical problem.
PropertyGrid has an internal ToolStrip, among other controls. This ToolStrip provides sorting commands, but sometimes it's necessary to add other commands to it, like importing and exporting reflected data, settings, etc.
This ToolStrip is not publicly exposed, so one alternate solution is to set ToolbarVisible = false and provide a custom ToolStrip.
But wouldn't it be easier if the internal ToolStrip can be modified at will?
That can't be too hard. The following code should do:
Now place MyPropertyGrid on a form, and check the Properties Window. There! Change InternalToolStrip any way seems fit.
Of course, this world ain't perfect. It's less desirable to edit this ToolStrip only from the Properties Window; plus, there's a bigger problem at runtime. They will be covered next.
PropertyGrid has an internal ToolStrip, among other controls. This ToolStrip provides sorting commands, but sometimes it's necessary to add other commands to it, like importing and exporting reflected data, settings, etc.
This ToolStrip is not publicly exposed, so one alternate solution is to set ToolbarVisible = false and provide a custom ToolStrip.
But wouldn't it be easier if the internal ToolStrip can be modified at will?
That can't be too hard. The following code should do:
public partial class MyPropertyGrid : System.Windows.Forms.PropertyGrid {
public ToolStrip InternalToolStrip {
get {
foreach (Control ctrl in base.Controls) {
if (ctrl is ToolStrip) {
return ctrl as ToolStrip;
}
}
return null;
}
}
}
Now place MyPropertyGrid on a form, and check the Properties Window. There! Change InternalToolStrip any way seems fit.
Of course, this world ain't perfect. It's less desirable to edit this ToolStrip only from the Properties Window; plus, there's a bigger problem at runtime. They will be covered next.
Sunday, November 11, 2007
RichTextBox is not a simple RichEdit wrapper?
There are several annoyances with the Rich Edit control. For example, using EM_STREAMIN / EM_STREAMOUT with EditStreamCallback will clear its undo and redo queues. Why would this caused by simply reading the stream is beyond me.
The .net RichTextBox inherits this problem, which means accessing its Text, Rtf, and Lines properties will clear the queues. What's more, calling its GetCharIndexFromPosition and GetPositionFromCharIndex functions will also clear the queues. Why is that? Don't GetCharIndexFromPosition and GetPositionFromCharIndex just wrap EM_CHARFROMPOS and EM_POSFROMCHAR?
Workarounds?
The .net RichTextBox inherits this problem, which means accessing its Text, Rtf, and Lines properties will clear the queues. What's more, calling its GetCharIndexFromPosition and GetPositionFromCharIndex functions will also clear the queues. Why is that? Don't GetCharIndexFromPosition and GetPositionFromCharIndex just wrap EM_CHARFROMPOS and EM_POSFROMCHAR?
Workarounds?
- Replace functions GetCharIndexFromPosition / GetPositionFromCharIndex with EM_CHARFROMPOS / EM_POSFROMCHAR
- Replace property Text with EM_GETTEXTEX / RichTextBox.SelectAll / SelectedText
- Replace property Lines with EM_GETTEXTEX / String.Split / Join
- Replace property Rtf with... no idea.
Friday, October 5, 2007
Why ToolStrip responses to WM_MOUSEACTIVATE differently?
When a user clicks on a .Net ToolStrip (or MsoCommandBar) on which the top-level window is not focused, the window will be focused (duh) but the ToolStrip will not process the associated mouse event. For example, when a user clicks on the New button when Microsoft Outlook is not focused, the new message dialog will not appear.
This is apparently due to the ToolStrip returning MA_ACTIVATEANDEAT on WM_MOUSEACTIVATE. I found this when I was coding my ToolStrip based tab control. A simple fix would be to return MA_ACTIVATE instead:
Annoying, and won't be cross platform, but at least it works!
This is apparently due to the ToolStrip returning MA_ACTIVATEANDEAT on WM_MOUSEACTIVATE. I found this when I was coding my ToolStrip based tab control. A simple fix would be to return MA_ACTIVATE instead:
protected const int WM_MOUSEACTIVATE = 0x0021;
protected const int MA_ACTIVATE = 1;
protected override void WndProc(ref Message m) {
if (m.Msg == WM_MOUSEACTIVATE) {
m.Result = new IntPtr(MA_ACTIVATE);
return;
}
base.WndProc(ref m);
}
Annoying, and won't be cross platform, but at least it works!
Tuesday, September 18, 2007
Will it fly?
Just bought a Micro Flyers™ Excalibur 8635R-1 couple days ago. Small, light, battery- powered, featuring double- blade rotors (top one is "stabilizer") and tail rotor, with a simple controller, anyone can be a pilot!
Or so I thought.
It's actually my first RC chopper. When I laid my hands on the controller I realized that I had no idea how to fly this thing. The throttle was too sensitive and I always crashed the black hawk seconds* after a successful† takeoff. With my !skill, I could only hold it up for another shot.
As with everything else, a little practice can help, and I'm getting better. Now I start to hate the "simplicity" of the controller, and wish it's multi-directional. But I guess for $25 I can't really ask for more...
* error C2146: syntax error : missing 'nano' before identifier 'seconds'
† #define successful 0.00000001
Wednesday, September 12, 2007
What's wrong with WS_EX_LAYERED and WS_EX_COMPOSITED?
From MSDN:
If WS_EX_COMPOSITED is set on a dialog window (e.g. to reduce flickers from child windows resize), then, with the default XP theme, mouse hover on the minimize button or mouse down on the close button will have no visual feedback. It seems that the back buffer for the non client area does not get updated. One obvious workaround is to only add WS_EX_COMPOSITED on WM_ENTERSIZEMOVE and remove it on WM_EXITSIZEMOVE.
What if WS_EX_LAYERED is also set? The above workaround will not work, and worse, the client area will have all sorts of update problems: edit window will not show or receive blinking insertion point, push button will not show sunken/raised border on mouse down/up. Again it seems that the back buffer is not updated. Yet another obvious workaround is to only add WS_EX_LAYERED on WM_ENTERSIZEMOVE and remove it on WM_EXITSIZEMOVE.
What's next? I'm waiting to be hit by more poorly documented styling bugs.
WS_EX_COMPOSITED- Windows XP: Paints all descendants of a window in bottom-to-top painting order using double-buffering. For more information, see Remarks. This cannot be used if the window has a class style of either CS_OWNDC or CS_CLASSDC.
Remarks
WS_EX_LAYERED- Windows 2000/XP: Creates a layered window. Note that this cannot be used for child windows. Also, this cannot be used if the window has a class style of either CS_OWNDC or CS_CLASSDC.
Windows XP: With WS_EX_COMPOSITED set, all descendants of a window get bottom-to-top painting order using double-buffering. Bottom-to-top painting order allows a descendent window to have translucency (alpha) and transparency (color-key) effects, but only if the descendent window also has the WS_EX_TRANSPARENT bit set. Double-buffering allows the window and its descendents to be painted without flicker.
If WS_EX_COMPOSITED is set on a dialog window (e.g. to reduce flickers from child windows resize), then, with the default XP theme, mouse hover on the minimize button or mouse down on the close button will have no visual feedback. It seems that the back buffer for the non client area does not get updated. One obvious workaround is to only add WS_EX_COMPOSITED on WM_ENTERSIZEMOVE and remove it on WM_EXITSIZEMOVE.
What if WS_EX_LAYERED is also set? The above workaround will not work, and worse, the client area will have all sorts of update problems: edit window will not show or receive blinking insertion point, push button will not show sunken/raised border on mouse down/up. Again it seems that the back buffer is not updated. Yet another obvious workaround is to only add WS_EX_LAYERED on WM_ENTERSIZEMOVE and remove it on WM_EXITSIZEMOVE.
What's next? I'm waiting to be hit by more poorly documented styling bugs.
Subscribe to:
Posts (Atom)