C#: Using IProgressDialog to show a “native” Progress Dialog from .NET in Windows

17. August 2009

CSharp-IProgressDialog-Native-dotNet-in-Windows A few months ago I posted some code that I originally wrote back in about 2004… Well, I was looking through some more of my prototypes that I’ve written and I came across the following example of how to use the IProgressDialog Win32 Interface to harness the power of the Built-in Progress Dialog in Windows within your own .NET applications.

I have test this on WIndows 7, but it should work as expected on Windows XP and Vista. The MSDN Documentation for IProgressDialog says it’s minimum supported operating systems are Windows 2000 and Windows XP.

The full source code for this example is listed below. Just copy from this post and paste it within your app to test.

“ProgressDialog” Usage Example

Here’s an example of how to use this example “ProgressDialog” class: This is an example of using it from Windows Forms, however it could be used from within WPF exactly the same way.

using System;
using System.Windows.Forms;

namespace WindowsProgressDialog
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private Pietschsoft.ProgressDialog pd;
        private uint progressPercent;

        private void button1_Click(object sender, EventArgs e)
        {
            pd = new Pietschsoft.ProgressDialog(this.Handle);
            pd.Title = "Performing Operation";
            pd.CancelMessage = "Please wait while the operation is cancelled";
            pd.Maximum = 100;
            pd.Value = 0;
            pd.Line1 = "Line One";
            pd.Line3 = "Calculating Time Remaining...";
            
            //pd.ShowDialog(); // Defaults to PROGDLG.Normal
            pd.ShowDialog(Pietschsoft.ProgressDialog.PROGDLG.Modal, Pietschsoft.ProgressDialog.PROGDLG.AutoTime, Pietschsoft.ProgressDialog.PROGDLG.NoMinimize);

            progressPercent = 0;
            timer1.Start();
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            progressPercent++;

            if (pd.HasUserCancelled)
            {
                timer1.Stop();
                pd.CloseDialog();
            }
            else
            {
                // Update the progress value
                pd.Value = progressPercent;

                pd.Line2 = "Percent " + progressPercent.ToString() + "%";

                if (progressPercent >= 100)
                {
                    timer1.Stop();
                    pd.CloseDialog();
                }
            }
        }
    }
}

 

“ProgressDialog” Class

And, here’s the full code for the “ProgressDialog” class itself:

using System;
using System.Runtime.InteropServices;
using System.Text;

namespace Pietschsoft
{
    public class ProgressDialog
    {
        private IntPtr _parentHandle;

        private Win32IProgressDialog pd = null;

        public ProgressDialog(IntPtr parentHandle)
        {
            this._parentHandle = parentHandle;
        }

        public void ShowDialog(params PROGDLG[] flags)
        {
            if (pd == null)
            {
                pd = (Win32IProgressDialog)new Win32ProgressDialog();
                
                pd.SetTitle(this._Title);
                pd.SetCancelMsg(this._CancelMessage, null);
                pd.SetLine(1, this._Line1, false, IntPtr.Zero);
                pd.SetLine(2, this._Line2, false, IntPtr.Zero);
                pd.SetLine(3, this._Line3, false, IntPtr.Zero);

                PROGDLG dialogFlags = PROGDLG.Normal;
                if (flags.Length != 0)
                {
                    dialogFlags = flags[0];
                    for (var i = 1; i < flags.Length; i++)
                    {
                        dialogFlags = dialogFlags | flags[i];
                    }
                }
                
                pd.StartProgressDialog(this._parentHandle, null, dialogFlags, IntPtr.Zero);
            }
        }

        public void CloseDialog()
        {
            if (pd != null)
            {
                pd.StopProgressDialog();
                //Marshal.ReleaseComObject(pd);
                pd = null;
            }
        }

        private string _Title = string.Empty;
        public string Title
        {
            get
            {
                return this._Title;
            }
            set
            {
                this._Title = value;
                if (pd != null)
                {
                    pd.SetTitle(this._Title);
                }
            }
        }

        private string _CancelMessage = string.Empty;
        public string CancelMessage
        {
            get
            {
                return this._CancelMessage;
            }
            set
            {
                this._CancelMessage = value;
                if (pd != null)
                {
                    pd.SetCancelMsg(this._CancelMessage, null);
                }
            }
        }

        private string _Line1 = string.Empty;
        public string Line1
        {
            get
            {
                return this._Line1;
            }
            set
            {
                this._Line1 = value;
                if (pd != null)
                {
                    pd.SetLine(1, this._Line1, false, IntPtr.Zero);
                }
            }
        }

        private string _Line2 = string.Empty;
        public string Line2
        {
            get
            {
                return this._Line2;
            }
            set
            {
                this._Line2 = value;
                if (pd != null)
                {
                    pd.SetLine(2, this._Line2, false, IntPtr.Zero);
                }
            }
        }

        private string _Line3 = string.Empty;
        public string Line3
        {
            get
            {
                return this._Line3;
            }
            set
            {
                this._Line3 = value;
                if (pd != null)
                {
                    pd.SetLine(3, this._Line3, false, IntPtr.Zero);
                }
            }
        }

        private uint _value = 0;
        public uint Value
        {
            get
            {
                return this._value;
            }
            set
            {
                this._value = value;
                if (pd != null)
                {
                    pd.SetProgress(this._value, this._maximum);
                }
            }
        }

        private uint _maximum = 100;
        public uint Maximum
        {
            get
            {
                return this._maximum;
            }
            set
            {
                this._maximum = value;
                if (pd != null)
                {
                    pd.SetProgress(this._value, this._maximum);
                }
            }
        }
        public bool HasUserCancelled
        {
            get
            {
                if (pd != null)
                {
                    return pd.HasUserCancelled();
                }
                else
                    return false;
            }
        }

        #region "Win32 Stuff"
        // The below was copied from: http://pinvoke.net/default.aspx/Interfaces/IProgressDialog.html

        public static class shlwapi
        {
            [DllImport("shlwapi.dll", CharSet = CharSet.Auto)]
            static extern bool PathCompactPath(IntPtr hDC, [In, Out] StringBuilder pszPath, int dx);
        }

        [ComImport]
        [Guid("EBBC7C04-315E-11d2-B62F-006097DF5BD4")]
        [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
        public interface Win32IProgressDialog
        {
            /// <summary>
            /// Starts the progress dialog box.
            /// </summary>
            /// <param name="hwndParent">A handle to the dialog box's parent window.</param>
            /// <param name="punkEnableModless">Reserved. Set to null.</param>
            /// <param name="dwFlags">Flags that control the operation of the progress dialog box. </param>
            /// <param name="pvResevered">Reserved. Set to IntPtr.Zero</param>
            void StartProgressDialog(
                IntPtr hwndParent, //HWND
                [MarshalAs(UnmanagedType.IUnknown)]    object punkEnableModless, //IUnknown
                PROGDLG dwFlags,  //DWORD
                IntPtr pvResevered //LPCVOID
                );

            /// <summary>
            /// Stops the progress dialog box and removes it from the screen.
            /// </summary>
            void StopProgressDialog();

            /// <summary>
            /// Sets the title of the progress dialog box.
            /// </summary>
            /// <param name="pwzTitle">A pointer to a null-terminated Unicode string that contains the dialog box title.</param>
            void SetTitle(
                [MarshalAs(UnmanagedType.LPWStr)] string pwzTitle //LPCWSTR
                );

            /// <summary>
            /// Specifies an Audio-Video Interleaved (AVI) clip that runs in the dialog box. Note: Note  This method is not supported in Windows Vista or later versions.
            /// </summary>
            /// <param name="hInstAnimation">An instance handle to the module from which the AVI resource should be loaded.</param>
            /// <param name="idAnimation">An AVI resource identifier. To create this value, use the MAKEINTRESOURCE macro. The control loads the AVI resource from the module specified by hInstAnimation.</param>
            void SetAnimation(
                IntPtr hInstAnimation, //HINSTANCE
                ushort idAnimation //UINT
                );

            /// <summary>
            /// Checks whether the user has canceled the operation.
            /// </summary>
            /// <returns>TRUE if the user has cancelled the operation; otherwise, FALSE.</returns>
            /// <remarks>
            /// The system does not send a message to the application when the user clicks the Cancel button.
            /// You must periodically use this function to poll the progress dialog box object to determine
            /// whether the operation has been canceled.
            /// </remarks>
            [PreserveSig]
            [return: MarshalAs(UnmanagedType.Bool)]
            bool HasUserCancelled();

            /// <summary>
            /// Updates the progress dialog box with the current state of the operation.
            /// </summary>
            /// <param name="dwCompleted">An application-defined value that indicates what proportion of the operation has been completed at the time the method was called.</param>
            /// <param name="dwTotal">An application-defined value that specifies what value dwCompleted will have when the operation is complete.</param>
            void SetProgress(
                uint dwCompleted, //DWORD
                uint dwTotal //DWORD
                );

            /// <summary>
            /// Updates the progress dialog box with the current state of the operation.
            /// </summary>
            /// <param name="ullCompleted">An application-defined value that indicates what proportion of the operation has been completed at the time the method was called.</param>
            /// <param name="ullTotal">An application-defined value that specifies what value ullCompleted will have when the operation is complete.</param>
            void SetProgress64(
                ulong ullCompleted, //ULONGLONG
                ulong ullTotal //ULONGLONG
                );

            /// <summary>
            /// Displays a message in the progress dialog.
            /// </summary>
            /// <param name="dwLineNum">The line number on which the text is to be displayed. Currently there are three lines—1, 2, and 3. If the PROGDLG_AUTOTIME flag was included in the dwFlags parameter when IProgressDialog::StartProgressDialog was called, only lines 1 and 2 can be used. The estimated time will be displayed on line 3.</param>
            /// <param name="pwzString">A null-terminated Unicode string that contains the text.</param>
            /// <param name="fCompactPath">TRUE to have path strings compacted if they are too large to fit on a line. The paths are compacted with PathCompactPath.</param>
            /// <param name="pvResevered"> Reserved. Set to IntPtr.Zero.</param>
            /// <remarks>This function is typically used to display a message such as "Item XXX is now being processed." typically, messages are displayed on lines 1 and 2, with line 3 reserved for the estimated time.</remarks>
            void SetLine(
                uint dwLineNum, //DWORD
                [MarshalAs(UnmanagedType.LPWStr)] string pwzString, //LPCWSTR
                [MarshalAs(UnmanagedType.VariantBool)] bool fCompactPath, //BOOL
                IntPtr pvResevered //LPCVOID
                );

            /// <summary>
            /// Sets a message to be displayed if the user cancels the operation.
            /// </summary>
            /// <param name="pwzCancelMsg">A pointer to a null-terminated Unicode string that contains the message to be displayed.</param>
            /// <param name="pvResevered">Reserved. Set to NULL.</param>
            /// <remarks>Even though the user clicks Cancel, the application cannot immediately call
            /// IProgressDialog::StopProgressDialog to close the dialog box. The application must wait until the
            /// next time it calls IProgressDialog::HasUserCancelled to discover that the user has canceled the
            /// operation. Since this delay might be significant, the progress dialog box provides the user with
            /// immediate feedback by clearing text lines 1 and 2 and displaying the cancel message on line 3.
            /// The message is intended to let the user know that the delay is normal and that the progress dialog
            /// box will be closed shortly.
            /// It is typically is set to something like "Please wait while ...". </remarks>
            void SetCancelMsg(
                [MarshalAs(UnmanagedType.LPWStr)] string pwzCancelMsg, //LPCWSTR
                object pvResevered //LPCVOID
                );

            /// <summary>
            /// Resets the progress dialog box timer to zero.
            /// </summary>
            /// <param name="dwTimerAction">Flags that indicate the action to be taken by the timer.</param>
            /// <param name="pvResevered">Reserved. Set to NULL.</param>
            /// <remarks>
            /// The timer is used to estimate the remaining time. It is started when your application
            /// calls IProgressDialog::StartProgressDialog. Unless your application will start immediately,
            /// it should call Timer just before starting the operation.
            /// This practice ensures that the time estimates will be as accurate as possible. This method
            /// should not be called after the first call to IProgressDialog::SetProgress.</remarks>
            void Timer(
                PDTIMER dwTimerAction, //DWORD
                object pvResevered //LPCVOID
                );

        }

        [ComImport]
        [Guid("F8383852-FCD3-11d1-A6B9-006097DF5BD4")]
        public class Win32ProgressDialog
        {
        }

        /// <summary>
        /// Flags that indicate the action to be taken by the ProgressDialog.SetTime() method.
        /// </summary>
        public enum PDTIMER : uint //DWORD
        {
            /// <summary>Resets the timer to zero. Progress will be calculated from the time this method is called.</summary>
            Reset = (0x01),
            /// <summary>Progress has been suspended.</summary>
            Pause = (0x02),
            /// <summary>Progress has been resumed.</summary>
            Resume = (0x03)
        }

        [Flags]
        public enum PROGDLG : uint //DWORD
        {
            /// <summary>Normal progress dialog box behavior.</summary>
            Normal = 0x00000000,
            /// <summary>The progress dialog box will be modal to the window specified by hwndParent. By default, a progress dialog box is modeless.</summary>
            Modal = 0x00000001,
            /// <summary>Automatically estimate the remaining time and display the estimate on line 3. </summary>
            /// <remarks>If this flag is set, IProgressDialog::SetLine can be used only to display text on lines 1 and 2.</remarks>
            AutoTime = 0x00000002,
            /// <summary>Do not show the "time remaining" text.</summary>
            NoTime = 0x00000004,
            /// <summary>Do not display a minimize button on the dialog box's caption bar.</summary>
            NoMinimize = 0x00000008,
            /// <summary>Do not display a progress bar.</summary>
            /// <remarks>Typically, an application can quantitatively determine how much of the operation remains and periodically pass that value to IProgressDialog::SetProgress. The progress dialog box uses this information to update its progress bar. This flag is typically set when the calling application must wait for an operation to finish, but does not have any quantitative information it can use to update the dialog box.</remarks>
            NoProgressBar = 0x00000010
        }
        #endregion
    }
}

C#, Win32API ,

.NET Framework: Communicate through NAT Router via UPnP (Universal Plug and Play)

5. February 2009

I've been working on an application recently that needs to be able to communicate through a router/firewall using TCP. I've read/heard a bit of information about NAT Routers and UPnP; the technoligies used in almost every router sold commercially. So, I knew that you could use the Universal Plug and Play (UPnP) features of the NAT Router to automatically open up the firewall via Port Forwarding to allow other computers on the Internet to connect directly to the one your application is running on. One thing I didn't know what that Windows (since Windows XP) has the NATUPnP 1.0 Type Library (NATUPNP.DLL) COM Component that you can utilize within your applications to do this for you.

I haven't found very many articles online (via Google search) on using the NATUPnP 1.0 Type Library; there's pretty much only small code snippets available. So, I've decided to explain a little further, in this article, the What and Why of communicating through a NAT Router utilizing UPnP.

What is NAT (Network Address Translation)?

Network Address Translation is the method employed by a Network Router (and Wireless Routers) that allows you to have multiple computers connected to the internet using your home/work broadband internet connection. Your Router gets assigned an Internet IP Address by your Internet Service Provider, and then the Router assigns a Private IP Address to each computer on your network. The Router then "routes" Incoming and Outgoing network traffic from the Internet to the appropriate computer on your Private Network. This technique is also known as "IP masquerading", since it hides your entire Private Network from the Internet making it appear as if it were only a single computer connected to the Internet.

One of the biggest side effets of NAT is that it essentially turns your Router into a Firewall. This is because when any unsolicited incoming traffic gets sent you the Router, it has no idea which computer on the Private Network might be the intended recipient. So, consiquentially, it just ignores or blocks the unsolicited incoming traffic. This is also the feature that allows you to safely enable File and Printer Sharing between the computers on the Private Network without the risk of any computer/server on the Internet being able to copy your files.

Also worth noting that NAT can be implemented by other devices than a Network Router, such as: a computer running Windows with Internet Connection Sharing and Windows Firewall, or a computer running software that provides NAT.

You can read more about NAT at the following links:

http://en.wikipedia.org/wiki/Network_address_translation

http://en.wikipedia.org/wiki/NAT_traversal

What is Port Forwarding / Static NAT?

Port Forwarding, also called "Static NAT", allows computers on the Internet to connect directly to a specific computer on your Private Network. This works by configuring your NAT Router to forward any incoming traffic on a specific Port to a specific computer on your network. For example you can run a Web Server by setting the NAT Router to forward all incoming traffic on Port 80 to your Web Server on your Private Network.

You can read more aboug Port Forwarding at the following link:

http://en.wikipedia.org/wiki/Port_forwarding

What is Universal Plug and Play (UPnP)?

Universal Plug and Play (UPnP) is a simple communications mechanism that allows computers and network appliances to auto-configure Port Forwarding on a NAT Router. This greatly simplifies the setup of P2P networks such that the user doesn't need to configure the NAT Router, instead the computer can connect directly to the Router and setup the appropriate Port Forwarding to allow it to accept direct connections from external computers on the Internet.

This is essentially the technology that allows Peer-to-Peer Software (like Bit Torrent) to communicate through a NAT Router (Firewall) and connect directly to other computers across the Internet. There is also alot of Software and Devices that rely on this to function, such as Windows Live Messenger and XBox 360.

You can read more about UPnP at the following link:

http://en.wikipedia.org/wiki/Universal_Plug_and_Play

Other Resources on Network Address Translation (NAT)

Including the above links there are many articles online that discuss in further detail what NAT and UPnP is. One resource that I really recommend is Episode #42 of the Security Now Podcast with Steve Gibson. If you prefer not to listen to the podcast, there is also a full transcript available for you to read. I really encourage you to read or listen to this.

Security Now! #42 - NAT Traversal

Using the NATUPnP 1.0 Type Library to Setup Port Forwarding

The NATUPnP 1.0 Type Library (NATUPNP.DLL) COM Component, which is part of Windows (since Windows XP), makes it possible to easily manage Network Address Translation (NAT) through Universal Plug and Play (UPnP). This library provides the ability to configure port mappings on a remote Gateway Device (IGD) that uses NAT. It is worth mentioning that the remote Gateway Device could be another computer running Windows with Internet Connection Sharing and Windows Firewall, or a dedicated hardware device.

The API Reference for the NATUPnP 1.0 Type Library can be found here: http://msdn.microsoft.com/en-us/library/aa366276(VS.85).aspx

In order to use the NATUPnP Library you need to first add a reference to it to your project within Visual Studio. To do so open the Add Reference dialog, click the COM tab and select the "NATUPnP 1.0 Type Library". The resulting reference that's added will be named "NATUPNPLib" and will be the namespace that contains all the functionality contained within the library (all Interfaces and Classes).



The NATUPnP 1.0 Type Library makes it so easy to setup Port Forwarding it literally only requires a couple lines of code.

Get a listing of all existing Static Port Mappings

To get a list of all the Static Port Mappings that are already setup in your NAT enabled Internet Gateway Device (such as Router) you simply create an instance of the UPnPNATClass object and access its StaticPortMappingCollection property.

NATUPNPLib.UPnPNATClass upnpnat = new NATUPNPLib.UPnPNATClass();
NATUPNPLib.IStaticPortMappingCollection mappings = upnpnat.StaticPortMappingCollection;

Once you get the collection of existing port mapping, you can iterate through them just as you would any normal Collection of objects:

foreach(NATUPNPLib.IStaticPortMapping portMapping in mappings)
{
    // do something with the port mapping, such as displaying it in a listbox
}

Add New Static Port Mapping

To add a new Static Port Mapping you simply call the Add method of the IStaticPortMappingCollection object that is returned by the UPnPNATClass.StaticPortMappingCollection property.

// Here's an example of opening up TCP Port 80 to forward to a specific Computer on the Private Network
mappings.Add(80, "TCP", 80, "192.168.1.100", true, "Local Web Server");

// Here's an example of forwarding the UDP traffic of Internet Port 80 to Port 8080 on a Computer on the Private Network
mappings.Add(80, "UDP", 8080, "192.168.1.100", true, "Local Web Server");

Remove Existing Static Port Mapping

To remove an existing Static Port Mapping you simply call the Remove method of the IStaticPortMappingCollection object that is returned by the UPnPNATClass.StaticPortMappingCollection property.

// Remove TCP forwarding for Port 80
mappings.Remove(80, "TCP");

// Remove UDP forwarding for Port 8080
mappings.Remove(8080, "UDP");

Example Port Forwarding Management Application

While working with the NATUPnP 1.0 Type Library I created a simple application that allows you to maintain the Static Port Mappings that are setup on your Private Networks NAT Router.

Download Sample: NATUPnPPortForwardManager.zip (20.68 kb)


Conclusion

Setup up Port Forwarding on your local router via UPnP makes it extremely convenient for users of your networking application. Instead of requiring them to manually setup Port Forwarding, we can automatically set it up for them. Many popular P2P applications have utilized this functionality for years, and I'm positive you've used at least one of these applications already at some point.

C#, vb.net, Win32API , , ,

C#: Flash Window in Taskbar via Win32 FlashWindowEx

26. January 2009

The Windows API (Win32) has the FlashWindowEx method within the User32 library; this method allows you (the developer) to Flash a Window, signifying to the user that some major event occurred within the application that requires their attention. The most common use of this is to flash the window until the user returns focus to the application. However, you can also flash the window a specified number of times, or just keep flashing it until you decide when to stop.

The use of the FlashWindowEx method however isn't built into the .NET Framework anywhere. In order to access it you need to use the Platform Invoke (PInvoke) features of .NET to "drop" down to the Windows API (Win32) and call it directly. Also, as with many other functionalities in the Windows API (that aren't directly exposed by .NET) the FlashWindowEx method can be a little tricky to use if you aren't familiar with working with the Windows API from within .NET.

Now rather than go too deep into the specifics of PInvoke or the Win32 FlashWindowEx method, below is a simple static class in C# that allows you to easily utilize this method. There is actually quite a bit of information needed to explain how to use PInvoke to utilize the Windows API (Win32), so maybe I'll cover that in a future article.

Here's some example usage of this static class:

// One this to note with this example usage code, is the "this" keyword is referring to
// the current System.Windows.Forms.Form.

// Flash window until it recieves focus
FlashWindow.Flash(this);

// Flash window 5 times
FlashWindow.Flash(this, 5);

// Start Flashing "Indefinately"
FlashWindow.Start(this);

// Stop the "Indefinate" Flashing
FlashWindow.Stop(this);

One thing to note about the FlashWindowEx method is that it requires (and will only work on) Windows 2000 or later.

Here's the code for the static class in C#:

public static class FlashWindow
{
    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool FlashWindowEx(ref FLASHWINFO pwfi);

    [StructLayout(LayoutKind.Sequential)]
    private struct FLASHWINFO
    {
        /// <summary>
        /// The size of the structure in bytes.
        /// </summary>
        public uint cbSize;
        /// <summary>
        /// A Handle to the Window to be Flashed. The window can be either opened or minimized.
        /// </summary>
        public IntPtr hwnd;
        /// <summary>
        /// The Flash Status.
        /// </summary>
        public uint dwFlags;
        /// <summary>
        /// The number of times to Flash the window.
        /// </summary>
        public uint uCount;
        /// <summary>
        /// The rate at which the Window is to be flashed, in milliseconds. If Zero, the function uses the default cursor blink rate.
        /// </summary>
        public uint dwTimeout;
    }

    /// <summary>
    /// Stop flashing. The system restores the window to its original stae.
    /// </summary>
    public const uint FLASHW_STOP = 0;
   
    /// <summary>
    /// Flash the window caption.
    /// </summary>
    public const uint FLASHW_CAPTION = 1;
   
    /// <summary>
    /// Flash the taskbar button.
    /// </summary>
    public const uint FLASHW_TRAY = 2;
   
    /// <summary>
    /// Flash both the window caption and taskbar button.
    /// This is equivalent to setting the FLASHW_CAPTION | FLASHW_TRAY flags.
    /// </summary>
    public const uint FLASHW_ALL = 3;

    /// <summary>
    /// Flash continuously, until the FLASHW_STOP flag is set.
    /// </summary>
    public const uint FLASHW_TIMER = 4;

    /// <summary>
    /// Flash continuously until the window comes to the foreground.
    /// </summary>
    public const uint FLASHW_TIMERNOFG = 12;


    /// <summary>
    /// Flash the spacified Window (Form) until it recieves focus.
    /// </summary>
    /// <param name="form">The Form (Window) to Flash.</param>
    /// <returns></returns>
    public static bool Flash(System.Windows.Forms.Form form)
    {
        // Make sure we're running under Windows 2000 or later
        if (Win2000OrLater)
        {
            FLASHWINFO fi = Create_FLASHWINFO(form.Handle, FLASHW_ALL | FLASHW_TIMERNOFG, uint.MaxValue, 0);
            return FlashWindowEx(ref fi);
        }
        return false;
    }

    private static FLASHWINFO Create_FLASHWINFO(IntPtr handle, uint flags, uint count, uint timeout)
    {
        FLASHWINFO fi = new FLASHWINFO();
        fi.cbSize = Convert.ToUInt32(Marshal.SizeOf(fi));
        fi.hwnd = handle;
        fi.dwFlags = flags;
        fi.uCount = count;
        fi.dwTimeout = timeout;
        return fi;
    }

    /// <summary>
    /// Flash the specified Window (form) for the specified number of times
    /// </summary>
    /// <param name="form">The Form (Window) to Flash.</param>
    /// <param name="count">The number of times to Flash.</param>
    /// <returns></returns>
    public static bool Flash(System.Windows.Forms.Form form, uint count)
    {
        if (Win2000OrLater)
        {
            FLASHWINFO fi = Create_FLASHWINFO(form.Handle, FLASHW_ALL, count, 0);
            return FlashWindowEx(ref fi);
        }
        return false;
    }

    /// <summary>
    /// Start Flashing the specified Window (form)
    /// </summary>
    /// <param name="form">The Form (Window) to Flash.</param>
    /// <returns></returns>
    public static bool Start(System.Windows.Forms.Form form)
    {
        if (Win2000OrLater)
        {
            FLASHWINFO fi = Create_FLASHWINFO(form.Handle, FLASHW_ALL, uint.MaxValue, 0);
            return FlashWindowEx(ref fi);
        }
        return false;
    }

    /// <summary>
    /// Stop Flashing the specified Window (form)
    /// </summary>
    /// <param name="form"></param>
    /// <returns></returns>
    public static bool Stop(System.Windows.Forms.Form form)
    {
        if (Win2000OrLater)
        {
            FLASHWINFO fi = Create_FLASHWINFO(form.Handle, FLASHW_STOP, uint.MaxValue, 0);
            return FlashWindowEx(ref fi);
        }
        return false;
    }

    /// <summary>
    /// A boolean value indicating whether the application is running on Windows 2000 or later.
    /// </summary>
    private static bool Win2000OrLater
    {
        get { return System.Environment.OSVersion.Version.Major >= 5; }
    }
}

 

C#, Win32API , ,

Add System Menu Items to WPF Window using Win32 API

27. March 2008

A couple weeks ago, I blog how to "Add System Menu Items to a Form using Windows API". Being that WPF has it's differences from Windows Forms, I began to wonder how simple or difficult this would be to do within a WPF application. As it turns out, there are a couple of extra things you need to do, but the code is pretty much the same.

This is something I used to have a code snippet for in VB6, but I haven't needed to do this in a while so I never wrote up an example in .NET. So, here's the simple code necessary to add some custom menu items to your applications System Menu within a WPF application. If you're not familiar, the System Menu is the menu that shows up if you click on the apps icon in the upper left or if you right click the app in the taskbar.

Below is a screenshot of it in action along with the complete source code in C#.

using System;
using System.Windows;
using System.Runtime.InteropServices;
using System.Windows.Interop;

namespace WpfApplication3
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Window1 : Window
    {
        #region Win32 API Stuff

        // Define the Win32 API methods we are going to use
        [DllImport("user32.dll")]
        private static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);

        [DllImport("user32.dll")]
        private static extern bool InsertMenu(IntPtr hMenu, Int32 wPosition, Int32 wFlags, Int32 wIDNewItem, string lpNewItem);

        /// Define our Constants we will use
        public const Int32 WM_SYSCOMMAND = 0x112;
        public const Int32 MF_SEPARATOR = 0x800;
        public const Int32 MF_BYPOSITION = 0x400;
        public const Int32 MF_STRING = 0x0;

        #endregion

        // The constants we'll use to identify our custom system menu items
        public const Int32 _SettingsSysMenuID = 1000;
        public const Int32 _AboutSysMenuID = 1001;

        /// <summary>
        /// This is the Win32 Interop Handle for this Window
        /// </summary>
        public IntPtr Handle
        {
            get
            {
                return new WindowInteropHelper(this).Handle;
            }
        }

        public Window1()
        {
            InitializeComponent();

            this.Loaded += new RoutedEventHandler(Window1_Loaded);
        }

        private void Window1_Loaded(object sender, RoutedEventArgs e)
        {
            /// Get the Handle for the Forms System Menu
            IntPtr systemMenuHandle = GetSystemMenu(this.Handle, false);

            /// Create our new System Menu items just before the Close menu item
            InsertMenu(systemMenuHandle, 5, MF_BYPOSITION | MF_SEPARATOR, 0, string.Empty); // <-- Add a menu seperator
            InsertMenu(systemMenuHandle, 6, MF_BYPOSITION, _SettingsSysMenuID, "Settings...");
            InsertMenu(systemMenuHandle, 7, MF_BYPOSITION, _AboutSysMenuID, "About...");

            // Attach our WndProc handler to this Window
            HwndSource source = HwndSource.FromHwnd(this.Handle);
            source.AddHook(new HwndSourceHook(WndProc));
        }

        private static IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            // Check if a System Command has been executed
            if (msg == WM_SYSCOMMAND)
            {
                // Execute the appropriate code for the System Menu item that was clicked
                switch (wParam.ToInt32())
                {
                    case _SettingsSysMenuID:
                        MessageBox.Show("\"Settings\" was clicked");
                        handled = true;
                        break;
                    case _AboutSysMenuID:
                        MessageBox.Show("\"About\" was clicked");
                        handled = true;
                        break;
                }
            }

            return IntPtr.Zero;
        }
    }
}

General, Win32API , , ,

Add System Menu Items to a Form using Windows API

4. March 2008

This is something I used to have a code snippet for in VB6, but I haven't needed to do this in a while so I never wrote up an example in .NET. So, here's the simple code necessary to add some custom menu items to your applications System Menu. If you're not familiar, the System Menu is the menu that shows up if you click on the apps icon in the upper left or if you right click the app in the taskbar.

Below is a screenshot of it in action along with the complete source code in C#.

using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace WinFormsSystemMenuTest
{
    public partial class Form1 : Form
    {
        #region Win32 API Stuff

        // Define the Win32 API methods we are going to use
        [DllImport("user32.dll")]
        private static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);

        [DllImport("user32.dll")]
        private static extern bool InsertMenu(IntPtr hMenu, Int32 wPosition, Int32 wFlags, Int32 wIDNewItem, string lpNewItem);

        /// Define our Constants we will use
        public const Int32 WM_SYSCOMMAND = 0x112;
        public const Int32 MF_SEPARATOR = 0x800;
        public const Int32 MF_BYPOSITION = 0x400;
        public const Int32 MF_STRING = 0x0;
       
        #endregion
       
        // The constants we'll use to identify our custom system menu items
        public const Int32 _SettingsSysMenuID = 1000;
        public const Int32 _AboutSysMenuID = 1001;

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            /// Get the Handle for the Forms System Menu
            IntPtr systemMenuHandle = GetSystemMenu(this.Handle, false);

            /// Create our new System Menu items just before the Close menu item
            InsertMenu(systemMenuHandle, 5, MF_BYPOSITION | MF_SEPARATOR, 0, string.Empty); // <-- Add a menu seperator
            InsertMenu(systemMenuHandle, 6, MF_BYPOSITION, _SettingsSysMenuID, "Settings...");
            InsertMenu(systemMenuHandle, 7, MF_BYPOSITION, _AboutSysMenuID, "About...");
        }

        protected override void WndProc(ref Message m)
        {
            // Check if a System Command has been executed
            if (m.Msg == WM_SYSCOMMAND)
            {
                // Execute the appropriate code for the System Menu item that was clicked
                switch (m.WParam.ToInt32())
                {
                    case _SettingsSysMenuID:
                        MessageBox.Show("\"Settings\" was clicked");
                        break;
                    case _AboutSysMenuID:
                        MessageBox.Show("\"About\" was clicked");
                        break;
                }
            }
           
            base.WndProc(ref m);
        }
    }
}

Here's a link on how to "Add System Menu Items to WPF Window using Win32 API"

General, Win32API , ,

VB.NET: Syntax Highlighting in a RichTextBox control

24. May 2005

This last weekend I expirimented a little bit with extending the functionality of the RichTextBox control. Below you'll find an example of a small class that enherits from the RichTextBox control and allows you to implement syntax highlighting (with the use of a couple Win32 API call to smooth over the process of course.) The code pretty much speaks for itself.

Public Class SyntaxRTB

   Inherits System.Windows.Forms.RichTextBox

   Private
Declare Function SendMessage Lib "user32" Alias "SendMessageA" _
      
(ByVal hWnd As IntPtr, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer

   Private Declare Function LockWindowUpdate Lib "user32" (ByVal hWnd As Integer) As Integer

   Private _SyntaxHighlight_CaseSensitive As Boolean = False

   Private Words As New DataTable

   'Contains Windows Messages for the SendMessage API call
   Private Enum EditMessages
      LineIndex = 187
      LineFromChar = 201
      GetFirstVisibleLine = 206
      CharFromPos = 215
      PosFromChar = 1062
   End Enum

   Protected Overrides Sub OnTextChanged(ByVal e As System.EventArgs)
      
ColorVisibleLines()
   
End Sub

   Public Sub ColorRtb()
      
Dim FirstVisibleChar As Integer
      
Dim i As Integer = 0

      
While i < Me.Lines.Length
         FirstVisibleChar = GetCharFromLineIndex(i)
         ColorLineNumber(i, FirstVisibleChar)
         i += 1
      End While
   
End Sub

   Public Sub ColorVisibleLines()
      
Dim FirstLine As Integer = FirstVisibleLine()
      Dim LastLine As Integer = LastVisibleLine()
      Dim FirstVisibleChar As Integer

      
If (FirstLine = 0) And (LastLine = 0) Then
         
'If there is no text it will error, so exit the sub
         
Exit Sub
      
Else
         While FirstLine < LastLine
            FirstVisibleChar = GetCharFromLineIndex(FirstLine)
            ColorLineNumber(FirstLine, FirstVisibleChar)
            FirstLine += 1
         
End While
      
End If

   
End Sub

   Public Sub ColorLineNumber(ByVal LineIndex As Integer, ByVal lStart As Integer)
      
Dim i As Integer = 0
      
Dim Instance As Integer
      
Dim LeadingChar, TrailingChar As String
      
Dim SelectionAt As Integer = Me.SelectionStart
      
Dim MyRow As DataRow
      
Dim Line() As String, MyI As Integer, MyStr As String

      
' Lock the update
      
LockWindowUpdate(Me.Handle.ToInt32)

      MyI = lStart

      
If CaseSensitive Then
         
Line = Split(Me.Lines(LineIndex).ToString, " ")
      
Else
         
Line = Split(Me.Lines(LineIndex).ToLower, " ")
      
End If

      
For Each MyStr In Line
         
Me.SelectionStart = MyI
         Me.SelectionLength = MyStr.Length

         
If Words.Rows.Contains(MyStr) Then
            
MyRow = Words.Rows.Find(MyStr)
            
If (Not CaseSensitive) Or (CaseSensitive And MyRow("Word") = MyStr) Then
               
Me.SelectionColor = Color.FromName(MyRow("Color"))
            
End If
         Else
            
Me.SelectionColor = Color.Black
         
End If

         
MyI += MyStr.Length + 1
      Next

      ' Restore the selectionstart
      
Me.SelectionStart = SelectionAt
      
Me.SelectionLength = 0
      
Me.SelectionColor = Color.Black

      
' Unlock the update
      
LockWindowUpdate(0)
   
End Sub

   Public Function GetCharFromLineIndex(ByVal LineIndex As Integer) As Integer
      
Return SendMessage(Me.Handle, EditMessages.LineIndex, LineIndex, 0)
   
End Function

   Public Function FirstVisibleLine() As Integer
      
Return SendMessage(Me.Handle, EditMessages.GetFirstVisibleLine, 0, 0)
   
End Function

   Public Function LastVisibleLine() As Integer
      
Dim LastLine As Integer = FirstVisibleLine() + (Me.Height / Me.Font.Height)

      
If LastLine > Me.Lines.Length Or LastLine = 0 Then
         
LastLine = Me.Lines.Length
      End If

      
Return LastLine
   
End Function

   Public Sub New()
      
Dim MyRow As DataRow
      
Dim arrKeyWords() As String, strKW As String

      
Me.AcceptsTab = True

      
''Load all the keywords and the colors to make them 
      
Words.Columns.Add("Word")
      Words.PrimaryKey =
New DataColumn() {Words.Columns(0)}
      Words.Columns.Add("Color")

      arrKeyWords =
New String() {"select", "insert", "delete", _
         "truncate", "from", "where", "into", "inner", "update", _
         "outer", "on", "is", "declare", "set", "use", "values", "as", _
         "order", "by", "drop", "view", "go", "trigger", "cube", _
         "binary", "varbinary", "image", "char", "varchar", "text", _
         "datetime", "smalldatetime", "decimal", "numeric", "float", _
         "real", "bigint", "int", "smallint", "tinyint", "money", _
         "smallmoney", "bit", "cursor", "timestamp", "uniqueidentifier", _
         "sql_variant", "table", "nchar", "nvarchar", "ntext", "left", _
         "right", "like","and","all","in","null","join","not","or"}

      For Each strKW In arrKeyWords
         MyRow = Words.NewRow()
         MyRow("Word") = strKW
         MyRow("Color") = Color.LightCoral.Name
         Words.Rows.Add(MyRow)
      
Next

   
End Sub

   Public Property CaseSensitive() As Boolean
      
Get
         
Return _SyntaxHighlight_CaseSensitive
      
End Get
      
Set(ByVal Value As Boolean)
         _SyntaxHighlight_CaseSensitive = Value
      
End Set
   End Property

 

End Class   

 

Update July 8th, 2008: Here's a link that shows a couple tips that may help in writing your own Syntax Highlighting RichTextBox control:

http://codebetter.com/blogs/patricksmacchia/archive/2008/07/07/some-richtextbox-tricks.aspx

General, vb.net, Win32API