rainit2006 / My_Windows

0 stars 0 forks source link

[B_C_D] #12

Open rainit2006 opened 7 years ago

rainit2006 commented 7 years ago

在C#里调用BIOS机能 1,调用S_SL的API。 ー 静态调用 需要定义一个和S_SL里一样的结构体(XxCDType), 变量Type改成C#里的。 unsigned short -> ushort unsigned char -> byte 指针(比如DWORD *) -> IntPtr

Note:结构体定义的前面要加上“[StructLayout(LayoutKind.Sequential)]”

2, string Dll_Path = "xxxxx/S_x_yU_tils.dll" [DllImport(Dll_Path, EntryPoint="xxCallDriver")] static extern bool xxCallDriver(XxCDType, InPtr pdwParam);

3,

xxCDType  getBCFState  = new xxCDType(0, 1, 0x17x, 0x55);

Byte[] byteArray = new byte[1];
IntPtr uipInParam = Marshal.AllocHGlobal(1);
Marshal.Copy(byteArray, 0, uipInParam, 1);

xxCallDriver(xxGetBCFState, uipInParam);
Marsh.Copy(uipInParam, byteArray, 0, 1);

Console.WriteLine(byteArray[0]); 

-- 动态调用 kernel32.dll中のLoadLibrary、GetProcAddress、FreeLibrary関数を利用して、C++のように実施する。

1,需要定义一个和S_SL里一样的结构体(XxCDType), 变量Type改成C#里的。 2,

// デリゲートの定義
delegate bool xxCallDriverHandler(XxCDType, InPtr pdwParam);

[DllImport("kernel32.dll")]
static extern IntPtr LoadLibrary(string lpFileName);
[DllImport("kernel32.dll")]
static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);
[DllImport("kernel32.dll")]
static extern bool FreeLibrary(IntPtr hLibModule);

static void Main(string[] args)
{
    // 関数ポインタを取得
    string Dll_Path = "xxxxx/S_x_yU_tils.dll"
    IntPtr ptrLib = LoadLibrary(Dll_Path);
    IntPtr ptrCallDriver = GetProcAddress(ptrLib, "xxCallDriver");
    // 関数ポインタをデリゲートに変換する 
    xxCallDriverHandler xxCallDriver 
        = (xxCallDriverHandler)Marshal.GetDelegateForFunctionPointer(ptrLoad, typeof(xxCallDriverHandler));

    // デリゲートを呼び出す
   xxCDType  getBCFState  = new xxCDType(0, 1, 0x17x, 0x55);
Byte[] byteArray = new byte[1];
IntPtr uipInParam = Marshal.AllocHGlobal(1);
Marshal.Copy(byteArray, 0, uipInParam, 1);
xxCallDriver(xxGetBCFState, uipInParam);
Marsh.Copy(uipInParam, byteArray, 0, 1);

 // ライブラリを解放
    FreeLibrary(ptrLib);
}
rainit2006 commented 7 years ago

Read xml file

Method1

static void XMLReadByXmlDocument()
        {
            string fileName = @"XfeaturesX.txt";
            XmlDocument doc = new XmlDocument();
            doc.Load(fileName);
            XmlNode xn = doc.SelectSingleNode("Plugins");

            XmlNodeList xnl = xn.ChildNodes;
            int i = 0;
            foreach (XmlNode xne in xnl)
            {
                i++;
                XmlElement xe = (XmlElement)xne;
                string name = xe.GetAttribute("Name");
                Console.WriteLine( "{0} plugin is {1}" , i, name);
                if(xe.ChildNodes.Count > 0)
                {
                    XmlNodeList nl = xe.ChildNodes;
                    XmlNode pluginHost = nl.Item(0);

                    string name2 = pluginHost.FirstChild.Name;
                    XmlElement host = (XmlElement)pluginHost.FirstChild;
                    Console.WriteLine(host.GetAttribute("Process"));

                }

            }
        }

Method2

static void XMLReadByXmlReder()
        {
            string fileName = @"XfeaturesX.txt";
            XmlTextReader reader = new XmlTextReader(fileName);

            while (reader.Read())
            {
                if (reader.NodeType == XmlNodeType.Element)
                {
                    if (reader.Name == "Plugin")
                    {
                        string pluginName = reader.GetAttribute(0);
                        string pluginEnabled = reader.GetAttribute(1);
                        Console.WriteLine("Plugin is {0}, {1}", pluginName, pluginEnabled);
                    }
                    if (reader.Name == "PluginHosts")
                    {
                        Console.WriteLine("------");
                    }
                    if (reader.Name == "Host")
                    {
                        Console.WriteLine("host is {0}", reader.GetAttribute(0));
                    }                 
                }
                if ((reader.Name == "PluginHosts") && (reader.NodeType == XmlNodeType.EndElement))
                {
                    Console.WriteLine("###########");
                }
            }
        }

Method3

static void XMLReadByLinq()
        {
            string fileName = @"XfeaturesX.txt";
            XElement  xe = XElement.Load(fileName);
            IEnumerable<XElement> elements = from ele in xe.Elements("Plugin") select ele;

            foreach (var ele in elements)
            {
                Console.WriteLine("Plugin is {0}", ele.Attribute("Name").Value);
                var PluginHosts = ele.Element("PluginHosts");
                if (PluginHosts != null)
                {
                    foreach (var host in PluginHosts.Elements("Host"))
                    {
                        Console.WriteLine(host.Attribute("Process").Value);
                    }

                }
            }

        }
rainit2006 commented 7 years ago

处理所经过时间的取得

namespace ConsoleApplication1
{
   class Program
   {
       static void Main(string[] args)
       {
           DateTime startDt = DateTime.Now;

           //
           // 何かの処理
           //
           System.Threading.Thread.Sleep(1000);

           DateTime endDt = DateTime.Now;

           TimeSpan ts = endDt - startDt; // 時間の差分を取得

           Console.WriteLine(ts.TotalMinutes); // 経過時間(分)
           Console.WriteLine(ts.TotalSeconds); // 経過時間(秒)
           Console.WriteLine(ts.TotalMilliseconds); // 経過時間(ミリ秒)

           Console.ReadKey();
       }
    }
}
rainit2006 commented 6 years ago

AppDomain.CurrentDomain Gets the current application domain for the current Thread. https://msdn.microsoft.com/ja-jp/library/system.appdomain.currentdomain(v=vs.110).aspx

共通言語ランタイム上では1つのプロセスの内部で複数の「アプリケーション」が実行可能となった。個々のアプリケーションは「アプリケーション・ドメイン」と呼ばれる処理単位でその型やセキュリティが管理され、独立して実行される。

Application.Current プロパティ Gets the Application object for the current AppDomain. Application is a per-AppDomain singleton type that implements the static Current property to provide shared access to the Application instance for the current AppDomain. This property is thread safe and is available from any thread. https://msdn.microsoft.com/ja-jp/library/system.windows.application.current(v=vs.110).aspx

获得当前Process的个数 int32 proNum = Process.GetProcessesByName(Process.GetCurrentProcess().ProcessName).Length;

AppDomain.BaseDirectory プロパティ string appFolder = System.AppDomain.CurrentDomain.BaseDirectory.ToSring();

注册重新启动程序,用于程序update等场景 RegisterApplicationRestart函数 https://msdn.microsoft.com/ja-jp/library/windows/desktop/aa373347(v=vs.85).aspx If you register for restart and the application encounters an unhandled exception or is not responsive, the user is offered the opportunity to restart the application; the application is not automatically restarted without the user's consent. However, if the application is being updated and requires a restart, the application is restarted automatically. To prevent cyclical restarts, the system will only restart the application if it has been running for a minimum of 60 seconds. Note that for an application to be restarted when the update requires a computer restart, the installer must call the ExitWindowsEx function with the EWX_RESTARTAPPS flag set or the InitiateShutdown function with the SHUTDOWN_RESTARTAPPS flag set.

[DllImport("kernel32.dll")]
protected static extern int RegisterApplicationRestart(string AppPath, uint RestartFlags);

protected override void OnStartup(StartupEventArgs e)
        {            
            base.OnStartup(e);

            RegisterApplicationRestart("VxxxShell.exe", 11);
...

给程序加入适当的FontFamily this.Resources.Add("fontFamily", new System.Windows.Media.FontFamily("Segoe UI"));


Application.ShutdownMode プロパティ https://msdn.microsoft.com/ja-jp/library/system.windows.application.shutdownmode(v=vs.110).aspx Applications stop running only when the Shutdown method of the Application is called. Shut down can occur implicitly or explicitly, as specified by the value of the ShutdownMode property.

The lifetime of some applications may not be dependent on when the main window or last window is closed, or may not be dependent on windows at all. For these scenarios you need to set the ShutdownMode property to OnExplicitShutdown, which requires an explicit Shutdown method call to stop the application. Otherwise, the application continues running in the background.

If you set ShutdownMode to OnLastWindowClose, Windows Presentation Foundation (WPF) implicitly calls Shutdown when the last window in an application closes, even if any currently instantiated windows are set as the main window (see MainWindow).

A ShutdownMode of OnMainWindowClose causes Windows Presentation Foundation (WPF) to implicitly call Shutdown when the MainWindow closes, even if other windows are currently open. ShutdownMode = ShutdownMode.OnExplicitShutdown;

Application.Exit イベント Occurs just before an application shuts down, and cannot be canceled.

An application can shut down for either of the following reasons: 1、The Shutdown method of the Application object is called, either explicitly or as determined by the ShutdownMode property. 2、The user ends the session by logging off or shutting down. You can detect when application shutdown occurs by handling the Exit event, and perform any additional processing as required. https://msdn.microsoft.com/ja-jp/library/system.windows.application.exit(v=vs.110).aspx Application.Current.Exit += xxxxx関数


SystemEvents.UserPreferenceChanged イベント Occurs when a user preference has changed.

SystemEvents.UserPreferenceChanged += new UserPreferenceChangedEventHandler(SystemEvents_UserPreferenceChanged); 

...
//ユーザー設定が変更されたとき
private void SystemEvents_UserPreferenceChanged(object sender,
        UserPreferenceChangedEventArgs e)
{
    string s = "";
    switch (e.Category)
    {
        case UserPreferenceCategory.Accessibility:
            s = "障害を持つユーザー用のシステムユーザー補助に"
                + "関連付けられているユーザー設定";
            break;
        case UserPreferenceCategory.Color:
            s = "ウィンドウやメニューの既定の色など、"
                + "システムカラーに関連付けられているユーザー設定";
            break;
        case UserPreferenceCategory.Desktop:
            s = "システムデスクトップに関連付けられているユーザー設定";
            break;
        case UserPreferenceCategory.General:
            s = "ほかのどのカテゴリにも関連付けられていないユーザー設定";
            break;
        case UserPreferenceCategory.Icon:
            s = "アイコンの高さや間隔など、"
                + "アイコン設定に関するユーザー設定";
            break;
        case UserPreferenceCategory.Keyboard:
            s = "キー入力時の文字の表示間隔など、"
                + "キーボード設定に関するユーザー設定";
            break;
        case UserPreferenceCategory.Locale:
            s = "文字エンコーディングやカルチャ文字列など、"
                + "カルチャに関するユーザー設定";
            break;
        case UserPreferenceCategory.Menu:
            s = "メニュー遅延やテキストの配置など、"
                + "メニュー設定に関するユーザー設定";
            break;
        case UserPreferenceCategory.Mouse:
            s = "ダブルクリックの速度やマウスの感度など、"
                + "マウス設定に関するユーザー設定";
            break;
        case UserPreferenceCategory.Policy:
            s = "ユーザー権限やアクセス レベルなど、"
                + "ポリシー設定に関するユーザー設定";
            break;
        case UserPreferenceCategory.Power:
            s = "システム電源の設定に関するユーザー設定";
            break;
        case UserPreferenceCategory.Screensaver:
            s = "スクリーンセーバーに関連付けられているユーザー設定";
            break;
        case UserPreferenceCategory.Window:
            s = "システムウィンドウの大きさや"
                + "特性に関連付けられているユーザー設定";
            break;
    }
    s += "が変更されました。";
    Console.WriteLine(s);
    Console.WriteLine(e.Category);
}

AppDomain.UnhandledException イベント Occurs when an exception is not caught.

AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.UnhandledException += new UnhandledExceptionEventHandler(MyHandler);
...
 static void MyHandler(object sender, UnhandledExceptionEventArgs args) 
   {
      Exception e = (Exception) args.ExceptionObject;
      Console.WriteLine("MyHandler caught : " + e.Message);
      Console.WriteLine("Runtime terminating: {0}", args.IsTerminating);
   }
rainit2006 commented 6 years ago

AppDomain.AssemblyResolve イベント Occurs when the resolution of an assembly fails. https://msdn.microsoft.com/ja-jp/library/system.appdomain.assemblyresolve(v=vs.110).aspx

AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.AssemblyResolve += new ResolveEventHandler(MyResolveEventHandler);

AssemblyResolveイベントは標準の方法でDLLサーチしても、要求に適応した型情報が見つからない場合に発生するイベントのようです。

というわけで、AssemblyResolveイベントにメソッドを追加して、自分でAssembly.LoadFrom()で好きなファイルパスからアセンブリを読み込んで返してやればよい事になります。 例えば、自分がDLLだったとして、そこからさらに"自分のパス(拡張子抜き)/lib"という「自分(DLL)用のDLLライブラリフォルダ」を探査するイメージでサンプルを書いてみます

他のイベント: AssemblyLoad :アセンブリが読み込まれたときに発生します。 AssemblyResolve : アセンブリの解決が失敗したときに発生します。 DomainUnload : AppDomain をアンロードしようとすると発生します。

rainit2006 commented 6 years ago

IEnumerable IEnumerable を継承したクラスは foreach で使えるようになります。 IEnumerable の場合は、 foreach で使えることに加えて、より大きなメリットがあります。

IEnumerable を継承するには GetEnumerator()メソッドを実装します。 IEnumerator<T> GetEnumerator(); 加えて、 IEnumerator の GetEnumerator() メソッドも実装する必要があります。 IEnumerator GetEnumerator()

class JaggedArray<TSource> : IEnumerable<TSource>
{
    private List<TSource>[]  _list;

    public JaggedArray(int rowmax)
    {
        _list = new List<TSource>[rowmax];
    }

    public bool Add(int row, TSource val, params TSource[] restvals)
    {
        // :
    }

    public IEnumerator<TSource> GetEnumerator()
    {
        foreach (List<TSource> sublist in _list)
        {
            if (sublist != null)
            {
                // 子側の配列をイテレート
                foreach (TSource val in sublist)
                {
                    yield return val;
                }
            }   
        }
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
}

// 結果表示用のメソッド
static string Dump<TSource>(IEnumerable<TSource> source)
{
    return "{" + string.Join(", ", source) + "}";
}

static void Main()
{
    JaggedArray<int> jagary = new JaggedArray<int>(5);
    jagary.Add(0, 1);
    jagary.Add(2, 1, 2, 3, 4);
    jagary.Add(3, 1, 2);
    jagary.Add(4, 5);

    foreach (int it in jagary)
    {
        Console.Write("{0} ", it);
    }
    Console.WriteLine("\n");

    // LINQ
    Console.WriteLine("Count = {0}", jagary.Count());
    Console.WriteLine("3 Contains ? = {0}", jagary.Contains(3));
    Console.WriteLine("Max = {0}", jagary.Max());
    Console.WriteLine("Sum = {0}", jagary.Sum());
    Console.WriteLine("Average = {0}", jagary.Average());
    Console.WriteLine("ToArray = {0}", Dump(jagary.ToArray()));
    Console.WriteLine("(Source) / 2.0 = {0}", Dump(jagary.Select(it=>it/2.0)));
}

実行結果 :
1 1 2 3 4 1 2 5 

Count = 8
3 Contains ? = True
Max = 5
Sum = 19
Average = 2.375
ToArray = {1, 1, 2, 3, 4, 1, 2, 5}
(Source) / 2.0 = {0.5, 0.5, 1, 1.5, 2, 0.5, 1, 2.5}
rainit2006 commented 6 years ago

View关联

MainWindow()

private void MainWindowKeyDown(object sender, KeyEventArgs e) { if (e.Key == Key.F5) { .... } }


- SystemEvents.DisplaySettingsChanged
`SystemEvents.DisplaySettingsChanged += DisplaySettingsChanged
`

- window.Activate()函数
让已启动的窗口置前

public static class WindowManager { public static void ShowOrActivate() where TWindow : Window, new() { // 対象Windowが開かれているか探す var window = Application.Current.Windows.OfType().FirstOrDefault(); if (window == null) { // 開かれてなかったら開く window = new TWindow(); window.Show(); } else { // 既に開かれていたらアクティブにする window.Activate(); } }

    // newでインスタンスが作れない時用
    public static void ShowOrActivate<TWindow>(Func<TWindow> factory)
        where TWindow : Window
    {
        // 対象Windowが開かれているか探す
        var window = Application.Current.Windows.OfType<TWindow>().FirstOrDefault();
        if (window == null)
        {
            // 開かれてなかったら開く
            window = factory();
            window.Show();
        }
        else
        {
            // 既に開かれていたらアクティブにする
            window.Activate();
        }
    }
}


**UINavigationManger**

- NavigationService.Navigating イベント
Occurs when a new navigation is requested.
Navigating is raised when a new navigation is requested, but before the source content is requested, including when:
1,Navigate is called.
2,GoBack or GoForward is called (or an entry is selected from a navigation UI).
3,A content fragment is navigated to.

- Frame.Navigated イベント
找到要跳转的content时发生该事件。
Occurs when the content that is being navigated to has been found, and is available from the Content property, although it may not have completed loading.

- NavigatingCancelEventHandler デリゲート
Represents the method that will handle Navigating events.
rainit2006 commented 6 years ago

Layout处理

public static Size ToPixelSize(double width, double height) { Size szRet = new Size(); using (var graphics = Graphics.FromHwnd(IntPtr.Zero)) {
// No rocket science, it's just a ratio var pixelWidth = (int) (width 96.0/graphics.DpiX );
var pixelHeight = (int) (height
96.0/graphics.DpiY); szRet.Height = pixelHeight; szRet.Width = pixelWidth; } return szRet; }

    public static Size ToPixelSize(Size sz)
    {
        return ToPixelSize(sz.Width,sz.Height);
    }

   public static Point ScreenPointToLogicalPoint(Point p)
    {
        Size szTemp = ToPixelSize(p.X, p.Y);

        return new Point(szTemp.Width,szTemp.Height);

    }

- 调整Window的location

Rectangle r = SystemInformation.WorkingArea;

        Size szDesktop = LayoutHelper.ToPixelSize(r.Size);

        Point p = new Point(r.Left,r.Top);
        Point newPt = ScreenPointToLogicalPoint(p);
        if (_mainWindow.Top != newPt.Y)
            _mainWindow.Top = newPt.Y;
        if (_mainWindow.Left != newPt.X)
            _mainWindow.Left = newPt.X;

        if (_mainWindow.MaxHeight != szDesktop.Height)
            _mainWindow.MaxHeight = szDesktop.Height;
        if (_mainWindow.MinHeight != szDesktop.Height)
            _mainWindow.MinHeight = szDesktop.Height;
        if(_mainWindow.MaxWidth != szDesktop.Width)
            _mainWindow.MaxWidth = szDesktop.Width;
        if (_mainWindow.MinWidth != szDesktop.Width)
            _mainWindow.MinWidth = szDesktop.Width;

        if (_mainWindow.Width != szDesktop.Width)
            _mainWindow.Width = szDesktop.Width;
        if(_mainWindow.Height != szDesktop.Height)
            _mainWindow.Height = szDesktop.Height;

- SystemInformation.WorkingArea プロパティ
Gets the size, in pixels, of the working area of the screen.

- SHAppBarMessage function
Sends an appbar message to the system.

UINT_PTR SHAppBarMessage( In DWORD dwMessage, Inout PAPPBARDATA pData );

dwMessage的值:
ABM_GETSTATE (0x00000004) :Retrieves the autohide and always-on-top states of the Windows taskbar.
http://www.geocities.jp/katayama_hirofumi_mz/imehackerz/ja/ABM_GETSTATE.html

- Graphics.FromHwnd
Creates a new Graphics from the specified handle to a window.

private void FromHwndHwnd(PaintEventArgs e) {

// Get handle to form.
IntPtr hwnd = this.Handle;

// Create new graphics object using handle to window.
Graphics newGraphics = Graphics.FromHwnd(hwnd);

// Draw rectangle to screen.
newGraphics.DrawRectangle(new Pen(Color.Red, 3), 0, 0, 200, 100);

// Dispose of new graphics.
newGraphics.Dispose();

}


一部の API は NULL を渡すとデスクトップウィンドウのハンドルを渡したのと「同じ」動作をします

Size szRet = new Size(); using (var graphics = Graphics.FromHwnd(IntPtr.Zero)) {
// No rocket science, it's just a ratio var pixelWidth = (int) (width 96.0/graphics.DpiX );
var pixelHeight = (int) (height
96.0/graphics.DpiY); szRet.Height = pixelHeight; szRet.Width = pixelWidth; } return szRet;


- DPI対応
https://qiita.com/felis_silv/items/efee4b1a397b0b95100a
DPI取得: Graphics.DpiX プロパティ
高DPIに対応するには、アプリがDPI aware(DPI許容?)を宣言する必要があります。

app.manifest

<?xml version="1.0" encoding="utf-8"?>

true
あるいは、P/Invokeを利用して、SetProcessDPIAwareを呼び出す方法もあります。

Program.cs

static void Main() { SetProcessDPIAware(); // 後略 }

[System.Runtime.InteropServices.DllImport( "user32.dll" )] private static extern bool SetProcessDPIAware();


また問題:
文字の大きさは、自動的にDPIの設定に合わせて大きくなりましたが、 TextBoxのようなコントローラは下記コードのようにサイズが変わらないと駄目。

// 四角い枠を描く float scale = grph.DpiX / 96f; grph.DrawRectangle( Pens.Aqua, 90 scale, 50 scale, 160 scale, 50 scale );

しかし、描画するものが多い時は、すべての箇所にこのような修正を加えるのは面倒なので、ワールド変換を利用してもいいでしょう。

// 四角い枠を描く float scale = grph.DpiX / 96f; Matrix morg = grph.Transform; grph.ScaleTransform( scale, scale ); grph.DrawRectangle( Pens.Aqua, 90, 50, 160, 50 ); grph.Transform = morg;

ただしこの場合、Penも拡大されて、アンチエイリアスで線がボケることがあるので注意です。線の太さを変えないためには、別途、scaleに反比例した太さのPenを作ります。
`Pen pen = new Pen( Color.Aqua, 1 / scale );`
また、DrawStringの文字も拡大されてしまうので、同じくフォントのサイズを調整します。
`Font font = new Font( "Arial", 20 / scale );`

最終的なコードは以下の通り。

private void Form1_Load( object sender, EventArgs e ) { Bitmap bmp = new Bitmap( pictureBox1.Width, pictureBox1.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb );

using ( Graphics grph = Graphics.FromImage( bmp ) ) {
    // 塗りつぶす
    grph.Clear( Color.Black );

    // DPIに合わせてワールド変換を設定
    float scale = grph.DpiX / 96f;
    Matrix morg = grph.Transform;
    grph.ScaleTransform( scale, scale );

    // 四角い枠を描く
    using ( Pen pen = new Pen( Color.Aqua, 1 / scale ) ) {
        grph.DrawRectangle( pen, 90, 50, 160, 50 );
    }

    // 枠の中に文字を書く
    using ( Font font = new Font( "Arial", 20 / scale ) ) {
        grph.DrawString( "ABCDEFG", font, Brushes.White, 100, 60 );
    }

    // ワールド変換を元に戻しておく
    grph.Transform = morg;
}

pictureBox1.Image = bmp;

}



もう一つの方法
Fontのコンストラクタにはもう一つ有用な形式があります。
public Font( string familyName, float emSize, GraphicsUnit unit )

ここで、第3パラメータにGraphicsUnit.Pixelを指定することで、フォントをポイント単位ではなく、DPIの設定に影響を受けない、ピクセル単位で作ることができます。

なおこの場合、ポイント単位のサイズからピクセル単位のサイズに変換する必要がありますが、Windowsの標準解像度は96DPIで、1ポイントは1/72インチなので、以下の計算式となります。

px = ( pt * 96f / 72f ) * ( DPI / 96f );
rainit2006 commented 6 years ago

在system tray里显示icon 使用NotifyIcon。

Here are the summary bits:

Create a WPF Window with ShowInTaskbar=False, and which is loaded in a non-Visible State.

At class-level:

private System.Windows.Forms.NotifyIcon notifyIcon = null; During OnInitialize():

notifyIcon = new System.Windows.Forms.NotifyIcon();
notifyIcon.Click += new EventHandler(notifyIcon_Click);
notifyIcon.DoubleClick += new EventHandler(notifyIcon_DoubleClick);
notifyIcon.Icon = IconHandles["QuickLaunch"];
During OnLoaded():

notifyIcon.Visible = true;

And for interaction (shown as notifyIcon.Click and DoubleClick above):

void notifyIcon_Click(object sender, EventArgs e)
{
    ShowQuickLaunchMenu();
}

From here you can resume the use of WPF Controls and APIs such as context menus, pop-up windows, etc.