C#

MyThreadPool – Quick&Dirty

Hallo,

Ich war wiedermal auf Optimierungstour durch mein momentanes Projekt, und dort ist mir aufgefallen, dass das scheinbar asynchrone abarbeiten nur teils asynchron ist da ich vor gut 2-3 Monaten als ich das implementiert hatte folgendermaßen vorgegangen war:

Nachteile:

  • Ein dauerhaft blockierender Thread (while(true)) startete seine Tochterthreads die, die eigentliche Arbeit erledigen, nachdem diese fertig waren, startete der Vaterthread wieder mehrere Tochterthreads, nur leider musste hier nun immer gewartet werden bis alle fertig sind, damit erneut weitere Threads gestartet werden konnte.

Abhilfe schafft die im Anhang befindliche Klasse, dieser übergibt man als Konstruktor eine Methode und eine Liste von Objekten die durchgegangen wird und abgearbeitet.

Vorteile:

  • Kein blockierender Thread mehr, da jeder Thread der fertig ist auch sofort einen neuen Thread startet.
  • Es laufen immer eine (fast) konstante Anzahl Threads gleichzeitig.

[codesyntax lang="csharp"]

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

namespace LWD.NET
{
    public class MyThreadPool
    {
        ParameterizedThreadStart m_ToDo;
        List<object[]> m_Threads = new List<object[]>();

        int m_index = 0;
        public MyThreadPool(IEnumerable<object> args, ParameterizedThreadStart start)
        {
            m_ToDo = start;

            //Creating a list for all threads, but dont start any thread!
            foreach (var obj in args)
            {
                try
                {
                    Thread t = new Thread(Execute);
                    t.IsBackground = true;
                    t.Name = obj.ToString();
                    t.SetApartmentState(ApartmentState.STA);
                    t.Priority = ThreadPriority.BelowNormal;
                    m_Threads.Add(new object[] { t, obj });
                }
                catch
                {

                }
            }

            for (int i = 0; i < Environment.ProcessorCount * 2; i++)
            {
                StartNext();
            }
        }

        private void StartNext()
        {
            lock (this)
            {
                if (m_index == m_Threads.Count)
                    return;
                var obj = m_Threads[m_index++];
                ((Thread)obj[0]).Start(obj[1]);
            }
        }

        private void Execute(object o)
        {
            try
            {
                m_ToDo(o);
            }
            catch
            {
            }
            finally
            {
                StartNext();
            }
        }
    }
}

[/codesyntax]

WPF-Binding auf eine statische Membervariable

Hallo,

Folgendes Problem stellte sich mir heute, eine statische Klasse beinhält eine Funktion zum (asynchronen) verkleinern von Bildern. Im Grunde wird in dieser Klasse ein Thread gestartet und dieser startet weitere Threads (Anzahl der CPU-Cores+1) die das verkleinern der Bilder übernimmt. Nun wollte ich irgendwo global anzeigen lassen wieviel Bilder schon verarbeitet wurden, hierfür deklarierte ich in dieser statischen Klasse eine “ObservableCollection” und diese hält immer die aktuell in Bearbeitung befindlichen Bilder. Um nun von WPF aus auf den Member für die Anzahl zuzugreifen muss man folgendermaßen vorgehen:

[codesyntax lang="xml"]
<TextBlock Text=”{Binding Source={x:Static local:AlbumVerwaltung.m_Worklist},Path=Count}”/>


[/codesyntax]

wobei local der lokale Namespace ist.

CopyPictures

Hallo!

Nachdem ich doch öfters mal Fotos mache, wollte ich einen Arbeitsschritt von mir automatisieren, das

Kopieren/Löschen der Bilder. Prinzipiell gehe ich hierbei immer so vor, dass ich die Bilder von der Speicherkarte herunterkopiere und der Ordner bekommt dort als Name das Aufnahmedatum (2010-07-23) verpasst, und bei belieben noch weitere Attribute hinzugefügt (z.b.: 2010-07-23 [Screenshots]).

Nun ist es für mich immer lästig wenn ich die Speicherkarte länger nicht “verwertet” habe, die Bilder sortiert zu

kopieren, dafür habe ich mir eben das kleine Programm hier geschrieben, das vollautomatisch von einem Laufwerk die Fotos herunterkopiert in den jeweils richtigen Ordner, und ich kann auch gleich sagen, dass die Bilder gelöscht werden sollen die kopiert wurden.

Wer will, ich häng hier das Mini-Projekt an.

P.s.: Habe hier absichtlich mit dem 2.0 Framework entwickelt um wiedermal eine Forms-Anwendung zu schreiben, hat mal nen Moment gedauert und gewisse Properties habe ich nicht auf Anhieb gefunden (“Wo zur Hölle ist “IsChecked”/”IsEnabled” verschwunden ? Compilerfehler ? Warum ?)

Download: CopyPictures – Quellcode und ausführbare Datei

Verwenden von TrayIcon´s ohne Referenz auf System.Windows.Forms

Hallo,

In meinem Zivildienstprojekt “LWD.NET” will ich vermeiden eine Referenz auf die Windows Forms Bibliothek zu vermeiden, da WPF-Mäßig keine Unterstützung für ein Tray-Icon (die Icons neben der System-Uhr) mitliefer habe ich mich ein wenig mit der WinAPI herumgeschlagen und eine kleine Klasse geschrieben die mir die Arbeit abnimmt.

Vorgehensweiße:

- Dem Konstruktor werden, WPF-Window, ein System.Drawing.Icon und der String für den Namen übergeben

- Dieser erzeugt die benötigte “NOTIFYICONDATA” Struktur, abonniert die Events Closing und StateChanged vom Window selber sowie mithilfe von “HwndSource” alle Windows-Messages die das Fenster erhält.

- Wird das Fenster nun minimiert, speichert sich die Klasse den vorherigen Status (Maximiert oder Resizeable), versteckt das Fenster (über Hide()) und setzt sich selber als Icon in den Traybereich

- Wird auf das Tray-Icon geklickt erhält das WPF-Window eine Windows-Message mit der ID “WM_MyMessage” (Achtung! Dadurch kann diese Klasse nicht mehrmals verwendet werden! Müsste eindeutig für jedes TrayIcon sein ODER die uID NOTIFYICONDATA) und das Tray-Icon wird entfernt sowie das Fenster wieder wiederhergestellt.

[codesyntax lang="csharp"]

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Runtime.InteropServices;
using System.Windows.Interop;

namespace LWD.NET
{
    public class TrayIcon
    {
        IntPtr m_Handle;
        Window m_Window;
        WindowState m_storedWindowState;
        NOTIFYICONDATA m_Data;
        System.Drawing.Icon m_Icon;

        const int TRAY_ADD = 0x00000000;
        const int TRAY_MODIFY = 0x00000001;
        const int TRAY_DELETE = 0x00000002;

        const int WM_MyMessage = 0x400;

        [DllImport("shell32.dll")]
        static extern bool Shell_NotifyIcon(uint dwMessage,[In] ref NOTIFYICONDATA pnid);
        struct NOTIFYICONDATA
        {
            /// <summary>
            /// Size of this structure, in bytes.
            /// </summary>
            public int cbSize;

            /// <summary>
            /// Handle to the window that receives notification messages associated with an icon in the
            /// taskbar status area. The Shell uses hWnd and uID to identify which icon to operate on
            /// when Shell_NotifyIcon is invoked.
            /// </summary>
            public IntPtr hwnd;

            /// <summary>
            /// Application-defined identifier of the taskbar icon. The Shell uses hWnd and uID to identify
            /// which icon to operate on when Shell_NotifyIcon is invoked. You can have multiple icons
            /// associated with a single hWnd by assigning each a different uID.
            /// </summary>
            public int uID;

            /// <summary>
            /// Flags that indicate which of the other members contain valid data. This member can be
            /// a combination of the NIF_XXX constants.
            /// </summary>
            public int uFlags;

            /// <summary>
            /// Application-defined message identifier. The system uses this identifier to send
            /// notifications to the window identified in hWnd.
            /// </summary>
            public int uCallbackMessage;

            /// <summary>
            /// Handle to the icon to be added, modified, or deleted.
            /// </summary>
            public IntPtr hIcon;

            /// <summary>
            /// String with the text for a standard ToolTip. It can have a maximum of 64 characters including
            /// the terminating NULL. For Version 5.0 and later, szTip can have a maximum of
            /// 128 characters, including the terminating NULL.
            /// </summary>
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
            public string szTip;

            /// <summary>
            /// State of the icon.
            /// </summary>
            public int dwState;

            /// <summary>
            /// A value that specifies which bits of the state member are retrieved or modified.
            /// For example, setting this member to NIS_HIDDEN causes only the item's hidden state to be retrieved.
            /// </summary>
            public int dwStateMask;

            /// <summary>
            /// String with the text for a balloon ToolTip. It can have a maximum of 255 characters.
            /// To remove the ToolTip, set the NIF_INFO flag in uFlags and set szInfo to an empty string.
            /// </summary>
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
            public string szInfo;

            /// <summary>
            /// NOTE: This field is also used for the Timeout value. Specifies whether the Shell notify
            /// icon interface should use Windows 95 or Windows 2000
            /// behavior. For more information on the differences in these two behaviors, see
            /// Shell_NotifyIcon. This member is only employed when using Shell_NotifyIcon to send an
            /// NIM_VERSION message.
            /// </summary>
            public int uVersion;

            /// <summary>
            /// String containing a title for a balloon ToolTip. This title appears in boldface
            /// above the text. It can have a maximum of 63 characters.
            /// </summary>
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
            public string szInfoTitle;

            /// <summary>
            /// Adds an icon to a balloon ToolTip. It is placed to the left of the title. If the
            /// szTitleInfo member is zero-length, the icon is not shown. See
            /// <see cref="BalloonIconStyle">RMUtils.WinAPI.Structs.BalloonIconStyle</see> for more
            /// information.
            /// </summary>
            public int dwInfoFlags;
        }

        public TrayIcon(Window win, System.Drawing.Icon Icon,string Tip)
        {
            m_Window = win;
            m_Icon = Icon;
            m_Handle = new WindowInteropHelper(m_Window).Handle;

            HwndSource source = PresentationSource.FromVisual(m_Window) as HwndSource;
            source.AddHook(WndProc);

            m_Window.Closing += new System.ComponentModel.CancelEventHandler(win_Closing);
            m_Window.StateChanged += new EventHandler(win_StateChanged);

            m_Data = new NOTIFYICONDATA();

            if (Environment.OSVersion.Version.Major >= 6)
            {
                m_Data.cbSize = Marshal.SizeOf(m_Data);
            }
            else
            {
                m_Data.cbSize = 504;
                m_Data.uVersion = 10;
            }
            m_Data.uFlags = 0x01 | 0x02 | 0x04;

            m_Data.hwnd = m_Handle;
            m_Data.uCallbackMessage = WM_MyMessage;
            m_Data.hIcon = Icon.Handle;
            m_Data.uID = 0x0;
            m_Data.szTip = Tip;
        }

        private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            if ((int)msg == WM_MyMessage)
            {
                if ((int)lParam == 0x201)
                {
                    //Click ?
                    m_Window.Show();
                    m_Window.WindowState = m_storedWindowState;
                }
            }
            return IntPtr.Zero;
        }

        public void ShowTrayIcon()
        {
            Shell_NotifyIcon(TRAY_ADD, ref m_Data);
        }

        public void HideTrayIcon()
        {
            Shell_NotifyIcon(TRAY_DELETE, ref m_Data);
        }

        void win_StateChanged(object sender, EventArgs e)
        {
            if (m_Window.WindowState == WindowState.Minimized)
            {
                m_Window.Hide();
                ShowTrayIcon();
            }
            else
            {
                m_storedWindowState = m_Window.WindowState;
                HideTrayIcon();
            }            
        }

        void win_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            HideTrayIcon();
        }

        void m_notifyIcon_Click(object sender, EventArgs e)
        {
            m_Window.Show();
            m_Window.WindowState = m_storedWindowState;
        }
    }
}

[/codesyntax]

ByteStorage

In meinem momentanen privaten Bilderverwaltungsprojekt habe ich eine effiziente Möglichkeit gesucht verkleinerte Vorschaubilder zu speichern. Die Angehängte kleine Klasse hat mir dabei geholfen.
Es werden 2 Dateien angelegt, eine Datei die, die Daten hält (*.cache) und eine die diese indiziert hat und den Zugriff beschleunigt (*.cacheindex).

Das ganze läuft Threadsafe ab und kann somit von mehreren Threads gleichzeitig verwendet werden ohne, dass die Zugriffe sich gegenseitig stören.

[codesyntax lang="csharp"]

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary; 

namespace mCollection
{
    public class ByteStorage : IDisposable
    {
        /// <summary>
        /// The Filestream which is beeing used
        /// </summary>
        private FileStream m_Stream;
        /// <summary>
        /// Contains string as key and a Tuple <long as length of data, long as offset>
        /// </summary>
        private Dictionary<string, Tuple<long, long >> Indices;

        private string PathToCachingFile;
        private string PathToIndexFile;

        /// <summary>
        /// Ctor
        /// </summary>
        /// <param name="TargetDirectory">Directory which contains the index&caching File</param>
        /// <param name="Filename">Filename (*.cacheindex and *.cache will be generated/opened)</param>
        public ByteStorage(string TargetDirectory, string Filename)
        {
            PathToCachingFile = Path.Combine(TargetDirectory, Filename + ".cache");
            PathToIndexFile = Path.Combine(TargetDirectory, Filename + ".cacheindex");

            m_Stream = new FileStream(PathToCachingFile, FileMode.OpenOrCreate);

            if (!File.Exists(PathToIndexFile))
            {
                Indices = new Dictionary<string, Tuple<long, long>>();
            }
            else
            {
                BinaryFormatter binFormatter = new BinaryFormatter();
                using (FileStream fs = new FileStream(PathToIndexFile, FileMode.Open))
                {
                    Indices = (Dictionary<string, Tuple<long, long>>)binFormatter.Deserialize(fs);
                }
            }
        }

        ~ByteStorage()
        {
            Dispose();
        }

        public byte[] GetData(string Key)
        {
            try
            {
                Tuple<long, long> tuple;
                if(Indices.TryGetValue(Key , out tuple))
                {
                    byte[] buffer = new byte[tuple.Item1];
                    lock (this)
                    {
                        long tmp = m_Stream.Position;
                        m_Stream.Position = tuple.Item2;
                        m_Stream.Read(buffer, 0, buffer.Length);
                        m_Stream.Position = tmp;
                    }
                    return buffer;
                }
                return null;
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// Method for adding some byte[] data to the cachingfile
        /// </summary>
        /// <param name="Key"></param>
        /// <param name="data"></param>
        /// <returns></returns>
        public bool AddData(string Key, byte[] data)
        {
            try
            {
                byte[] buffer = new byte[data.Length + sizeof(int)];
                Array.Copy(data, buffer, data.Length);
                byte[] length = System.BitConverter.GetBytes(data.Length);
                Array.Copy(length, 0, buffer, data.Length, length.Length);

                long offset = 0;
                lock (this)
                {
                    long tmp = m_Stream.Position;
                    offset = m_Stream.Length;
                    m_Stream.Position = offset;
                    m_Stream.Write(buffer, 0, buffer.Length);
                    m_Stream.Position = tmp;
                    Indices.Add(Key, new Tuple<long, long>(data.Length, offset));
                    SafeIndices();
                }
                return true;
            }
            catch
            {
                return false;
            }
        }

        private void SafeIndices()
        {
            if (Indices == null)
                return;

            BinaryFormatter binFormatter = new BinaryFormatter();
            lock (this)
            {
                using (FileStream fs = new FileStream(PathToIndexFile, FileMode.OpenOrCreate))
                {
                    binFormatter.Serialize(fs, Indices);
                }
            }
        }

        public void Dispose()
        {
            SafeIndices();
            if (m_Stream == null)
                return;
            m_Stream.Close();
            m_Stream.Dispose();
            m_Stream = null;
        }
    }
}

[/codesyntax]