Here’s the first version of my code using Microsoft.IO.RecyclableMemoryStream

I mentioned a while back that I was going to do this…

The app is heavy with its usage of streams, so replacing all references to MemoryStream with one that reuses pooled buffers seemed like a logical thing to do. I already use an object pool for thumbnail control instances (the most used object in the application), so it seems like an obvious way to improve performance and possibly memory management too. And it works well.

If you are interested in reading about RecyclableMemoryStream, read their announcement post.

If you missed my post describing what my code does, you can read it here. The code is still shared in the same place on Google drive here.

As for the code, usage is quite straightforward… You create an instance of their memory stream manager, and instead of creating MemoryStream instances as you normally would, use its GetStream method. That’s about it… The only thing that changed for me is when using such streams as read-only buffers (the MemoryStream(Byte[]) constructor) because this package doesn’t have a GetStream() method with identical signature. They do have an equivalent one though… it just takes a couple of extra parameters, such as tag, offset, and length. My implementation of the code changes in my solution simply used a Singleton for the memory stream manager, and then I had to replace all occurrences of “new MemoryStream…) in the code, which was slightly tedious because of the different signature for read-only mode streams.

Advertisements
Posted in Programming | Tagged | Leave a comment

Another example of a control hosted in a ToolStrip: ToolStripCustomProgressBar

First off, while taking screenshots to use in this post, I found an issue in my ffmpeg wrapper, which specified “libvo_aacenc” for the aac audio encoder. It turns out that has since changed and because I recently updated the ffmpeg referenced, I had to change it to use “aac” for the built-in encoder. The source code archive has been updated. By the way, I use the Windows 64-bit and 32-bit static builds of ffmpeg, from this site.

I originally shared the code for my custom ProgressBar in September 2013.

That’s a progress bar that does some custom drawing to show a disabled state, and in the dialogs shown below, my code to suspend a process is used when the user clicks the Pause/Resume button to suspend or resume the underlying ffmpeg process that’s being controlled. (Actually I wrote the pause/resume process code and this control entirely because of this foolish feature that allows you to do as many simultaneous video conversions as you like. I also allow you to set the video conversion process priority higher than the application process priority. Good luck clicking those buttons then. If I were smarter, I’d probably have limited the number of allowed simultaneous conversions and had one batch dialog to show them all. But I’m not that smart. Sorry.)

So… this is what the custom progress bar normally looks like:

SNAGHTML4eb7f9d2

And here it is disabled when the video conversion is paused:

SNAGHTML4eb8c10a

(Once again, these dialogs are using my stupid code that draws shadows for common controls. I can’t remember if I shared that but can’t seem to find it on this blog.)

You can refer to the old post or the full source code archive (see the first paragraph of this post) for the progress bar itself. I just wanted to show (again) how easy it is to host a control in a ToolStrip or StatusStrip.

Here’s the hosted control, showing normal and disabled state in a StatusStrip… To force a long-running process, I used a feature of my application that allows the user to precache all thumbnails in a hierarchy of nested directories. One of my less intuitive/intelligent pieces of code that supports being paused by clicking on the label next to the progress bar.

SNAGHTML4ebfb2ef

SNAGHTML4ec0234c

And here’s the Toolstrip-hosted code for that control… I’ve shared this simply to show again how easy it is to host such a control… the actual code required to get all this working isn’t complete in this post, because I didn’t include the interface where the disabled progress bar colours are defined.

The point here is to emphasize how easy it is to host such a control. In this case, the control hosted derives from ProgressBar, so all the relevant properties and methods of the hosted (wrapped) control are exposed such that using it programmatically is then exactly the same as using a ProgressBar directly.

using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using System.Windows.Forms.Design;

namespace Romy.Controls
{
    /// <summary>A ProgressBar that uses custom drawing to show if it is disabled, hosted in a ToolStrip.</summary>
    [ToolStripItemDesignerAvailability(ToolStripItemDesignerAvailability.ToolStrip | ToolStripItemDesignerAvailability.StatusStrip)]
    public class ToolStripCustomProgressBar : ToolStripControlHost, IDisabledProgressBarColors
    {
        public ToolStripCustomProgressBar()
            : base(CreateControlInstance())
        { }

        [Description("The color used for the bottom of the ProgressBar background when it is disabled."), Category("Disabled Appearance")]
        public Color BackGradientBottomColor
        {
            get => ProgressBar.BackGradientBottomColor;
            set => ProgressBar.BackGradientBottomColor = value;
        }

        [Description("The color used for the middle of the ProgressBar background when it is disabled."), Category("Disabled Appearance")]
        public Color BackGradientMiddleColor
        {
            get => ProgressBar.BackGradientMiddleColor;
            set => ProgressBar.BackGradientMiddleColor = value;
        }

        [Description("The color used for the top of the ProgressBar background when it is disabled."), Category("Disabled Appearance")]
        public Color BackGradientTopColor
        {
            get => ProgressBar.BackGradientTopColor;
            set => ProgressBar.BackGradientTopColor = value;
        }

        [Description("The color used for the bottom border of the ProgressBar when it is disabled."), Category("Disabled Appearance")]
        public Color BorderGradientBottomColor
        {
            get => ProgressBar.BorderGradientBottomColor;
            set => ProgressBar.BorderGradientBottomColor = value;
        }

        [Description("The color used for the top border of the ProgressBar when it is disabled."), Category("Disabled Appearance")]
        public Color BorderGradientTopColor
        {
            get => ProgressBar.BorderGradientTopColor;
            set => ProgressBar.BorderGradientTopColor = value;
        }

        [Description("The color used for the bottom of the ProgressBar progress value when it is disabled."), Category("Disabled Appearance")]
        public Color ProgressGradientBottomColor
        {
            get => ProgressBar.ProgressGradientBottomColor;
            set => ProgressBar.ProgressGradientBottomColor = value;
        }

        [Description("The color used for the middle of the ProgressBar progress value when it is disabled."), Category("Disabled Appearance")]
        public Color ProgressGradientMiddleColor
        {
            get => ProgressBar.ProgressGradientMiddleColor;
            set => ProgressBar.ProgressGradientMiddleColor = value;
        }

        [Description("The color used for the top of the ProgressBar progress value when it is disabled."), Category("Disabled Appearance")]
        public Color ProgressGradientTopColor
        {
            get => ProgressBar.ProgressGradientTopColor;
            set => ProgressBar.ProgressGradientTopColor = value;
        }

        [Description("The color used to highlight the bottom of the ProgressBar progress value when it is disabled."), Category("Disabled Appearance")]
        public Color ProgressHighlightColor
        {
            get => ProgressBar.ProgressHighlightColor;
            set => ProgressBar.ProgressHighlightColor = value;
        }

        public CustomProgressBar ProgressBar => Control as CustomProgressBar;

        public int Maximum
        {
            get => ProgressBar.Maximum;
            set => ProgressBar.Maximum = value;
        }

        public int Minimum
        {
            get => ProgressBar.Minimum;
            set => ProgressBar.Minimum = value;
        }

        public int Value
        {
            get => ProgressBar.Value;
            set => ProgressBar.Value = value;
        }

        public ProgressBarStyle Style
        {
            get => ProgressBar.Style;
            set => ProgressBar.Style = value;
        }

        public void Increment(int value) => ProgressBar.Increment(value);

        public void PerformStep() => ProgressBar.PerformStep();

        private static Control CreateControlInstance() => new CustomProgressBar { Size = new Size(100, 16) };
    }
}
Posted in Programming | Tagged , | Leave a comment

I see my ImageComboBox and ToolStripImageComboBox was broken.

So I fixed it. Not sure when it broke. Also I don’t think I shared it before… Anyway, here it is now, and it’s fixed in my full source code archive too. The issue was the native struct pinvoke declaration for ComboboxInfo was wrong. Bizarrely, that caused the ComboBox to display its image correctly in the designer but it failed to get the control’s handle at runtime, and I never noticed because I don’t really use it for anything.

Similarly to my recently shared Slider/ToolStripSlider control, I have a custom ComboBox control, called an ImageComboBox that displays an image, kind of like the search box in Windows explorer, and a corresponding control that I use on a ToolStrip. It doesn’t display images for items in the ComboBox because that’s really fucking annoying and unnecessary, but does display an image for the top level of the control. Typically I give it a 16×16 glyph of some sort. Here’s the working version…

It looks like this – the search ComboBox displayed on the top right of my main form:

SNAGHTML49086cf9

First the native methods and structs… I’ve only copied the one method in the NativeMethods class used by this control so hopefully I didn’t forget anything.

We need these because we use a native window to get to the edit window inside the ComboBox, then draw an image on there. Actually that’s where this code went wrong… My ComboBoxInfo declaration was missing the button states. Maybe that was changed since I last used this.

using System.Runtime.InteropServices;
using System.Security;

namespace Romy.Controls
{
    [SuppressUnmanagedCodeSecurity]
    internal static class NativeMethods
    {
        [DllImport("user32.dll")]
        public static extern bool GetComboBoxInfo(System.IntPtr hwndCombo, ref ComboBoxInfo info);
    }
}
using System;
using System.Runtime.InteropServices;

namespace Romy.Controls
{
    [StructLayout(LayoutKind.Sequential)]
    internal struct RECT
    {
        public int left;

        public int top;

        public int right;

        public int bottom;
    }

    [StructLayout(LayoutKind.Sequential)]
    internal struct ComboBoxInfo
    {
        public Int32 cbSize;

        public RECT rcItem;

        public RECT rcButton;

        public ComboBoxButtonState buttonState;

        public IntPtr hwndCombo;

        public IntPtr hwndEdit;

        public IntPtr hwndList;
    }

    internal enum ComboBoxButtonState
    {
        STATE_SYSTEM_NONE = 0,
        STATE_SYSTEM_INVISIBLE = 0x00008000,
        STATE_SYSTEM_PRESSED = 0x00000008
    }
}

Then the control and its NativeWindow class… Not much code here really. We extend the control and intercept some Windows messages sent to its edit window and change the way it gets painted, shoving our image in there on the right. I also added a call to SelectAll when the control is clicked, because typically I use it for a search ComboBox, and when I click it, I want to overwrite anything already typed there. Feel free to remove that if you don’t like it. And lastly I added some code to allow pasting into the control. It’s been a while, but as far as I can remember, pasting into the control didn’t work out of the box.

using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;

namespace Romy.Controls
{
    /// <summary>A ComboBox that supports an image in its TextBox part. The image is
    /// drawn on the right of the edit box.</summary>
    /// <remarks>Originally this was based on a component with the same name I found
    /// on CodeProject. However, since I was only interested in an image in the edit
    /// box part (to create a standard-looking search ComboBox), I stripped out all
    /// other functionality.</remarks>
    public class ImageComboBox : ComboBox
    {
        private readonly ComboEditWindow editBox;

        public ImageComboBox()
        {
            editBox = new ComboEditWindow(this);
            DoubleBuffered = true;
        }

        [Category("Appearance"), Description("The Image to display for the editBox part of the ComboBox."),
        DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public Image Image
        {
            get => editBox.Image;
            set => editBox.Image = value;
        }

        /// <summary>Suport Ctrl+V pasting text into the ComboBox.</summary>
        public override bool PreProcessMessage(ref Message msg)
        {
            Keys keys = (Keys)msg.WParam.ToInt32();

            if (keys == Keys.V && ModifierKeys == Keys.Control)
            {
                Text = Clipboard.GetDataObject().GetData(typeof(string)) as string;
                return string.IsNullOrEmpty(Text);
            }

            return base.PreProcessMessage(ref msg);
        }

        protected override void OnClick(EventArgs e)
        {
            SelectAll();
            base.OnClick(e);
        }
    }
}
using System.Drawing;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using System.Windows.Forms;

namespace Romy.Controls
{
    /// <summary>A helper class that accesses the windows message stream directed towards the Edit
    /// portion of the ComboBox. Gets assigned the handle of the TextBox of the ComboBox.</summary>
    public sealed class ComboEditWindow : NativeWindow
    {
        private ComboBoxInfo cbxinfo;

        private ImageComboBox Owner;

        private const int WM_CHAR = 0x102;

        private const int WM_GETTEXT = 0xd;

        private const int WM_GETTEXTLENGTH = 0xe;

        private const int WM_KEYDOWN = 0x100;

        private const int WM_KEYUP = 0x101;

        private const int WM_LBUTTONDOWN = 0x201;

        private const int WM_PAINT = 0xF;

        private const int WM_SETCURSOR = 0x20;

        private readonly Control parent;

        public ComboEditWindow(Control parent)
        {
            cbxinfo = new ComboBoxInfo();

            this.parent = parent;
            parent.HandleCreated += (sender, e) => AssignTextBoxHandle(sender as ImageComboBox);
            parent.HandleDestroyed += (sender, e) => ReleaseHandle();
        }

        public Image Image { get; set; }

        /// <summary>The native window's original handle is released
        /// and the handle of the TextBox is assigned to it.</summary>
        public void AssignTextBoxHandle(ImageComboBox owner)
        {
            Owner = owner;
            cbxinfo.cbSize = Marshal.SizeOf(cbxinfo);
            NativeMethods.GetComboBoxInfo(Owner.Handle, ref cbxinfo);
            AssignHandle(cbxinfo.hwndEdit);
        }

        /// <summary>Whenever the textbox is repainted, draw the image.</summary>
        public void DrawImage()
        {
            if (Image != null)
            {
                new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand();

                using (Graphics graphics = Graphics.FromHwnd(Handle))
                {
                    if (Owner != null)
                    {
                        using (SolidBrush solidBrush = new SolidBrush(Owner.BackColor))
                        {
                            graphics.FillRectangle(solidBrush, new Rectangle((int)(graphics.VisibleClipBounds.Width - Image.Width), 0, Image.Width, (int)graphics.VisibleClipBounds.Height));
                        }
                    }
                    graphics.DrawImage(Image, graphics.VisibleClipBounds.Width - Image.Width, 0);
                }
            }
        }

        /// <summary>Override the WndProc method to redraw the
        /// TextBox when the textbox is repainted.</summary>
        protected override void WndProc(ref Message m)
        {
            switch (m.Msg)
            {
                case WM_PAINT:
                case WM_LBUTTONDOWN:
                case WM_KEYDOWN:
                case WM_KEYUP:
                case WM_CHAR:
                case WM_GETTEXTLENGTH:
                case WM_GETTEXT:
                    base.WndProc(ref m);
                    DrawImage();
                    break;
                default:
                    base.WndProc(ref m);
                    break;
            }
        }
    }
}

Originally I started the above control with one I’d downloaded from CodeProject. (This was years ago and I didn’t save the URL.) The control I used did a lot more and painted images for the items too… We tend to forget this but a ComboBox is actually a composite control – a text box with a drop down list view. So it has separate Windows handles for its different parts… One for the edit window, one for the list view, maybe a button on the right, etc. I don’t really know the details. I stripped out the functionality I didn’t want, but also ended up rewriting the control almost completely, taking the design for the NativeWindow in terms of when to assign its handle, almost as is from the MSDN sample. As for which Windows messages to intercept, I changed that too. All I did was debug and look at the messages received, to choose which ones need the image to be painted.

And to host it on a ToolStrip, it uses this ToolStripControlHost-derived class…

This is very similar to my last example that hosted my custom Slider. The pattern to host a control on a ToolStrip is quite straightforward. Derive a class from ToolStripControlHost; create the control in a method called by your constructor – mine is always called CreateControlInstance; pick the properties in the hosted control you need to expose; and lastly expose any events of the hosted control (as many as you like) and hook/unhook them in your overridden OnSubscribeControlEvents and OnUnsubscribeControlEvents. Once you’ve done it once or twice, it only takes a couple of minutes to write the code to host whatever control you want.

using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using System.Windows.Forms.Design;

namespace Romy.Controls
{
    /// <summary>ToolStripControlHost-derived control to support hosting a ComboBox with an
    /// image in its editBox part on the ToolStrip family of container controls.</summary>
    [ToolStripItemDesignerAvailability(ToolStripItemDesignerAvailability.None | ToolStripItemDesignerAvailability.ToolStrip | ToolStripItemDesignerAvailability.StatusStrip)]
    public class ToolStripImageComboBox : ToolStripControlHost
    {
        public ToolStripImageComboBox()
            : base(CreateControlInstance())
        { }

        public ComboBox.ObjectCollection Items => IsDisposed ? null : ImageComboBoxControl.Items;

        public ComboBoxStyle DropDownStyle
        {
            get => ImageComboBoxControl.DropDownStyle;
            set => ImageComboBoxControl.DropDownStyle = value;
        }

        public DrawMode DrawMode
        {
            get => ImageComboBoxControl.DrawMode;
            set => ImageComboBoxControl.DrawMode = value;
        }

        [Description("This property is not applicable for this type.")]
        public override Image BackgroundImage
        {
            get => null;
            set => base.BackgroundImage = null;
        }

        [Category("Appearance")]
        [Browsable(true)]
        [Description("The Image to display for the EditBox part of the ComboBox.")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public new Image Image
        {
            get => ImageComboBoxControl.Image;
            set => ImageComboBoxControl.Image = value;
        }

        public ImageComboBox ImageComboBoxControl => Control as ImageComboBox;

        [Description("This property is not applicable for this type.")]
        public override ImageLayout BackgroundImageLayout
        {
            get => base.BackgroundImageLayout;
            set { }
        }

        public new int Height
        {
            get => ImageComboBoxControl.Height;
            set => ImageComboBoxControl.Height = value;
        }

        public int MaxDropDownItems
        {
            get => ImageComboBoxControl.MaxDropDownItems;
            set => ImageComboBoxControl.MaxDropDownItems = value;
        }

        public int MaxLength
        {
            get => ImageComboBoxControl.MaxLength;
            set => ImageComboBoxControl.MaxLength = value;
        }

        public int SelectedIndex
        {
            get => ImageComboBoxControl.SelectedIndex;
            set => ImageComboBoxControl.SelectedIndex = value;
        }

        public new int Width
        {
            get => ImageComboBoxControl.Width;
            set => ImageComboBoxControl.Width = value;
        }

        public object SelectedItem
        {
            get => ImageComboBoxControl.SelectedItem;
            set => ImageComboBoxControl.SelectedItem = value;
        }

        protected override Size DefaultSize => new Size(100, 22);

        public override string Text
        {
            get => ImageComboBoxControl.IsHandleCreated ? base.Text : string.Empty;
            set => base.Text = value;
        }

        public new event KeyPressEventHandler KeyPress;

        public event EventHandler SelectedIndexChanged;

        public new event EventHandler TextChanged;

        public void BeginUpdate() => ImageComboBoxControl.BeginUpdate();

        public void EndUpdate() => ImageComboBoxControl.EndUpdate();

        protected override void OnSubscribeControlEvents(Control control)
        {
            base.OnSubscribeControlEvents(control);

            ImageComboBoxControl.SelectedIndexChanged += new EventHandler(ImageComboBoxControl_SelectedIndexChanged);
            ImageComboBoxControl.KeyPress += new KeyPressEventHandler(ImageComboBoxControl_KeyPress);
            ImageComboBoxControl.TextChanged += new EventHandler(ImageComboBoxControl_TextChanged);
        }

        protected override void OnUnsubscribeControlEvents(Control control)
        {
            base.OnUnsubscribeControlEvents(control);

            ImageComboBoxControl.SelectedIndexChanged -= new EventHandler(ImageComboBoxControl_SelectedIndexChanged);
            ImageComboBoxControl.KeyPress -= new KeyPressEventHandler(ImageComboBoxControl_KeyPress);
            ImageComboBoxControl.TextChanged -= new EventHandler(ImageComboBoxControl_TextChanged);
        }

        private static Control CreateControlInstance() => new ImageComboBox { Size = new Size(100, 22) };

        private void ImageComboBoxControl_KeyPress(object sender, KeyPressEventArgs e) => KeyPress?.Invoke(this, e);

        private void ImageComboBoxControl_SelectedIndexChanged(object sender, EventArgs e) => SelectedIndexChanged?.Invoke(this, e);

        private void ImageComboBoxControl_TextChanged(object sender, EventArgs e) => TextChanged?.Invoke(this, e);
    }
}

One cool thing about creating your own hosted ToolStrip controls is that they show up in the editor just like standard ones. For example, in my own solution the controls are all in a class library project that’s part of the solution, referenced by the application project, so when clicking the button to add more controls on my form in the designer, I can pick standard controls or my own ones, and it looks like this:

image

Posted in Programming | Tagged , , , | Leave a comment

My custom ToolStripSlider Windows Forms control

In the last post I shared the full source code of my jack-of-all-trades application.

Since you can get the code there, I can now share classes and the code for controls, even if they refer to image resources in the project, because you can get it all from the zip file of the full source code.

When I created those controls, I was particularly happy with the ToolStripSlider control. It’s basically a Trackbar-alternative control in a ToolStrip, except instead of using a standard trackbar, it inherits from ToolStripControlHost, and hosts my Slider custom control, because that’s how you render other controls in a ToolStrip.

I originally wrote the Slider control because I just don’t like the way a standard Trackbar behaves. It has a couple of baffling SmallChange and LargeChange properties, and really doesn’t seem intuitive at all to use. All I wanted was a slider to use for seeking videos and setting the volume. It’s behaviour is totally the way I wanted it… That is, when you click it anywhere, it sets the value to one that corresponds with the click, and moves the tracker to the location where you clicked it. Also when you click it, it immediately sets its state to pressed, and allows you to drag the slider to a new location, then sets the state back to normal when you release the mouse or it loses focus.

In the app’s video player form shown below, I used the Slider control twice… The controls at the bottom are on a panel, with a Slider control docked to the top to act as a seeker control, and a ToolStripSlider control shown on the right of the ToolStrip, which is docked to the bottom of the panel.

It looks like this when using my normal theme…

And this when using my dark theme…

Of course the archive linked to has the code for all the controls, but today I’m just focusing on the slider.

The slider hosted on the ToolStrip is this:

using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using System.Windows.Forms.Design;

namespace Romy.Controls
{
    /// <summary>A transparent Slider control for any of the containers in the ToolStrip family of controls.</summary>
    /// <remarks>This hosts my <see cref="Slider"/> control, not a standard <see cref="TrackBar"/>.</remarks>
    [ToolStripItemDesignerAvailability(ToolStripItemDesignerAvailability.None | ToolStripItemDesignerAvailability.ToolStrip | ToolStripItemDesignerAvailability.StatusStrip)]
    public class ToolStripSlider : ToolStripControlHost
    {
        #region Public Constructors

        public ToolStripSlider()
            : base(CreateControlInstance())
        { }

        #endregion Public Constructors

        #region Public Events

        public event EventHandler ValueChanged;

        #endregion Public Events

        #region Public Properties

        [Description("This property is not applicable for this type.")]
        public override Color BackColor
        {
            get => base.BackColor;
            set { }
        }

        [Description("This property is not applicable for this type.")]
        public override Image BackgroundImage
        {
            get => null;
            set => base.BackgroundImage = null;
        }

        [Description("This property is not applicable for this type.")]
        public override ImageLayout BackgroundImageLayout
        {
            get => base.BackgroundImageLayout;
            set { }
        }

        [Browsable(true), Category("Appearance"), Description("Changes the Silder's color scheme."), DefaultValue(SliderColorScheme.Grey)]
        public SliderColorScheme ColorScheme
        {
            get => SliderControl.ColorScheme;
            set => SliderControl.ColorScheme = value;
        }

        [Browsable(true), Category("Behavior"), DefaultValue(100),
        Description("The maximum value for the position of the slider on the track.")]
        public int Maximum
        {
            get => this.SliderControl.Maximum;
            set => this.SliderControl.Maximum = value;
        }

        [Browsable(true), Category("Behavior"), DefaultValue(0),
        Description("The minimum value for the position of the slider on the track.")]
        public int Minimum
        {
            get => this.SliderControl.Minimum;
            set => this.SliderControl.Minimum = value;
        }

        [Category("Behavior"), Description("Allows resetting the slider by pressing the space bar."), DefaultValue(true)]
        public bool ResetEnabled
        {
            get => this.SliderControl.ResetEnabled;
            set => this.SliderControl.ResetEnabled = value;
        }

        [Browsable(true), Category("Behavior"),
        Description("When the space bar is pressed while the slider has input focus, the slider will reset to this value.")]
        public int ResetValue
        {
            get => this.SliderControl.ResetValue;
            set => this.SliderControl.ResetValue = value;
        }

        public Slider SliderControl => Control as Slider;

        [Category("Behavior"), DefaultValue(false),
                        Description("If enabled, allows moving the slider with the left and right arrow keyboard keys.")]
        public bool UsesArrowKeys
        {
            get => this.SliderControl.UsesArrowKeys;
            set => this.SliderControl.UsesArrowKeys = value;
        }

        [Browsable(true), Category("Behavior"),
        Description("The position of the slider.")]
        public double Value
        {
            get => SliderControl.Value;
            set => SliderControl.Value = value;
        }

        #endregion Public Properties

        #region Protected Properties

        protected override Padding DefaultMargin => new Padding(0);

        protected override Padding DefaultPadding => new Padding(0);

        protected override Size DefaultSize => new Size(100, 20);

        #endregion Protected Properties

        #region Protected Methods

        protected override void OnSubscribeControlEvents(Control control)
        {
            base.OnSubscribeControlEvents(control);
            this.SliderControl.ValueChanged += new EventHandler(ToolStripSlider_ValueChanged);
        }

        protected override void OnUnsubscribeControlEvents(Control control)
        {
            base.OnUnsubscribeControlEvents(control);
            this.SliderControl.ValueChanged -= new EventHandler(ToolStripSlider_ValueChanged);
        }

        #endregion Protected Methods

        #region Private Methods

        private static Control CreateControlInstance() => new Slider(100, new Size(80, 20));

        private void ToolStripSlider_ValueChanged(object sender, EventArgs e) => ValueChanged?.Invoke(this, EventArgs.Empty);

        #endregion Private Methods
    }
}

And the control it hosts is this:

using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Security.Permissions;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Romy.Controls
{
    public enum SliderColorScheme
    {
        Grey,
        Sepia
    }

    /// <summary>Specifies the visual state of a Slider thumb.</summary>
    /// <remarks>This is a copy of <see cref="System.Windows.Forms.VisualStyles.TrackBarThumbState"/>.</remarks>
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue",
    Justification = "This is a copy of System.Windows.Forms.VisualStyles.TrackBarThumbState, which has the same issue.")]
    public enum SliderThumbState
    {
        /// <summary>The slider appears normal.</summary>
        Normal = 1,

        /// <summary>The thumb appears hot.</summary>
        Hot = 2,

        /// <summary>The thumb appears pressed.</summary>
        Pressed = 3,

        /// <summary>The thumb appears disabled.</summary>
        Disabled = 5,
    }

    /// <summary>Custom TrackBar-like control that supports a transparent BackColor.
    /// (This control currently only supports a horizontal track orientation.)</summary>
    /// <remarks><para>
    /// This was originally designed to be used only by this application's media player,
    /// in terms of its beheviour when clicked (described below) and its appearance (similar
    /// to the other controls on the media player). However, when a similar control was
    /// needed elsewhere, the <b>ColorScheme</b> property was added.</para>
    /// <para>
    /// Currently two schemes are supported: Grey is intended to have colours similar to
    /// a standard TrackBar (so that it doesn't look out of place when used with standard
    /// controls, e.g. on a ToolStrip), and Sepia is the theme used on the media player.
    /// </para><para>
    /// Note that the afore-mentioned ColorSchemes property has nothing to do with the
    /// Themes used throughout the application. I wrote this control long before thinking
    /// of implementing color-themes on the application level.</para>
    /// <para>
    /// See <see cref="ToolStripSlider"/> for a <b>Slider</b> that can be
    /// used on any of the containers in the ToolStrip family of controls. (Hosted by a
    /// <see cref="ToolStripControlHost"/>.)</para>
    /// <para>
    /// This is designed to behave the way I would have preferred the TrackBar control to
    /// behave, at least when the track is clicked. It doesn't have the SmallChange and
    /// LargeChange properties of a standard TrackBar. Clicking the track anywhere (rather
    /// than within a limited range of the slider's thumb button) sets the Value property,
    /// moves the thumb to the point clicked, and sets its state to pressed. Then with the
    /// mouse button held down, you can drag to slide the slider and seek to a new position.
    /// (and optionally make fine adjustments using the keyboard's arrow keys.)</para>
    /// <para>
    /// This was originally based on the TrackBarRenderer example in the Visual Studio 2010
    /// help, but has changed drastically since its initial implementation. Most notably, all
    /// references to the TrackBarRenderer were removed, and all drawing is hand-coded.</para>
    /// </remarks>
    public class Slider : Control
    {
        private double tickSpace;

        private double value;

        private const int TrackHeight = 4;

        private const int WM_KEYDOWN = 0x0100;

        private static readonly Lazy<Image> thumb = new Lazy<Image>(() => Properties.Resources.thumb);

        private static readonly Lazy<Image> thumbGrey = new Lazy<Image>(() => Properties.Resources.thumbGrey);

        private static readonly Lazy<Image> thumbGreyHot = new Lazy<Image>(() => Properties.Resources.thumbGreyHot);

        private static readonly Lazy<Image> thumbGreyPressed = new Lazy<Image>(() => Properties.Resources.thumbGreyPressed);

        private static readonly Lazy<Image> thumbHot = new Lazy<Image>(() => Properties.Resources.thumbHot);

        private static readonly Lazy<Image> thumbPressed = new Lazy<Image>(() => Properties.Resources.thumbPressed);

        private static readonly Lazy<Image> trackEdge = new Lazy<Image>(() => Properties.Resources.trackEdge);

        private static readonly Lazy<Image> trackGreyEdge = new Lazy<Image>(() => Properties.Resources.trackGreyEdge);

        private static readonly Lazy<Image> trackGreyMiddle = new Lazy<Image>(() => Properties.Resources.trackGreyMiddle);

        private static readonly Lazy<Image> trackMiddle = new Lazy<Image>(() => Properties.Resources.trackMiddle);

        private Rectangle thumbRectangle = new Rectangle();

        private Rectangle trackRectangle = new Rectangle();

        private SliderThumbState thumbState = SliderThumbState.Normal;

        public Slider(int maximum, Size sliderSize)
        {
            SetRange(0, maximum);
            this.SetBounds(this.Bounds.Left, this.Bounds.Top, sliderSize.Width, sliderSize.Height);

            Initialize();
        }

        public Slider() : this(100, new Size(100, 20))
        { }

        [Category("Behavior"), Description("Allows resetting the slider by pressing the space bar."), DefaultValue(true)]
        public bool ResetEnabled { get; set; } = true;

        [Browsable(false)]
        public bool ThumbClicked { get; private set; }

        /// <summary>Set true to allow the left and right arrow keyboard keys to fine-adjust the slider value.</summary>
        /// <remarks>Depending on where the control is situated, this may not be feasible. It is generally
        /// better to do this from a parent form's OnKeyDown handler, hence this defaults to false.</remarks>
        [Category("Behavior"), DefaultValue(false),
        Description("If enabled, allows moving the slider with the left and right arrow keyboard keys.")]
        public bool UsesArrowKeys { get; set; }

        [Description("This property is not applicable for this type.")]
        public override Color BackColor
        {
            get => base.BackColor;
            set { }
        }

        [Category("Behavior"), Description("The position of the slider.")]
        public double Value
        {
            get => value;
            set
            {
                this.value = value < this.Minimum ? this.Minimum : value > this.Maximum ? this.Maximum : value;
                UpdateThumbLocation();

                ValueChanged?.Invoke(this, EventArgs.Empty);
            }
        }

        [Description("This property is not applicable for this type.")]
        public override Image BackgroundImage
        {
            get => null;
            set => base.BackgroundImage = null;
        }

        public static Image Thumb => thumb.Value;

        public static Image ThumbGrey => thumbGrey.Value;

        public static Image ThumbGreyHot => thumbGreyHot.Value;

        public static Image ThumbGreyPressed => thumbGreyPressed.Value;

        public static Image ThumbHot => thumbHot.Value;

        public static Image ThumbPressed => thumbPressed.Value;

        public static Image TrackEdge => trackEdge.Value;

        public static Image TrackGreyEdge => trackGreyEdge.Value;

        public static Image TrackGreyMiddle => trackGreyMiddle.Value;

        public static Image TrackMiddle => trackMiddle.Value;

        [Description("This property is not applicable for this type.")]
        public override ImageLayout BackgroundImageLayout
        {
            get => base.BackgroundImageLayout;
            set { }
        }

        [Category("Behavior"), DefaultValue(100),
        Description("The maximum value for the position of the slider on the track.")]
        public int Maximum { get; set; }

        [Category("Behavior"),
        Description("The minimum value for the position of the slider on the track.")]
        public int Minimum { get; set; }

        [Category("Behavior"),
        Description("Pressing the space bar when ResetEnabled is set and the slider has input focus \"resets\" the slider to this value.")]
        public int ResetValue { get; set; }

        [Category("Appearance"), Description("Changes the Silder's color scheme."), DefaultValue(SliderColorScheme.Grey)]
        public SliderColorScheme ColorScheme { get; set; }

        protected SliderThumbState ThumbState
        {
            get => thumbState;
            set
            {
                thumbState = value;
                this.Invalidate();

                if (thumbState == SliderThumbState.Hot)
                    CheckThumbstate();
            }
        }

        public event EventHandler ValueChanged;

        /// <summary>Allows using the keyboard to modify the slider value.</summary>
        public override bool PreProcessMessage(ref Message msg)
        {
            new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand();

            bool processedMessage = false;

            if (msg.Msg == WM_KEYDOWN)
            {
                Keys keys = (Keys)msg.WParam.ToInt32();

                switch (keys)
                {
                    case Keys.Left:
                        if (UsesArrowKeys)
                        {
                            if (this.Value > this.Minimum)
                                this.Value--;

                            processedMessage = true;
                        }
                        break;
                    case Keys.Right:
                        if (UsesArrowKeys)
                        {
                            if (this.Value < this.Maximum)
                                this.Value++;

                            processedMessage = true;
                        }
                        break;
                    case Keys.Space:
                        if (ResetEnabled)
                        {
                            this.Value = this.ResetValue;
                            processedMessage = true;
                        }
                        break;
                }
            }
            return processedMessage;
        }

        /// <summary>Sets the minimum and maximum values for a <see cref="Slider"/>.</summary>
        /// <remarks>Like the <see cref="TrackBar.SetRange(int, int)"/> method, you can
        /// use this method to set the entire range for the <see cref="Slider"/> at the same time.
        /// To set the minimum or maximum values individually, use the <see cref="Minimum"/>
        /// and <see cref="Maximum"/> properties. If the <i>minValue</i> parameter is greater
        /// than the <i>maxValue</i> parameter, <i>maxValue</i> is set equal to <i>minValue</i>.</remarks>
        /// <param name="minValue">The lower limit of the range of the slider. </param>
        /// <param name="maxValue">The upper limit of the range of the slider.</param>
        public void SetRange(int minValue, int maxValue)
        {
            if (minValue > maxValue)
                maxValue = minValue;

            this.Minimum = minValue;
            this.Maximum = maxValue;
        }

        protected override void OnLeave(EventArgs e)
        {
            StopSliding();
            base.OnLeave(e);
        }

        protected override void OnLostFocus(EventArgs e)
        {
            StopSliding();
            base.OnLostFocus(e);
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                if (tickSpace > 0) // Set the value to the position clicked.
                {
                    int newValue = ValueFromXCoordinate(e);

                    if (newValue < this.Minimum)
                        newValue = this.Minimum;

                    if (newValue > this.Maximum)
                        newValue = this.Maximum;

                    Value = newValue;
                    ThumbClicked = true;
                    ThumbState = SliderThumbState.Pressed;
                }
            }
            base.OnMouseDown(e);
        }

        /// <summary>Grab focus to enable receiving key messages.</summary>
        protected override void OnMouseHover(EventArgs e)
        {
            Form form = this.FindForm();

            if (form == Form.ActiveForm)
                this.SelectAndFocus();

            base.OnMouseHover(e);
        }

        /// <summary>Track cursor movements.</summary>
        protected override void OnMouseMove(MouseEventArgs e)
        {
            // The user is moving the thumb.
            if (ThumbClicked)
            {
                int newValue = ValueFromXCoordinate(e);

                if (newValue < this.Minimum)
                    newValue = this.Minimum;

                if (newValue > this.Maximum)
                    newValue = this.Maximum;

                Value = newValue;

                if (!thumbRectangle.Contains(e.Location))
                    this.OnLostFocus(EventArgs.Empty);
                else
                    ThumbState = SliderThumbState.Pressed;
            }
            else // The cursor is passing over the track.
                ThumbState = thumbRectangle.Contains(e.Location) ? SliderThumbState.Hot : SliderThumbState.Normal;

            base.OnMouseMove(e);
        }

        /// <summary>Redraw the slider thumb if the user has moved it.</summary>
        protected override void OnMouseUp(MouseEventArgs e)
        {
            if (ThumbClicked)
            {
                if (e.Location.X > trackRectangle.X && e.Location.X < (trackRectangle.X + trackRectangle.Width - thumbRectangle.Width))
                {
                    ThumbClicked = false;
                    ThumbState = SliderThumbState.Hot;
                }

                ThumbClicked = false;
            }

            base.OnMouseUp(e);
        }

        /// <summary>Draw the slider.</summary>
        protected override void OnPaint(PaintEventArgs e)
        {
            DrawHorizontalTrack(e);
            DrawVerticalThumb(e);

            base.OnPaint(e);
        }

        protected override void OnSizeChanged(EventArgs e)
        {
            if (Width > 0)
                this.SetBounds(this.Bounds.Left, this.Bounds.Top, Width, Height);

            InitializeSlider();

            base.OnSizeChanged(e);
        }

        private int ValueFromXCoordinate(MouseEventArgs e)
        {
            // Note that this and XCoordinateFromValue are the same equation, just solving for a different value.
            return this.trackRectangle.Width == 0 ? 0 : (int)(((double)e.Location.X / this.trackRectangle.Width) * (this.Maximum - this.Minimum) + this.Minimum);
        }

        private int XCoordinateFromValue()
        {
            return this.Maximum == 0 || this.trackRectangle.Width == 0 ? 0 : (int)((double)(value - this.Minimum) / (this.Maximum - this.Minimum) * this.trackRectangle.Width);
        }

        /// <summary>Draws a horizontal slider track.</summary>
        private void DrawHorizontalTrack(PaintEventArgs e)
        {
            // Draw the left edge of the track. (Using my 1x4 pixel "edge" graphic.)
            e.Graphics.DrawImage(this.ColorScheme == SliderColorScheme.Sepia ? TrackEdge : TrackGreyEdge, trackRectangle.Left, trackRectangle.Top, 1, trackRectangle.Height);

            // Draw the middle of the track. (It simply tiles the 1x4 pixel middle graphic.)
            using (TextureBrush trackMiddleBrush = new TextureBrush(this.ColorScheme == SliderColorScheme.Sepia ? TrackMiddle : TrackGreyMiddle, WrapMode.Tile))
            {
                Rectangle rect = new Rectangle(trackRectangle.Left + 1, trackRectangle.Top, trackRectangle.Width - 2, trackRectangle.Height);
                e.Graphics.FillRectangle(trackMiddleBrush, rect);
            }

            // Draw the right edge of the track. (Draws the 1x4 pixel edge graphic again.)
            e.Graphics.DrawImage(this.ColorScheme == SliderColorScheme.Sepia ? TrackEdge : TrackGreyEdge, trackRectangle.Left + trackRectangle.Width - 1, trackRectangle.Top, 1, trackRectangle.Height);
        }

        /// <summary>Draws a vertical thumb button (the thumb button is vertical; the track is horizontal).
        /// This button is symmetrical, so the concept of bottom-pointing or top-pointing is irrelevant.</summary>
        private void DrawVerticalThumb(PaintEventArgs e)
        {
            if (this.ColorScheme == SliderColorScheme.Sepia)
                e.Graphics.DrawImage(ThumbState == SliderThumbState.Normal ? Thumb : ThumbState == SliderThumbState.Hot ? ThumbHot : ThumbPressed, new PointF(thumbRectangle.Left, thumbRectangle.Top - 1));
            else
                e.Graphics.DrawImage(ThumbState == SliderThumbState.Normal ? ThumbGrey : ThumbState == SliderThumbState.Hot ? ThumbGreyHot : ThumbGreyPressed, new PointF(thumbRectangle.Left, thumbRectangle.Top - 1));
        }

        private void Initialize()
        {
            this.SetStyle(ControlStyles.UserPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.SupportsTransparentBackColor, true);
            this.BackColor = Color.Transparent;
            this.DoubleBuffered = true;
            this.ColorScheme = SliderColorScheme.Grey;
        }

        private void InitializeSlider()
        {
            this.Value = this.Minimum;

            using (Graphics g = this.CreateGraphics())
            {
                // Calculate the size of the track bar.
                trackRectangle = new Rectangle(
                    ClientRectangle.X + 4,
                    (ClientRectangle.Height - TrackHeight) / 2,
                    ClientRectangle.Width - 8,
                    TrackHeight);

                // Note: Leftmost position indicates value Minimum, not necessarily 0.
                tickSpace = (double)(trackRectangle.Width) / (Maximum - Minimum);

                thumbRectangle.Size = Thumb.Size;

                thumbRectangle.X = XCoordinateFromValue();
                thumbRectangle.Y = 2 + trackRectangle.Y - thumbRectangle.Size.Height / 2;
            }
        }

        private void StopSliding()
        {
            /* In case of a mouse down event with no corresponding mouse up; ensure the thumb isn't left in the pressed state.
             * Otherwise it could be very confusing to the user if they happen to move their mouse over the control. */
            if (ThumbClicked)
            {
                ThumbClicked = false;
                ThumbState = SliderThumbState.Normal;
            }
        }

        private void UpdateThumbLocation()
        {
            int x = XCoordinateFromValue();

            /* These adjustments allow for the width of the thumb itself, such that when you drag the slider to the min
             * and max positions, there is no "bouncing" affect as its position gets corrected, and there is no visible
             * edge of the track sticking out from behind the thumb when it is orientated in either min or max position. */
            int minValueAllowed = this.trackRectangle.Left - this.thumbRectangle.Width / 2;
            int maxAllowedValue = this.trackRectangle.Left + this.trackRectangle.Width - this.thumbRectangle.Width / 2;

            if (x < minValueAllowed)
                x = minValueAllowed;

            if (x > maxAllowedValue)
                x = maxAllowedValue;

            thumbRectangle.X = x;
            this.Invalidate();
        }

        /// <summary>Prevent the thumb button getting left in the hot state. (This can happen if the
        /// control loses focus without any relevant handlers being called, e.g. Alt+Tab.)</summary>
        private async void CheckThumbstate()
        {
            try
            {
                while (ThumbState == SliderThumbState.Hot && thumbRectangle.Contains(this.PointToClient(MousePosition)))
                {
                    await Task.Delay(TimeSpan.FromSeconds(1));
                }

                if (ThumbState == SliderThumbState.Hot)
                    ThumbState = SliderThumbState.Normal;
            }
            catch (ObjectDisposedException) { } // The owning form may be closed before our PointToClient call. (which will then cause an attempt to recreate the Handle of the already disposed form)
        }
    }
}
Posted in Programming | Tagged , , , , | Leave a comment

All the c# source code of the last few articles, and then some…

I guess it will never really be ready to share, so here it is… as is.

My old share that you’ll find links to in older posts, was shared on OneDrive. This is on Google drive. You’ll find two zip files in the shared directory. I’ll keep these updated until I put this in a shared repo.

  1. RomyView.zip contains the full source code.
  2. RomyView Release.zip contains the built binaries.

The name of the app is just a shortened version of my first name with “view” slapped onto it.

The reason it’s so large is that it includes all the code it references, and two static builds of ffmpeg.exe. If you use the video conversion part of the application, it then uses either the 32 bit or 64 bit executable depending on your machine.

The solution expects Visual Studio 2019, and .NET Framework version 4.8. I had to download 4.8 manually, because on my Windows 10 machines, the latest was 4.7.2. If everything is set up correctly on your machine, you should be able to run BuildAndBackup.cmd in the extracted directory, and that should then build the release, then create the two zip files that you downloaded as well as create a backup directory, where it puts the existing zip files. And obviously the solution should open and build with no issues in Visual Studio 2019.

The application itself launches with a splash screen that fades from completely opaque to invisible, before the browser form is shown. It supports being opened with a directory name parameter and only allows one instance. If you try to open it again, it will pass the command line on to the original instance. Then, it can open some image formats internally, view most videos (if you install Direct Show codecs like the KLite-Codec Pack), and it can open zip files (without password) internally. It also converts videos using ffmpeg, and in an unfortunate dodgy way, will allow you to do as many video conversions as you want simultaneously, each one showing a separate progress dialog which allows you to pause or resume the process, as well as set the process priority – even to realtime which will bring your machine to a grinding halt. Used unwisely, the application can turn your machine into a toaster.

The video player has some dodgy features, such as automatically persisting 10 playlists, and some repeat modes as well as starting and ending position for videos and slow-motion play… that only a meth-head could have written while tweaking on porn.

The image viewer can do some limited image editing, mostly applying image filters for contrast, hue, saturation, brightness, and so on. Most of the image filter dialogs have an implementation of my own pattern that can apply filters to all files in a directory asynchronously. So you can do crazy shit like, use ffmpeg to extract all frames of a video, then apply the same image filters to all the frames, then re-encode all those frames back to a video. Actually I used to do the last part with a batch file – I never added the option to select separate audio and video streams and join them with my ffmpeg wrapper.

Everything is custom-drawn, even the toolstrip and menustrips. So hovering the mouse over menu items draws a nice gradient-filled rounded rectangle like selected thumbnails.

I did some crazy shit with drawing shadows for all the windows controls. For example, this dialog… Not a very intelligent batch rename dialog, but notice the thumbnail control on the right is the same as that used in the main form, plus all the panels and buttons on this dialog have shadows.

SNAGHTML34ec01fa

Here’s a screenshot that shows the custom-drawn context menu: (Open in new tab to see it properly.)

image

Thumbnails are cached to binary on the filesystem, while thumbnail controls themselves are reused via an object pool.

There’s some good code here, and some… well, dodgy code. Mostly there’s code that I’m proud of and it pisses me off that nobody ever uses it.

Some screenshots of the application…

The main form:

SNAGHTML34d553c3

The zip viewer:

SNAGHTML34d5cc3f

The image viewer, viewing a screenshot of the post that got me banned from Facebook:

SNAGHTML34de3274

The video convertor, having selected a video of my two beautiful children:

SNAGHTML34dcbcad

The video player playing a “Satanic Winnie the Pooh” video

If you don’t find this funny, I feel sorry for you.

image

You don’t wanna know how long I tweaked on the images and custom slider to make this form look alright, back when I was still a meth-head. Here’s the same form when my app is using its dark theme…

The controls shown on this form, all are all custom ToolStripButtons and a slider, with images carefully created by hand in Photoshop. From left to right, they are: play, pause, stop, fast-forward, step back (hidden in half-size mode due to the window size and overflow settings), step forward, next file, and previous file; then play-rate, zoom mode, repeat (folder) mode , time/duration label, volume/mute button, and custom slider.

image

Since the code is now shared, I can write other posts that refer to bits of it and link back here.

Posted in Programming | Tagged , | Leave a comment

Ever wondered how code can estimate the size of an image to be saved?

This is an idea I came up with a few years ago, while high on meth, so how clever it really is, is probably debatable.

I’d noticed how applications that can save images always have this feature where they tell you the size that the image will be on disk, and decided to implement it myself. Here’s how I did it:

  1. Write a method that supports saving an image in jpeg format, and specifying the quality.
  2. Call the method in a background thread, triggered whenever anyone changes the quality.
  3. Display the size of the image you saved in the background as the “estimated” size.

So all that code does is use this extension, and display the array length (in bytes) as the estimated size.

/// <summary>Convert an image to a byte array in jpeg format at the quality specified. This is useful
/// for saving in advance, and displaying the "estimated" file size (i.e. the array length).</summary>
public static byte[] ToArray(this Image image, long quality)
{
    if (image == null)
        throw new ArgumentNullException(nameof(image));

    using (MemoryStream stream = new MemoryStream())
    {
        image.SaveJpeg(stream, quality);
        return stream.ToArray();
    }
}

I’m not sharing the calling code, but here is an example that uses it… It’s not a standard dialog but my custom “Save as” dialog, which uses my horrible shell treeview mentioned in the previous post, as well as my thumbnail control and custom FlowLayoutPanel – and the part relevant here… my custom slider control that allows the user to change the desired image quality. Whenever the user changes the quality (and then stops playing with the slider for the time interval I define as having finished), a background thread is fired off to save the image at that quality using the code shown above, and the file size label is updated to reflect the size of the image bytes. It doesn’t throw away the image byte array saved in the background, but caches it, so if the user clicks save and there is a precached image, the save action appears to be instant, because all it needs to do at that point is save the image bytes to a new file stream.

SNAGHTML338a6c18

The code to format the size was shared the other day, in this post. But I do find it amusing that the best way to “estimate” how big an image conversion will be, is to actually just do the conversion. I’ve used the same technique elsewhere… any place where some action can be performed on an object and we have default or persisted options to use, just go ahead and perform the action in a background thread and cache the result. If the user cancels, discard the precached result (and clean up any memory used); but if the user proceeds, using the cached result makes the application seem faster. (The background work can be done via an async method that returns a Task, and takes a CancellationTokenSource parameter.  Thus if the user cancels the operation completely and that task is still in flight, cancel it.)

Oops… I forgot that SaveJpeg was one of my extensions too. Here’s my whole class of image extensions. (Disclaimer: All the code I’m sharing today was written years ago while I was high on methamphetamine, tweaking my ass off and writing code all night every night. Use at your own risk and if you see anything dumb in the code, feel free to fix it and comment here. But yeah… I really was out of my mind on meth when I wrote this.)

using Romy.Core;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

namespace System.Drawing
{
    /// <summary>Several synchronous and asynchronous extension methods
    /// to make saving and converting images a little easier.</summary>
    public static class ImageExtensions
    {
        #region Private Fields

        /// <summary>Cached encoder for improved performance.</summary>
        private static readonly Lazy<ImageCodecInfo> jpegEncoder = new Lazy<ImageCodecInfo>(() => ImageCodecInfo.GetImageDecoders().FirstOrDefault(j => j.FormatID == ImageFormat.Jpeg.Guid));

        #endregion Private Fields

        #region Internal Properties

        internal static ImageCodecInfo JpegEncoder => jpegEncoder.Value;

        #endregion Internal Properties

        #region Public Methods

        /// <summary>Creates a 24 bit-per-pixel copy of the source image.</summary>
        public static Image CopyImage(this Image image) => CopyImage(image, PixelFormat.Format24bppRgb);

        /// <summary>Creates a copy of the source image with the specified pixel format.</summary><remarks>
        /// This can also be achieved with the <see cref="Bitmap.Clone(int, int, PixelFormat)"/>
        /// overload, but I have had issues with that method.</remarks>
        public static Image CopyImage(this Image image, PixelFormat format)
        {
            if (image == null)
                throw new ArgumentNullException(nameof(image));

            // Don't try to draw a new Bitmap with an indexed pixel format.
            if (format == PixelFormat.Format1bppIndexed || format == PixelFormat.Format4bppIndexed || format == PixelFormat.Format8bppIndexed || format == PixelFormat.Indexed)
                return (image as Bitmap).Clone(new Rectangle(0, 0, image.Width, image.Height), format);

            Image result = null;
            try
            {
                result = new Bitmap(image.Width, image.Height, format);

                using (Graphics graphics = Graphics.FromImage(result))
                {
                    graphics.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBicubic;
                    graphics.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias;
                    graphics.CompositingQuality = Drawing2D.CompositingQuality.HighQuality;

                    graphics.DrawImage(image, 0, 0, result.Width, result.Height);
                }
            }
            catch
            {
                if (result != null)
                    result.Dispose();

                throw;
            }
            return result;
        }

        public static void GetFormatAndSave(this Image image, string filename)
        {
            if (image == null)
                throw new ArgumentNullException(nameof(image));

            if (filename == null)
                throw new ArgumentNullException(nameof(filename));

            using (FileStream stream = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None, Constants.BufferSize, false))
            {
                /* This makes use of my existing logic in ImageManager, to figure out what image format
                 * should be used to save the image to a stream, hopefully without throwing an exception.
                 * Of course, any formats I don't know how to handle will probably throw an exception. */
                ImageManager manager = new ImageManager(image, filename);

                using (MemoryStream memStream = new MemoryStream(manager.Data.ToArray()))
                {
                    memStream.CopyTo(stream);
                }
            }
        }

        /// <summary>Asynchronously saves the image to the specified file, after resolving the format to use.</summary>
        /// <param name="image">The image to save.</param>
        /// <param name="filename">The full path and file name to which the image must be saved.</param>
        public static async Task GetFormatAndSaveAsync(this Image image, string filename)
        {
            if (image == null)
                throw new ArgumentNullException(nameof(image));

            if (filename == null)
                throw new ArgumentNullException(nameof(filename));

            using (FileStream stream = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None, Constants.BufferSize, true))
            {
                /* This makes use of my existing logic in ImageManager, to figure out what image format
                 * should be used to save the image to a stream without throwing an exception.
                 * Of course, any formats I don't know how to handle will probably throw an exception. */
                ImageManager manager = new ImageManager(image, filename);

                using (MemoryStream memStream = new MemoryStream(manager.Data.ToArray()))
                {
                    await memStream.CopyToStreamAsync(stream);
                }
            }
        }

        /// <summary>Resizes an image, optionally maintaining width:height ratios.</summary>
        /// <param name="image">The <see cref="Image"/> that you wish to resize.</param>
        /// <param name="width">The desired width of the resulting image.</param>
        /// <param name="height">The desired height of the resulting image.</param>
        /// <param name="maintainAspectRatio"><b>True</b> to maintain aspect ratio,
        /// otherwise <b>false</b>. This defaults to <b>true</b>.</param>
        /// <returns>The resulting resized <see cref="Image"/> object.</returns>
        public static Image Resize(this Image image, int width, int height, bool maintainAspectRatio = true)
        {
            if (image == null)
                throw new ArgumentNullException(nameof(image));

            if (maintainAspectRatio)
            {
                // calculate resize ratio
                double ratio = (double)width / image.Width;

                if (ratio * image.Height > height)
                    ratio = (double)height / image.Height;

                width = (int)Math.Round(ratio * image.Width, MidpointRounding.AwayFromZero);
                height = (int)Math.Round(ratio * image.Height, MidpointRounding.AwayFromZero);
            }

            Bitmap bmp = null;
            try
            {
                bmp = new Bitmap(width, height);

                using (Graphics g = Graphics.FromImage(bmp))
                {
                    g.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBicubic;
                    g.SmoothingMode = Drawing2D.SmoothingMode.HighQuality;
                    g.CompositingQuality = Drawing2D.CompositingQuality.HighQuality;
                    g.DrawImage(image, 0, 0, width, height);
                }

                return bmp;
            }
            catch
            {
                if (bmp != null)
                    bmp.Dispose();
                throw;
            }
        }

        /// <summary>Save the specified image to the specified stream, in jpeg format, at the quality specified.
        /// </summary><remarks>Since this method is called frequently, the jpeg encoder is cached.</remarks>
        /// <param name="image">The image to save.</param>
        /// <param name="stream">The stream to which the image will be saved.</param>
        /// <param name="quality">The quality to use for the saved image.
        /// (where higher quality means less compression, and a larger file)</param>
        public static void SaveJpeg(this Image image, Stream stream, long quality = 80L)
        {
            if (image == null)
                throw new ArgumentNullException(nameof(image));

            if (stream == null)
                throw new ArgumentNullException(nameof(stream));

            using (Bitmap tempBitmap = image.CopyImage(image.PixelFormat) as Bitmap)
            {
                using (EncoderParameters encoderParameters = new EncoderParameters(1))
                {
                    encoderParameters.Param[0] = new EncoderParameter(Encoder.Quality, quality);
                    tempBitmap.Save(stream, JpegEncoder, encoderParameters);
                }
            }
        }

        /// <summary>Save the specified image to the file specified, at the quality specified.</summary>
        /// <param name="image">The image to save.</param>
        /// <param name="filename">The full path and file name to which the image must be saved.</param>
        /// <param name="quality">The quality to use for the saved image.
        /// (where higher quality means less compression, and a larger file)</param>
        public static void SaveJpeg(this Image image, string filename, long quality = 80L)
        {
            if (filename == null)
                throw new ArgumentNullException(nameof(filename));

            using (FileStream stream = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None, Constants.BufferSize, true))
            {
                image.SaveJpeg(stream, quality);
            }
        }

        /// <summary>Asnchronously save the specified image to the file specified, at the quality specified.</summary>
        /// <param name="image">The image to save.</param>
        /// <param name="filename">The full path and file name to which the image must be saved.</param>
        /// <param name="quality">The quality to use for the saved image.
        /// (where higher quality means less compression, and a larger file)</param>
        public static async Task SaveJpegAsync(this Image image, string filename, long quality = 80L)
        {
            if (image == null)
                throw new ArgumentNullException(nameof(image));

            if (filename == null)
                throw new ArgumentNullException(nameof(filename));

            using (MemoryStream memStream = new MemoryStream())
            {
                image.SaveJpeg(memStream, quality);

                using (FileStream stream = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None, Constants.BufferSize, true))
                {
                    memStream.Position = 0;
                    await memStream.CopyToStreamAsync(stream);
                }
            }
        }

        /// <summary>Asynchronously save the specified image to the specified stream,
        /// in jpeg format, at the quality specified.</summary>
        /// <param name="image">The image to save.</param>
        /// <param name="stream">The stream to which the image will be saved.</param>
        /// <param name="quality">The quality to use for the saved image.
        /// (where higher quality means less compression, and a larger file)</param>
        public static async Task SaveJpegAsync(this Image image, Stream stream, long quality = 80L)
        {
            if (image == null)
                throw new ArgumentNullException(nameof(image));

            if (stream == null)
                throw new ArgumentNullException(nameof(stream));

            using (MemoryStream memStream = new MemoryStream())
            {
                image.SaveJpeg(memStream, quality);
                memStream.Position = 0;
                await memStream.CopyToStreamAsync(stream);
            }
        }

        /// <summary>Overwrites an existing jpeg file after calculating the required quality
        /// to save it as close to, but not smaller than the existing file size.</summary>
        public static async Task<long> SaveJpegPreserveFileSizeAsync(this Image modified, string filename)
        {
            if (modified == null)
                throw new ArgumentNullException(nameof(modified));

            if (filename == null)
                throw new ArgumentNullException(nameof(filename));

            long existingFileSize = new FileInfo(filename).Length;

            byte[] current = null, previous = null;

            for (long quality = 99L; quality > 0L; quality--)
            {
                if (current != null)
                    previous = current;

                /* My ToArray extension method saves the jpeg to memory at the specified quality, and returns the memory
                 * as a byte array. Loop until we find a file size less than the target size; then use previous value. */
                current = modified.ToArray(quality);

                if (current.Length < existingFileSize)
                {
                    if (previous == null) // This can only happen when quality == 99 and previous is uninitialized.
                        previous = modified.ToArray(++quality);

                    using (MemoryStream stream = new MemoryStream(previous))
                    {
                        using (FileStream destStream = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None, Constants.BufferSize, true))
                        {
                            await stream.CopyToStreamAsync(destStream);
                        }
                    }
                    return quality;
                }
            }

            // It should never get here, but if it does, save with no compression.
            await modified.SaveJpegAsync(filename, 100L);
            return 100L;
        }

        /// <summary>Convert an image to a byte array in png format.</summary>
        public static byte[] ToArray(this Image image)
        {
            if (image == null)
                throw new ArgumentNullException(nameof(image));

            using (MemoryStream stream = new MemoryStream())
            {
                image = image.CopyImage(PixelFormat.Format32bppArgb);
                image.Save(stream, ImageFormat.Png);
                return stream.ToArray();
            }
        }

        /// <summary>Convert an image to a byte array in jpeg format at the quality specified. This is useful
        /// for saving in advance, and displaying the "estimated" file size (i.e. the array length).</summary>
        public static byte[] ToArray(this Image image, long quality)
        {
            if (image == null)
                throw new ArgumentNullException(nameof(image));

            using (MemoryStream stream = new MemoryStream())
            {
                image.SaveJpeg(stream, quality);
                return stream.ToArray();
            }
        }

        #endregion Public Methods
    }
}

One or two of those extensions use my ImageManager class, which is an internal class I use to manage references to images, and manage the types of images my application supports: (I’m not including my IO static class, which has methods like IO.IsJPeg and so on. Those just check the files based on file extension… because including that class will open yet another can of worms and then I’ll need to include other code.)

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.ComTypes;

namespace Romy.Core
{
    /// <summary>A type to manage image references so that they can safely be manipulated asynchronously.</summary>
    /// <remarks><para>
    /// The intention is to solve the issue of accessing a GDI+ image reference asynchronously, and having it
    /// ripped out from under you unexpectedly.</para>
    /// <para>
    /// This gets tricky in async methods, despite "protecting" the image instances in either try...finally blocks
    /// or using directives. While the compiler-generated code saves state, returns and later resumes execution via
    /// a state machine, image references may be disposed on the main thread, leading to unexpected unhandled
    /// exceptions in our processing threads, possibly severe enough even to take down the application (although
    /// generally just annoying: "A generic error occurred in GDI+")</para>
    /// <para>
    /// Rather than holding a reference to the image itself, this type simply stores the image bytes. Thus each
    /// time the <see cref="ToImage()"/> or <seealso cref="ToBitmap()"/> method is called,
    /// the caller will get a unique image from the original image data.</para>
    /// <para>
    /// This type can slow performance if used excessively or when it is unnecessary, but the overhead of the
    /// mechanism used to create the unique image instances, when used as intended from async Tasks, is negligable.</para>
    /// <para>
    /// This type doesn't really do anything smart like create an actual image that can be manipulated. It just caches
    /// the managed image bytes. (And effectively manages <i>references</i> to the image. (I do this because I know
    /// nothing of graphics processing, but wanted a way to safely pass references to a managed image in async threads,
    /// even though I may dispose the original image.) For a similar type that is more useful (to developers who know
    /// what they are doing w.r.t. graphics programming), I suggest using something like the AForge.Net
    /// <see cref="AForge.Imaging.UnmanagedImage"/>.</para></remarks>
    public sealed class ImageManager
    {
        #region Private Fields

        /// <summary>This is used to test if an image is an animated GIF file. If so, we then load
        /// the image using an alternative method so as not to lose subsequent frames.</summary>
        private static readonly Lazy<ImageCodecInfo> gifEncoder = new Lazy<ImageCodecInfo>(() => ImageCodecInfo.GetImageDecoders().FirstOrDefault(g => g.FormatID == ImageFormat.Gif.Guid));

        /// <summary>Holds the actual image data.</summary>
        private byte[] data;

        #endregion Private Fields

        #region Public Constructors

        public ImageManager(Image image, string filename) => Initialize(image, filename);

        /// <summary>Intended use of this type is to be passed an Image,
        /// but if all we have is a file name, try to load it.</summary>
        /// <remarks>It will load the image for a supported image type
        /// from disk, or try to load a thumbnail image for a directory
        /// name or video file.</remarks>
        public ImageManager(string filename)
        {
            Image image = null;
            try
            {
                if (IO.IsImageFile(filename))
                {
                    image = ImageFile.OpenBitmap(filename);
                }
                else if (Directory.Exists(filename) || IO.IsMediaFile(filename))
                    // Load the thumbnail image, if we can. (256x256)
                    image = ShellItemImageFactory.GetImage(filename);

                if (image != null)
                    Initialize(image, filename);
                else
                    throw new InvalidOperationException(string.Format("Unable to load an image from '{0}'.", filename));
            }
            finally
            {
                if (image != null)
                    image.Dispose();
            }
        }

        #endregion Public Constructors

        #region Public Properties

        public IEnumerable<byte> Data => data;

        public ImageFormat Format { get; private set; }

        #endregion Public Properties

        #region Public Methods

        public Bitmap ToBitmap() => ToImage() as Bitmap;

        public Image ToImage()
        {
            using (MemoryStream stream = new MemoryStream(data))
            {
                return new Bitmap(stream);
            }
        }

        #endregion Public Methods

        #region Private Methods

        private static ImageFormat GetImageFormat(Image image, string filename)
        {
            ImageFormat format = image.RawFormat;

            if (IO.IsJpeg(filename))
                format = ImageFormat.Jpeg;
            else if (IO.IsPng(filename) || Directory.Exists(filename) || IO.IsMediaFile(filename))
                format = ImageFormat.Png;
            else if (IO.IsTif(filename))
                format = ImageFormat.Tiff;
            else if (IO.IsIcon(filename))
                format = ImageFormat.Icon;
            else if (IO.IsGif(filename))
                format = ImageFormat.Gif;
            else if (IO.IsBmp(filename))
                format = ImageFormat.Bmp;

            return format;
        }

        private void Initialize(Image image, string filename)
        {
            /* Although one might expect saving the image data to a stream to be
             * as simple as specifying the image with it's raw format, doing so
             * throws an exception. Thus to save it successfully, we must check
             * for image formats we support, and handle each format appropriately. */
            using (MemoryStream stream = new MemoryStream())
            {
                Format = GetImageFormat(image, filename);

                /* In each case, save the image to our internal stream in the
                 * appropriate format, so that the bytes can be stored. */
                if (Format == ImageFormat.Jpeg)
                {
                    image.SaveJpeg(stream, 100L);
                }
                else if (Format == ImageFormat.Icon)
                {
                    /* My IconFileWriter only supports writing png images to the images collection of icon files.
                     * This is probably the safest way to handle all icons. (But most modern icons *are* png anyway.) */
                    image = image.CopyImage(PixelFormat.Format32bppArgb);
                    image.Save(stream, ImageFormat.Png);
                }
                else if (Format == ImageFormat.Gif)
                {
                    if (IO.IsGif(filename) && File.Exists(filename))
                    {
                        /* This IF block only applies to GIF images that we load from file.
                         * It does not apply when we are converting something else to GIF. */
                        ImageCodecInfo encoder = gifEncoder.Value;

                        if (encoder != null && encoder.FormatID == image.RawFormat.Guid && ImageAnimator.CanAnimate(image))
                        {
                            /* This will load the GIF file properly with all it's
                             * frames. Otherwise we only get the first frame. */
                            image = (Bitmap)Image.FromFile(filename);
                        }
                    }
                    image = image.CopyImage(PixelFormat.Format16bppRgb565);
                    image.Save(stream, ImageFormat.Gif);
                }
                else if (Format == ImageFormat.Tiff)
                {
                    image = image.CopyImage(PixelFormat.Format24bppRgb);
                    image.Save(stream, ImageFormat.Tiff);
                }
                else if (Format == ImageFormat.Bmp)
                {
                    image.CopyImage(PixelFormat.Format32bppArgb).Save(stream, ImageFormat.Bmp);
                }
                else
                    // Ignore the RawFormat completely and force png format; it's only going to throw otherwise.
                    image.CopyImage(PixelFormat.Format32bppArgb).Save(stream, ImageFormat.Png);

                // Store the image byte data.
                data = stream.ToArray();
            }
        }

        #endregion Private Methods
    }
}

And lastly, this is the code I use to load image files from disk: (You can remove the bits that refer to PSD files.)

using Photoshop;
using System;
using System.Drawing;
using System.IO;
using System.Threading.Tasks;

namespace Romy.Core
{
    public static class ImageFile
    {
        /// <summary>Opens and returns an in-memory copy of the specified image, so as not to lock the file.</summary>
        public static Bitmap OpenBitmap(string filename)
        {
            Bitmap image = null;

            void disposeImageOnError()
            {
                if (image != null)
                {
                    image.Dispose();
                    image = null;
                }
            }

            try
            {
                /* For animated GIF files, opening them any other way will 
                 * result in an error when trying to animate them later. */
                if (string.Compare(Path.GetExtension(filename), ".gif", StringComparison.InvariantCultureIgnoreCase) == 0)
                    image = Image.FromFile(filename) as Bitmap;
                else
                {
                    using (MemoryStream stream = new MemoryStream(File.ReadAllBytes(filename)))
                    {
                        if (string.Compare(Path.GetExtension(filename), ".psd", StringComparison.InvariantCultureIgnoreCase) != 0)
                            image = new Bitmap(stream);
                        else
                        {
                            using (PsdFile psdFile = new PsdFile(stream))
                                image = psdFile.CompositImage.Clone() as Bitmap;
                        }
                    }
                }
            }
            catch (IOException) { disposeImageOnError(); }
            catch (UnauthorizedAccessException) { disposeImageOnError(); }
            catch (Exception ex)
            {
                ex.Log();
                disposeImageOnError();
            }

            return image;
        }

        /// <summary>Opens and returns a Bitmap using asynchronous IO, using the Task-based Asynchronous Pattern (TAP) pattern.
        public static async Task<Bitmap> OpenBitmapAsync(string filename)
        {
            Bitmap image = null;

            try
            {
                /* For animated GIF files, opening them any other way will 
                 * result in an error when trying to animate them later. */
                if (string.Compare(Path.GetExtension(filename), ".gif", StringComparison.InvariantCultureIgnoreCase) == 0)
                    image = Image.FromFile(filename) as Bitmap;
                else
                {
                    using (MemoryStream memStream = new MemoryStream(await FileAsync.ReadAllBytesAsync(filename, Constants.LargeBufferSize)))
                    {
                        if (memStream.Length > 0) // My ReadAllBytesAsync method can return an empty array. Via Enumerable.Empty<byte>().ToArray()
                        {
                            if (string.Compare(Path.GetExtension(filename), ".psd", StringComparison.InvariantCultureIgnoreCase) != 0)
                                image = new Bitmap(memStream);
                            else
                            {
                                using (PsdFile psdFile = new PsdFile(memStream))
                                    image = psdFile.CompositImage.Clone() as Bitmap;
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                ex.Log();

                if (image != null)
                {
                    image.Dispose();
                    image = null;
                }
            }

            return image;
        }
    }
}
Posted in Programming | Tagged , | Leave a comment

Still not ready to share the full source code

I mentioned yesterday that I intend sharing the full source code of my mostly experimental application. But I’m not quite ready yet. I’ve updated the code base fully now, for VS 2019, and addressed the worst bugs that I knew about. So it’s more or less ready to share. But the one thing missing is that I would like to try changing all references to MemoryStream to use The Bing Team’s RecyclableMemoryStream instead.

That’s an experimental change but one that I think is worth trying. Also my code no longer uses my own custom TaskScheduler by default, although it’s options still refer to properties of that scheduler. Rather it uses a scheduler from Microsoft sample code, but one that’s much better (faster) at populating thumbnails (when uncached) when browsing directories that contain many files. Also my caching has been improved. Browsing a directory when the images are cached results in them displaying instantly, always.

And lastly, there is one buggy control I may never fix… that shitty treeview on the left in the image below. It looks nice and seems similar to the Windows explorer one, and it uses all the right shell methods to list the directories there, but the code is nasty… and even has hard-coded English names for special folders. The whole control really needs to be replaced or rewritten, and yet it drives the application’s navigation so the whole app is tightly coupled with this horrible control.

RomyView

Posted in Programming | Tagged | Leave a comment

Extracting shell images for files and directories in c#

I did share my application’s full source code years ago, but that was way out of date and I should update it at some point. Anyway… my app is mostly an image viewer like the old ACDSee application, and it displays the files it supports in a Windows explorer like interface. So it uses the Windows shell interfaces to fetch thumbnail images for the files it supports, and also for directories. The main form looks like this:

Browser form

You’ll have to open that in a new tab to see it properly. Ignore the fact that I’m using my own custom drawn controls (main panel is a custom FlowlayoutPanel, thumbnails are a custom control, and images are drawn everywhere, including the thumbnails, on a custom control rather than the PictureBox), with a gradient-filled rounded rectangle for the selected file. Sharing all the code to do that would involve far too much writing for my liking. So today I’m just sharing how I got those images from the Windows shell.

What it comes down to is, the code gets the images one of two ways:

  1. In Windows 7 and up (I’m on Windows 10 Pro here), it uses IShellItemImageFactory.GetImage.
  2. In Windows XP, it uses the much slower IExtractImage.Extract.

Either way, it ends up having to work around opaque images with solid colour background instead of transparent ones for directories, and does so via an extension method that uses an image library to flood fill them, and then trims the outside colour to force transparency. (Not a perfect solution, but a workaround. It fails when directory images, with white background, have white inside that’s connected to the outside of the image, because in that case the code can sometimes set parts of the inner image transparent too.) I’ll share that code as well, but not the image library. Note that although I use the AForge.NET framework, which is available via Nuget, I use a slightly customized version of their source code because mine needed to be strongly signed.

Here’s another view of the main form with a directory thumbnail selected and a mouse-over effect for a file, so you can see the workaround for opaque directory images actually works… If it didn’t work, that gradient-filled rounded rectangle drawn to show the selected thumbnail wouldn’t look quite the way it does. A complete failure would leave a solid white background around the outside of the “folder”, while over-aggressive filling and “trimming” would remove the white area inside the image, and you’d see the selection gradient painted inside the image.

Browser form

On with the code.

Here is my wrapper for IShellItemImageFactory:

using System.Drawing;
using System.Security;

namespace System.Runtime.InteropServices.ComTypes
{
    internal enum SIGDN : uint
    {
        NORMALDISPLAY = 0,
        PARENTRELATIVEPARSING = 0x80018001,
        PARENTRELATIVEFORADDRESSBAR = 0x8001c001,
        DESKTOPABSOLUTEPARSING = 0x80028000,
        PARENTRELATIVEEDITING = 0x80031001,
        DESKTOPABSOLUTEEDITING = 0x8004c000,
        FILESYSPATH = 0x80058000,
        URL = 0x80068000
    }

    [Flags]
    internal enum SIIGBF
    {
        SIIGBF_RESIZETOFIT = 0x00,
        SIIGBF_BIGGERSIZEOK = 0x01,
        SIIGBF_MEMORYONLY = 0x02,
        SIIGBF_ICONONLY = 0x04,
        SIIGBF_THUMBNAILONLY = 0x08,
        SIIGBF_INCACHEONLY = 0x10,
    }

    [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
        Guid("43826d1e-e718-42ee-bc55-a1e261c37bfe")]
    [SuppressUnmanagedCodeSecurity]
    internal interface IShellItem
    {
        void BindToHandler(IntPtr pbc,
            [MarshalAs(UnmanagedType.LPStruct)]Guid bhid,
            [MarshalAs(UnmanagedType.LPStruct)]Guid riid,
            out IntPtr ppv);

        void GetParent(out IShellItem ppsi);

        void GetDisplayName(SIGDN sigdnName, out IntPtr ppszName);

        void GetAttributes(uint sfgaoMask, out uint psfgaoAttribs);

        void Compare(IShellItem psi, uint hint, out int piOrder);
    };

    [ComImport, Guid("bcc18b79-ba16-442f-80c4-8a59c30c463b"),
        InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    [SuppressUnmanagedCodeSecurity]
    internal interface IShellItemImageFactory
    {
        [PreserveSig]
        HResult GetImage(
        [In, MarshalAs(UnmanagedType.Struct)] SIZE size,
        [In] SIIGBF flags,
        [Out] out IntPtr phbm);
    }

    [StructLayout(LayoutKind.Sequential)]
    internal struct SIZE
    {
        public SIZE(int cx, int cy)
        {
            this.cx = cx;
            this.cy = cy;
        }

        public int cx;

        public int cy;
    }

    /// <summary>Extracts thumbnail images using Windows Shell interface methods.</summary>
    /// <remarks>
    /// In Windows Vista/7 up, the IShellItemImageFactory interface is used to extract the image,
    /// while in older Windows operating systems such as Windows XP, the IExtractImage interface
    /// is used instead.</remarks>
    public static class ShellItemImageFactory
    {
        #region Private Fields

        private static Guid IID_ShellItem = new Guid("43826d1e-e718-42ee-bc55-a1e261c37bfe");

        #endregion Private Fields

        #region Private Enums

        private enum ShellImageSize
        {
            Small = 16,
            Medium = 48,
            Large = 96,
            ExtraLarge = 256
        }

        #endregion Private Enums

        #region Public Properties

        public static bool SupportsShellItemImageFactory => (Environment.OSVersion.Version.Major >= 6);

        #endregion Public Properties

        #region Public Methods

        public static Bitmap GetImage(string filename)
        {
            if (filename == null)
                throw new ArgumentNullException(nameof(filename));

            if (!SupportsShellItemImageFactory)
            {
                // Use my IExtractImage wrapper instead
                return ExtractImage.ExtractThumbnail(filename);
            }

            if (NativeMethods.SHCreateItemFromParsingName(filename, IntPtr.Zero, IID_ShellItem, out IShellItem ppsi) == HResult.S_OK)
            {
                HResult hRes = ((IShellItemImageFactory)ppsi).GetImage(new SIZE((int)ShellImageSize.ExtraLarge, (int)ShellImageSize.ExtraLarge), SIIGBF.SIIGBF_BIGGERSIZEOK, out IntPtr hbitmap);

                if (hRes == HResult.S_OK)
                {
                    try
                    {
                        return Romy.Core.Shell.Common.FixShellBitmap(filename, Image.FromHbitmap(hbitmap));
                        //return Bitmap.FromHbitmap(hbitmap);
                    }
                    finally { NativeMethods.DeleteObject(hbitmap); }
                }
                else { Marshal.ThrowExceptionForHR((int)hRes); }
            }

            return null;
        }

        #endregion Public Methods

        #region Private Classes

        [SuppressUnmanagedCodeSecurity]
        private static class NativeMethods
        {
            #region Public Methods

            [DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool DeleteObject(IntPtr hObject);

            [DllImport("shell32.dll", CharSet = CharSet.Unicode, PreserveSig = true)]
            public static extern HResult SHCreateItemFromParsingName(
                [In][MarshalAs(UnmanagedType.LPWStr)] string pszPath,
                [In] IntPtr pbc,
                [In][MarshalAs(UnmanagedType.LPStruct)] Guid riid,
                [Out][MarshalAs(UnmanagedType.Interface, IidParameterIndex = 2)] out IShellItem ppv);
            #endregion Public Methods
        }

        #endregion Private Classes
    }
}

Here’s my wrapper for IExtractImage:

using System.Drawing;
using System.IO;
using System.Runtime.CompilerServices;
using System.Security;
using System.Text;

namespace System.Runtime.InteropServices.ComTypes
{
    /// <summary>Extracts thumbnail images using the IExtractImage interface. Windows XP.</summary>
    /// <remarks><para>
    /// In Windows Vista and later, the <b>IShellItemImageFactory</b> interface should be used instead,
    /// as used by my <see cref="ShellItemImageFactory"/> type. This is the old way of extracting shell
    /// images. It is slower and the implementation is non-trivial. In fact, most implementations of it
    /// that can be found online are copies of the same poorly written code, and perform needless extra
    /// enumeration. (While fetching each individual thumbnail in a directory, for each file they
    /// enumerate the whole directory over again. I have seen this error in all examples online.)</para>
    /// <para>
    /// Also, this extracts thumbnails using the older Windows XP logic. i.e. directories only get
    /// thumbnails if they contain files that are thumbnailed, such as images and videos.</para></remarks>
    public static class ExtractImage
    {
        #region Private Fields

        private static Guid IID_IExtractImage = new Guid("{BB2E617C-0920-11d1-9A0B-00C04FC2D6C1}");

        private static Guid IID_ShellFolder = new Guid("000214E6-0000-0000-C000-000000000046");

        #endregion Private Fields

        #region Private Enums

        private enum EIEIFLAG
        {
            IEIFLAG_ASYNC = 1,
            IEIFLAG_CACHE = 2,
            IEIFLAG_ASPECT = 4,
            IEIFLAG_OFFLINE = 8,
            IEIFLAG_GLEAM = 16,
            IEIFLAG_SCREEN = 32,
            IEIFLAG_ORIGSIZE = 64,
            IEIFLAG_NOSTAMP = 128,
            IEIFLAG_NOBORDER = 256,
            IEIFLAG_QUALITY = 512
        }

        [Flags]
        private enum ESFGAO
        {
            SFGAO_CANCOPY = 1,
            SFGAO_CANMOVE = 2,
            SFGAO_CANLINK = 4,
            SFGAO_CANRENAME = 16,
            SFGAO_CANDELETE = 32,
            SFGAO_HASPROPSHEET = 64,
            SFGAO_DROPTARGET = 256,
            SFGAO_CAPABILITYMASK = 375,
            SFGAO_LINK = 65536,
            SFGAO_SHARE = 131072,
            SFGAO_READONLY = 262144,
            SFGAO_GHOSTED = 524288,
            SFGAO_DISPLAYATTRMASK = 983040,
            SFGAO_FILESYSANCESTOR = 268435456,
            SFGAO_FOLDER = 536870912,
            SFGAO_FILESYSTEM = 1073741824,
            SFGAO_HASSUBFOLDER = -2147483648,
            SFGAO_CONTENTSMASK = -2147483648,
            SFGAO_VALIDATE = 16777216,
            SFGAO_REMOVABLE = 33554432,
            SFGAO_COMPRESSED = 67108864,
        }

        [Flags]
        private enum ESHCONTF
        {
            SHCONTF_FOLDERS = 32,
            SHCONTF_NONFOLDERS = 64,
            SHCONTF_INCLUDEHIDDEN = 128,
        }

        [Flags]
        private enum ESHGDN
        {
            SHGDN_NORMAL = 0,
            SHGDN_INFOLDER = 1,
            SHGDN_FORADDRESSBAR = 16384,
            SHGDN_FORPARSING = 32768
        }

        #endregion Private Enums

        #region Internal Interfaces

        [ComImport, Guid("000214F2-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
        internal interface IEnumIDList
        {
            [PreserveSig]
            [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
            HResult Next(uint celt, out IntPtr rgelt, out uint pceltFetched);

            [PreserveSig]
            [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
            HResult Skip([In] uint celt);

            [PreserveSig]
            [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
            HResult Reset();

            [PreserveSig]
            [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
            HResult Clone([MarshalAs(UnmanagedType.Interface)] out IEnumIDList ppenum);
        }

        #endregion Internal Interfaces

        #region Private Interfaces

        [ComImport(), Guid("BB2E617C-0920-11d1-9A0B-00C04FC2D6C1"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
        private interface IExtractImage
        {
            [PreserveSig()]
            HResult GetLocation([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszPathBuffer, int cch, ref int pdwPriority, ref SIZE prgSize, int dwRecClrDepth, ref int pdwFlags);

            [PreserveSig()]
            HResult Extract(ref IntPtr phBmpThumbnail);
        }

        [ComImport()]
        [Guid("000214E6-0000-0000-C000-000000000046")]
        [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
        private interface IShellFolder
        {
            /// <summary>
            /// Translates a file object's or folder's display name into an item identifier items.
            /// </summary>
            /// <returns>error code, if any</returns>
            [PreserveSig()]
            HResult ParseDisplayName(
                // Optional window handle
                IntPtr hwnd,
                // Optional bind context that controls the
                // parsing operation. This parameter is
                // normally set to NULL.
                IntPtr pbc,
                // Null-terminated UNICODE string with the display name.
                [In(), MarshalAs(UnmanagedType.LPWStr)]
                string pszDisplayName,
                // Pointer to a ULONG value that receives the
                // number of characters of the
                // display name that was parsed.
                out uint pchEaten,
                // Pointer to an ITEMIDLIST pointer that receives
                // the item identifier items for
                // the object.
                out IntPtr ppidl,
                // Optional parameter that can be used to
                // query for file attributes.
                // this can be values from the SFGAO enum
                ref uint pdwAttributes);

            [PreserveSig()]
            HResult EnumObjects(IntPtr hwndOwner, [MarshalAs(UnmanagedType.U4)]ESHCONTF grfFlags, ref IEnumIDList ppenumIDList);

            /// <summary>
            /// Retrieves an IShellFolder object for a subfolder.
            /// </summary>
            /// <returns>error code, if any</returns>
            [PreserveSig()]
            HResult BindToObject(
                // Address of an ITEMIDLIST structure (PIDL)
                // that identifies the subfolder.
                IntPtr pidl,
                // Optional address of an IBindCtx interface on
                // a bind context object to be
                // used during this operation.
                IntPtr pbc,
                // Identifier of the interface to return.
                [In()] ref Guid riid,
                // Address that receives the interface pointer.
                //[MarshalAs(UnmanagedType.Interface)]
                out IntPtr ppv);

            void BindToStorage(IntPtr pidl, IntPtr pbcReserved, ref Guid riid, IntPtr ppvObj);

            [PreserveSig()]
            HResult CompareIDs(IntPtr lParam, IntPtr pidl1, IntPtr pidl2);

            [PreserveSig()]
            HResult CreateViewObject(IntPtr hwndOwner, ref Guid riid, IntPtr ppvOut);

            [PreserveSig()]
            HResult GetAttributesOf(int cidl, IntPtr apidl, [MarshalAs(UnmanagedType.U4)]ref ESFGAO rgfInOut);

            [PreserveSig()]
            HResult GetUIObjectOf(IntPtr hwndOwner, int cidl, ref IntPtr apidl, ref Guid riid, ref int prgfInOut, ref IntPtr ppvOut);

            [PreserveSig()]
            HResult GetDisplayNameOf(IntPtr pidl, [MarshalAs(UnmanagedType.U4)]ESHGDN uFlags, out IntPtr lpName);

            [PreserveSig()]
            HResult SetNameOf(IntPtr hwndOwner, IntPtr pidl, [MarshalAs(UnmanagedType.LPWStr)]string lpszName, [MarshalAs(UnmanagedType.U4)] ESHCONTF uFlags, ref IntPtr ppidlOut);
        }

        #endregion Private Interfaces

        #region Public Methods

        public static Bitmap ExtractThumbnail(string path)
        {
            if (path == null)
                throw new ArgumentNullException(nameof(path));

            if (File.Exists(path) || Directory.Exists(path))
            {
                IntPtr pidlMain = IntPtr.Zero;
                IntPtr pidlChild = IntPtr.Zero;
                if (NativeMethods.SHGetDesktopFolder(out IShellFolder desktopFolder) == HResult.S_OK)
                {
                    IShellFolder parentFolder = null;
                    try
                    {
                        uint pdwAttrib = 0;
                        string directroryPath = Path.GetDirectoryName(path);
                        string basePath = Path.GetFileName(path);

                        HResult hRes = desktopFolder.ParseDisplayName(IntPtr.Zero, IntPtr.Zero, directroryPath, out uint cParsed, out pidlMain, ref pdwAttrib);

                        if (hRes == HResult.S_OK)
                        {
                            IntPtr ppv = IntPtr.Zero;
                            hRes = desktopFolder.BindToObject(pidlMain, IntPtr.Zero, ref IID_ShellFolder, out ppv);
                            if (hRes == HResult.S_OK)
                            {
                                parentFolder = (IShellFolder)Marshal.GetTypedObjectForIUnknown(ppv, typeof(IShellFolder));

                                if (parentFolder != null)
                                {
                                    uint childAttrib = 0;

                                    hRes = parentFolder.ParseDisplayName(IntPtr.Zero, IntPtr.Zero, basePath, out uint parsedChild, out pidlChild, ref childAttrib);

                                    if (hRes == HResult.S_OK)
                                    {
                                        int prgf = 0;
                                        IntPtr ppvOut = IntPtr.Zero;

                                        hRes = parentFolder.GetUIObjectOf(IntPtr.Zero, 1, ref pidlChild, ref IID_IExtractImage, ref prgf, ref ppvOut);

                                        if (hRes == HResult.S_OK)
                                        {
                                            IExtractImage extractImage = (IExtractImage)Marshal.GetTypedObjectForIUnknown(ppvOut, typeof(IExtractImage));

                                            if (extractImage != null)
                                            {
                                                IntPtr hbitmap = IntPtr.Zero;
                                                try
                                                {
                                                    SIZE sz = new SIZE { cx = 256, cy = 256 };

                                                    StringBuilder location = new StringBuilder(260);
                                                    int priority = 0;
                                                    EIEIFLAG flags = EIEIFLAG.IEIFLAG_ORIGSIZE | EIEIFLAG.IEIFLAG_QUALITY;
                                                    int uFlags = (int)flags;

                                                    hRes = extractImage.GetLocation(location, location.Capacity, ref priority, ref sz, 32, ref uFlags);

                                                    if (hRes == HResult.S_OK)
                                                    {
                                                        hRes = extractImage.Extract(ref hbitmap);

                                                        if (hRes == HResult.S_OK && hbitmap != IntPtr.Zero)
                                                            return Romy.Core.Shell.Common.FixShellBitmap(path, Image.FromHbitmap(hbitmap));
                                                    }
                                                }
                                                finally
                                                {
                                                    Marshal.ReleaseComObject(extractImage);

                                                    if (hbitmap != IntPtr.Zero)
                                                        NativeMethods.DeleteObject(hbitmap);
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                    finally
                    {
                        if (pidlChild != IntPtr.Zero)
                            Marshal.FreeCoTaskMem(pidlChild);

                        if (pidlMain != IntPtr.Zero)
                            Marshal.FreeCoTaskMem(pidlMain);

                        if (parentFolder != null)
                            Marshal.ReleaseComObject(parentFolder);

                        Marshal.ReleaseComObject(desktopFolder);
                    }
                }
            }
            return null;
        }

        #endregion Public Methods

        #region Private Structs

        [StructLayout(LayoutKind.Sequential)]
        private struct SIZE
        {
            public int cx;

            public int cy;
        }

        #endregion Private Structs

        #region Private Classes

        [SuppressUnmanagedCodeSecurity]
        private static class NativeMethods
        {
            #region Internal Methods

            [DllImport("gdi32", CharSet = CharSet.Auto)]
            internal extern static int DeleteObject(IntPtr hObject);

            [DllImport("shell32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
            internal extern static HResult SHGetDesktopFolder([MarshalAs(UnmanagedType.Interface)] out IShellFolder ppshf);

            #endregion Internal Methods
        }

        #endregion Private Classes
    }
}

Both of those call this method to fix directory thumbnails:

using System;
using System.Drawing;
using System.IO;

namespace Romy.Core.Shell
{
    internal static class Common
    {
        public static Bitmap FixShellBitmap(string filename, Bitmap result)
        {
            // Some special handling for certain thumbnail types.
            try
            {
                if (Directory.Exists(filename))
                {
                    result = result.FloodFill(Point.Empty, Color.White, Color.FromArgb(1, 20, 20)) as Bitmap;
                    result.MakeTransparent();
                }

                if (result != null && (result.Size.Width > 256 || result.Size.Height > 256))
                    result = result.Resize(256, 256) as Bitmap;
            }
            catch (Exception ex)
            {
                ex.Log();
                if (result != null)
                {
                    result.Dispose();
                    result = null;
                }
            }
            return result;
        }
    }
}

And that helper method calls my image Resize method that I shared recently, as well as the following code that uses AForge.NET to flood fill the directory thumbnails:

using AForge.Imaging;
using AForge.Imaging.Filters;
using System.Drawing.Imaging;

namespace System.Drawing
{
    /// <summary>Flood-fill wrapper that currently wraps the AForge.Net <b>PointedColorFloodFill</b>.</summary>
    /// <remarks>This was added to the solution because of an issue with directory thumbnails. The system
    /// returns them with a white region around the outside, where it should be transparent.</remarks>
    public static class FloodFiller
    {
        /// <summary>A Flood-Fill extension method on <see cref="Image"/>.</summary>
        /// <remarks>
        /// This overload uses a default location and fills the target <b>Image</b> from the outside. To
        /// specify a location, use one of the overloads that accepts a <see cref="Point"/>
        /// parameter.</remarks>
        /// <param name="source">The source image to flood-fill.</param>
        /// <param name="fillColor">The colour with which to flood-fill.</param>
        /// <returns>A new image, that is a flood-filled copy of the source image.</returns>
        public static Image FloodFill(this Image source, Color fillColor) => source.FloodFill(Point.Empty, fillColor, Color.FromArgb(20, 20, 20));

        /// <summary>A Flood-Fill extension method on <see cref="Image"/>.</summary>
        /// <param name="source">The source image to flood-fill.</param>
        /// <param name="location">The location in source at which to start the flood-fill.</param>
        /// <param name="fillColor">The colour with which to flood-fill.</param>
        /// <returns>A new image, that is a flood-filled copy of the source image.</returns>
        public static Image FloodFill(this Image source, Point location, Color fillColor) => FloodFill(source, location, fillColor, Color.FromArgb(20, 20, 20));

        /// <summary>A Flood-Fill extension method on <see cref="Image"/>.</summary>
        /// <param name="source">The source image to flood-fill.</param>
        /// <param name="location">The location in source at which to start the flood-fill.</param>
        /// <param name="fillColor">The colour with which to flood-fill.</param>
        /// <param name="tolerance">A tolerance which determines the sensitivity of the flood-fill operation.</param>
        /// <returns>A new image, that is a flood-filled copy of the source image.</returns>
        public static Image FloodFill(this Image source, Point location, Color fillColor, Color tolerance)
        {
            /* Combining the flood-fill with the Shrink filter works better than a flood-fill alone
             * in this application, since we use 256x256 thumbnails - shrinking the outer colour away
             * before scaling the image down may produce a slightly larger image in the thumb bounds. */
            UnmanagedImage unmanagedImage = UnmanagedImage.FromManagedImage(
                source.PixelFormat == PixelFormat.Format24bppRgb ? source as Bitmap : source.CopyImage() as Bitmap); // Shrink requires 24 bit.

            FiltersSequence sequence = new FiltersSequence(
                new PointedColorFloodFill
                {
                    FillColor = fillColor,
                    StartingPoint = new AForge.IntPoint(location.X, location.Y),
                    Tolerance = tolerance
                },
                new Shrink(fillColor));

            return sequence.Apply(unmanagedImage).ToManagedImage();
        }

        /// <summary>Trims pixels of the specified colour from the outside of the
        /// source image, and returns a transparent copy of the image.</summary>
        /// <remarks>Flood-fills and calls MakeTransparent.</remarks>
        public static Image Trim(this Image source, Color colorToRemove)
        {
            Bitmap trimmed = null;
            try
            {
                trimmed = source.FloodFill(Point.Empty, colorToRemove, Color.FromArgb(20, 20, 20)) as Bitmap;
                trimmed.MakeTransparent();
            }
            catch
            {
                if (trimmed != null)
                    trimmed.Dispose();

                throw;
            }
            return trimmed;
        }

        public static Image Trim(this Image source, Color colorToRemove, Color tolerance)
        {
            Bitmap trimmed = null;
            try
            {
                trimmed = source.FloodFill(Point.Empty, colorToRemove, tolerance) as Bitmap;
                trimmed.MakeTransparent();
            }
            catch
            {
                if (trimmed != null)
                    trimmed.Dispose();

                throw;
            }
            return trimmed;
        }
    }
}

The above code also uses my Image.CopyImage extension method. I shared that recently here, but here it is again:

/// <summary>Creates a 24 bit-per-pixel copy of the source image.</summary>
public static Image CopyImage(this Image image) => CopyImage(image, PixelFormat.Format24bppRgb);

/// <summary>Creates a copy of the source image with the specified pixel format.</summary><remarks>
/// This can also be achieved with the <see cref="Bitmap.Clone(int, int, PixelFormat)"/>
/// overload, but I have had issues with that method.</remarks>
public static Image CopyImage(this Image image, PixelFormat format)
{
    if (image == null)
        throw new ArgumentNullException(nameof(image));

    // Don't try to draw a new Bitmap with an indexed pixel format.
    if (format == PixelFormat.Format1bppIndexed || format == PixelFormat.Format4bppIndexed || format == PixelFormat.Format8bppIndexed || format == PixelFormat.Indexed)
        return (image as Bitmap).Clone(new Rectangle(0, 0, image.Width, image.Height), format);

    Image result = null;
    try
    {
        result = new Bitmap(image.Width, image.Height, format);

        using (Graphics graphics = Graphics.FromImage(result))
        {
            graphics.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBicubic;
            graphics.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias;
            graphics.CompositingQuality = Drawing2D.CompositingQuality.HighQuality;

            graphics.DrawImage(image, 0, 0, result.Width, result.Height);
        }
    }
    catch
    {
        if (result != null)
            result.Dispose();

        throw;
    }
    return result;
}
Posted in Programming | Tagged , , , , | Leave a comment

Some Task extensions

I’m on a 30 day Facebook ban because I called some Donald Trump supporters white trash. Apparently I, a white South African, am guilty of hate speech and racism against white people. But actual hate speech, often subtle, always insidious, goes unchecked. (Actual hate speech which consists, for example, of rhetoric where white people argue about the very definition of concentration camps.) Sorry, I try to avoid getting at all political here (unlike my other blog)… but I thought it might be worth mentioning. I’m active here again lately because I’m not spending time sharing memes and political stuff on Facebook.

I’ve collected some Task extension methods over a couple of years… These are not mine, but were written by various others, including Jon Skeet, Stephen Toub, and others. Of these, I use TimeoutAfter and ForEachAsync extensively.

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace Romy.Core
{
    internal struct VoidTypeStruct { }

    public static class TaskExtensions
    {
        /* This came out of the EduAsync series of blog posts by Jon Skeet. */

        /// <summary>Returns a sequence of tasks which will be observed to complete with the same set of
        /// results as the given input tasks, but in the order in which the original tasks complete.</summary>
        public static IEnumerable<Task<T>> OrderByCompletion<T>(this IEnumerable<Task<T>> inputTasks)
        {
            // Copy the input so we know it'll be stable, and we don't evaluate it twice
            List<Task<T>> inputTaskList = inputTasks.ToList();

            // Could use Enumerable.Range here, if we wanted...
            List<TaskCompletionSource<T>> completionSourceList = new List<TaskCompletionSource<T>>(inputTaskList.Count);
            for (int i = 0; i < inputTaskList.Count; i++)
            {
                completionSourceList.Add(new TaskCompletionSource<T>());
            }

            // At any one time, this is "the index of the box we've just filled".
            // It would be nice to make it nextIndex and start with 0, but Interlocked.Increment
            // returns the incremented value...
            int prevIndex = -1;

            // We don't have to create this outside the loop, but it makes it clearer
            // that the continuation is the same for all tasks.
            void continuation(Task<T> completedTask)
            {
                int index = Interlocked.Increment(ref prevIndex);
                TaskCompletionSource<T> source = completionSourceList[index];
                PropagateResult(completedTask, source);
            }

            foreach (Task<T> inputTask in inputTaskList)
            {
                // TODO: Work out whether TaskScheduler.Default is really the right one to use.
                inputTask.ContinueWith(continuation,
                                       CancellationToken.None,
                                       TaskContinuationOptions.ExecuteSynchronously,
                                       TaskScheduler.Default);
            }

            return completionSourceList.Select(source => source.Task);
        }

        /// <summary>Propagates the status of the given task (which must be
        /// completed) to a task completion source (which should not be).</summary>
        private static void PropagateResult<T>(Task<T> completedTask,
            TaskCompletionSource<T> completionSource)
        {
            switch (completedTask.Status)
            {
                case TaskStatus.Canceled:
                    completionSource.TrySetCanceled();
                    break;
                case TaskStatus.Faulted:
                    completionSource.TrySetException(completedTask.Exception.InnerExceptions);
                    break;
                case TaskStatus.RanToCompletion:
                    completionSource.TrySetResult(completedTask.Result);
                    break;
                default:
                    // TODO: Work out whether this is really appropriate. Could set
                    // an exception in the completion source, of course...
                    completionSource.TrySetException(new ArgumentException("Task was not completed"));
                    break;
            }
        }

        /* These are from an article by Stephen Toub. */
        public static async Task Then(this Task antecedent, Action continuation)
        {
            await antecedent;
            continuation();
        }

        public static async Task<TNewResult> Then<TNewResult>(this Task antecedent, Func<TNewResult> continuation)
        {
            await antecedent;
            return continuation();
        }

        public static async Task Then<TResult>(this Task<TResult> antecedent, Action<TResult> continuation) => continuation(await antecedent);

        public static async Task<TNewResult> Then<TResult, TNewResult>(this Task<TResult> antecedent, Func<TResult, TNewResult> continuation) => continuation(await antecedent);

        public static async Task Then(this Task task, Func<Task> continuation)
        {
            await task;
            await continuation();
        }

        public static async Task<TNewResult> Then<TNewResult>(this Task task, Func<Task<TNewResult>> continuation)
        {
            await task;
            return await continuation();
        }

        public static async Task Then<TResult>(this Task<TResult> task, Func<TResult, Task> continuation) => await continuation(await task);

        public static async Task<TNewResult> Then<TResult, TNewResult>(this Task<TResult> task, Func<TResult, Task<TNewResult>> continuation) => await continuation(await task);

        /* Task.TimeoutAfter extension methods: Based on sample code by Joe Hoag of Microsoft, from the article at
         * http://blogs.msdn.com/b/pfxteam/archive/2011/11/10/10235834.aspx, Crafting a Task.TimeoutAfter Method.
         * I did not change the original code; only added the overloads that take a TimeSpan argument.
         *
         * Note that the returned Task on a timeout is set to the faulted state with a TimeoutException, so when
         * calling this you need to catch the TimeoutException, when it times out. */
        public static Task TimeoutAfter(this Task task, TimeSpan timeoutSpan) => task.TimeoutAfter((int)timeoutSpan.TotalMilliseconds);

        public static Task<TResult> TimeoutAfter<TResult>(this Task<TResult> task, TimeSpan timeoutSpan) => task.TimeoutAfter((int)timeoutSpan.TotalMilliseconds) as Task<TResult>;

        public static Task TimeoutAfter(this Task task, int millisecondsTimeout)
        {
            // Short-circuit #1: infinite timeout or task already completed
            if (task.IsCompleted || (millisecondsTimeout == Timeout.Infinite))
            {
                // Either the task has already completed or timeout will never occur.
                // No proxy necessary.
                return task;
            }

            // tcs.Task will be returned as a proxy to the caller
            TaskCompletionSource<VoidTypeStruct> tcs = new TaskCompletionSource<VoidTypeStruct>();

            // Short-circuit #2: zero timeout
            if (millisecondsTimeout == 0)
            {
                // We've already timed out.
                tcs.SetException(new TimeoutException());
                return tcs.Task;
            }

            // Set up a timer to complete after the specified timeout period
            Timer timer = new Timer(state =>
            {
                // Recover your state information
                // Fault our proxy with a TimeoutException
                ((TaskCompletionSource<VoidTypeStruct>)state).TrySetException(new TimeoutException());
            }, tcs, millisecondsTimeout, Timeout.Infinite);

            // Wire up the logic for what happens when source task completes
            task.ContinueWith((antecedent, state) =>
            {
                // Recover our state data
                Tuple<Timer, TaskCompletionSource<VoidTypeStruct>> tuple = (Tuple<Timer, TaskCompletionSource<VoidTypeStruct>>)state;

                // Cancel the Timer
                tuple.Item1.Dispose();

                // Marshal results to proxy
                MarshalTaskResults(antecedent, tuple.Item2);
            }, Tuple.Create(timer, tcs), CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);

            return tcs.Task;
        }

        public static Task TimeoutAfter<TResult>(this Task<TResult> task, int millisecondsTimeout)
        {
            // Short-circuit #1: infinite timeout or task already completed
            if (task.IsCompleted || (millisecondsTimeout == Timeout.Infinite))
            {
                // Either the task has already completed or timeout will never occur.
                // No proxy necessary.
                return task;
            }

            // tcs.Task will be returned as a proxy to the caller
            TaskCompletionSource<TResult> tcs = new TaskCompletionSource<TResult>();

            // Short-circuit #2: zero timeout
            if (millisecondsTimeout == 0)
            {
                // We've already timed out.
                tcs.SetException(new TimeoutException());
                return tcs.Task;
            }

            // Set up a timer to complete after the specified timeout period
            Timer timer = new Timer(state =>
            {
                // Recover your state information
                // Fault our proxy with a TimeoutException
                ((TaskCompletionSource<TResult>)state).TrySetException(new TimeoutException());
            }, tcs, millisecondsTimeout, Timeout.Infinite);

            // Wire up the logic for what happens when source task completes
            task.ContinueWith((antecedent, state) =>
            {
                // Recover our state data
                Tuple<Timer, TaskCompletionSource<TResult>> tuple = (Tuple<Timer, TaskCompletionSource<TResult>>)state;

                // Cancel the Timer
                tuple.Item1.Dispose();

                // Marshal results to proxy
                MarshalTaskResults(antecedent, tuple.Item2);
            }, Tuple.Create(timer, tcs), CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);

            return tcs.Task;
        }

        internal static void MarshalTaskResults<TResult>(Task source, TaskCompletionSource<TResult> proxy)
        {
            switch (source.Status)
            {
                case TaskStatus.Faulted:
                    proxy.TrySetException(source.Exception);
                    break;
                case TaskStatus.Canceled:
                    proxy.TrySetCanceled();
                    break;
                case TaskStatus.RanToCompletion:
                    proxy.TrySetResult(
                        !(source is Task<TResult> castedSource) ? default : // source is a Task
                            castedSource.Result);                 // source is a Task<TResult>
                    break;
            }
        }

        public static Task YieldTo(this Task task, TaskScheduler scheduler) => task.ContinueWith(antecedent =>
                                                                                         {
                                                                                             Task.Yield();
                                                                                             return antecedent.ConfigureAwait(false);
                                                                                         }, scheduler);

        /* Technically, these two should be declared in CollectionExtensions, but this method is used by Zipper.exe.
         * Zipper can add a reference to this file, but not to CollectionExtensions.cs, which references System.Windows.Forms */

        /// <summary>A ForEachAsync implementation. Based on a sample in an article by Stephen Toub,
        /// <a href="http://blogs.msdn.com/b/pfxteam/archive/2012/03/05/10278165.aspx">
        /// Implementing a simple ForEachAsync, part 2</a>.</summary>
        /// <remarks>
        /// I've changed this from calling <b>Task.Run</b> to call <b>Task.Factory.StartNew</b> in order to have it use my
        /// custom ParallelTaskScheduler rather than the default. (This was intended to be an experimental change, but I
        /// decided to leave it like this.)</remarks>
        public static Task ForEachAsync<T>(this IEnumerable<T> source,
            int maxDegreeOfParallelism,
            Func<T, Task> body) => Task.WhenAll(
                from partition in Partitioner.Create(source).GetPartitions(maxDegreeOfParallelism)
                select Tasks.Run(async () =>
                {
                    using (partition)
                        while (partition.MoveNext())
                            await body(partition.Current);
                }));

        /// <summary>An asynchronous ForAsync implementation.</summary>
        /// <remarks>It simply creates an <b>Enumerable.Range</b> and wraps <b>ForEachAsync</b>.</remarks>
        public static Task ForAsync(int fromInclusive, int toExclusive, int maxDegreeOfParallelism, Func<int, Task> body) => Enumerable.Range(
                fromInclusive, toExclusive).ForEachAsync(maxDegreeOfParallelism, async i => await body(i));
    }
}
Posted in Programming | Tagged , | Leave a comment

Here are my Stream extensions, including a c# wrapper for the ReOpenFile Windows API function, and some code that uses my CopyToStreamAsync extension method.

I don’t have many Stream extension methods, in fact there are only two, but I think they are useful.

  1. CopyToStreamAsync, an extension to Stream, is an alternative to Stream.CopyToAsync, and is featured in one of my most popular posts.
  2. ReOpen, an extension to FileStream, allows reopening a file a second time, and is a .NET wrapper around the ReOpenFile Windows API function.

I’ve shared the first extension above, but never shared the whole file.

I wrote the ReOpen method back in 2013 after reading a blog post by Raymond Chen explaining why you might want to use that function. Long story short – say you open a file synchronously, and want to change the flags and open it again asynchronously. Anyway, my implementation calls the native API and returns a .NET FileStream via a SafeHandle around the native file handle.

So here’s the two Stream extensions… Enjoy.

using Microsoft.Win32.SafeHandles;
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading.Tasks;

namespace Romy.Core
{
    public static class StreamExtensions
    {
        #region Private Fields

        private const uint FILE_FLAG_OVERLAPPED = 0x40000000;

        #endregion Private Fields

        #region Private Enums

        [Flags]
        private enum EFileAccess : uint
        {
            // Standard Section
            AccessSystemSecurity = 0x1000000,   // AccessSystemAcl access type

            MaximumAllowed = 0x2000000,     // MaximumAllowed access type

            Delete = 0x10000,
            ReadControl = 0x20000,
            WriteDAC = 0x40000,
            WriteOwner = 0x80000,
            Synchronize = 0x100000,

            StandardRightsRequired = 0xF0000,
            StandardRightsRead = ReadControl,
            StandardRightsWrite = ReadControl,
            StandardRightsExecute = ReadControl,
            StandardRightsAll = 0x1F0000,
            SpecificRightsAll = 0xFFFF,

            FILE_READ_DATA = 0x0001,        // file & pipe
            FILE_LIST_DIRECTORY = 0x0001,       // directory
            FILE_WRITE_DATA = 0x0002,       // file & pipe
            FILE_ADD_FILE = 0x0002,         // directory
            FILE_APPEND_DATA = 0x0004,      // file
            FILE_ADD_SUBDIRECTORY = 0x0004,     // directory
            FILE_CREATE_PIPE_INSTANCE = 0x0004, // named pipe
            FILE_READ_EA = 0x0008,          // file & directory
            FILE_WRITE_EA = 0x0010,         // file & directory
            FILE_EXECUTE = 0x0020,          // file
            FILE_TRAVERSE = 0x0020,         // directory
            FILE_DELETE_CHILD = 0x0040,     // directory
            FILE_READ_ATTRIBUTES = 0x0080,      // all
            FILE_WRITE_ATTRIBUTES = 0x0100,     // all

            // Generic Section
            GenericRead = 0x80000000,

            GenericWrite = 0x40000000,
            GenericExecute = 0x20000000,
            GenericAll = 0x10000000,

            SPECIFIC_RIGHTS_ALL = 0x00FFFF,

            FILE_ALL_ACCESS =
            StandardRightsRequired |
            Synchronize |
            0x1FF,

            FILE_GENERIC_READ =
            StandardRightsRead |
            FILE_READ_DATA |
            FILE_READ_ATTRIBUTES |
            FILE_READ_EA |
            Synchronize,

            FILE_GENERIC_WRITE =
            StandardRightsWrite |
            FILE_WRITE_DATA |
            FILE_WRITE_ATTRIBUTES |
            FILE_WRITE_EA |
            FILE_APPEND_DATA |
            Synchronize,

            FILE_GENERIC_EXECUTE =
            StandardRightsExecute |
              FILE_READ_ATTRIBUTES |
              FILE_EXECUTE |
              Synchronize
        }

        [Flags]
        private enum EFileShare : uint
        {
            None = 0x00000000,

            /// <summary>
            /// Enables subsequent open operations on an object to request read access.
            /// Otherwise, other processes cannot open the object if they request read access.
            /// If this flag is not specified, but the object has been opened for read access, the function fails.
            /// </summary>
            Read = 0x00000001,

            /// <summary>
            /// Enables subsequent open operations on an object to request write access.
            /// Otherwise, other processes cannot open the object if they request write access.
            /// If this flag is not specified, but the object has been opened for write access, the function fails.
            /// </summary>
            Write = 0x00000002,

            /// <summary>
            /// Enables subsequent open operations on an object to request delete access.
            /// Otherwise, other processes cannot open the object if they request delete access.
            /// If this flag is not specified, but the object has been opened for delete access, the function fails.
            /// </summary>
            Delete = 0x00000004
        }

        #endregion Private Enums

        #region Public Methods

        public static Task CopyToStreamAsync(this Stream source, Stream destination) => source.CopyToStreamAsync(destination, Constants.BufferSize);

        /// <summary>An implementation to copy asynchronously from one stream to another,
        /// similar to <see cref="Stream.CopyToAsync(Stream)"/></summary>
        /// <remarks>
        /// This was written because the default implementation would sometimes throw OutOfMemoryExceptions
        /// in my FileAsync.ReadAllBytesAsync method, when opening large Bitmap files. Reading with a fixed
        /// size small buffer works well as a general solution, though I have also begun using a larger
        /// buffer to improve performance with larger files.</remarks>
        public static async Task CopyToStreamAsync(this Stream source, Stream destination, int bufferSize)
        {
            if (source == null)
                throw new ArgumentNullException(nameof(source));

            if (destination == null)
                throw new ArgumentNullException(nameof(destination));

            if (bufferSize <= 0)
                throw new ArgumentOutOfRangeException(nameof(bufferSize), $"{nameof(bufferSize)} must be greater than zero");

            /* The source stream may not support seeking; e.g. a stream
             * returned by ZipArchiveEntry.Open() or a network stream. */
            int size = bufferSize;
            bool canSeek = source.CanSeek;

            if (canSeek)
            {
                try
                {
                    size = (int)Math.Min(bufferSize, source.Length);
                }
                catch (NotSupportedException) { canSeek = false; }
            }

            byte[] buffer = new byte[size];
            long remaining = canSeek ? source.Length : 0;

            /* If the stream is seekable, seek through it until all bytes are read.
             * If we read less than the expected number of bytes, it indicates an
             * error, so throw the appropriate exception.
             *
             * If the stream is not seekable, loop until we read 0 bytes. (It's not
             * an error in this case.) */
            while (!canSeek || remaining > 0)
            {
                int read = await source.ReadAsync(buffer, 0, size);

                if (read <= 0)
                {
                    if (canSeek)
                        throw new EndOfStreamException(
                            string.Format("End of stream reached, but {0} remained to be read.",
                            FormatBytes(remaining)));
                    else
                        break;
                }

                await destination.WriteAsync(buffer, 0, read);
                remaining -= canSeek ? read : 0;
            }
        }

        public static FileStream Reopen(this FileStream source, FileAccess access, FileShare share, int bufferSize, bool useAsync)
        {
            EFileAccess fileAccess = EFileAccess.GenericWrite;

            switch (access)
            {
                case FileAccess.Read:
                    fileAccess = EFileAccess.GenericRead;
                    break;
                case FileAccess.ReadWrite:
                    fileAccess = EFileAccess.GenericRead | EFileAccess.GenericWrite;
                    break;
                case FileAccess.Write:
                    fileAccess = EFileAccess.GenericWrite;
                    break;
            }

            EFileShare fileShare = EFileShare.Read | EFileShare.Write;

            switch (share)
            {
                case FileShare.Delete:
                    fileShare = EFileShare.Delete;
                    break;
                case FileShare.Inheritable:
                    fileShare = source.CanRead ? EFileShare.Read : source.CanWrite ? EFileShare.Write : EFileShare.None;
                    break;
                case FileShare.None:
                    fileShare = EFileShare.None;
                    break;
                case FileShare.Read:
                    fileShare = EFileShare.Read;
                    break;
                case FileShare.ReadWrite:
                    fileShare = EFileShare.Read | EFileShare.Write;
                    break;
                case FileShare.Write:
                    fileShare = EFileShare.Write;
                    break;
            }

            return new FileStream(NativeMethods.ReOpenFile(source.SafeFileHandle, (uint)fileAccess, (uint)fileShare, useAsync ? FILE_FLAG_OVERLAPPED : 0), access, bufferSize, useAsync);
        }

        #endregion Public Methods

        #region Private Methods

        /// <summary>Redefined here because this is also used in the Zipper project,
        /// which can't use FileUtils, or any other code that calls ex.Log().</summary>
        private static string FormatBytes(long bytes)
        {
            const long KiloByte = 1024L;
            const long MegaByte = KiloByte * KiloByte;
            const long GigaByte = MegaByte * KiloByte;
            const long TeraByte = GigaByte * KiloByte;
            const long PetaByte = TeraByte * KiloByte;
            const long ExaByte = PetaByte * KiloByte;

            string formattedBytes = string.Empty;

            if (bytes < KiloByte)
                formattedBytes = string.Format("{0:F2} bytes", bytes);
            else if (bytes >= KiloByte && bytes < MegaByte)
                formattedBytes = string.Format("{0:F2} KB", Math.Round((double)bytes / KiloByte, 2, MidpointRounding.AwayFromZero));
            else if (bytes >= MegaByte && bytes < GigaByte)
                formattedBytes = string.Format("{0:F2} MB", Math.Round((double)bytes / MegaByte, 2, MidpointRounding.AwayFromZero));
            else if (bytes >= GigaByte && bytes < TeraByte)
                formattedBytes = string.Format("{0:F2} GB", Math.Round((double)bytes / GigaByte, 2, MidpointRounding.AwayFromZero));
            else if (bytes >= TeraByte && bytes < PetaByte)
                formattedBytes = string.Format("{0:F2} TB", Math.Round((double)bytes / TeraByte, 2, MidpointRounding.AwayFromZero));
            else if (bytes >= PetaByte && bytes < ExaByte)
                formattedBytes = string.Format("{0:F2} PB", Math.Round((double)bytes / PetaByte, 2, MidpointRounding.AwayFromZero));
            else if (bytes >= ExaByte)
                formattedBytes = string.Format("{0:F2} EB", Math.Round((double)bytes / ExaByte, 2, MidpointRounding.AwayFromZero));

            return formattedBytes;
        }

        #endregion Private Methods

        #region Private Classes

        private static class NativeMethods
        {
            #region Public Methods

            [DllImport("kernel32", SetLastError = true)]
            public static extern SafeFileHandle ReOpenFile(SafeFileHandle hOriginalFile, uint dwAccess, uint dwShareMode, uint dwFlags);

            #endregion Public Methods
        }

        #endregion Private Classes
    }
}

A practical example of using CopyToStreamAsync follows… These are async equivalents of methods like File.ReadAllBytes. Actually that was why I wrote CopyToStreamAsync. I found that Stream.CopyToAsync would throw OutOfMemoryExceptions too often in my FileAsync.ReadAllBytesAsync when reading large bitmaps. Mine does not suffer from that problem, although I don’t really know why.

using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Romy.Core
{
    /// <summary>These are not extension methods, but are static methods
    /// similar to certain synchronous static methods on System.IO.File.
    /// (File.ReadAllBytes; File.WriteAllBytes; File.ReadAllLines etc.)</summary>
    /// <remarks>All these methods rely on my other extension methods,
    /// especially <b>StreamExtensions.CopyToStreamAsync</b>.</remarks>
    public static class FileAsync
    {
        #region Public Fields

        public const int BufferSize = 0x2000;

        public const int LargeBufferSize = BufferSize * 1024;

        #endregion Public Fields

        #region Private Fields

        private static readonly byte[] NoBytes = Enumerable.Empty<byte>().ToArray();

        #endregion Private Fields

        #region Public Methods

        public static Task AppendAllBytesAsync(string path, byte[] bytes) => AppendAllBytesAsync(path, bytes, BufferSize);

        public static async Task AppendAllBytesAsync(string path, byte[] bytes, int bufferSize)
        {
            using (MemoryStream memoryStream = new MemoryStream(bytes))
            {
                using (FileStream stream = new FileStream(path, FileMode.Append, FileAccess.Write, FileShare.None, bufferSize, true))
                {
                    await memoryStream.CopyToStreamAsync(stream, bufferSize);
                }
            }
        }

        /// <summary>Asynchronously appends lines to a file, and then closes the file.</summary>
        /// <param name="path">The file to append the lines to. The file is created if it does not already exist.</param>
        /// <param name="contents">The lines to append to the file.</param>
        /// <returns>A Task that represents completion of the method.</returns>
        public static async Task AppendAllLinesAsync(string path, IEnumerable<string> contents)
        {
            List<string> lines = File.Exists(path) ? new List<string>(await ReadAllLinesAsync(path)) : new List<string>();
            lines.AddRange(contents);

            await WriteAllLinesAsync(path, lines.ToArray());
        }

        /// <summary>Asynchronously opens a binary file, reads the contents of
        /// the file into a byte array, and then closes the file.</summary>
        /// <param name="filename">The file to open for reading.</param>
        /// <returns>A Task that will, on completion of the method,
        /// contain a byte array of the contents of the file.</returns>
        public static Task<byte[]> ReadAllBytesAsync(string filename) => ReadAllBytesAsync(filename, BufferSize);

        public static async Task<byte[]> ReadAllBytesAsync(string filename, int bufferSize)
        {
            try
            {
                int length = (int)new FileInfo(filename).Length;
                byte[] bytes = NoBytes;

                using (FileStream stream = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize, true))
                {
                    using (MemoryStream memoryStream = new MemoryStream(length))
                    {
                        await stream.CopyToStreamAsync(memoryStream, bufferSize);
                        bytes = memoryStream.ToArray();
                    }
                }
                return bytes;
            }
            catch { return NoBytes; }
        }

        /// <summary>Asynchronously opens a text file, reads all
        /// lines of the file, and then closes the file.</summary>
        /// <param name="path">The file to open for reading.</param>
        /// <returns>A Task that will, on completion of the method,
        /// contain a string array containing all lines of the file.</returns>
        public static async Task<string[]> ReadAllLinesAsync(string path)
        {
            List<string> lines = new List<string>();
            string line = string.Empty;

            using (StreamReader reader = new StreamReader(path, true))
            {
                while ((line = await reader.ReadLineAsync()) != null)
                    lines.Add(line);
            }

            return lines.ToArray();
        }

        /// <summary>Creates a new file, writes the specified byte array to the file asynchronously,
        /// and then closes the file. If the target file already exists, it is overwritten.</summary>
        /// <param name="path">The file to write to.</param>
        /// <param name="bytes">The bytes to write to the file.</param>
        public static async Task WriteAllBytesAsync(string path, byte[] bytes)
        {
            using (MemoryStream memoryStream = new MemoryStream(bytes))
            {
                using (FileStream stream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, Constants.BufferSize, true))
                {
                    await memoryStream.CopyToStreamAsync(stream, Constants.BufferSize);
                }
            }
        }

        public static async Task WriteAllBytesAsync(string path, byte[] bytes, int bufferSize)
        {
            using (MemoryStream memoryStream = new MemoryStream(bytes))
            {
                using (FileStream stream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, bufferSize, true))
                {
                    await memoryStream.CopyToStreamAsync(stream, bufferSize);
                }
            }
        }

        /// <summary>Asynchronously creates a new file, writes a collection
        /// of strings to the file, and then closes the file.</summary>
        /// <param name="path">The file to write to.</param>
        /// <param name="contents">The lines to write to the file.</param>
        /// <returns>A Task that represents completion of the method.</returns>
        public static async Task WriteAllLinesAsync(string path, IEnumerable<string> contents) => await WriteAllLinesAsync(path, contents, Encoding.UTF8);

        /// <summary>Asynchronously creates a new file by using the specified encoding,
        /// writes a collection of strings to the file, and then closes the file.</summary>
        /// <param name="path">The file to write to.</param>
        /// <param name="contents">The lines to write to the file.</param>
        /// <param name="encoding">The character encoding to use.</param>
        /// <returns>A Task that represents completion of the method.</returns>
        public static async Task WriteAllLinesAsync(string path, IEnumerable<string> contents, Encoding encoding)
        {
            using (MemoryStream memoryStream = new MemoryStream(contents.SelectMany(s => encoding.GetBytes(s.EndsWith("\r\n") ? s : s.TrimEnd() + "\r\n")).ToArray()))
            {
                using (FileStream stream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, Constants.BufferSize, true))
                {
                    await memoryStream.CopyToStreamAsync(stream, Constants.BufferSize);
                }
            }
        }

        /// <summary>Asynchronously creates a new file, writes the specified string to the file,
        /// and then closes the file. If the target file already exists, it is overwritten.
        /// </summary>
        /// <param name="path">The file to write to.</param>
        /// <param name="contents">The string to write to the file.</param>
        /// <returns>A Task that represents completion of the method.</returns>
        public static async Task WriteAllTextAsync(string path, string contents) => await WriteAllLinesAsync(path, new string[] { contents });

        #endregion Public Methods
    }
}
Posted in Programming | Tagged , , , , , , , , , | Leave a comment

How to pause or resume a process with c#

I wrote this post already, but the plugin I used back then formatted the code badly, so here is the subject again… The code shared today is slightly different to the original post since I have been updating my old code to use newer language features. Other than formatting changes, the main difference here is that in the Suspend and Resume methods, Action<T> delegates in the old code have changed to local functions in the updated code. If you’re still using an old version of the .NET Framework or an older version of the compiler or Visual Studio, you can use the old code. Any new code I share going forward will use the newer language features. (Other newer c# language features I use quite widely, though not in this code, are expression-bodied members, the nameof operator, and auto-implemented properties with initializers.)

Assuming that you created and are controlling another process programmatically, a useful feature of the Process API would be a way to “pause” that process. But there isn’t an API to do so, not for processes anyway. To get around this, my Suspend and Resume extension methods to the Process class iterate and then either suspend or resume all threads of the process. I use this in my code when controlling the ffmpeg console application process, which I use to convert videos.

Firstly, it uses another extension method to convert an ICollection to an array. This:

/// <summary>Converts a non-generic collection to an array.</summary>
public static T[] ToArray<T>(this ICollection collection)
{
    T[] items = new T[collection.Count];
    collection.CopyTo(items, 0);

    return items;
}

And here are my Process extensions:

using System;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security;
using System.Threading.Tasks;

namespace Romy.Core
{
    /// <summary>Suspend/resume a <see cref="Process"/>
    /// by suspending or resuming all of its threads.</summary>
    /// <remarks><para>
    /// These methods use the <see cref="CollectionExtensions.ToArray&lt;T&gt;"/>
    /// extension method on <b>ICollection</b> to convert a <b>ProcessThreadCollection</b>
    /// to an array.</para>
    /// <para>
    /// The threads are suspended/resumed in parallel for no particular reason, other than the
    /// hope that it may be better to do so as close to all at once as we can.</para></remarks>
    public static class ProcessExtensions
    {
        #region Private Enums

        [Flags]
        private enum ThreadAccess : int
        {
            TERMINATE = (0x0001),
            SUSPEND_RESUME = (0x0002),
            GET_CONTEXT = (0x0008),
            SET_CONTEXT = (0x0010),
            SET_INFORMATION = (0x0020),
            QUERY_INFORMATION = (0x0040),
            SET_THREAD_TOKEN = (0x0080),
            IMPERSONATE = (0x0100),
            DIRECT_IMPERSONATION = (0x0200)
        }

        #endregion Private Enums

        #region Public Methods

        public static void Resume(this Process process)
        {
            void resume(ProcessThread pt)
            {
                IntPtr threadHandle = NativeMethods.OpenThread(
                    ThreadAccess.SUSPEND_RESUME, false, (uint)pt.Id);

                if (threadHandle != IntPtr.Zero)
                {
                    try { NativeMethods.ResumeThread(threadHandle); }
                    finally { NativeMethods.CloseHandle(threadHandle); }
                }
            }

            ProcessThread[] threads = process.Threads.ToArray<ProcessThread>();

            if (threads.Length > 1)
            {
                Parallel.ForEach(threads,
                    new ParallelOptions { MaxDegreeOfParallelism = threads.Length },
                    pt => resume(pt));
            }
            else resume(threads[0]);
        }

        public static void Suspend(this Process process)
        {
            void suspend(ProcessThread pt)
            {
                IntPtr threadHandle = NativeMethods.OpenThread(
                    ThreadAccess.SUSPEND_RESUME, false, (uint)pt.Id);

                if (threadHandle != IntPtr.Zero)
                {
                    try { NativeMethods.SuspendThread(threadHandle); }
                    finally { NativeMethods.CloseHandle(threadHandle); }
                };
            }

            ProcessThread[] threads = process.Threads.ToArray<ProcessThread>();

            if (threads.Length > 1)
            {
                Parallel.ForEach(threads,
                    new ParallelOptions { MaxDegreeOfParallelism = threads.Length },
                    pt => suspend(pt));
            }
            else suspend(threads[0]);
        }

        #endregion Public Methods

        #region Private Classes

        [SuppressUnmanagedCodeSecurity]
        private static class NativeMethods
        {
            #region Public Methods

            [DllImport("kernel32.dll")]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool CloseHandle(IntPtr hObject);

            [DllImport("kernel32.dll")]
            public static extern IntPtr OpenThread(
                ThreadAccess dwDesiredAccess, bool bInheritHandle, uint dwThreadId);

            [DllImport("kernel32.dll")]
            public static extern uint ResumeThread(IntPtr hThread);

            [DllImport("kernel32.dll")]
            public static extern uint SuspendThread(IntPtr hThread);

            #endregion Public Methods
        }

        #endregion Private Classes
    }
}
Posted in Programming | Tagged , , | Leave a comment

Resize an image in c#

Still looking through my old code as I update it for Visual Studio 2019, I found this… I have several extension methods for saving and converting images. Some of them use my async extension methods for handling streams and some are tightly coupled with other of my code, but this one is pretty simple and stands alone. It results in a good quality image resize. I’ll assume you know how to load and save an image and leave that to you…

This is from my now updated code, and uses the c# nameof operator.

This code is designed to be called blindly with the same values for the new width and height, and the maintainAspectRatio flag set to true, so that it can calculate what size to use. That works for me… if you don’t like that, remove the flag parameter and the entire IF block that uses it.

There are other ways to resize an image. This one simply creates a new image of the required size and draws the image passed in onto the new one, and then returns the new image. It works well.

/// <summary>Resizes an image, optionally maintaining width:height ratios.</summary>
/// <param name="image">The <see cref="Image"/> that you wish to resize.</param>
/// <param name="width">The desired width of the resulting image.</param>
/// <param name="height">The desired height of the resulting image.</param>
/// <param name="maintainAspectRatio"><b>True</b> to maintain aspect ratio,
/// otherwise <b>false</b>. This defaults to <b>true</b>.</param>
/// <returns>The resulting resized <see cref="Image"/> object.</returns>
public static Image Resize(this Image image, int width, int height, bool maintainAspectRatio = true)
{
    if (image == null)
        throw new ArgumentNullException(nameof(image));

    if (maintainAspectRatio)
    {
        // calculate resize ratio
        double ratio = (double)width / image.Width;

        if (ratio * image.Height > height)
            ratio = (double)height / image.Height;

        width = (int)Math.Round(ratio * image.Width, MidpointRounding.AwayFromZero);
        height = (int)Math.Round(ratio * image.Height, MidpointRounding.AwayFromZero);
    }

    Bitmap bmp = null;
    try
    {
        bmp = new Bitmap(width, height);

        using (Graphics g = Graphics.FromImage(bmp))
        {
            g.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBicubic;
            g.SmoothingMode = Drawing2D.SmoothingMode.HighQuality;
            g.CompositingQuality = Drawing2D.CompositingQuality.HighQuality;
            g.DrawImage(image, 0, 0, width, height);
        }

        return bmp;
    }
    catch
    {
        if (bmp != null)
            bmp.Dispose();
        throw;
    }
}
Posted in Programming | Tagged | Leave a comment

How to indicate the Windows display is in use (and also enable or disable the screen saver) with c#

Still going through my old code, and I found this…

One part of my application is a video player, but if you’ve ever written one and used it to watch a video that’s more than a couple of minutes or seconds, depending on your power management and screen saver settings, you’ve probably had Windows pull the carpet out from under you and either have the screen saver or sleep mode suddenly take over.

So the code I’m sharing here is intended to solve those two problems.

My video player class has code that simply does this:

        private void MediaViewer_VisibleChanged(object sender, EventArgs e)
        {
            if (Visible)
            {
                // When playing a video, disable screen saver (if it is enabled).
                try
                {
                    userScreenSaverEnabled = ScreenSaver.Enabled;

                    if (userScreenSaverEnabled)
                        ScreenSaver.Enabled = false;
                }
                catch (Exception ex) { ex.Log(); }

                RefreshVolume();

                // Prevent the system from entering standby mode while we are playing video.
                IndicateDisplayInUse();
            }
            else
            {
                // If we disabled the screen saver, enable it again.
                if (userScreenSaverEnabled)
                    ScreenSaver.Enabled = true;

                CloseClip();
            }
        }

Ignore RefreshVolume() and CloseClip(). What I’d like to draw your attention to is:

  1. ScreenSaver.Enabled
  2. IndicateDisplayInUse

Firstly…

Enabling or disabling the screen saver

This one is probably not necessary to most people anymore, because most of us don’t bother with screen savers. Also, when I wrote this, the API way of doing it was buggy in Windows 7, so this code goes directly to the registry.

using Microsoft.Win32;
using System.Linq;

namespace System
{
    /// <summary>Enable/disable the screen saver. </summary>
    /// <remarks><para>
    /// This should be done via calls to <b>SystemParametersInfo</b>, but due to a bug
    /// in Windows 7 where the call to test if the screen saver is active always returns
    /// true, we use the registry directly (as recommended in the MSDN documentation).</para>
    /// <para>
    /// The registry settings are modified in the "Control Panel\Desktop" registry key, and after I
    /// started working at a company that overrides this for all users via a registry setting, it is
    /// also modified, if necessary, in "Software\Policies\Microsoft\Windows\Control Panel\Desktop".
    /// </para></remarks>
    public static class ScreenSaver
    {
        #region Private Fields

        private static readonly RegKeyComparer Comparer = new RegKeyComparer();

        private static string allUsersLastScreenSaver;

        private static string currentUserLastScreenSaver;

        #endregion Private Fields

        #region Public Properties

        public static bool AllUsersPolicyEnabled
        {
            get => IsAllUsersScreenSaverEnabled();
            set
            {
                if (!value)
                {
                    if (IsAllUsersScreenSaverEnabled())
                        RemoveAllUsersScreenSaver();
                }
                else if (!IsAllUsersScreenSaverEnabled() && !string.IsNullOrEmpty(allUsersLastScreenSaver))
                    RestoreAllUsersScreenSaver();
            }
        }

        /// <summary>Enable/disable screen saver.</summary>
        /// <remarks>When the screen saver is enabled, the registry value "Control Panel\Desktop\SCRNSAVE.EXE"
        /// contains the full path to the active screen saver. When disabled, the value does not exist.</remarks>
        public static bool Enabled
        {
            get => IsCurrentUserScreenSaverEnabled();
            set
            {
                if (!value)
                {
                    if (IsCurrentUserScreenSaverEnabled())
                        RemoveCurrentUserScreenSaver();

                    if (IsAllUsersScreenSaverEnabled())
                        RemoveAllUsersScreenSaver();
                }
                else
                {
                    if (!IsCurrentUserScreenSaverEnabled() && !string.IsNullOrEmpty(currentUserLastScreenSaver))
                        RestoreCurrentUserScreenSaver();

                    if (!IsAllUsersScreenSaverEnabled() && !string.IsNullOrEmpty(allUsersLastScreenSaver))
                        RestoreAllUsersScreenSaver();
                }
            }
        }

        #endregion Public Properties

        #region Private Methods

        /// <summary>Checks if there is a policy-defined screen saver for all users.</summary>
        /// <remarks>If such a screen saver is enabled, we store the current value.</remarks>
        /// <returns><b>True</b> if there is a policy-defined screen saver for all users; <b>false</b> otherwise.</returns>
        private static bool IsAllUsersScreenSaverEnabled()
        {
            using (RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Software\Policies\Microsoft\Windows\Control Panel\Desktop"))
            {
                bool result = key != null && key.GetValueNames().Contains("SCRNSAVE.EXE", Comparer);

                if (result)
                    allUsersLastScreenSaver = (string)key.GetValue("SCRNSAVE.EXE");

                return result;
            }
        }

        /// <summary>Checks if the current user has the screen saver enabled.</summary>
        /// <remarks>If such a screen saver is enabled, we store the current value.</remarks>
        /// <returns><b>True</b> if the current user has a screen saver enabled, otherwise <b>false</b>.</returns>
        private static bool IsCurrentUserScreenSaverEnabled()
        {
            using (RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Control Panel\Desktop"))
            {
                bool result = key != null && key.GetValueNames().Contains("SCRNSAVE.EXE", Comparer);

                if (result)
                    currentUserLastScreenSaver = (string)key.GetValue("SCRNSAVE.EXE");

                return result;
            }
        }

        private static void RemoveAllUsersScreenSaver()
        {
            using (RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Software\Policies\Microsoft\Windows\Control Panel\Desktop", true))
            {
                if (key != null)
                {
                    if (key.GetValueNames().Contains("SCRNSAVE.EXE", Comparer))
                        key.DeleteValue("SCRNSAVE.EXE");

                    if (key.GetValueNames().Contains("ScreenSaveTimeOut", Comparer))
                        key.DeleteValue("ScreenSaveTimeOut");

                    if (key.GetValueNames().Contains("ScreenSaverIsSecure", Comparer))
                        key.DeleteValue("ScreenSaverIsSecure");
                }
            }
        }

        private static void RemoveCurrentUserScreenSaver()
        {
            using (RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Control Panel\Desktop", true))
            {
                if (key != null)
                    key.DeleteValue("SCRNSAVE.EXE");
            }
        }

        private static void RestoreAllUsersScreenSaver()
        {
            using (RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Software\Policies\Microsoft\Windows\Control Panel\Desktop", true))
            {
                if (key != null)
                    key.SetValue("SCRNSAVE.EXE", allUsersLastScreenSaver);
            }
        }

        private static void RestoreCurrentUserScreenSaver()
        {
            using (RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Control Panel\Desktop", true))
            {
                if (key != null)
                    key.SetValue("SCRNSAVE.EXE", currentUserLastScreenSaver);
            }
        }

        #endregion Private Methods
    }
}

Update… I forgot that this code needed a comparer, which is this:

using System.Collections.Generic;

namespace System
{
    /// <summary>A simple <see cref="Collections.Generic.IEqualityComparer&lt;string&gt;"/>
    /// implementation to support a case insensitive comparison of registry values.</summary>
    internal class RegKeyComparer : IEqualityComparer<string>
    {
        public bool Equals(string x, string y)
        {
            return string.Compare(x, y, StringComparison.InvariantCultureIgnoreCase) == 0;
        }

        public int GetHashCode(string obj)
        {
            return (obj as string).GetHashCode();
        }
    }
}

Next…

Indicate the display is in use.

In this case, it’s a simple call to SetThreadExecutionState, with a flag to reset the display idle timer. I like this technique, because it is not a flag that needs to be set back to another state to say we are done. Just call it every now and then in a loop as long as the video player is visible, and don’t call it anymore when we are done.

My method to poll is done via an async timer method I shared years ago, but I’ll include it again here. I call it like this:

protected void IndicateDisplayInUse() => CreatePollingTimer(() => !IsDisposed && Visible, TimeSpan.FromMinutes(1), () => PowerManagement.DisplayIsInUse());

The CreatePollingTimer method is this:

        /// <summary>Starts an asynchronous polling timer, similar to a thread timer.</summary>
        /// <param name="condition">The condition which indicates that polling should continue.</param>
        /// <param name="pollInterval">Similar to a timer tick, this determines the polling frequency.</param>
        /// <param name="pollingAction">The action to be executed after each poll interval, as long as the condition
        /// evaluates to true. This works like a timer event handler.</param>
        /// <param name="afterAction">An optional action to be executed after the condition evaluates to false.</param>
        protected async void CreatePollingTimer(Func<bool> condition, TimeSpan pollInterval, Action pollingAction, Action afterAction = null)
        {
            while (true)
            {
                await Task.Delay(pollInterval);

                if (condition())
                    pollingAction();
                else
                    break;
            }

            afterAction?.Invoke();
        }

You don’t need the above – that’s just how I call it. The actual code to tell the system not to shut down the display is this call to SetThreadExecutionState:

using System.Runtime.InteropServices;
using System.Security;

namespace System
{
    /// <summary>Notifies the system that the display is in use. To be used, for example,
    /// when playing video, since the system is unable to detect video activity alone and
    /// will incorrectly detect that it is idle, and enter sleep mode.</summary>
    public static class PowerManagement
    {
        /// <summary>Informs the system that the display is in use.</summary>
        public static void DisplayIsInUse() => NativeMethods.SetThreadExecutionState(ExecutionStates.DisplayRequired);

        [SuppressUnmanagedCodeSecurity]
        internal static class NativeMethods
        {
            [DllImport("kernel32.dll", SetLastError = true)]
            public static extern ExecutionStates SetThreadExecutionState(ExecutionStates esFlags);
        }

        [Flags]
        internal enum ExecutionStates
        {
            /// <summary>No state configured.</summary>
            None = 0,

            /// <summary>Forces the system to be in the working state by resetting the system idle timer.</summary>
            SystemRequired = 0x1,

            /// <summary>Forces the display to be on by resetting the display idle timer.</summary>
            DisplayRequired = 0x2,

            /// <summary>Enables away mode. This value must be specified with ExecutionStates.Continuous.
            /// Windows Server 2003 and Windows XP/2000: ExecutionStates.AwayModeRequired is not supported.</summary>
            AwayModeRequired = 0x40,

            /// <summary>Informs the system that the state being set should remain in effect until the next
            /// call that uses ExecutionStates.Continuous and one of the other state flags is cleared.</summary>
            Continuous = unchecked((int)0x80000000)
        }
    }
}
Posted in Programming | Tagged , , | Leave a comment

Using c# to copy, move, rename files, and delete to the recycle bin via SHFileOperation

While we’re on the subject of deleting files as in the last post, I found this old code of mine in the same solution. I don’t use it anymore because I use IFileOperation instead, but this code still works. (The IFileOperation code wasn’t written by me so I shouldn’t share that – it was some helper classes I found somewhere, written by Stephen Toub of Microsoft.)

Usage should be self explanatory, so here’s the code…

using System.Collections.Generic;
using System.Security;
using System.Text;
using System.Threading.Tasks;

namespace System.Runtime.InteropServices.ComTypes
{
    /// <summary>Helper class, wrapping SHFileOperation, to enable easily deleting to the Windows Recycle bin
    /// from C# without having to reference the Visual Basic Library, as well as copying/moving files with
    /// native Windows progress dialogs.</summary>
    /// <remarks>According to the online help, SHFileOperation calls should be replaced with calls to
    /// IFileOperation from Windows Vista up...</remarks>
    public static class NativeFileOperation
    {
        private const int FO_COPY = 0x0002;

        private const int FO_DELETE = 0x0003;

        private const int FO_MOVE = 0x0001;

        private const int FO_RENAME = 0x0004;

        private const int FOF_ALLOWUNDO = 0x40;

        private const int FOF_NOCONFIRMATION = 0x10;

        private const int FOF_NOCONFIRMMKDIR = 0x0200;

#pragma warning disable IDE0051 // Remove unused private members
        private const int FOF_RENAMEONCOLLISION = 0x0008;
#pragma warning restore IDE0051 // Remove unused private members

        private enum Operation
        {
            Copy,
            Move,
            Delete,
            Rename
        }

        public static bool CopyFiles(IEnumerable<string> files, string destinationDirectory)
        {
            return PerformFileOperation(Operation.Copy, files, destinationDirectory);
        }

        public static bool MoveFiles(IEnumerable<string> files, string destinationDirectory)
        {
            return PerformFileOperation(Operation.Move, files, destinationDirectory);
        }

        private static bool PerformFileOperation(Operation op, IEnumerable<string> files, string destinationDirectory = null)
        {
            StringBuilder builder = new StringBuilder();

            foreach (string filename in files)
            {
                builder.Append(filename + '\0');
            }
            builder.Append('\0');

            SHFILEOPSTRUCT shf = new SHFILEOPSTRUCT
            {
                fFlags = FOF_NOCONFIRMATION,
                pFrom = builder.ToString()
            };

            switch (op)
            {
                case Operation.Copy:
                    shf.wFunc = FO_COPY;
                    shf.fFlags |= FOF_NOCONFIRMMKDIR;
                    shf.pTo = destinationDirectory.TrimEnd() + "\0\0";
                    break;
                case Operation.Delete:
                    shf.wFunc = FO_DELETE;
                    shf.fFlags |= FOF_ALLOWUNDO;
                    shf.pTo = null;
                    break;
                case Operation.Move:
                    shf.fFlags |= FOF_NOCONFIRMMKDIR;
                    shf.wFunc = FO_MOVE;
                    shf.pTo = destinationDirectory.TrimEnd() + "\0\0";
                    break;
                case Operation.Rename:
                    shf.fFlags |= FOF_NOCONFIRMMKDIR;
                    shf.fFlags |= FOF_ALLOWUNDO;
                    shf.wFunc = FO_RENAME;
                    shf.pTo = destinationDirectory.TrimEnd() + "\0\0";
                    break;
            }

            return NativeMethods.SHFileOperation(ref shf) == 0;
        }

        [SuppressUnmanagedCodeSecurity]
        internal static class NativeMethods
        {
            [DllImport("shell32.dll", CharSet = CharSet.Unicode)]
            internal static extern int SHFileOperation(ref SHFILEOPSTRUCT FileOp);
        }

        public static async Task<bool> CopyFileAsync(string fileName, string destinationDirectory) => await CopyFilesAsync(new string[] { fileName }, destinationDirectory);

        public static async Task<bool> CopyFilesAsync(IEnumerable<string> files, string destinationDirectory) => await PerformFileOperationAsync(Operation.Copy, files, destinationDirectory);

        public static async Task<bool> DeleteToRecycleBinAsync(string fileName) => await DeleteToRecycleBinAsync(new string[] { fileName });

        public static async Task<bool> DeleteToRecycleBinAsync(IEnumerable<string> files) => await PerformFileOperationAsync(Operation.Delete, files);

        public static async Task<bool> MoveFileAsync(string fileName, string destinationDirectory) => await MoveFilesAsync(new string[] { fileName }, destinationDirectory);

        public static async Task<bool> MoveFilesAsync(IEnumerable<string> files, string destinationDirectory) => await PerformFileOperationAsync(Operation.Move, files, destinationDirectory);

        public static async Task<bool> RenameFileAsync(string filename, string destinationDirectory) => await PerformFileOperationAsync(Operation.Rename, new string[] { filename }, destinationDirectory);

        public static async Task<bool> RenameFilesAsync(IEnumerable<string> files, string destinationDirectory) => await PerformFileOperationAsync(Operation.Rename, files, destinationDirectory);

        private static async Task<bool> PerformFileOperationAsync(Operation op, IEnumerable<string> files, string destinationDirectory = null)
        {
            return await Task.Run(() =>
            {
                StringBuilder builder = new StringBuilder();

                foreach (string filename in files)
                {
                    builder.Append(filename + '\0');
                }
                builder.Append('\0');

                SHFILEOPSTRUCT shf = new SHFILEOPSTRUCT
                {
                    fFlags = FOF_NOCONFIRMATION,
                    pFrom = builder.ToString()
                };

                switch (op)
                {
                    case Operation.Copy:
                        shf.wFunc = FO_COPY;
                        shf.fFlags |= FOF_NOCONFIRMMKDIR;
                        shf.pTo = destinationDirectory.TrimEnd() + "\0\0";
                        break;
                    case Operation.Delete:
                        shf.wFunc = FO_DELETE;
                        shf.fFlags |= FOF_ALLOWUNDO;
                        shf.pTo = null;
                        break;
                    case Operation.Move:
                        shf.fFlags |= FOF_NOCONFIRMMKDIR;
                        shf.wFunc = FO_MOVE;
                        shf.pTo = destinationDirectory.TrimEnd() + "\0\0";
                        break;
                    case Operation.Rename:
                        shf.fFlags |= FOF_NOCONFIRMMKDIR;
                        shf.fFlags |= FOF_ALLOWUNDO;
                        shf.wFunc = FO_RENAME;
                        shf.pTo = destinationDirectory.TrimEnd() + "\0\0";
                        break;
                }

                return NativeMethods.SHFileOperation(ref shf) == 0;
            });
        }

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        internal struct SHFILEOPSTRUCT
        {
            private readonly IntPtr hwnd;

            [MarshalAs(UnmanagedType.U4)]
            public int wFunc;

            public string pFrom;

            public string pTo;

            public short fFlags;

            [MarshalAs(UnmanagedType.Bool)]
            public bool fAnyOperationsAborted;

            private readonly IntPtr hNameMappings;

            public string lpszProgressTitle;
        }
    }
}
Posted in General, Programming | Tagged , , | Leave a comment

Some slight overkill for a method that deletes files and directories

Still cleaning up and improving my old code… I found this. It’s a method in my base class for console applications, to handle deleting files. This is something I’ve approached in different ways in different places – even in the same solution. Not sure how useful it is, but regardless, this method does it’s job, which is to delete files or directories and ignore any exceptions. LogError code not included so if you use this code, you need to decide what to do there…

        /// <summary>Delete the file or directory specified,
        /// without throwing an exception on failure.</summary>
        public static bool DeleteFileSystemObject(string path)
        {
            try
            {
                /* File.Exists seems to return true for directories,
                 * so always check with Directory.Exists first... */
                if (Directory.Exists(path))
                {
                    DirectoryInfo info = new DirectoryInfo(path);

                    // If path == current directory, change the current directory.
                    if (string.Compare(Directory.GetCurrentDirectory(), path, StringComparison.InvariantCultureIgnoreCase) == 0 && Directory.Exists(Path.GetDirectoryName(path)))
                        Directory.SetCurrentDirectory(Path.GetDirectoryName(path));

                    // Try to silently delete readonly/hidden directories.
                    if ((info.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly || (info.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden)
                        info.Attributes = FileAttributes.Directory & FileAttributes.Archive;

                    Directory.Delete(path, true);
                }
                else if (File.Exists(path))
                {
                    // Try to silently delete readonly/hidden files.
                    if ((File.GetAttributes(path) & FileAttributes.ReadOnly) == FileAttributes.ReadOnly || (File.GetAttributes(path) & FileAttributes.Hidden) == FileAttributes.Hidden)
                        File.SetAttributes(path, FileAttributes.Normal);

                    File.Delete(path);
                }
                return true;
            }
            catch (IOException) { }
            catch (UnauthorizedAccessException) { }
            catch (Exception ex) { LogError(ex); }
            return false;
        }
Posted in General, Programming | Tagged , | Leave a comment

Using a Folder Browser Common Dialog in c#

Lately I have been taking my old solution, which contains a large amount of my personal and experimental code, and updating it for Visual Studio 2019, since there are several wizards which help people like me who have missed all the recent language improvements, to be able to apply them.

Anyway, along the way I’ve stumbled on code I wrote years ago that might be useful for others. Today I’m sharing my implementation of the Folder Browser Common Dialog. And by Folder Browser Common Dialog, I mean the standard file selection dialog, but in folder mode, which is available in Windows but not implemented in .Net, which instead gives us the flimsy “Browse For Folder” dialog, which contains only a TreeView and doesn’t look like a standard dialog.

First, here’s an example that uses it, taken from my monolithic solution.

using Romy.Core.Dialogs;
using System;
using System.ComponentModel;
using System.Drawing.Design;
using System.Windows.Forms;

namespace Romy.UI
{
    public class FolderPathEditor : UITypeEditor
    {
        public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
        {
            return UITypeEditorEditStyle.Modal;
        }

        public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
        {
            FolderBrowser browser = new FolderBrowser();
            if (value != null)
                browser.Folder = string.Format("{0}", value);

            if (browser.ShowDialog(null) == DialogResult.OK)
                return browser.Folder;

            return value;
        }
    }
}

You may have noticed that class is a UITypeEditor – that’s because I use it from my options dialog, which has a property grid. So I can then browse for a folder from a property grid. It looks like this:

SNAGHTML24fafe3b

The dialog itself, as displayed when you click the ellipses next to the Cache Directory value in that property grid, is this:

SNAGHTML24fbc3ec

So, here’s the code for the dialog… The actual code is very little and all in the ShowDialog method, while the rest is all the platform invoked code and declarations needed to hook it all up. Use at your own risk… All of this was coded years ago when I was a meth addict. (Honestly, I don’t have the patience to write this sort of code when I’m clean, which I have been since September 2013 by the way.)

using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Security;
using System.Windows.Forms;

namespace Romy.Core.Dialogs
{
    /// <summary>Wraps the Common Item Dialog with flags to use it as a folder browser, since this
    /// is not implemented in managed code and I do not want to use the Windows API CodePack.</summary>
#pragma warning disable CA1712 // Do not prefix enum values with type name
    public class FolderBrowser
    {
        #region Internal Enums

        internal enum FDAP
        {
            FDAP_BOTTOM = 0,
            FDAP_TOP = 1
        }

        internal enum FDE_OVERWRITE_RESPONSE
        {
            FDEOR_DEFAULT = 0x00000000,
            FDEOR_ACCEPT = 0x00000001,
            FDEOR_REFUSE = 0x00000002
        }

        internal enum FDE_SHAREVIOLATION_RESPONSE
        {
            FDESVR_DEFAULT = 0x00000000,
            FDESVR_ACCEPT = 0x00000001,
            FDESVR_REFUSE = 0x00000002
        }

        [Flags]
        internal enum FOS : uint
        {
            FOS_OVERWRITEPROMPT = 0x00000002,
            FOS_STRICTFILETYPES = 0x00000004,
            FOS_NOCHANGEDIR = 0x00000008,
            FOS_PICKFOLDERS = 0x00000020,
            FOS_FORCEFILESYSTEM = 0x00000040, // Ensure that items returned are filesystem items.
            FOS_ALLNONSTORAGEITEMS = 0x00000080, // Allow choosing items that have no storage.
            FOS_NOVALIDATE = 0x00000100,
            FOS_ALLOWMULTISELECT = 0x00000200,
            FOS_PATHMUSTEXIST = 0x00000800,
            FOS_FILEMUSTEXIST = 0x00001000,
            FOS_CREATEPROMPT = 0x00002000,
            FOS_SHAREAWARE = 0x00004000,
            FOS_NOREADONLYRETURN = 0x00008000,
            FOS_NOTESTFILECREATE = 0x00010000,
            FOS_HIDEMRUPLACES = 0x00020000,
            FOS_HIDEPINNEDPLACES = 0x00040000,
            FOS_NODEREFERENCELINKS = 0x00100000,
            FOS_DONTADDTORECENT = 0x02000000,
            FOS_FORCESHOWHIDDEN = 0x10000000,
            FOS_DEFAULTNOMINIMODE = 0x20000000
        }

        internal enum SIATTRIBFLAGS
        {
            SIATTRIBFLAGS_AND = 1,
            SIATTRIBFLAGS_APPCOMPAT = 3,
            SIATTRIBFLAGS_OR = 2
        }

        internal enum SIGDN : uint
        {
            SIGDN_DESKTOPABSOLUTEEDITING = 0x8004c000,
            SIGDN_DESKTOPABSOLUTEPARSING = 0x80028000,
            SIGDN_FILESYSPATH = 0x80058000,
            SIGDN_NORMALDISPLAY = 0,
            SIGDN_PARENTRELATIVE = 0x80080001,
            SIGDN_PARENTRELATIVEEDITING = 0x80031001,
            SIGDN_PARENTRELATIVEFORADDRESSBAR = 0x8007c001,
            SIGDN_PARENTRELATIVEPARSING = 0x80018001,
            SIGDN_URL = 0x80068000
        }

        #endregion Internal Enums

        #region Public Properties

        public string Folder { get; set; }

        public string Title { get; set; }

        #endregion Public Properties

        #region Public Methods

        public DialogResult ShowDialog(IWin32Window owner)
        {
            IntPtr hwndOwner = owner != null ? owner.Handle : NativeMethods.GetActiveWindow();

            NativeInterfaces.IFileOpenDialog dialog = (NativeInterfaces.IFileOpenDialog)new FileOpenDialog();
            try
            {
                NativeInterfaces.IShellItem item;

                if (!string.IsNullOrEmpty(Folder))
                {
                    Guid _shellItemGuid = typeof(NativeInterfaces.IShellItem).GUID;
                    item = (NativeInterfaces.IShellItem)NativeMethods.SHCreateItemFromParsingName(Folder, null, ref _shellItemGuid);

                    if (item != null)
                        dialog.SetFolder(item);
                }

                dialog.SetOptions(FOS.FOS_PICKFOLDERS | FOS.FOS_FORCEFILESYSTEM);

                if (!string.IsNullOrEmpty(Title))
                    dialog.SetTitle(Title);

                int hr = dialog.Show(hwndOwner);
                if (hr == NativeMethods.ERROR_CANCELLED)
                    return DialogResult.Cancel;

                if (hr != 0)
                    return DialogResult.Abort;

                dialog.GetResult(out item);

                item.GetDisplayName(SIGDN.SIGDN_FILESYSPATH, out string path);
                Folder = path;

                return DialogResult.OK;
            }
            finally { Marshal.ReleaseComObject(dialog); }
        }

        #endregion Public Methods

        #region Internal Structs

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 4)]
        internal struct COMDLG_FILTERSPEC
        {
            [MarshalAs(UnmanagedType.LPWStr)]
            public string pszName;

            [MarshalAs(UnmanagedType.LPWStr)]
            public string pszSpec;
        }

        [StructLayout(LayoutKind.Sequential, Pack = 4)]
        internal struct PROPERTYKEY
        {
            public Guid fmtid;

            public uint pid;
        }

        #endregion Internal Structs

        #region Internal Classes

        internal static class NativeInterfaces
        {
            #region Public Interfaces

            [ComImport, Guid("42f85136-db7e-439c-85f1-e4075d135fc8"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
            public interface IFileDialog : IModalWindow
            {
                [PreserveSig]
                new int Show([In] IntPtr parent);

                void SetFileTypes([In] uint cFileTypes, [In] [MarshalAs(UnmanagedType.LPArray)]COMDLG_FILTERSPEC[] rgFilterSpec);

                void SetFileTypeIndex([In] uint iFileType);

                void GetFileTypeIndex(out uint piFileType);

                void Advise([In, MarshalAs(UnmanagedType.Interface)] IFileDialogEvents pfde, out uint pdwCookie);

                void Unadvise([In] uint dwCookie);

                void SetOptions([In] FOS fos);

                void GetOptions(out FOS pfos);

                void SetDefaultFolder([In, MarshalAs(UnmanagedType.Interface)] IShellItem psi);

                void SetFolder([In, MarshalAs(UnmanagedType.Interface)] IShellItem psi);

                void GetFolder([MarshalAs(UnmanagedType.Interface)] out IShellItem ppsi);

                void GetCurrentSelection([MarshalAs(UnmanagedType.Interface)] out IShellItem ppsi);

                void SetFileName([In, MarshalAs(UnmanagedType.LPWStr)] string pszName);

                void GetFileName([MarshalAs(UnmanagedType.LPWStr)] out string pszName);

                void SetTitle([In, MarshalAs(UnmanagedType.LPWStr)] string pszTitle);

                void SetOkButtonLabel([In, MarshalAs(UnmanagedType.LPWStr)] string pszText);

                void SetFileNameLabel([In, MarshalAs(UnmanagedType.LPWStr)] string pszLabel);

                void GetResult([MarshalAs(UnmanagedType.Interface)] out IShellItem ppsi);

                void AddPlace([In, MarshalAs(UnmanagedType.Interface)] IShellItem psi, int alignment);

                void SetDefaultExtension([In, MarshalAs(UnmanagedType.LPWStr)] string pszDefaultExtension);

                void Close([MarshalAs(UnmanagedType.Error)] int hr);

                void SetClientGuid([In] ref Guid guid);

                void ClearClientData();

                void SetFilter([MarshalAs(UnmanagedType.Interface)] IntPtr pFilter);
            }

            [ComImport, Guid("973510DB-7D7F-452B-8975-74A85828D354"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
            public interface IFileDialogEvents
            {
                [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), PreserveSig]
                int OnFileOk([In, MarshalAs(UnmanagedType.Interface)] IFileDialog pfd);

                [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), PreserveSig]
                int OnFolderChanging([In, MarshalAs(UnmanagedType.Interface)] IFileDialog pfd,
                             [In, MarshalAs(UnmanagedType.Interface)] IShellItem psiFolder);

                [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
                void OnFolderChange([In, MarshalAs(UnmanagedType.Interface)] IFileDialog pfd);

                [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
                void OnSelectionChange([In, MarshalAs(UnmanagedType.Interface)] IFileDialog pfd);

                [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
                void OnShareViolation([In, MarshalAs(UnmanagedType.Interface)] IFileDialog pfd,
                          [In, MarshalAs(UnmanagedType.Interface)] IShellItem psi,
                          out FDE_SHAREVIOLATION_RESPONSE pResponse);

                [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
                void OnTypeChange([In, MarshalAs(UnmanagedType.Interface)] IFileDialog pfd);

                [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
                void OnOverwrite([In, MarshalAs(UnmanagedType.Interface)] IFileDialog pfd,
                         [In, MarshalAs(UnmanagedType.Interface)] IShellItem psi,
                         out FDE_OVERWRITE_RESPONSE pResponse);
            }

            [ComImport, Guid("d57c7288-d4ad-4768-be02-9d969532d960"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
            public interface IFileOpenDialog : IFileDialog
            {
                void AddPlace([In, MarshalAs(UnmanagedType.Interface)] IShellItem psi, FileDialogCustomPlace fdcp);

                void GetResults([MarshalAs(UnmanagedType.Interface)] out IShellItemArray ppenum);

                void GetSelectedItems([MarshalAs(UnmanagedType.Interface)] out IShellItemArray ppsai);
            }

            [ComImport, Guid("b4db1657-70d7-485e-8e3e-6fcb5a5c1802"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
            public interface IModalWindow
            {
                [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), PreserveSig]
                uint Show([In] IntPtr parent);
            }

            [ComImport, Guid("43826D1E-E718-42EE-BC55-A1E261C37BFE"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
            public interface IShellItem
            {
                void BindToHandler([In, MarshalAs(UnmanagedType.Interface)] IntPtr pbc, [In] ref Guid bhid, [In] ref Guid riid, out IntPtr ppv);

                void GetParent([MarshalAs(UnmanagedType.Interface)] out IShellItem ppsi);

                void GetDisplayName([In] SIGDN sigdnName, [MarshalAs(UnmanagedType.LPWStr)] out string ppszName);

                void GetAttributes([In] uint sfgaoMask, out uint psfgaoAttribs);

                void Compare([In, MarshalAs(UnmanagedType.Interface)] IShellItem psi, [In] uint hint, out int piOrder);
            }

            [ComImport, Guid("B63EA76D-1F85-456F-A19C-48159EFA858B"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
            public interface IShellItemArray
            {
                [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
                void BindToHandler([In, MarshalAs(UnmanagedType.Interface)] IntPtr pbc, [In] ref Guid rbhid,
                        [In] ref Guid riid, out IntPtr ppvOut);

                [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
                void GetPropertyStore([In] int Flags, [In] ref Guid riid, out IntPtr ppv);

                [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
                void GetPropertyDescriptionList([In] ref PROPERTYKEY keyType, [In] ref Guid riid, out IntPtr ppv);

                [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
                void GetAttributes([In] SIATTRIBFLAGS dwAttribFlags, [In] uint sfgaoMask, out uint psfgaoAttribs);

                [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
                void GetCount(out uint pdwNumItems);

                [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
                void GetItemAt([In] uint dwIndex, [MarshalAs(UnmanagedType.Interface)] out IShellItem ppsi);

                [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
                void EnumItems([MarshalAs(UnmanagedType.Interface)] out IntPtr ppenumShellItems);
            }

            #endregion Public Interfaces
        }

        [SuppressUnmanagedCodeSecurity]
        internal static class NativeMethods
        {
            #region Public Fields

            public const int ERROR_CANCELLED = unchecked((int)0x800704C7);

            #endregion Public Fields

            #region Public Methods

            [DllImport("user32.dll")]
            public static extern IntPtr GetActiveWindow();

            [DllImport("shell32.dll", SetLastError = true, CharSet = CharSet.Unicode, PreserveSig = false)]
            [return: MarshalAs(UnmanagedType.Interface)]
            public static extern object SHCreateItemFromParsingName(
                [MarshalAs(UnmanagedType.LPWStr)] string pszPath, IBindCtx pbc, ref Guid riid);

            #endregion Public Methods
        }

        #endregion Internal Classes

        #region Private Classes

        [ComImport, Guid("DC1C5A9C-E88A-4dde-A5A1-60F82A20AEF7")]
        private class FileOpenDialog { }

        #endregion Private Classes
    }
#pragma warning restore CA1712 // Do not prefix enum values with type name
}
Posted in Programming | Tagged , , | Leave a comment

Saving a collection of images to a high quality GIF animation using c#

I ran into an interesting problem this weekend… I was showing off a feature of my code to my son, and it didn’t work as expected…

I have this application with years of my personal code, which I have recently updated to use the latest version of Visual Studio (2019). I was showing him how it views a GIF I downloaded, but he complained that the animation was too slow. So, I used my old trusty code to extract the frames of the GIF, then encoded it back to GIF format using a shorter frame delay (with the Gif Encoder I downloaded years ago). But, although everything worked, the image looked shitty, because the encoder uses Gif format for each frame.

Here follows my solution for this annoying problem.

For this code to work, you will need:

  1. Bumpkit.GifEncoder. Download here.
  2. AForge.Net image library. Download here.

My code now works around the issue by reducing the colours of each Bitmap before adding a frame via the Gif encoder.

The code below should be self-explanatory. The ExtractFrames method at the bottom uses standard .Net Framework libraries to extract all the frames from an animated Gif file. I call it from a user interface and then save all the frames to a directory. (That code not shown.)

The other two methods, SaveAnimatedGifImage, and its async counterpart, are the ones I’m focusing on here. I worked around the loss of image quality problem by using a colour quantizer from the AForge.Net image library to reduce each image to 256 colours before adding a frame with the Gif encoder.

The resulting image looked like this:

giphy

And here is the code:

using AForge.Imaging.ColorReduction;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading.Tasks;

namespace Romy.Core
{
    public static class GifExtensions
    {
        /// <summary>Saves the images as frames to an animated Gif Image.</summary>
        /// <param name="images">The images to save.</param>
        /// <param name="path">The path of the Gif file to create.</param>
        /// <param name="delay">The delay between frames, in milliseconds.</param>
        /// <param name="repeat">The number of times the animation should repeat. Leave this zero
        /// for it to loop forever, or specify a value to limit the number of repetitions.</param>
        public static void SaveAnimatedGifImage(this IEnumerable<Image> images, string path, int delay = 100, int repeat = 0)
        {
            var quantizer = new ColorImageQuantizer(new MedianCutQuantizer())
            {
                UseCaching = true
            };

            using (MemoryStream stream = new MemoryStream())
            {
                using (BumpKit.GifEncoder encoder = new BumpKit.GifEncoder(stream, null, null, repeat))
                {
                    Image[] imageArray = images.ToArray();

                    for (int i = 0; i < imageArray[0].Height; i++)
                    {
                        for (int j = 0; j < imageArray[0].Width; j++)
                        {
                            quantizer.Quantizer.AddColor(
                                (imageArray[0] as Bitmap).GetPixel(j, i));
                        }
                    }

                    for (int i = 0; i < imageArray.Length; i++)
                    {
                        var image = quantizer.ReduceColors(imageArray[i] as Bitmap, 256);
                        encoder.AddFrame((Image)image.CopyImage(), 0, 0, TimeSpan.FromMilliseconds(delay));
                    }
                }

                stream.Position = 0;

                using (FileStream fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, Constants.BufferSize, false))
                {
                    stream.WriteTo(fileStream);
                }
            }
        }

        /// <summary>Asynchronously saves the images as frames to an animated Gif Image.</summary>
        /// <param name="images">The images to save.</param>
        /// <param name="path">The path of the Gif file to create.</param>
        /// <param name="delay">The delay between frames, in milliseconds.</param>
        /// <param name="repeat">The number of times the animation should repeat. Leave this zero
        /// for it to loop forever, or specify a value to limit the number of repetitions.</param>
        public static async Task SaveAnimatedGifImageAsync(this IEnumerable<Image> images, string path, int delay = 100, int repeat = 0)
        {
            var quantizer = new ColorImageQuantizer(new MedianCutQuantizer())
            {
                UseCaching = true
            };

            using (MemoryStream stream = new MemoryStream())
            {
                using (BumpKit.GifEncoder encoder = new BumpKit.GifEncoder(stream, null, null, repeat))
                {
                    Image[] imageArray = images.ToArray();

                    for (int i = 0; i < imageArray[0].Height; i++)
                    {
                        for (int j = 0; j < imageArray[0].Width; j++)
                        {
                            quantizer.Quantizer.AddColor(
                                (imageArray[0] as Bitmap).GetPixel(j, i));
                        }
                    }

                    for (int i = 0; i < imageArray.Length; i++)
                    {
                        var image = quantizer.ReduceColors(imageArray[i] as Bitmap, 256);
                        encoder.AddFrame((Image)image.CopyImage(), 0, 0, TimeSpan.FromMilliseconds(delay));
                    }
                }

                stream.Position = 0;

                using (FileStream fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, Constants.BufferSize, true))
                {
                    await stream.CopyToStreamAsync(fileStream, Constants.BufferSize);
                }
            }
        }

        /// <summary>Extracts the frames and the delay for each frame from an animated Gif Image.</summary>
        /// <returns>An enumerable of key-value pairs, where the key is the image, and the value is that
        /// frame's delay, in milliseconds.</returns>
        public static IEnumerable<KeyValuePair<Image, int>> ExportFrames(this Image gifImage)
        {
            if (gifImage.RawFormat.Equals(ImageFormat.Gif) && ImageAnimator.CanAnimate(gifImage))
            {
                FrameDimension frameDimension = new FrameDimension(gifImage.FrameDimensionsList[0]);
                int frameCount = gifImage.GetFrameCount(frameDimension);
                int index = 0;

                for (int i = 0; i < frameCount; i++)
                {
                    int delay = BitConverter.ToInt32(gifImage.GetPropertyItem(20736).Value, index) * 10;
                    index += Marshal.SizeOf(index);

                    gifImage.SelectActiveFrame(frameDimension, i);
                    yield return new KeyValuePair<Image, int>(gifImage.CopyImage(), delay);
                }
            }
        }
    }
}

Oops, I see the method to extract frames uses my alternative to Clone() to make a copy of the image. Maybe that is what went wrong in the first place? Anyway, you could just change it to use Clone, but if you want my extension method so the above will compile as is, here you go… (Btw, my CopyImage extension method exists to avoid the dreaded “A generic error occurred in GDI+” error message.)

Regardless, if you’re not doing something contrived like encoding frames extracted from an existing GIF but with a shorter frame delay, but rather encoding frames you got from a video, the colour reduction algorithm might be useful anyway.

        /// <summary>Creates a 24 bit-per-pixel copy of the source image.</summary>
        public static Image CopyImage(this Image image) => CopyImage(image, PixelFormat.Format24bppRgb);

        /// <summary>Creates a copy of the source image with the specified pixel format.</summary><remarks>
        /// This can also be achieved with the <see cref="Bitmap.Clone(int, int, PixelFormat)"/>
        /// overload, but I have had issues with that method.</remarks>
        public static Image CopyImage(this Image image, PixelFormat format)
        {
            if (image == null)
                throw new ArgumentNullException("image");

            // Don't try to draw a new Bitmap with an indexed pixel format.
            if (format == PixelFormat.Format1bppIndexed || format == PixelFormat.Format4bppIndexed || format == PixelFormat.Format8bppIndexed || format == PixelFormat.Indexed)
                return (image as Bitmap).Clone(new Rectangle(0, 0, image.Width, image.Height), format);

            Image result = null;
            try
            {
                result = new Bitmap(image.Width, image.Height, format);

                using (Graphics graphics = Graphics.FromImage(result))
                {
                    graphics.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBicubic;
                    graphics.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias;
                    graphics.CompositingQuality = Drawing2D.CompositingQuality.HighQuality;

                    graphics.DrawImage(image, 0, 0, result.Width, result.Height);
                }
            }
            catch
            {
                if (result != null)
                    result.Dispose();

                throw;
            }
            return result;
        }

And another oops… I see the async method used my own Stream copying extension method. I wrote about it a while back and you could just use the built-in Stream.CopyToAsync(Stream) method, but here is my one again anyway…

        /// <summary>An implementation to copy asynchronously from one stream to another,
        /// similar to <see cref="Stream.CopyToAsync(Stream)"/></summary>
        /// <remarks>
        /// This was written because the default implementation would sometimes throw OutOfMemoryExceptions
        /// in my FileAsync.ReadAllBytesAsync method, when opening large Bitmap files. Reading with a fixed
        /// size small buffer works well as a general solution, though I have also begun using a larger
        /// buffer to improve performance with larger files.</remarks>
        public static async Task CopyToStreamAsync(this Stream source, Stream destination, int bufferSize)
        {
            if (source == null)
                throw new ArgumentNullException("source");

            if (destination == null)
                throw new ArgumentNullException("destination");

            if (bufferSize <= 0)
                throw new ArgumentOutOfRangeException("bufferSize", "bufferSize must be greater than zero");

            /* The source stream may not support seeking; e.g. a stream
             * returned by ZipArchiveEntry.Open() or a network stream. */
            int size = bufferSize;
            bool canSeek = source.CanSeek;

            if (canSeek)
            {
                try
                {
                    size = (int)Math.Min(bufferSize, source.Length);
                }
                catch (NotSupportedException) { canSeek = false; }
            }

            byte[] buffer = new byte[size];
            long remaining = canSeek ? source.Length : 0;

            /* If the stream is seekable, seek through it until all bytes are read.
             * If we read less than the expected number of bytes, it indicates an
             * error, so throw the appropriate exception.
             *
             * If the stream is not seekable, loop until we read 0 bytes. (It's not
             * an error in this case.) */
            while (!canSeek || remaining > 0)
            {
                int read = await source.ReadAsync(buffer, 0, size);

                if (read <= 0)
                {
                    if (canSeek)
                        throw new EndOfStreamException(
                            string.Format("End of stream reached, but {0} remained to be read.",
                            FormatBytes(remaining)));
                    else
                        break;
                }

                await destination.WriteAsync(buffer, 0, read);
                remaining -= canSeek ? read : 0;
            }
        }

Oh no… I guess you need this too:

        private static string FormatBytes(long bytes)
        {
            const long KiloByte = 1024L;
            const long MegaByte = KiloByte * KiloByte;
            const long GigaByte = MegaByte * KiloByte;
            const long TeraByte = GigaByte * KiloByte;
            const long PetaByte = TeraByte * KiloByte;
            const long ExaByte = PetaByte * KiloByte;

            string formattedBytes = string.Empty;

            if (bytes < KiloByte)
                formattedBytes = string.Format("{0:F2} bytes", bytes);
            else if (bytes >= KiloByte && bytes < MegaByte)
                formattedBytes = string.Format("{0:F2} KB", Math.Round((double)bytes / KiloByte, 2, MidpointRounding.AwayFromZero));
            else if (bytes >= MegaByte && bytes < GigaByte)
                formattedBytes = string.Format("{0:F2} MB", Math.Round((double)bytes / MegaByte, 2, MidpointRounding.AwayFromZero));
            else if (bytes >= GigaByte && bytes < TeraByte)
                formattedBytes = string.Format("{0:F2} GB", Math.Round((double)bytes / GigaByte, 2, MidpointRounding.AwayFromZero));
            else if (bytes >= TeraByte && bytes < PetaByte)
                formattedBytes = string.Format("{0:F2} TB", Math.Round((double)bytes / TeraByte, 2, MidpointRounding.AwayFromZero));
            else if (bytes >= PetaByte && bytes < ExaByte)
                formattedBytes = string.Format("{0:F2} PB", Math.Round((double)bytes / PetaByte, 2, MidpointRounding.AwayFromZero));
            else if (bytes >= ExaByte)
                formattedBytes = string.Format("{0:F2} EB", Math.Round((double)bytes / ExaByte, 2, MidpointRounding.AwayFromZero));

            return formattedBytes;
        }

Posted in Programming | Tagged , | Leave a comment

Locating and running msbuild from the command line in Visual Studio 2019

Yesterday I installed Visual Studio 2019 Community Edition on my home machine. I went straight up from 2015 to 2019, and lo and behold, my build script, for my sample app that contains all my experimental code, no longer works. This is because there is no longer an environment variable pointing to the install location, which in my case sits in G:\Program Files…

My build script, which worked for years, always looked something like this:

@ECHO OFF
CALL "%VS140COMNTOOLS%\vsvars32.bat"
msbuild RomyView.sln /t:Rebuild /m /nologo /v:m /p:Configuration=Release

Of course this now results in the old familiar error: ‘msbuild’ is not recognized as an internal or external command, operable program or batch file.

So off to Stack Overflow I go… I found some rather useless comments on it, like one twat who wrote, “Just run the developer command prompt from the start menu”. Like, seriously? When all I want to do is double-click a batch file in my directory that’s worked for fucking years?

This one has the answer, which I have modified slightly for my own script. It turns out that although the install path may vary, the Visual Studio installer path is fixed, and it has a utility called vswhere that you can query for the Visual Studio path. My script now looks like this:

@ECHO OFF

set INSTALLPATH=

if exist "%programfiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" (
  for /F "tokens=* USEBACKQ" %%F in (`"%programfiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -version 16.0 -property installationPath`) do set INSTALLPATH=%%F
)

call "%INSTALLPATH%\Common7\Tools\VsDevCmd.bat"
msbuild RomyView.sln /t:Rebuild /m /nologo /v:m /p:Configuration=Release

I hope you find this or the relevant Stack Overflow Q & A before you get as annoyed as I did.

Posted in Programming | Tagged | Leave a comment

Easily draw semi-transparent text on an image in c#

I had a requirement to return an image with text on it, and it turns out this is really easy to do in c#.

My code is running in a WCF service so my input and outputs are base64-encoded strings, but if you don’t need that part, it should be easy enough to change this code as you need.

        private static string DrawTextOnImage(string inputImage)
        {
            string modifiedImage = string.Empty;

            using (var stream = new MemoryStream(Convert.FromBase64String(inputImage)))
            {
                using (Image image = Image.FromStream(stream))
                {
                    using (Graphics graphics = Graphics.FromImage(image))
                    {
                        graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
                        graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;

                        using (Font font = new Font("Arial", 24, FontStyle.Bold))
                        {
                            string text = "Copyright © 2019";

                            // Measure string to figure out the width needed.
                            SizeF stringSize = graphics.MeasureString(text, font);

                            /* Draw twice, first in transparent black and then 
                             * transparent white, so we have a shadow effect. */
                            using (SolidBrush shadowBrush = new SolidBrush(Color.FromArgb(100, 0, 0, 0)),
                                textBrush = new SolidBrush(Color.FromArgb(100, 255, 255, 255)))
                            {
                                float x = (image.Width - stringSize.Width) / 2F;
                                float y = image.Height / 2F;

                                graphics.DrawString(text, font, shadowBrush, new PointF(x + 1, y + 1));
                                graphics.DrawString(text, font, textBrush, new PointF(x, y));
                            }
                        }
                    }

                    // Save image to file for testing
                    image.Save(@"C:\Temp\Test.jpg", ImageFormat.Jpeg);

                    // Convert the image back to a base64 encoded string
                    using (MemoryStream m = new MemoryStream())
                    {
                        image.Save(m, ImageFormat.Jpeg);
                        modifiedImage = Convert.ToBase64String(m.ToArray());
                    }
                }
            }
            return modifiedImage;
        }
Posted in Programming | Tagged , | Leave a comment

Subclassing Exception to log some contextual information

Today I arrived at work to find some truly wonderful emails in my inbox, containing the informative error:

String or binary data would be truncated.

So somewhere in a public method, one of the members of a structure in scope was too big for the database field we tried to save it to, and something somewhere was unable to save. Easy to fix if I know what method and what field it was, but I know nothing of the sort. Now what?

The error handlers in this application simply email the error message to everyone in the team. I needed a quick and dirty way to extend them such that they logged whatever relevant contextual information was available in the scope of the method that broke.

I decided to subclass Exception, adding a collection of objects to my custom exception, then have a LogError method that returns a string, on my derived class, so that I can simply call it in the methods that handle exceptions and send emails. It worked out quite nicely.

Unlike last time, I can share this code, because this time it is all downloaded from StackOverflow. I used the code from…

What this code does is, it logs every error message from the topmost Exception and every inner Exception, and then lists all of the instances added to the contextInfo parameter by class name, with the name and value of each public property of those instances on a new line. This was more than enough to tell me exactly what the issue was.

Here is the code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SampleWcfService
{
    internal static class ExceptionExtensions
    {
        public static IEnumerable<TSource> FromHierarchy<TSource>(this TSource source,
            Func<TSource, TSource> nextItem, Func<TSource, bool> canContinue)
        {
            for (var current = source; canContinue(current); current = nextItem(current))
            {
                yield return current;
            }
        }

        public static IEnumerable<TSource> FromHierarchy<TSource>(this TSource source,
            Func<TSource, TSource> nextItem) where TSource : class
        {
            return FromHierarchy(source, nextItem, s => s != null);
        }

        public static string GetaAllMessages(this Exception exception)
        {
            var messages = exception.FromHierarchy(ex => ex.InnerException)
                .Select(ex => ex.Message);
            return String.Join(Environment.NewLine, messages);
        }
    }

    public class SampleServiceException : Exception
    {
        private List<object> contextInfo = new List<object>();

        public SampleServiceException(string message, Exception innerException, List<object> contextInfo)
            : base(message, innerException)
        {
            this.contextInfo = contextInfo;
        }

        public string LogError()
        {
            StringBuilder builder = new StringBuilder();

            builder.AppendLine("Exception: " + this.GetaAllMessages());
            builder.AppendLine();

            if (contextInfo.Count > 0)
            {
                foreach (var obj in contextInfo)
                {
                    builder.AppendLine(obj.GetType().Name + ":");

                    foreach (var prop in obj.GetType().GetProperties())
                    {
                        builder.AppendLine(prop.Name + " = " + prop.GetValue(obj, null));
                    }
                    builder.AppendLine();
                }
            }

            return builder.ToString();
        }
    }
}

And here is an example of its usage. For brevity, I have omitted error checking and null checking. In practice it may be better to, for example, check that everything you pass to the context info collection is valid, and if you have collections whose properties you need to see, iterate them and pass the members you need…

        public Response ExampleMethod(Request request)
        {
            Response response = new Response();

            try
            {
                try
                {
                    // Do something here that populates response...
                }
                catch (Exception ex)
                {
                    List<object> contextInfo = new List<object>();

                    contextInfo.Add(request);
                    contextInfo.Add(response);

                    throw new SampleServiceException("Unexpected error in ExampleMethod.", ex, contextInfo);
                }
            }
            catch (SampleServiceException sex)
            {
                var errorBody = sex.LogError();

                // Code to notify here, using email or whatever...
            }

            return response;
        }
Posted in Programming | Tagged , | Leave a comment

Sorry I took that last post down…

This past Monday I published a post with a helper class for relaying HTTP POST requests from one WCF service to another, detailing how to create a relay service in c#. Then I took it down right away. Sorry about that…

Here’s what happened: I wrote the code to solve a problem at work on the Friday before that. It took a couple of hours and the code was written inside my WCF service implementation. Then on the Sunday when I wrote the blog post, I refactored that code into a generic helper class, making it different to my code at work. The helper class changed the WCF service such that each endpoint’s code became a one line call to the helper, which was why I saw value in sharing it. Then I scheduled the blog post for 5PM (South African time) on Monday.

However, on Monday I refactored my work code to use that helper class, and published the service. The end result was that apart from the namespace and the service itself (because the code is meant to help create a relay out of any such WCF service), the code on my blog was identical to the code of a service running in production. Even though it did not reveal anything of my employer’s business, that’s not the point. The point is, since I used the code for a production service, it is not my intellectual property.

Maybe some time in future, time permitting, I can extend the class into a framework that handles other Http verbs, and serializes JSON as well as XML. And then I can republish it here. But until then, that is not code I can share.

Posted in General, Work | Leave a comment

The most annoying kind of error message?

What is the most annoying kind of error message, from a technical perspective? Or from the perspective of an error that was reported from an end user where you are a developer or in technical support?

I’ve changed my mind over the years. It used to be “the operation completed successfully” or “catastrophic failure” but nowadays I rate those as amusing.

There’s always the dreaded NullReferenceException:

3264.4

But as a programmer, I see that more as an embarrassing error. If someone reports one of these to the company I work for, it probably means a sloppy programming error. It also happens to be the easiest error to get, when one is being sloppy. So it can be annoying. But it isn’t the worst.

Actually I have two errors that annoy the shit out of me. I can’t say which is worse, so I’ll describe both…

The “error in someone else’s code incorrectly reported to us” error

Sometimes we receive error reports from a client liaison, someone who deals with clients who integrate with our software.

To give you a hypothetical example:

  1. I wrote a piece of software, a service that takes some info about doodads that people claim to own, and then queries the World Doodad Consortium to check if the doodads are valid and really belong to those people.
  2. People then use my service, by calling it programmatically inside their own software.
  3. Then tech support sends me an email that says the doodad service is not working. The email contains a screenshot of software I have never seen, which is displaying an error message, “the doodad service is offline”.
  4. Actually number three is the paraphrased version of the error message. To see it, one must scroll through thirty pages of email messages, several replies, angry rants demanding that this should be sorted out immediately, and so on, from three weeks ago… all of which only reach me now.

The kicker: My software never, under any circumstances, ever returns an error with the text “the doodad service is offline”. Hence I do not have any way of knowing what the error means.

It might mean:

  • The World Doodad Consortium returned invalid results because there was an issue there. My service reported this as per the software manual that their implementing developer should have read.
  • Everything is working perfectly but the integrator’s code failed to parse the response and their code fell into a catch-all that displayed an irrelevant general error, because their code assumes that if anything goes wrong in this block of code, it means the doodad service must have failed.
  • Their code never called us at all, but put the request into a queue to send later, and then because of some issue, maybe they set up a new client installation and didn’t configure it correctly, the queue never gets emptied.
  • In rare circumstances, perhaps 0.1% of all errors reported, the service genuinely failed, by returning something unexpected, which crashed the integrating client software.

Of course I have to take every error report seriously, and find out at the very least if:

  1. The request actually reached the service.
  2. If it did, whether or not we returned a valid response to the client.

Of course, more than nine times out of ten the request never reached the service, and the error actually means something went wrong in their software. And in the rare cases where the requests can be found on this end, more than nine times out of ten it turns out that we returned a perfectly valid response. Hence these errors are annoying.

Of course, there are some factors to keep in mind: The person who reported the error is a client liaison. His or her job is to keep the customer happy. He or she may not have enough context to determine the error is probably on the client system. Furthermore, the person who reported the error from the client side may be a manager, who also has no context on how or why the error happened. It would be extremely unprofessional not to give all those involved the benefit of doubt and treat their errors as important. Failing to attend to them and take them seriously may involve losing an important client.

The “system is behaving as designed” error

I’ll illustrate this kind of error with a hypothetical email… Note that the subject and message body contradict one another.

Subject: User Jack Ass can not access the High Security Risk page!!!

Message: I created user Jack Ass on the Doodad system, and gave him access to the High Security Risk page.

But Jack can access the High Security Risk Page! He should not be allowed to access that page! Please investigate and advise.

I don’t think this kind of error needs any further comment.

Posted in General, Programming, Work | Tagged | Leave a comment

My poor man’s factory pattern in c# – one way of instantiating all derived classes of an abstract base class

Apologies for deleting yesterday’s post. It used my actual code and the syntax highlighting was broken in my Open Live Writer. This one uses simplified code that I wrote in an example solution, and thus does not involve me sharing code that I do not own, and I managed to get the Open Live Writer plugin working again.

In a recent post I mentioned my “poor man’s factory pattern”. I’m sharing it here in case anyone needs something similar.

As I understand it, a factory pattern, typically implemented as either an abstract factory or a factory method, is a pattern where your classes decide which class to instantiate. So it’s a pattern all about instantiating objects, typically related objects that are all registered products used by association by some sort of owner, or factory.

Well, I wanted something similar, but also wanted to keep it simple.

I have a control that exposes a dialog, which is used by a user to capture data via a USB device. I have three different such devices, each with a completely different API, and so my project uses all of them via their SDK’s. You don’t need to know the details. The point is, a user might have any one of those three devices plugged in. My capture dialog needs to use whichever one is plugged in (or the first one that works if the user plugs more than one in, but they are not expected to do that).

So what I did was:

  1. Create a base class, an abstract class called Device.
  2. One of its properties is called DeviceFound, which returns a bool.
  3. Each derived type only sets that true if the device is plugged in and working. (Again, how they do that is implementation-specific details you don’t need to know.)

The control that uses the device contains a single property of the base type. So all that needs to happen, in the constructor of the control, is this:

  1. Iterate all the types in my assembly that are subtypes of Device, are not abstract, and have a parameterless public constructor.
  2. Create each one and check if DeviceFound is true.
  3. If that property is true, use that device; otherwise try the next one.
  4. For clarity, “use that device” means assign the working subtype found to the control’s single Device property, and use it when the user scans an image. If no working device is found, although that’s outside of the scope of this post, I simply display an error. (That code is not shown here.)

That is, I have three subtypes, but only want to use one of them; the first one I find to be valid. Unfortunately I must instantiate each in turn to check if it is the right one. It sounds expensive but in practice is really very fast.

Here is the code:

/* Get all subtypes of Device that are not abstract. This won't work with interfaces
 * but that's OK because we defined the base class as the Device abstract class. */
var scanners = from domainAssembly in AppDomain.CurrentDomain.GetAssemblies()
               from assemblyType in domainAssembly.GetTypes()
               where assemblyType.IsClass && !assemblyType.IsAbstract && assemblyType.IsSubclassOf(typeof(Device))
               select assemblyType;

foreach (Type type in scanners)
{
    try
    {
        var constructor = (type).GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, Type.EmptyTypes, null);
        if (constructor != null)
        {
            var scanner = constructor.Invoke(null) as Device;
            if (scanner.DeviceFound)
            {
                this.scanner = scanner;
                break;
            }
        }
    }
    catch
    {
        errorMessages.Add(string.Format("Unable to instantiate {0} scanner.", type.ToString()));
    }
}

The technique used here to find all subtypes of a type is based on the most popular answer on this StackOverflow question, except I optimized it somewhat to short circuit out anything found that isn’t a concrete class, and it uses Type.IsSubclassOf rather than Type.IsAssignableFrom, because I did not want the base class in my list, and it does not need to find interfaces.

If you want to do something similar, you can use IsAssignableFrom for interfaces instead. It could be further optimized to omit the check for a null constructor but I like it the way it is. I also need my control to behave and work even if, for example, two of the three device drivers have not been installed, in which case the code must still use the device that works (and swallow the exceptions for the other devices). Also, I do not take their last step and copy the enumeration to an array, for brevity. This is only an example. In production code, especially if your list of derived types is not going to change, you might want to cache the results in a List<Type> or array, and not enumerate every time your code is called.

And for interest sake, this is what my abstract class looks like:

using System.Collections.Generic;
using System.Linq;

namespace Example
{
    public abstract class Device
    {
        #region Properties

        public abstract bool DeviceFound { get; }

        public virtual IEnumerable<string> ErrorMessages
        {
            get
            {
                return Enumerable.Empty<string>();
            }
        }

        public abstract string Wsq64BitImage { get; }

        #endregion Properties

        #region Methods

        #region Public Methods

        public abstract bool ScanImage();

        #endregion Public Methods

        #endregion Methods
    }
}
Posted in Programming | Tagged , , | Leave a comment

A straightforward implementation of ISynchronizeInvoke in c#

I ran into an unusual problem the other day…

I’m developing a control that under the hood uses one of three implemented fingerprint scanner API’s in c#. My code is not complicated… just use the relevant DLLs and have their dependencies in my project directory. I have a base scanner class, a single property of that type, and one of the abstract properties of derived classes (ScannerFound) tells the dialog which control it found. It’s like a poor man’s factory pattern – declare a single property of the base type and then iterate and create each derived type found in the assembly; if ScannerFound == true we are done; else try the next one.

But for some reason I cannot fathom, one of the scanners API’s expects a class that implements ISynchronizeInvoke to be passed to its constructor. (Not cool. My Scanner base class is just a plain abstract class and is completely decoupled from the form or control that contains it. I am not passing the Form – which happens to implement ISynchronizeInvoke, to the class, so this means one derived class needs to contain a reference to a class that implements the interface, or implement it itself and in the process add a bunch of unnecessary bullshit to my nice clean class structure.) It’s not an interface I would ever think to implement myself. Why they did this is beyond me because I’d really rather just use an async method or my own thread (if necessary) and don’t need to be tied to the way a Winforms control calls a delegate. In my case, this implementation is only necessary to pass to a constructor, and is used internally in their API by a method I don’t even call. I could probably have used a class that “implements” it by throwing NotImplementedException everywhere but that would just feel wrong.

Anyway, this example I found looks good and I used it as is. To be honest, I’d probably have implemented it myself just for fun if I really cared about it and my code actually used it for more than passing a dummy type, but oh well, I feel almost guilty about not writing here on this blog with the silly header image any more.

Posted in Programming | Tagged , | Leave a comment

Updating a table using a join in SQL Server (UPDATE FROM)

Yes, this is most probably trivial to everybody except me. For whatever reason, I simply don’t remember this syntax. Every time I have to do this, I Google it and usually end up on this solution. You can go there if you like – I’ve added nothing of value other than putting it in terms of the structure of the tables I’m working with (although I’ve changed field and table names). This way, if I read this post myself a few times, maybe I’ll remember this shit.

Let’s say you have an Widget table and a Customer table, where Widget has a CustomerID foreign key. Both have a foreign key to a Contact table, which was recently added. So I changed the code that generates (inserts) widgets to always use the configured contact on the customer, if there is one as it is a nullable foreign key. (i.e. FK added using WITH NOCHECK ADD CONSTRAINT.) That’s great, but now I need to update the existing widgets to have the contact ID as well.

(The business rule I have is simply that widgets must have the default customer contact when created. If you change the contact on a customer, it does not get applied retroactively on widgets. However, after adding these new fields, I do need to fix the existing widgets. I could leave them null but it makes sense to update all of them to use the correct customer contact as set up today.)

The solution is to use the UPDATE FROM syntax, which I so happen to always forget. I don’t know why that is. Maybe it’s old age approaching; maybe it is the price I pay for my years of drug abuse. (I’m four years clean at the time of writing this, by the way. Meth is a helluva drug.) I don’t know, but it seems that my brain cells dedicated to remembering SQL are unreliable. It’s easy though, and for the tables I have described, the update query would look like so:

UPDATE w
SET 
    w.ContactID = c.ContactID
FROM 
    Widget w
    JOIN Customer c ON c.ID = w.CustomerID
WHERE 
    w.ContactID IS NULL AND c.ContactID IS NOT NULL
Posted in Database, SQL | Tagged , , | Leave a comment

Split a List<T> into smaller lists of size N.

Well, my stupid header (created years ago when I was still a tweaker) promises an occasional descent into madness, so here you go… use this code at your own risk.

Every so often I find that I have a generic list of something or other, and it makes sense to split the list into smaller lists and then process those “batches” in parallel. This was never a generic method or an extension method, but was a piece of code I copied and pasted all over the show. I made it an extension method now right before checking how other people have done it.

Anyway, if you don’t want to risk running code that I wrote while high on crystal meth a few years ago, you might want to take an answer from this StackOverflow page.

But if you’re feeling adventurous, here’s my code:

using System.Collections.Generic;
using System.Linq;

namespace Demo
{
    public static class Example
    {
        public static List<List<T>> SplitIntoBatchesOfSize<T>(this List<T> list, int count)
        {
            var result = new List<List<T>>();

            if (list.Count > count)
            {
                var listQuery = list.GroupBy(num => list.IndexOf(num) / count);

                for (int i = 0; i < listQuery.Count(); i++)
                {
                    var batch = listQuery.ElementAt(i).ToList();
                    result.Add(batch);
                }
            }
            else result.Add(list);

            return result;
        }
    }
}

Come to think of it, such methods are unnecessary. GroupBy is good enough. You can just use that and then iterate the batches, calling ToList<> or ToArray<> if necessary. For example: (from a custom Task scheduler I shared a long time ago)…

if (taskList.Count > 0)
{
    var batches = taskList.GroupBy(
        task => taskList.IndexOf(task) / maxDegreeOfParallelism);

    foreach (var batch in batches)
        ParallelInvoke(t => base.TryExecuteTask(t), batch.ToArray());
}
Posted in General, Work | Tagged , | Leave a comment

Emails I hate receiving

I often wonder if I am the only one who hates receiving emails like this… Names have been omitted.

Subject: Problem with [product brand name].

Hi asshole

I trust this email finds you well.

Please assist with the below. OR…
Kindly find the attached.

Kind regards
Twat

[Nested thread of somewhere around seventy emails with correspondence between sender and various others. Somewhere down there is the context they’re thinking of (or maybe not), and somewhere else there may be clarification on what the brand name, that refers to multiple software projects in our system, might refer to. Or maybe not at all.]

Let’s analyse their email, shall we?

  1. Subject does not contain relevant context. The brand name could refer to any one of several products within the software that we have developed. The client’s client might thus be one of several different kinds of users, since site users and integrating users are different, even within the same product. The subject literally tells me nothing. They could just as well have left it blank.
  2. “I trust this email finds you well.” What does that even mean? It’s not just verbose, since at least verbosity still tries to convey something pertinent, albeit in too many words. This literally adds nothing but annoying false civility.
  3. “Please assist with the below.” Really? So not only does the subject tell me nothing, but the message content doesn’t convey anything either, other than telling me I need to scroll down and try to make sense of their previous correspondence backwards. Even if I skip to the bottom, all I find is something where the context is assumed because it’s from a client they have a relationship with who uses one product (and they know what it is but neglect to mention it in the email), who wrote some generic message about the system not working, or referred to an error that they have had before.
  4. “Kindly find the attached.” Ooh, it’s a spread sheet. Gee, thanks. It’s very nice!
  5. “Kind regards”. What’s the difference between “kind regards” and “regards”? Again, “kind” is redundant. “Regards” would suffice perfectly.

Thank goodness I am normally a CC recipient rather than the primary one. But still, it wasn’t always that way. It’s difficult to figure out how to reply to those kinds of emails… When I used to receive them directly, I’d always reply with a generic response asking for context, but they didn’t learn anything, and later I’d get another useless email from the same person.

Of course, you know that in half an hour or so, you’ll get another email with…

Subject: RE: Problem with [product brand name].

Hi asshole,

Any feedback on the below?

Kind regards
Twat

Posted in General, Work | Leave a comment

How to initialize an enum from a string or int value in c#

I didn’t know you could do this, but I tried and it seems fine so it’s worth sharing.

I’m working in a large code base, where my input might come from a web form, or an imported file, or even be passed in to a WCF service. I didn’t write this originally, but I need to bolster the validation of one of the input fields, which involves doing different validations depending on the type of the thing passed in. By “type”, I refer not to the .NET type but the kind of thing it is. For example, if it were an account it could be a cheque or savings account; if it were an ID it could be a local or international passport or a local ID document or birth certificate number, and so on. We already have the type, but it wasn’t being used.

My problem is, the type is sometimes an integer (hard-coded by my predecessor – for example hard-coded in the markup of a combobox control and it is the ID of the thing type) and other times a string. While I could change all the code everywhere to look up the ID in the database, that would involve a lot of work, such as changing the code that hard-codes values to bind to the database properly… and so on.

What I need then, is to be able to initialize an enum in all of those places, using either:

  1. A string literal.
  2. An integer that represents the value of the enum.

It turns out to be easy. For an integer, I can cast directly to the enum type (in other words, an explicit conversion), and for a string, I use the Enum.Parse method.

Here is a simplified code example that demonstrates how easy it is. This code is only intended to demonstrate how easy the initialization of the enum from a correct value or string is. Note that in my real code, I am not blindly casting a user input field into an enum. I’m using the enum that represents a type of item, which I know to be valid, to validate a second field.

using System;

namespace Demo
{
    enum Thing : int
    {
        Nothing = 0,
        Gizmo = 1,
        Doodad = 2,
        Thingamajig = 3,
        Whatchamacallit = 4
    }

    public static class Example
    {
        public static bool ValidateStringThing(string thing)
        {
            Thing value = (Thing)Enum.Parse(typeof(Thing), thing);
            return ValidateThing(value);
        }

        public static bool ValidateIntThing(int thing)
        {
            Thing value = (Thing)thing;
            return ValidateThing(value);
        }

        private static bool ValidateThing(Thing thing)
        {
            switch (thing)
            {
                case Thing.Nothing:
                    return false;
                case Thing.Gizmo:
                case Thing.Doodad:
                case Thing.Thingamajig:
                case Thing.Whatchamacallit:
                    return true;
                default:
                    return false;
            }
        }
    }
}
Posted in Programming | Tagged , | Leave a comment

New methods not showing up in WCF Test Client? Are you using IIS Express and did you move your code to a new directory without deleting the old one?

I gave the answer to my problem of today in the title…

I work on multiple projects. But we also recently changed our source control provider from SVN to Git. If I haven’t touched a code-base for months, I don’t bother to clone the new repository until I need to work on it. That’s what happened today.

I cloned the repository for this particular solution, and started working on it. I added new code to a few projects, including one new WCF method. Tested everything else, then when I wanted to test the WCF service changes, two things went wrong:

  1. Pressing F5 launched my browser rather than the WCF Test Client.
  2. After I figured out how to fix the first issue, the newly added method was missing.

To fix the first issue, all I had to do was right-click the source file containing my service contract and choose Set As Start Page.

Then off to Google and StackOverflow, but I found nothing that solved my problem.

In my case, what happened was IIS Express was still pointing to the old contract, in the source folder that I used before switching to Git. I confirmed it by trying to move that directory and saw the usual Windows error message telling me that directory was in use.

To fix the issue, I had to manually edit the applicationhost.config file found in:
C:\Users\[My username]\Documents\IISExpress\config

Fixing it was just a matter of searching for my project name, and right away I saw the paths pointing to the wrong directory.

I hope this helps somebody who has a similar problem…

Posted in Programming | Tagged | Leave a comment

Sample code to send email via gmail in c# (updated)

I wrote a post recently with some code to send email via gmail in c#. The other day, I needed to update it to allow for multiple attachments. Rather than update the original post, I’ve written this new one… (The rest is copied and pasted, altered for the new code.)


For some time, I’ve had a block of code that I copied and pasted all over the place, to send emails via gmail. It didn’t add an attachment, but I needed to do so today. So rather than copy and paste it yet again, I’ve created a simple static class that can be reused.

Note that:

  1. I hard-code my gmail address (the username) and my password as constants. (Changed to invalid values here.) You’ll need to change them to something valid.
  2. When debugging, I don’t use gmail. I use an application called Papercut, and there is a similar one called smtp4dev I think… to mock a local SMTP server. It then “receives” the email.
  3. I don’t do anything if there is an exception when trying to send the email. That’s because the applications using this code typically send emails from their exception handlers when there are errors. You might want to do something different.
  4. I’m not going to explain how to enable SMTP on a gmail account. I trust that anybody using this can figure that out for themselves.

This is really simple code, and I consider it junior developer level. I’m just putting it here, to make it easier to reuse. Please don’t ask me any questions about it. It does the basic job I need it to do and that’s all.

I’ll paste an example that uses it below…

using System.Collections.Generic;
using System.Net.Mail;

namespace Example
{
    public static class EmailHandler
    {
        private const string UserName = "name@gmail.com";
        private const string Password = "YourPasswordHere";

        public static void SendEmail(IEnumerable<string> toAddresses, IEnumerable<string> copyAddresses, IEnumerable<string> bccAddresses, 
            string subject, string from, string body, IEnumerable<Attachment> attachments = null, bool isHtml = true)
        {
            using (MailMessage mailMessage = new MailMessage())
            {
                foreach (var to in toAddresses)
                    mailMessage.To.Add(to);

                foreach (var copy in copyAddresses)
                    mailMessage.CC.Add(copy);

                foreach (var copy in bccAddresses)
                    mailMessage.Bcc.Add(copy);

                mailMessage.Subject = subject;

                mailMessage.From = new MailAddress(from);
                mailMessage.Body = body;
                mailMessage.IsBodyHtml = isHtml;

                if (attachments != null)
                {
                    foreach (var attachment in attachments)
                        mailMessage.Attachments.Add(attachment);
                }

                try
                {
#if DEBUG
                    using (SmtpClient smtp = new SmtpClient("127.0.0.1"))
                    {
                        smtp.EnableSsl = false;
                        smtp.Port = 25;
                        smtp.Send(mailMessage);
                    }
#else
                            using (SmtpClient smtp = new SmtpClient("smtp.gmail.com", 587)
                            {
                                Credentials = new System.Net.NetworkCredential(UserName, Password),
                                EnableSsl = true
                            })
                            {
                                smtp.Send(mailMessage);
                            }
#endif
                }
                catch { }
            }
        }
    }
}

In my example that uses the above, I’m calling it from ASP.Net. The attachment I pass to it is a PDF rendered from an SSRS report, using the ReportViewer control. You shouldn’t really need this code, except for the part that initializes the attachment, because it may not be obvious how to do that. The attachment can be initialized via a file name or stream, and needs a MIME type. I use the constructor that takes a stream, a file name, and the content type name. Bizarrely there is also one that takes a content type itself and a stream, without a file name. That one is kind of shitty.

The update… My example code still uses an SSRS report that gets rendered to PDF, but the site also allows the user to upload files. I’m using DevExpress controls, and as each file is uploaded to the site, I put the contents in the session. The code to clear those variables, which is not shown, is called after sending or cancelling sending the email… Just so you can see what’s in the session, the handler for each uploaded file looks like this:

protected void ASPxUploadControl1_FileUploadComplete(object sender, DevExpress.Web.ASPxUploadControl.FileUploadCompleteEventArgs e)
{
    e.CallbackData = e.UploadedFile.FileName;

    Dictionary<string, byte[]> attachments = Session["Attachments"] == null ? new Dictionary<string, byte[]>() : (Dictionary<string, byte[]>)Session["Attachments"];

    attachments[e.UploadedFile.FileName] = e.UploadedFile.FileBytes;

    Session["Attachments"] = attachments;
}

Here is the updated code that sends the email. Note that the handling of attachments is a little messy. For each attachment, I use a Stream to initialize the attachment object, but all those streams need to remain in scope while the email is being sent. Then they are all closed afterwards. So I couldn’t use a using statement, as I normally would…

So this example looks more complicated than it actually is. Code to use the wrapper to send a simple email would be trivial. I trust that most developers, even junior ones, should be able to use my wrapper without reading the example code.

protected void cbSendEmail_Callback(object source, DevExpress.Web.ASPxCallback.CallbackEventArgs e)
{
    string[] toEmails = ((string)flEmail.GetNestedControlValueByFieldName("ToEmail")).Replace(";", ",").Split(',');

    if (toEmails.Length > 0)
    {
        string subject = (string)flEmail.GetNestedControlValueByFieldName("Subject");

        string body = htmlMessage.Html;

        int id = (int)Session["InvoiceID"];
        ReportParameter p = new ReportParameter("ClientInvoiceID", id.ToString());

        reportViewer.ServerReport.SetParameters(new ReportParameter[] { p });
        byte[] data = reportViewer.ServerReport.Render("pdf");

        string invoiceNumber = (string)queryHandler.OpenQuery("SELECT InvoiceNumber FROM ClientInvoice WHERE ID = @ID", new Dictionary<string, object> { { "@ID", id } }).Rows[0]["InvoiceNumber"];

        Dictionary<string, byte[]> attachments = Session["Attachments"] == null ? new Dictionary<string, byte[]>() : (Dictionary<string, byte[]>)Session["Attachments"];

        using (MemoryStream stream = new MemoryStream(data))
        {
            System.Net.Mail.Attachment attachment = new System.Net.Mail.Attachment(stream, string.Format("Invoice {0}.pdf", invoiceNumber), System.Net.Mime.MediaTypeNames.Application.Pdf);

            var emailAttachments = new List<System.Net.Mail.Attachment>();

            emailAttachments.Add(attachment);

            List<Stream> streams = new List<Stream>();

            try
            {
                foreach (var kvp in attachments)
                {
                    streams.Add(new MemoryStream(kvp.Value));
                    emailAttachments.Add(new System.Net.Mail.Attachment(streams[streams.Count - 1], kvp.Key));
                }

                EmailHandler.SendEmail(toEmails, Enumerable.Empty<string>(), new string[] { "redacted@someplace.co.za" }, subject, "redacted@someplace.co.za", body, emailAttachments);
            }
            finally
            {
                foreach (var memStream in streams)
                    stream.Close();
            }
        }
    }
}
Posted in Programming | Tagged , | Leave a comment

Sample code to send email via gmail in c#

For some time, I’ve had a block of code that I copied and pasted all over the place, to send emails via gmail. It didn’t add an attachment, but I needed to do so today. So rather than copy and paste it yet again, I’ve created a simple static class that can be reused.

Note that:

  1. I hard-code my gmail address (the username) and my password as constants. (Changed to invalid values here.) You’ll need to change them to something valid.
  2. When debugging, I don’t use gmail. I use an application called Papercut, and there is a similar one called smtp4dev I think… to mock a local SMTP server. It then “receives” the email.
  3. I don’t do anything if there is an exception when trying to send the email. That’s because the applications using this code typically send emails from their exception handlers when there are errors. You might want to do something different.
  4. I’m not going to explain how to enable SMTP on a gmail account. I trust that anybody using this can figure that out for themselves.

This is really simple code, and I consider it junior developer level. I’m just putting it here, to make it easier to reuse. Please don’t ask me any questions about it. It does the basic job I need it to do and that’s all.

I’ll paste an example that uses it below…

using System.Collections.Generic;
using System.Net.Mail;

namespace Example
{
    public static class EmailHandler
    {
        private const string UserName = "name@gmail.com";
        private const string Password = "password123%";

        public static void SendEmail(IEnumerable<string> toAddresses, IEnumerable<string> copyAddresses, string subject, string from, string body, System.Net.Mail.Attachment attachment = null, bool isHtml = true)
        {
            using (MailMessage mailMessage = new MailMessage())
            {
                foreach (var to in toAddresses)
                    mailMessage.To.Add(to);

                foreach (var copy in copyAddresses)
                    mailMessage.CC.Add(copy);

                mailMessage.Subject = subject;

                mailMessage.From = new MailAddress(from);
                mailMessage.Body = body;
                mailMessage.IsBodyHtml = isHtml;

                if (attachment != null)
                    mailMessage.Attachments.Add(attachment);

                try
                {
#if DEBUG
                    using (SmtpClient smtp = new SmtpClient("127.0.0.1"))
                    {
                        smtp.EnableSsl = false;
                        smtp.Port = 25;
                        smtp.Send(mailMessage);
                    }
#else
                            using (SmtpClient smtp = new SmtpClient("smtp.gmail.com", 587)
                            {
                                Credentials = new System.Net.NetworkCredential(UserName, Password),
                                EnableSsl = true
                            })
                            {
                                smtp.Send(mailMessage);
                            }
#endif
                }
                catch { }
            }
        }
    }
}

In my example that uses the above, I’m calling it from ASP.Net. The attachment I pass to it is a PDF rendered from an SSRS report, using the ReportViewer control. You shouldn’t really need this code, except for the part that initializes the attachment, because it may not be obvious how to do that. The attachment can be initialized via a file name or stream, and needs a MIME type. I use the constructor that takes a stream, a file name, and the content type name. Bizarrely there is also one that takes a content type itself and a stream, without a file name. That one is kind of shitty.

        protected void cbSendEmail_Callback(object source, DevExpress.Web.ASPxCallback.CallbackEventArgs e)
        {
            string[] toEmails = ((string)flEmail.GetNestedControlValueByFieldName("ToEmail")).Replace(";", ",").Split(',');

            if (toEmails.Length > 0)
            {
                string subject = (string)flEmail.GetNestedControlValueByFieldName("Subject");

                string body = htmlMessage.Html;

                int id = (int)Session["InvoiceID"];
                ReportParameter p = new ReportParameter("InvoiceID", id.ToString());

                reportViewer.ServerReport.SetParameters(new ReportParameter[] { p });
                byte[] data = reportViewer.ServerReport.Render("pdf");

                using (MemoryStream stream = new MemoryStream(data))
                {
                    System.Net.Mail.Attachment attachment = new System.Net.Mail.Attachment(stream, "Invoice.pdf", System.Net.Mime.MediaTypeNames.Application.Pdf);
                    EmailHandler.SendEmail(toEmails, Enumerable.Empty<string>(), subject, "redacted@redacted.co.za", body, attachment);
                }
            }
        }
Posted in Programming | Tagged , | Leave a comment

Beware of calling Contains in the Where predicate on IQueryable<T> in LINQ to SQL if your collection has more than 2100 items

Recently I ran into a problem I’d never seen before – the 2100 parameter limit in the SQL that’s generated when you call Contains in the IQueryable<T>.Where predicate in LINQ to SQL. To make matters worse, this happened when filtering data in the Selecting event of a DevExpress LinqServerModeDataSource… which suppresses the error, resulting in an empty result set. (I only saw the error after stepping over the call, and then inspecting the result set in the debugger.)

You don’t hit the problem when your collection contains exactly 2100 items… it happens with less items than that. The 2100 number refers to a limit that’s hit in the SQL code that’s generated. So even as many as 2000 items in your collection will probably trigger the issue. I haven’t experimented or dug deep enough to figure out exactly what the limit is. But it doesn’t matter. What matters is that calling Contains when you don’t know how many items are in your collection, is asking for trouble.

The solution to this is simple – it was finding the problem that took so long. If I have a collection that contains thousands of items, then I split it up into smaller collections, filtering the data each time and adding the result sets to a List<T>, which I then convert back to an IQueryable<T> at the end.

The code below is an example selecting event for the data source. I’ve put that rather than only the relevant code, so that it’s a better example of how one can filter using such an event. It’s using a strongly typed Linq to SQL class for a view. I then have an ASPxGridView control that’s bound to the LinqServerModeDataSource.

You can ignore the first bit… which gets the lines from a memo in the drop down edit window of a drop down edit control, and puts any valid ones into a List<long>. The idea here is that users can filter the data by pasting in a column of account numbers they copied from Excel. Just in case they have bad data, I then ignore anything that isn’t numeric. (Of course this introduced the problem. Allowing users to paste in an unlimited number of rows was probably a bad idea, although in theory it solved a problem they had. But until this point, we never needed to filter a list of many thousands of items using Contains.)

Also, my way of splitting the list into smaller lists is to use IEnumerable<T>GroupBy with index/count – you could achieve the same thing with less code, using Skip and Take in a loop. I like writing it this way because I find the resulting code easier to read.

Note also that the unfortunate part of this is it will put the whole data set in memory. For that reason, I don’t use this approach if there are less than 1000 items in the list, and in my real code (this one is simplified and has the view name changed), this is the last step of filtering the data.

Aside: The closest I came to finding a solution online was this question on StackOverflow, which doesn’t have an accepted answer, but ironically the top one that I tried moved the call to Contains such that it actually threw the Exception that had been suppressed all the time in my code. Hopefully what comes across in this post is not so much exactly how I implemented a solution for this problem, but that the solution is to split the call to Contains between smaller collections, add all the results to a List<T>, and convert back to the IQueryable<T> at the end. If you understand the principle, you don’t even need to use my code…

Edit: The important code to take note of is really from the block comment downwards. You should be able to see all of it when dragging the scrollbar around at the bottom of this code block. That block comment got its indenting a little messed up when I unindented the code and copied it here… (And since this code was copy-pasted to a temporary class that I deleted, it’s too much trouble to fix on here. Sorry.)

protected void dsFutureDatedTransactions_Selecting(object sender, DevExpress.Data.Linq.LinqServerModeDataSourceSelectEventArgs e)
{
    FutureDatedTransactionsDataContext Context = new FutureDatedTransactionsDataContext();
    IQueryable<FutureDatedTransactions> Data = Context.FutureDatedTransactionss;

    // Account numbers
    ASPxMemo memo = (ASPxMemo)ddeAccountNumbers.FindControl("memoAccountNumbers");

    if (!string.IsNullOrEmpty(memo.Text))
    {
        List<long> accountNumbers = new List<long>();

        string[] numbers = memo.Text.Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);

        foreach (string accountText in numbers)
        {
            long accountNumber = 0;

            if (long.TryParse(accountText, out accountNumber))
            {
                accountNumbers.Add(accountNumber);
            }
        }

        if (accountNumbers.Count > 0)
        {
            /* If there are too many elements in the list, the call to Contains will suppress an Exception from the
                * underlying SQL which cannot contain more than 2100 parameters. (Known issue in LinqToSQL.) Then we get 
                * an EMPTY grid with no error. To work around this, using 1000 to be safe, if there more than 1000 items, 
                * break it into batches, and call Contains on each batch of 1000, add the results to a List<T>, then 
                * convert back to IQueryable<T> at the end. */
            if (accountNumbers.Count <= 1000)
            {
                Data = Data.Where(a => accountNumbers.Contains(a.AccountNumber));
            }
            else
            {
                var accountQuery = accountNumbers.GroupBy(num => accountNumbers.IndexOf(num) / 1000);

                List<FutureDatedTransactions> dataList = new List<FutureDatedTransactions>();

                for (int i = 0; i < accountQuery.Count(); i++)
                {
                    var batch = accountQuery.ElementAt(i).ToList();

                    var subSet = Data.Where(a => batch.Contains(a.AccountNumber)).ToList();
                    dataList.AddRange(subSet);
                }

                Data = dataList.AsQueryable<FutureDatedTransactions>();
            }
        }
    }

    e.KeyExpression = "ID";
    e.QueryableSource = Data;
}
Posted in Programming | Tagged , | Leave a comment

How to load a cursor from embedded resources in C#

Last night, being the last night my son was staying with his cousins, I was bored and spent some time looking at some old code. I don’t know if anyone else still uses Windows Forms applications, but in case you do, I’m sharing this…

Via some code that I found and then modified, my image viewer, when viewing an image that’s larger than the viewable area, allows you to use the mouse to drag around and pan to different parts of the image. When doing this, the program changes the mouse cursor, to one of two cursor images, one for a hand that’s up and one for a hand that’s down.

But as it so often happened to me when building that application, dot Net has no way of loading a cursor from the application’s embedded resources. It can load normal images, but not CUR files. That shit just isn’t implemented. So I had to do so myself, and it took a while to figure out because there are several different, confusing API’s that work with cursors, and the MSDN documentation is confusing.

Here’s a screenshot what it looks like when the hand down cursor is displayed. (Image viewer code not included here. This is just about the code to load the cursor.)

viewer

And here’s the code that loads the cursor, which is saved as a file resource, from the applications resources… (The plugin I have used for ages, that shows syntax highlighting in the code, doesn’t seem to work properly anymore. Also the code is wrapped on the blog, but you can copy it to a file.)

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

namespace Romy.Core
{
    public static class CursorResourceLoader
    {
        #region Methods

        public static Cursor LoadEmbeddedCursor(byte[] cursorResource, int imageIndex = 0)
        {
            var resourceHandle = GCHandle.Alloc(cursorResource, GCHandleType.Pinned);
            var iconImage = IntPtr.Zero;
            var cursorHandle = IntPtr.Zero;

            try
            {
                var header = (IconHeader)Marshal.PtrToStructure(resourceHandle.AddrOfPinnedObject(), typeof(IconHeader));

                if (imageIndex >= header.count)
                    throw new ArgumentOutOfRangeException("imageIndex");

                var iconInfoPtr = resourceHandle.AddrOfPinnedObject() + Marshal.SizeOf(typeof(IconHeader)) + imageIndex * Marshal.SizeOf(typeof(IconInfo));
                var info = (IconInfo)Marshal.PtrToStructure(iconInfoPtr, typeof(IconInfo));

                iconImage = Marshal.AllocHGlobal(info.size + 4);
                Marshal.WriteInt16(iconImage + 0, info.hotspot_x);
                Marshal.WriteInt16(iconImage + 2, info.hotspot_y);
                Marshal.Copy(cursorResource, info.offset, iconImage + 4, info.size);

                cursorHandle = NativeMethods.CreateIconFromResource(iconImage, info.size + 4, false, 0x30000);
                return new Cursor(cursorHandle);
            }
            finally
            {
                if (cursorHandle != IntPtr.Zero)
                    NativeMethods.DestroyIcon(cursorHandle);

                if (iconImage != IntPtr.Zero)
                    Marshal.FreeHGlobal(iconImage);

                if (resourceHandle.IsAllocated)
                    resourceHandle.Free();
            }
        }

        #endregion Methods

        #region Native Methods

        [SuppressUnmanagedCodeSecurity]
        static class NativeMethods
        {
            [DllImportAttribute("user32.dll", CharSet = CharSet.Unicode)]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool DestroyIcon(IntPtr hIcon);

            [DllImport("user32.dll", SetLastError = true)]
            public static extern IntPtr CreateIconFromResource(IntPtr pbIconBits, int dwResSize, bool fIcon, int dwVer);
        }

        #endregion Native Methods

        #region Native Structures

        [StructLayout(LayoutKind.Explicit, Pack = 1)]
        struct IconHeader
        {
            [FieldOffset(0)]
            public short reserved;

            [FieldOffset(2)]
            public short type;

            [FieldOffset(4)]
            public short count;
        }

        /// <summary>Union structure for icons and cursors.</summary>
        /// <remarks>For icons, field offset 4 is used for planes and field offset 6 for 
        /// bits-per-pixel, while for cursors field offset 4 is used for the x coordinate 
        /// of the hotspot, and field offset 6 is used for the y coordinate.</remarks>
        [StructLayout(LayoutKind.Explicit, Pack = 1)]
        struct IconInfo
        {
            [FieldOffset(0)]
            public byte width;

            [FieldOffset(1)]
            public byte height;

            [FieldOffset(2)]
            public byte colors;

            [FieldOffset(3)]
            public byte reserved;

            [FieldOffset(4)]
            public short planes;

            [FieldOffset(6)]
            public short bpp;

            [FieldOffset(4)]
            public short hotspot_x;

            [FieldOffset(6)]
            public short hotspot_y;

            [FieldOffset(8)]
            public int size;

            [FieldOffset(12)]
            public int offset;
        }

        #endregion Native Structures
    }
}

The code that uses it, in another class, is just this:

/// <summary>Loads the two hand cursors from project resources.</summary>
private static void InitializePanBoxCursors()
{
    if (handUpCursor == null && handDownCursor == null)
    {
        handUpCursor = CursorResourceLoader.LoadEmbeddedCursor(Properties.Resources.Hand_up);
        handDownCursor = CursorResourceLoader.LoadEmbeddedCursor(Properties.Resources.Hand_down);
    }
}

Note that those cursors were added to the project resources as file resources, with the names Hand_down and Hand_up. They are then accessible by name in code that Visual Studio generates. Those resources give you each cursor as a byte array, and my code wraps the Windows API CreateIconFromResource function.

Edit: Now I see there is a “Community Addition” at the bottom of that MSDN article linked immediately above, that tells you not to destroy the loaded icon using DestroyIcon. Ignore that annoying bullshit comment. Of course you must release the memory of the icon image. My code creates a new cursor from the loaded resource handle, and then releases the handle properly, and also releases the other memory it allocates. (A pinned handle of the unmanaged resource, unmanaged memory for the icon image which is used to create the cursor resource, and the Windows handle of the cursor itself.) One should always release such handles and any memory allocated explicitly. This is common sense. Honestly, it was better before MSDN allowed idiot programmers to make random “Community Addition” bullshit comments on their documentation. The comment complains about leaks when allocating thousands of cursors, while contradicting itself by telling you not to free the memory you allocate. Yes, that is a sure way to leak memory…

Posted in Programming | Tagged , , | 1 Comment

A pet hate and anti-pattern: A method returning a bool that can never return false, but can throw an Exception.

Recently I came across some code that, while syntactically correct, does something that always gets to me.

Let’s illustrate it with an example:

using System;

namespace Example
{
    public class AntiPatterns
    {
        public bool NeverReturnsFalse()
        {
            try
            {
                // Do something complicated
                // that can fail and throw.
            }
            catch (Exception ex)
            {
                // Code that logs the Exception.

                throw;
            }

            return true;
        }
    }
}

Full disclosure: I didn’t study programming or computer science, but slipped into this industry by accident. I’m a self taught programmer. However, one must use one’s common sense…

Methods that return a bool should return either true or false. (I can’t believe I had to write that.) They represent actions attempted which may succeed or fail. The above method can never return false. Its return value is hard-coded as true, so it will only ever return true unless an error occurs, in which case it will throw an Exception.

Why return a bool at all here? It makes no sense because the operation attempted, whatever it may be, can’t possibly return false (although it can fail). That means whoever consumes the method is probably going to write code, either in an else block or maybe a conditional statement, that checks the return value of this method. That is, whoever consumes this method is going to waste their time writing code that can never run, or check a condition that doesn’t need checking, because unless they have access to this code – which they won’t if it is a library – they must assume that a method returning a bool can either succeed or fail.

A less serious issue is that it can throw an Exception, but this is only less serious because everybody should always catch Exceptions anyway. However, what they need to do in their Exception handler is now ambiguous. Should I treat an Exception like false? Is it a recoverable error? Who knows? If I’m calling it from a thread that’s not critical to my application, I’m going to have to abort whatever that thread does gracefully and just report that it failed (and hope that it might work next time?). If I’m calling it from the one and only thread of a desktop application, I have no choice but to crash the application because I really have no idea what this error means and it is thus unsafe to continue. And that’s not cool.

Just don’t do that.

What common sense dictates you do instead:

  1. If you’re going to return a bool, it must be possible for it to be true or false. Otherwise don’t return a bool. Return void.
  2. If you’re writing a library that a third party may use, but won’t have your source code, don’t just rethrow any random Exception. Throw your own custom Exception if you must, but document that it can do so and what it means, possibly with the random one in the InnerException property.

Of course, if you do throw random Exceptions from a library, it’s probably not going to be used by anyone for long.

Posted in Programming | Tagged | Leave a comment

Beware of WCF POST service methods that receive JSON that contain DateTime values

I ran into a weird problem today… I have a third party that returns some data to our endpoint in JSON format.

I created my endpoint, posted JSON to it, and all seemed well. But when the third party posted it, the service returned Http 400, Bad Request. Turns out the third party was seeing this:

The server encountered an error processing the request. The exception message is ‘There was an error deserializing the object of type ReturnService.ResponseStructure. DateTime content ‘2016-10-26 13:02:38’ does not start with ‘\/Date(‘ and end with ‘)\/’ as required for JSON.’. See server logs for more details.

So far, I had been working with JSON that I serialized and deserialized myself using JavaScriptSerializer. However, when creating a service method to do the deserializing for me, the WCF service uses something else based on a JSON spec older than ISO8601, and expects the dates to be in a very different format. Since this behaviour happens before it even hits my code, it’s rather annoying.

The hacky solution I used was to treat the dates as strings, ironically something I always advise everybody not to do. Then use DateTime.TryParseExact to get the actual dates.

That is, my service method and the class for the JSON is now declared like this:

using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;

namespace ExampleService
{
    [ServiceContract]
    public interface IExampleReturnService
    {
        [OperationContract]
        [WebInvoke(UriTemplate = "/PostJSONExample",
            Method = "POST",
            RequestFormat = WebMessageFormat.Json)]
        void PostJSONExample(ResponseStructure request);
    }

    #region JSON structure

    [DataContract]
    public class record
    {
        [DataMember]
        public string text { get; set; }

        [DataMember]
        public string timestamp { get; set; }

        [DataMember]
        public int pairedMessageId { get; set; }

        [DataMember]
        public string receiver { get; set; }
    }

    [DataContract]
    public class data
    {
        [DataMember]
        public record[] record { get; set; }
    }

    [DataContract]
    public class ResponseStructure
    {
        [DataMember]
        public data data { get; set; }
    }

    #endregion
}

And the implementation, with only the code relevant to parsing the date, is this:

    public class ExampleReturnService : IExampleReturnService
    {
        public void PostJSONExample(ResponseStructure request)
        {
            foreach (record r in request.data.record)
            {
                DateTime responseDate;
                if (DateTime.TryParseExact(
                    r.timestamp, 
                    "yyyy-MM-dd HH:mm:ss", 
                    CultureInfo.InvariantCulture, 
                    DateTimeStyles.AdjustToUniversal, out responseDate))
                {
                    DateTime date = responseDate.ToLocalTime();
                    // Do something with date...
                }
            }
        }
    }
Posted in Programming | Tagged , , , | Leave a comment

T-SQL to get the counts of records within age ranges

This post is written just in case future me has to do this again…

I was emailed a spread sheet containing thousands of birth dates by my boss, and asked to return a segregation based on the number of them less than 20 years old, between 20 and 30, and so on… (The dates were, of course, strings in a weird format, but handling that is outside of the scope of this post.)

What I did was, import the dates into a table in SQL Server, which for want of better names, I called DOB, with a single field in it called DateOfBirth. I found an almost working solution on that god-forsaken portal of yesteryear, experts-exchange, of all places.

This query is based on the solution there, except it works properly.

select result.Age, count(*) Count
from 
(
select
  case when age < 20 then 'less than 20'
           when age between 20 and 30 then '20-30'
           when age between 31 and 40 then '31-40'
           when age between 41 and 50 then '41-50'
           when age between 51 and 300 then '51+'
/* and so on */
  end Age
from (SELECT DATEDIFF(year,DateOfBirth, getdate()) Age 
    from DOB where DateOfBirth is not null) age
) result
group by result.Age
order by Age

Posted in Database | Tagged | Leave a comment

How to convert a string to ASCII in c#

This is one of those cases where smart-ass developers will tell you that you should not be converting strings to ASCII, because reasons

However, if you’re sending a file to a host-to-host banking system, or sending text to an external web service that then sends SMS messages, you might find yourself in a situation where your request will be rejected. Now while it might be frowned upon to blindly try converting Unicode to ASCII, I prefer, when submitting millions (or maybe thousands) of debit orders to a bank, that the whole fucking batch doesn’t get rejected because of some stupid character that the bank doesn’t understand.

Anyway, I can’t remember where I found this code, but the key is to use String.Normalize, to normalize a string using full compatibility decomposition – which generally results in a conversion that looks about as correct as you’re going to get by manually fixing a badly input string… and then throw away all characters that are outside of the ASCII range.

This works:

using System.Linq;
using System.Text;

namespace Demo
{
    class Example
    {
        public static string LatinToAscii(string s)
        {
            var builder = new StringBuilder();
            builder.Append(s.Normalize(NormalizationForm.FormKD)
                                            .Where(x => x < 128)
                                            .ToArray());
            return builder.ToString();
        }
    }
}
Posted in General, Programming | Tagged | Leave a comment

How to save a simple tabular Excel file from C# using EPPlus

An application I wrote was exporting a couple of different sets of objects as CSV files, but the people using it wanted Excel files. Although Microsoft designed C# and everybody involved assumed that such formats are native to dot Net, they are not.

I used a class library called EPPlus, which seems to be the most popular way of doing this. Unfortunately the examples on their website are not useful, showing things like how to set some arbitrary cell on an existing spread sheet to an image or bar chart. The library also supports functions and all sorts of shit, but really, the only thing I care about is exporting a damn table because the users don’t know how to import CSV into their proprietary system.

All I want is to take a list of objects, that I was exporting to CSV before, and write it to a new XLSX file. I found a solution on StackOverflow here, but here is a more complete implementation. (Assuming that you have an instance of a class called Response. This should be general enough for you to modify it according to whatever your objects and fields are. I’ve simplified this so that it doesn’t look anything like the actual objects I work with.)

The EPPlus Worksheet class has a handy method to load a worksheet from a table, and if you load it in the very first cell of a spread sheet, it gives you what you want. (Why didn’t they document that? There is nothing standard or intuitive about it at all. Like, I’d expect a newly instantiated work sheet to have no cells, and have an empty collection by default, and would never have guessed that I can just load a table into a magically accessed cell A1.) So all I needed to do was create a temporary DataTable instance to hold the data, and then it was easy… I don’t already have the objects in a table, because I’m a couple of levels of abstraction away from that. So this code does everything it needs to do, including creating a temporary in-memory table, a new file to contain the data, and then writes the data to the file.

public void SaveToExcel(List<Response> responses)
{
    string responsePath = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) + "\\Responses";

    DataTable table = new DataTable();
    table.Columns.Add("Response", typeof(string));
    table.Columns.Add("ResponseDate", typeof(DateTime));
    table.Columns.Add("CellNumber", typeof(string));

    foreach (var response in responses)
        table.Rows.Add(response.Response, response.ResponseDate, response.CellNumber);

    string filename = DateTime.Now.ToString("yyyy-MM-dd HHmmssfff") + ".xlsx";
    filename = Path.Combine(responsePath, filename);
    Directory.CreateDirectory(responsePath);

    using (var stream = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None, 0x2000, false))
    {
        using (ExcelPackage pck = new ExcelPackage(stream))
        {
            ExcelWorksheet ws = pck.Workbook.Worksheets.Add("Responses");
            ws.Cells["A1"].LoadFromDataTable(table, true);
            pck.Save();
        }
    }
}
Posted in Export XLSX file | Tagged | 1 Comment

A SQL helper class that allows executing a query or running a stored procedure, with or without dynamic parameters

A while back I wrote about my extension method to copy a non-generic collection to an array. A commenter pointed out that it isn’t really necessary. Oh well… here is some code that uses it… (If you use this code, you will need the extension method from the linked post, or else just do what it does instead of calling ToArray<DataColumn> in the code included.)

I use this all over the show, where I want to do a simple SQL query or run a stored procedure and get the results in a table. I like it because it makes it easy to do queries with any number of parameters, by using a collection initializer for a Dictionary type.

An example of usage is this:

        private void Example()
        {
            int id = 666;
            var queryHanlder = new QueryHandler(this.connectionString);

            var data = queryHanlder.OpenQuery(
                "SELECT Devil FROM Details WHERE ID = @ID", 
                new Dictionary<string, object> { {"@ID", id} });

            // Do something with data...
        }

And here’s the class… It contains the method called in the example above, overloaded to be called with or without parameters, and similar overloaded methods to call a stored procedure with or without parameters. I wrote this code years ago, but use it all the time, and it has crept into every project at work.

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;

namespace Demo
{
    public class QueryHandler
    {
        private string connectionString;

        public QueryHandler(string connectionString)
        {
            this.connectionString = connectionString;
        }

        public DataTable OpenQuery(string query)
        {
            return OpenQuery(query, new Dictionary<string, object>());
        }

        public DataTable OpenQuery(string query, Dictionary<string, object> parameters, CommandType commandType = CommandType.Text)
        {
            using (var connection = new SqlConnection(this.connectionString))
            {
                using (var adapter = new SqlDataAdapter(query, connection))
                {
                    connection.Open();
                    try
                    {
                        var table = new DataTable();
                        adapter.SelectCommand.CommandType = commandType;

                        foreach (var kvp in parameters)
                            adapter.SelectCommand.Parameters.AddWithValue(kvp.Key, kvp.Value);

                        using (var reader = adapter.SelectCommand.ExecuteReader())
                        {
                            using (var schemaTable = reader.GetSchemaTable())
                            {
                                foreach (DataRow row in schemaTable.Rows)
                                    table.Columns.Add((string)row["ColumnName"], Type.GetType(row["DataType"].ToString()));
                            }

                            var columns = table.Columns.ToArray<DataColumn>();

                            while (reader.Read())
                                table.Rows.Add(columns.Select(c => reader[c.Ordinal]).ToArray());
                        }
                        return table;
                    }
                    finally { connection.Close(); }
                }
            }
        }

        public DataTable OpenStoredProcedure(string procName)
        {
            return OpenQuery(procName, new Dictionary<string, object>(), CommandType.StoredProcedure);
        }

        public DataTable OpenStoredProcedure(string procName, Dictionary<string, object> parameters)
        {
            return OpenQuery(procName, parameters, CommandType.StoredProcedure);
        }
    }
}
Posted in Database, SQL | Tagged , | 1 Comment

Who else modified QBASIC’s NIBBLES to run on Windows 95?

I just read a blog post by Raymond Chen. It’s a  historical post about getting an old MS-DOS game to run on Windows 95…

But what really got my attention was one of the comments, where the commenter mentioned a problem with the old QBASIC game, NIBBLES. He mentioned the issue it had when trying to run it on Windows 95, and the obvious fix that anyone could make since it came with source code.

I did that too! I feel like such an old geek now. I wonder who else did that back then?

Posted in General | Tagged | Leave a comment

I hate SQL. One way of doing a subquery in a JOIN statement

I really do.

I’m writing this mostly for myself because I never remember this shit. I wrote this query  now but I know that I won’t remember how to do this in future, so here is the public note to self for next time. (This is slightly changed from the entity names at work so that I can’t get into trouble.)

I want a simple query that returns a couple of fields, the cash on hand, and the sum of the balance of orders in progress for a particular store, given these conditions…

  1. We have generic things called Stores. Each store has a balance.
  2. We also have generic things called purchase orders, multiple of them per store, where each refers to the store by a foreign key.
  3. The purchase orders have an amount, but not a balance column.
  4. There are multiple stock received entries against every purchase order.
  5. The purchase order balance is thus the purchase order amount, minus the sum of all the stock received entries against that purchase order.

So I really need two select statements… One from Store and one from PurchaseOrders. The problem is the purchase order balance is calculated by joining with the stock received table, and aggregating, so I can’t do the whole thing in one simple query, or so I thought.

It turns out that you can do a subquery in a join statement, then simply alias it, and this worked for me… All I needed to do was add the store ID field to the subquery so that I could treat it just like a table and join against it. I’ve done something like this before, and forgotten how… hence this stupid post. Did I mention I hate SQL?

SELECT Store.Balance CashOnHand, ISNULL(SUM(t.Balance), 0) OrderInProgress
FROM Store 
LEFT JOIN (
    SELECT po.Amount - ISNULL(SUM(s.amount), 0) Balance, po.StoreID
    FROM            
        PurchaseOrder po 
        LEFT JOIN StockReceived s ON s.PurchaseOrderID = po.id 
    WHERE 
        po.StoreID = @StoreID
    GROUP BY 
        po.ID, po.Amount, po.StoreID ) t ON t.StoreID = @StoreID
WHERE Store.ID = @StoreID
GROUP BY Store.Balance
Posted in Database, SQL | Tagged | 1 Comment

When cleanup fails and tells me that I need to run cleanup

Huh? Normally I wouldn’t write a post like this because I don’t have a solution. But this one is just too funny in its own stupid way.

Yesterday I needed to commit a simple change to a project, and we use TortoiseSVN. Before that, I had to do an update because my colleague had made an unrelated change to the same page. This was an ASP.Net project. Then there were some unrelated conflicts. For whatever unknown reason, it always screws up the publishing profile. I lose my password for the remote machine. So I resolved the conflict. I published the site. Then I want to commit the simple change… just two files. And lo and behold, I get the error displayed in the screenshot. But I get that error for anything I try to do in TortoiseSVN, including cleanup.

When I find a solution, I’ll probably add it. This is not too big a deal right now, because it’s only a tiny change and not the main project I’m working on. One solution would be to delete the whole directory… make a new repository and download all the code from scratch. Not the end of the world because I can back up my tiny change and then overwrite the files. So this is not a train-stopper… It’s just annoying. (And more than a little annoying because I have some configuration files in the directory tree that I never commit. So I’d have to back up quite a few files, but that solution will probably work.)

Cleanup

Posted in Programming, Work | Tagged | Leave a comment

Convert a non-generic collection to an array – my most useful extension method ever…

Funny how things work out… I wrote one of my most simple extension methods years ago, one that converts a non-generic collection to an array (because arrays implement IEnumerable<T> on which I can call Linq extension methods).

Lately I find I’ve been using this all the time. This:

using System.Collections;

namespace Example
{
    public static class Extensions
    {
        /// <summary>Converts a non-generic collection to an array.</summary>
        public static T[] ToArray<T>(this ICollection collection)
        {
            var items = new T[collection.Count];
            collection.CopyTo(items, 0);

            return items;
        }
    }
}

An example where this becomes useful is this… Say you have an in-memory dataset in the form of a table, called “table” for want of a better name, containing some rows, and one of the columns is an amount field. If you wanted the sum of the amount, you could just do this:

            var rows = table.Rows.ToArray<DataRow>();
            var total = rows.Sum(r => (double)r["Amount"]);
Posted in Programming | Tagged , , | 2 Comments

How to get the row index in the client side code of a DataItemTemplate in an ASPxGridView column

This was pissing me off for far too long today. I also have a DataItemTemplate with an ASPxHyperlink control in one of my columns of an ASPxGridView. When the user clicks the hyperlink, I need to call a JavaScript function that uses the index of that row in the grid… (Then, and this is outside the scope of this post but for information and context, I do a callback to the server side, look up some more specific information in the database, pass some data back to the client, and when the callback is complete, show a confirmation popup with a that shit data that I returned from the server. The user can then click an OK button on the popup which does another callback and that does some other stuff.) For some unknown reason, the complicated-sounding part was easy, but I got stuck on step one… How to get the index of the row in the grid where the link was clicked?

Consider that when the link is clicked, it nether sets the grid row selected nor focused. So the usual suspects (the devexpress controls’ functions to get visible row index or focused row index) are not helpful here… They return null.

So the column in the markup of the grid looks like this… (Nothing special here.)

<dx:GridViewDataTextColumn FieldName="Status" ShowInCustomizationForm="True" VisibleIndex="7">
    <DataItemTemplate>
        <dx:ASPxHyperLink ID="hlAuthorise" runat="server" Text='<%# Eval("Status") %>' OnInit="hlAuthorise_Init">
        </dx:ASPxHyperLink>
    </DataItemTemplate>
</dx:GridViewDataTextColumn>

The JavaScript function that needs to be called when the link gets clicked is this… (Actually it’s not this, but you don’t need to see it. All that matters is it needs to be passed the index.)

        function AuthoriseClick(s, e, index) {
            alert(index);
        }

And the way to do it turns out to be easy. Just not so easy to find online. The trick is to set the index on the server side for each row, via the Init event of the hyperlink. (So I lied when I said there was nothing special above. If you scrolled to the right, you’d have noticed the Init event handler in the markup.) The C# code on the server-side is this:

        protected void hlAuthorise_Init(object sender, EventArgs e)
        {
            /* This is the only way I've found to allow the client side code in a 
             * DataItemTemplate to get the index of the row in its container grid. */
            ASPxHyperLink link = sender as ASPxHyperLink;
            GridViewDataItemTemplateContainer container = ((ASPxHyperLink)sender).NamingContainer as GridViewDataItemTemplateContainer;
            link.ClientSideEvents.Click = String.Format("function (s, e) {{ AuthoriseClick(s, e, {0}); }}", container.VisibleIndex);
        }
Posted in Programming | Tagged , , , | 2 Comments

How to insert multiple literals explicitly into a table, only if they don’t already exist

It might seem silly, but recently I needed to insert some values into a new table I added to a system, but only if those values didn’t already exist, and I did not know how. (Yes, it’s a new table and is thus empty. But this technique should work when adding other values into it in future.) I know how to do that when inserting a selection from another table, but in this case I wanted to insert literals, and all my Googling came up empty…

So even though this is really quite an easy task, I figured writing it here might help somebody from going to the few minutes of trouble I had to. (And more importantly, forgetful me can refer to this post when I forget how I did it.)

To illustrate what I mean, let’s start with a simple table, which will hold the names of some super heroes. (I can’t give the details of the real table I created if I value my employment.) Because I’m lazy, I create tables in the designer in SQL Server Management Studio, and then script them afterwards. The creation script for such a table could be as follows:

CREATE TABLE [dbo].[JusticeLeague](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [Code] [varchar](10) NOT NULL,
    [Description] [varchar](100) NOT NULL,
    [DateCreated] [datetime] NOT NULL,
 CONSTRAINT [PK_JusticeLeague] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

I usually insert multiple literals like so:

DECLARE @Today datetime = getdate();

INSERT INTO JusticeLeague (Code, Description, DateCreated) values
('SUP', 'Superman', @Today),
('BAT', 'Batman', @Today),
('WON', 'Wonder Woman', @Today),
('FLASH', 'The Flash', @Today),
('AQUM', 'Aquaman', @Today)

But that doesn’t prevent duplicates. What I want my script to do is:

  • Insert the rows only if they aren’t already in the table.
  • If run more than once, it should only insert the first time. When run again, it should do nothing.
  • If new rows are added to the script, when rerun it should only insert those new rows.

Since my searches came up empty (actually I got nothing but irrelevant results), I came up with a way… All this does is:

  1. Select the values as if they are a table, by selecting all rows’ explicit values and doing a union.
  2. Then add a filter using the EXISTS keyword.

It works quite well. So my solution was this:

DECLARE @Today datetime = getdate();

INSERT INTO JusticeLeague (Code, Description, DateCreated)
select t.* from (
select 'SUP' Code, 'Superman' Description, @Today DateCreated union all
select 'BAT', 'Batman', @Today union all
select 'WON', 'Wonder Woman', @Today union all
select 'FLASH', 'The Flash', @Today union all
select 'AQUM', 'Aquaman', @Today) t
where not exists(select 1 from JusticeLeague i where i.Code = t.Code)

This script can be rerun, and when adding new values, which will definitely happen in the real table that I based this contrived example on, the script can safely be rerun and only the new rows will be inserted.

For example, I can safely change the script as below, and only one new hero, Green Lantern, will be added:

DECLARE @Today datetime = getdate();

INSERT INTO JusticeLeague (Code, Description, DateCreated)
select t.* from (
select 'SUP' Code, 'Superman' Description, @Today DateCreated union all
select 'BAT', 'Batman', @Today union all
select 'WON', 'Wonder Woman', @Today union all
select 'FLASH', 'The Flash', @Today union all
select 'GRE', 'Green Lantern', @Today union all
select 'AQUM', 'Aquaman', @Today) t
where not exists(select 1 from JusticeLeague i where i.Code = t.Code)

Note that I’m using UNION ALL. This would actually cause duplicates if the script contained duplicate values, but performs better than UNION, which scans for duplicates. This is fine for me, because when building the script, I take care not to include duplicate literals.

Posted in SQL | Tagged , | Leave a comment

How to add to the Windows recent documents in C#

I realize that I haven’t written anything here for ages… so I looked through my old code for something simple but useful, and found this.

If ever you need to add a file to the Windows recent document list, this does the trick. I’m using this in a Windows Forms application that can open and view a number of file types, such as videos, audio and text files. Adding them to the recent documents allows me to open the files using the registered file handlers (I don’t register my application to handle the files on a system level) to compare my viewers to others. The code is simple and self-explanatory, so here it is without any explanation:

using System.Runtime.InteropServices;
using System.Security;

namespace Sample
{
    public static class Example
    {
        public static void AddToRecentDocuments(string path)
        {
            NativeMethods.SHAddToRecentDocs(ShellAddToRecentDocsFlags.Path, path);
        }
    }

    internal enum ShellAddToRecentDocsFlags
    {
        Pidl = 0x001,
        Path = 0x002,
    }

    [SuppressUnmanagedCodeSecurity]
    internal static class NativeMethods
    {
        // Note: The function call fails if we use CharSet.Unicode or CharSet.Auto or if path is not Ansi.
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA2101:SpecifyMarshalingForPInvokeStringArguments", MessageId = "1"), DllImport("shell32.dll", CharSet = CharSet.Ansi)]
        public static extern void SHAddToRecentDocs(ShellAddToRecentDocsFlags flag, [MarshalAs(UnmanagedType.LPStr)] string path);
    }
}
Posted in Programming | Tagged , | Leave a comment

Spammers need better targeting algorithms

I don’t check this blog’s stats much; my other blog is the one I focus more on these days. Since I just published something here, I decided to clean up the spam.

Why would you auto-spam a programming blog, in South Africa, with something about “cheap eggs UK”? For that matter, why would you target any blogs with spam about fucking buying eggs? If you want to get some cheap-ass eggs, do you scan random WordPress blogs to find them, for fuck’s sake?

Posted in General | Tagged | 2 Comments

How to ensure that a number is always round up to the nearest integer

Yes, this is silly. I never used Math.Ceiling before; so I never even knew what it was for.

In my use case, I’m sending SMS messages via a third party WCF service. It works well, but in my own WCF service, I forgot to limit the length of the message text to 160 characters. That turned out to be a good thing, because they send it anyway, and charge us for the extra messages, which is fine. So all I needed to do was add a parts field to our database table, so that we know how many messages to bill our clients for.

So the requirement is that any multiple of 160 characters gets a part. Anything below 160 characters is one message. Anything greater than 160 characters and less than 320 characters is two messages, and so on…

So, for example, 168 characters is 168 / 160 = 1.05. Math.Round will round down to one. I want it to be two, because any part of a message is a whole message, obviously.

Somebody asked the question on StackOverflow. Actually I found it via this duplicate. Note that I am ignoring the most efficient answer, the “correct” answer if you will… I like the simplest answer, which in my code comes out to:

command.Parameters.AddWithValue("@Parts", (int)Math.Ceiling(message.Length/160D));

So in my example that would be (pseudocode) Math.Ceiling(168/160), which equals 2.

Posted in Programming | Tagged , | Leave a comment

Mailto links with subject and body

This is most probably not news to anybody besides myself, but regardless, I didn’t know that you can create email links including both the subject and body…

I was writing something on my other blog and needed to include some snark with a link to my email address. I found the solution on StackOverflow here.

I didn’t specify the body, but the idea is, you can just include the body and subject in the link as query strings. Really, I had no idea. For example, just with the subject:

<a href="mailto:viveirosjerome@gmail.com?subject=My Banking Details">Send me your banking details</a>

And to specify the body too:

<a href="mailto:viveirosjerome@gmail.com?subject=My Banking Details&body=I didn't know you could do this">Send me your banking details</a>

Here follows the bit of my other blog-post that used it:


Cure your ailments with magic socks

I love reading about science based medicine, and I love the Science based Medicine Blog and it’s debunking of nonscience nonsense. To think that somewhere in the civilized world, “experts” in a naturopathic university advocate the healing of all your ailments with magic socks (also known as cold wet socks taken out of the fucking freezer). The mind boggles! Read the thorough debunking by an actual medical doctor here. (My brief version: Naturopaths come up with some solid sounding sophisticated pseudoscientific nonsense… some hand-waving and the child having to maintain homoeostasis while wearing freezing socks all night kick starts the immune system. But according to the author, naturopaths’ feet fixation does not end with magic socks.)

By the way, if you believe in magic socks, I have just the thing for you. Send me your banking details (please attach permission for me to debit your account) and physical address, and I’ll send you the secret to infinite riches, along with some magic beans.

Posted in HTML | Tagged | Leave a comment