Zune Now Playing support for Digsby and Winamp-compatible nowplaying software.

July 17, 2010 by Dave · 4 Comments
Filed under: Code Snippets, Software 

The Zune media player delivers Now Playing information to Windows Live Messenger, but other software isn’t able to pick up these messages.  Quite a few users have asked for Zune Now Playing support in Digsby, so I put together this little app that lets this work.  The application itself is generic, converting Zune now playing messages into the more widely implemented Winamp now playing method.

Download the executable and source (C++).

Code Listing:

#include "stdafx.h"
#include "ZuneNowPlaying.h"
#include "LimitSingleInstance.h" // KB243953
#include <regex>
 
const std::tr1::wregex pattern(L".*?\\\\0Music\\\\0.*?\\\\0.*?\\\\0(.*?)\\\\0(.*?)\\\\0");
 
CLimitSingleInstance g_SingleInstanceObj(L"{24F3CD83-9FFE-4DE6-951D-63958942F834}");
 
HWND hWndWinamp;
HWND hWndWindowsLiveMessenger;
 
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message)
	{
		case WM_COPYDATA:
			{
				COPYDATASTRUCT *cds = (COPYDATASTRUCT*)lParam;
				wchar_t *str = (wchar_t*)cds-&gt;lpData;
 
				std::tr1::wcmatch result;
				if(std::tr1::regex_search(str, result, pattern))
				{
					std::wstring ret = result[2].str() + L" - " + result[1].str();
					SetWindowText(hWndWinamp, ret.c_str());
				}
				else
				{
					SetWindowText(hWndWinamp, L"");
				}
				break;
			}
		case WM_DESTROY:
			PostQuitMessage(0);
			break;
		case WM_USER:
			if (lParam == 104)
			{
				return 1; // Winamp=Playing (always)
			}
	}
	return DefWindowProc(hWnd, message, wParam, lParam);
}
 
void RegisterClasses(HINSTANCE hInstance)
{
	WNDCLASSEX wcex = WNDCLASSEX();
	wcex.cbSize = sizeof(WNDCLASSEX);
	wcex.lpfnWndProc	= WndProc;
	wcex.hInstance		= hInstance;
	wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
	wcex.lpszClassName	= L"MsnMsgrUIManager";
	RegisterClassEx(&amp;wcex);
 
	wcex.lpszClassName	= L"Winamp v1.x";
	RegisterClassEx(&amp;wcex);
}
 
bool CreateWindows(HINSTANCE hInstance)
{
	// Pretend to be Windows Live Messenger, so WM_COPYDATA messages are sent to us.
	hWndWindowsLiveMessenger = CreateWindow(L"MsnMsgrUIManager", L"", WS_DISABLED,
		CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
	// Pretend to be Winamp (any version), so apps will pick us up for Now Playing.
	hWndWinamp = CreateWindow(L"Winamp v1.x", L"", WS_DISABLED,
		CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
 
	return hWndWinamp &amp;&amp; hWndWindowsLiveMessenger;
}
 
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPTSTR /*lpCmdLine*/, int /*nCmdShow*/)
{
	MSG msg = MSG();
	if (!g_SingleInstanceObj.IsAnotherInstanceRunning())
	{
		RegisterClasses(hInstance);
 
		if (CreateWindows(hInstance))
		{
			while (GetMessage(&amp;msg, NULL, 0, 0))
			{
				TranslateMessage(&amp;msg);
				DispatchMessage(&amp;msg);
			}
		}
	}
	return (int)msg.wParam;
}</regex>

How it works:

It’s pretty simple, Zune software sends a WM_COPYDATA message to a window with class name MsnMsgrUIManager, the message contains a pointer to a unicode string, that looks something like this:

ZUNE\0Music\01\0{0} - {1}\0TRACK_TITLE\0TRACK_ARTIST\0TRACK_ALBUM\0zune:ZUNE_GUID\0
 
// TRACK_TITLE: Title
// TRACK_ARTIST: Artist
// TRACK_ALBUM: Album
// ZUNE_GUID:  A GUID that may refer to the Zune device, Zune account, or Zune software itself.
 
// Note:  {0} - {1} is the format of how the string should be displayed in WLM - I ignore this entirely.
// Note2:  This method is used by other software, so I don't gate on ZUNE for the sake of maximum flexibility.  
// Note3:  The "1" after Music specifies the playing state.  0=Stopped, 1=Playing.  I ignore this and instead just use whether or not the other fields exist.

Note:  The \0’s in the above string are literally \ and 0, not NULL characters.

Using a regular expression to match the fields, The artist and title are extracted from the string, and set as the window caption for a window with class name Winamp v1.x. Software (like Digsby, or other now playing apps) may use FindWindow to locate the fake Winamp player, and read the caption with GetWindowText.

You may need to install the Microsoft Visual C++ 2008 SP1 Redistributable Package (x86).

WPF Windows 7 Explorer Toolbar Control

February 12, 2010 by Dave · 2 Comments
Filed under: Code Snippets 

Today I have been working on emulating the Windows 7 Explorer Toolbar in WPF.  I really like this control as it blends the older toolbar and menubar concepts into one which may support icons and dropdown menus.

WPF makes creating new controls quite easy.  I decided to base my controls off of System.Windows.Controls.Toolbar, and similarly each item in the Toolbar is based off of the Button control.

Here’s the XAML that I came up with

Creating a new WPF application and using this code for Window1.xaml, you should see this:

Note that while this control is visually similar to the Windows 7 Explorer Toolbar, it does not handle overflow, or collapse menu items like the Explorer one does. Perhaps an eventual update to this control will provide more advanced support.

This control does handle the IsMouseOver, IsMouseDown, and IsKeyboardFocused states.  Adding the menu glyph is a WPF Polygon.

C# Threading Shorthand

January 22, 2010 by Dave · Leave a Comment
Filed under: Code Snippets 

As the complexity of an application increases, often so does the threading complication.  I have two snippets which often come in useful when dealing with threads in C#.

Quickly execute code in the background:

new Thread((ThreadStart)delegate()
    {
        // code here is executed on a new thread
        // blocking operations will not block the calling thread
    }).Start();

Note that if this code is to be called often, a ThreadPool may be the better choice. ThreadPools’ have less overhead for instances when many threads would be created and destroyed.

Execute code that manipulates UI:

if (Control.IsHandleCreated)
{
   Control.Invoke((MethodInvoker)delegate()
   {
        // code here is safe to interact with Control
   });
}

Replace ‘Control’ with ‘this’ inside the Form class. Control may refer to any control created on the UI thread.

Search Urban Dictionary with C#

January 22, 2010 by Dave · Leave a Comment
Filed under: Code Snippets 

Here’s a quick example of how one might scrape the search results from Urban Dictionary.  This shows some nice use of Regular Expressions and WebClient.

(View Text)

Thankfully Urban Dictionary is a very scrape-friendly site, finding results is as easy as locating two div’s and extracting the contents.

UrbanDictionary.Search(string); will return a list of key-value pairs which contain the word as the key, and the Urban Dictionary definition as the value.

TraceListener to a textbox

January 16, 2010 by Dave · Leave a Comment
Filed under: Code Snippets 

Trace, in System.Diagnostics is useful for determining where an application failed. .NET has build-in support for writing to the console or a file on disk, but it’s a little bit more complicated to direct this output to a TextBox on a windows form.

Here’s the class you need to get the trace information.

    class FormTracer : TraceListener
    {
        public delegate void OnTextHandler(string msg, bool newLine);
        public event OnTextHandler OnText;
 
        public override void Write(string message)
        {
            if (OnText != null)
                OnText.Invoke(message, false);
        }
 
        public override void WriteLine(string message)
        {
            if (OnText != null)
                OnText.Invoke(message, true);
        }
 
        public FormTracer()
        {
            Trace.Listeners.Add(this);
        }
 
         ~FormTracer()
        {
            Trace.Listeners.Remove(this);
        }
    }

When created, FormTracer will add itself to the tracers collection and start receiving messages. To add these messages to your form, create a FormTracer object like so:

        public Form1()
        {
            InitializeComponent();
 
            FormTracer ft = new FormTracer();
            ft.OnText += new FormTracer.OnTextHandler(ft_OnText);
        }

The event handler may be called on threads outside the UI, so you’ll want to make the OnText handler look something like this

        void ft_OnText(string msg, bool newLine)
        {
            if (this.IsHandleCreated)
            {
                this.Invoke((MethodInvoker)delegate()
                {
                    textBox1.Text += msg;
                    if (newLine) textBox1.Text += "\r\n";
                });
            }
        }

Add a button and handle the Click event for a test Trace message.

          private void button1_Click(object sender, EventArgs e)
        {
            Trace.WriteLine("Button Pressed!");
        }

C#: Don’t display the startup form.

September 26, 2009 by Dave · Leave a Comment
Filed under: Code Snippets, Quick Tips 

Something that often seems to be a problem with beginning C# developers is the display of the initial form.  Normally one would always want the startup form to be displayed as soon as possible–but sometimes the application lives in the tray, or should not initially display a window.

One may first think that an event in System.Windows.Forms.Form can be handled, and the form may be directed to Hide.  Unfortunately, this won’t work as expected, the Form will always get the SW_SHOW message when Application.Run() is executed.

When creating a new project, Visual Studio will generate a Program.cs which looks similar to the following:

namespace CamOn
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
        }
    }
}

When Application.Run is invoked with a Form parameter, it will create and show the window. The easiest way to override this behavior is to simply not call Application.Run with the form:

namespace CamOn
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            new Form1();
            Application.Run();
        }
    }
}

Form1 will still be created and pump messages – but it won’t initially be shown.

Reading Motion-JPEG camera streams

September 26, 2009 by Dave · Leave a Comment
Filed under: Code Snippets 

I’ve recently come across a Hawking HNC230G network camera, which shares internals with the Edimax IC-1500Wg. These cameras pump out a Motion-JPEG stream which is unlike many other “Motion-JPEG” IP cameras.

Many of these devices will create video with a constantly updating jpeg file, served via HTTP. This is generally a better solution than what exists in my camera. Reading these streams is simple in many languages. The HNC230G took a little more probing and experimentation before I could read data without artifacts.

Camera Services

HTTP Administration website – Port 80
Motion-JPEG stream – Port 4321

There does not appear to be any images on the HTTP administration website. All video data passes through the service on 4321. Video data is not encrypted or secured in any way – there is no authentication.

Finished Protocol

After a socket is connected to the service on 4321, it should send the single command which is supported: 0110\n. Sending 0110 followed by a linebreak will signal the device to send a JFIF frame.

The camera will send 4 bytes of data before the JFIF header, these bytes can be interpreted as follows:

  • Byte 1: High 8 bits of the frame size
  • Byte 2: Low 8 bits of the frame size
  • Byte 3: Active users on the camera
  • Byte 4: Unknown reserved byte

The size of the frame is calculated only after the 4-byte header. The process can be repeated once the frame has been received by the client. The image data received will contain a JFIF (JPEG File Interchange Format) frame.

I’ve been able to achieve approximately 10fps at 640×480 with this method.

Limitations

The key limitations with this method relate to the firmware design for this camera.

  • Image size can only be set through the HTTP web service.
  • LED state can only be queried or set through the HTTP web service.
  • The device will sometimes disconnect the client or return invalid data. This is solved by reconnecting the client.
  • As the number of users increase, the possible frames-per-second decreases. Frame rates are 25-50% less when adding a second user.

Sample C# Code

       public byte[] GetFrame()
        {
            if (!conn.Connected)
            {
                Connect();
                if (!conn.Connected)
                {
                    throw new Exception("Cannot fetch frame from camera.  Not Connected.");
                }
            }
 
            StreamWriter sw = new StreamWriter(conn.GetStream());
 
            // request a frame
            sw.WriteLine("0110");
            sw.Flush();
 
                // get the high 8bits of the size
                int size = conn.GetStream().ReadByte() &lt;&lt; 8;
                // get the low 8bits of the size
                size += conn.GetStream().ReadByte();
 
                // active users
                int j1 = conn.GetStream().ReadByte();
                ActiveUsers = j1;
                // unknown byte
                int j2 = conn.GetStream().ReadByte();
 
                if (size == 0)
                {
                    return null;
                }
 
                return new BinaryReader(conn.GetStream()).ReadBytes(size);
        }

C#: WebClient Usage

May 9, 2008 by Dave · 12 Comments
Filed under: Code Snippets, Quick Tips 

Microsoft has provided a great utility since .NET 1.0 for doing quick I/O via websites.  The WebClient class provides basic functionality for downloading from and uploading to webservers.  the WebClient example on MSDN leaves a lot to be desired, especially for beginners.  I’d like to expand on that, as well as provide some example code.

C#: Using WebClient to fetch a page:

        // create a new instance of WebClient
        WebClient client = new WebClient();
 
        // set the user agent to IE6
        client.Headers.Add("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 1.0.3705;)");
        try
        {
            // actually execute the GET request
            string ret = client.DownloadString("http://www.google.com/");
 
            // ret now contains the contents of the webpage
            Console.WriteLine("First 256 bytes of response: " + ret.Substring(0,265));
        }
        catch (WebException we)
        {
            // WebException.Status holds useful information
            Console.WriteLine(we.Message + "\n" + we.Status.ToString());
        }
        catch (NotSupportedException ne)
        {
            // other errors
            Console.WriteLine(ne.Message);
        }

(This code uses DownloadString, you can also use DownloadData for a more binary-friendly version)

This is great for fetching simple pages that have data encoded in the querystring, but there are some problems with the basic DownloadString method of WebClient.  It’s synchronous, so it will block until the operation completes.  So for slow connections, or large files, this would need to run in another thread.  There is a better way.  But first, here’s another example of another basic, but important, method, DownloadFile.

        // create a new instance of WebClient
        WebClient client = new WebClient();
 
        // set the user agent to IE6
        client.Headers.Add("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 1.0.3705;)");
        try
        {
            // actually execute the GET request
            client.DownloadFile("http://www.google.com/","google_fetch.txt");
 
            // ret now contains the contents of the webpage
            Console.WriteLine("File Saved.");
        }
        catch (WebException we)
        {
            // WebException.Status holds useful information
            Console.WriteLine(we.Message + "\n" + we.Status.ToString());
        }
        catch (NotSupportedException ne)
        {
            // other errors
            Console.WriteLine(ne.Message);
        }

This example is almost identical to the above, aside from the client.DownloadFile method.  This will save the file to disk, instead of returning a string–often what you would end up doing with the string anyway.

But can I send POST data?

Yes!  Using WebClient.UploadString or WebClient.UploadData you can POST data to the server easily.  I’ll show an example using UploadData, since UploadString is used in the same manner as DownloadString.

            byte[] bret = client.UploadData("http://www.website.com/post.php", "POST",
                System.Text.Encoding.ASCII.GetBytes("field1=value1&amp;field2=value2") );
 
            string sret = System.Text.Encoding.ASCII.GetString(bret);

UploadData returns a byte array (byte[]) which contains the contents of the page.  This can easily be converted to a string by using the System.Text.Encoding.ASCII.GetString() method.  You can also use the other encoding types in System.Text.Encoding (such as UTF8) to do the same.

Also note the use of GetBytes(), the UploadData method will only take a byte array as a buffer for the upload.  This works well if you are uploading a binary file.

What about the asynchronous methods of WebClient?

WebClient provides asynchronous methods for fetching webpages as well, they are named similarly to the synchronous methods, DownloadFileAsync, DownloadDataAsync, DownloadStringAsync, UploadFileAsync, UploadDataAsync, UploadStringAsync, and UploadValuesAsync.

In order to execute an Async request, you will need to attach event handlers before the method is executed.  Without these event handlers you will not have any sense of when the operation finishes, or what stage it is at.

C# WebClient Asynchronous call example:

void do_upload() {
   WebClient client = new WebClient();
   // add event handlers for completed and progress changed
   client.UploadProgressChanged += new UploadProgressChangedEventHandler(client_UploadProgressChanged);
 
   client.UploadFileCompleted += new UploadFileCompletedEventHandler(client_UploadFileCompleted);
  // carry out the operation as normal
   client.UploadFileAsync("http://www.daveamenta.com/up.php", @"c:\somefile.bin");
}
 
void client_UploadProgressChanged(object sender, UploadProgressChangedEventArgs e)
{
   Console.WriteLine(e.ProgressPercentage);
}
 
void client_UploadFileCompleted(object sender, UploadFileCompletedEventArgs e)
{
   if(e.Result != null) {
      Console.WriteLine(System.Text.Encoding.ASCII.GetString(e.Result));
   }
}

The call to client.DownloadFileAsync will no longer block the thread, and will execute in the background, periodically calling the event handlers to display progress.  The other methods can be used in this same way.

C#: Send text to a pastebin (HttpWebRequest POST example)

May 9, 2008 by Dave · 4 Comments
Filed under: Code Snippets 

I’m working on a small application that hooks into the clipboard monitor chain and displays a list of options whenever text is saved to the clipbaord.  One of the options I wanted was to be able to send data to pastebin.com, a popular text/code sharing service.  This is a relatively mundane task, faking the postdata for a webpage, and sending the requests as a browser.  There were some hicups though, mostly with Finding the redirect URL using HttpWebRequest/HttpWebResponse.

A little background:

HttpWebRequest and HttpWebResponse live in the System.Net namespace, and provide the ability to quickly send HTTP requests to a webserver.  This is what a simple GET request would look like:

try
{
 
    HttpWebRequest request  = (HttpWebRequest) WebRequest.Create("http://davux.pastebin.com/pastebin.php");
 
    HttpWebResponse res =(HttpWebResponse) request.GetResponse();
    if (res.StatusCode == HttpStatusCode.OK)
    {
          StreamReader reader = new StreamReader(res.GetResponseStream());
          Console.WriteLine(reader.ReadToEnd());
    } else {
         // handle errors
    }
 
    res.Close();
 
}
catch (Exception ex)
{
    Console.WriteLine("Error: " + ex.Message);
}

Note: For most simple tasks, it is wise to use System.Net.WebClient, which is not suitable for this task, because it does not expose the ability to disable automatic redirection.

To send new data to pastebin, it needs to be sent via HTTP POST.  Using Firefox LiveHTTPHeaders, I determined what string needs to be sent in order to create a new entry.  This can also be determined by reading the source of the page, and looking at each of the fields contained in the <form> block.

string post = "&amp;parent_pid=&amp;format=text&amp;code2=" + code_text_to_send + "&amp;poster=" + poster_name + "&amp;paste=Send&amp;expiry=" + expiry + "&amp;email=";
  • code_text_to_send – The data which should be used to create a new page.
  • poster_name – The author of the post
  • expiry – How long the data should be retained for.  Options are ‘d’ for one day, ‘m’ for one month, and ‘f’ for indefinitely.

Note: Make sure to URLEncode each field that is sent!

After further examination of the headers and data sent back, I decided that the best way to extract the link was to catch the HTTP 302 Document Found response.

HTTP/1.x 302 Found
Date: Thu, 08 May 2008 01:06:08 GMT
Server: Apache/1.3.33 (Debian GNU/Linux) mod_python/2.7.10 Python/2.3.4 PHP/4.3.10-22 mod_perl/1.29
X-Powered-By: PHP/4.3.10-22
Location: http://davux.pastebin.com/m10a794d6
Keep-Alive: timeout=15, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: text/html; charset=iso-8859-1

HttpWebRequest, by default, has AllowAutoRedirect set to true, which means that, internally, it will resolve this message, and fetch the new URL.  This extra operation is needless in this circumstance, so it is best to disable it and extract the URL from the header.  The headers associated with each request or response are found in the Headers object for each class.  We wish to look at the Location field to find our URL.
C# WebClient HTTP POST Example:

try
{
 
    HttpWebRequest request = (HttpWebRequest)
        WebRequest.Create("http://davux.pastebin.com/pastebin.php");
 
    request.AllowAutoRedirect = false;
    request.Method = "POST";
 
    string post = "&amp;parent_pid=&amp;format=text&amp;code2=" + HttpUtility.UrlEncode(text) + "&amp;poster=Dave&amp;paste=Send&amp;expiry=m&amp;email=";
    byte[] data = System.Text.Encoding.ASCII.GetBytes(post);
 
    request.ContentType = "application/x-www-form-urlencoded";
    request.ContentLength = data.Length;
 
    Stream response = request.GetRequestStream();
 
    response.Write(data,0,data.Length);
 
    response.Close();
 
    HttpWebResponse res =(HttpWebResponse) request.GetResponse();
    res.Close();
    // note that there is no need to hook up a StreamReader and
    // look at the response data, since it is of no need
 
    if (res.StatusCode == HttpStatusCode.Found)
    {
        Console.WriteLine(res.Headers["location"]);
    }
    else
    {
        Console.WriteLine("Error");
    }
 
}
catch (Exception ex)
{
    Console.WriteLine("Error: " + ex.Message);
}

(Some error checking and extra code have been removed for the sake of clarity)

The values for expiry and author have been hard-coded in order to save space.  This basic example will create a new Pastebin entry on my not so private pastebin, and print the URL of the entry to the console.  If there is an error, it will print the exception message.  If the HTTP code returned is not 302/Found, it will print a generic error.  (If the server is not returning a 302 for this request, there is nothing codewise that can be fixed, and thus the only option for error is generic.)

C#: Delete a file to the recycle bin

May 5, 2008 by Dave · 7 Comments
Filed under: Code Snippets 

I had a friend ask me yesterday what the easiest/best way to delete a file to the recycle bin was, in C# that is.  I knew about SHFileOperation, but I didn’t know if .NET 2.0 had native support for sending files to the recycle bin.  As it turns out, there are two ways in which you can move files to the recycle bin.  Microsoft has included the support in the Microsoft.VisualBasic namespace, and referencing the Microsoft.VisualBasic assembly will allow you to use that function directly.

Deleting a file using Managed code in C# or VB.NET:

(Remember to add a reference to Microsoft.VisualBasic)

using Microsoft.VisualBasic;
 
string path = @"c:\myfile.txt";
FileIO.FileSystem.DeleteDirectory(path, FileIO.UIOption.OnlyErrorDialogs, .RecycleOption.SendToRecycleBin);

The other alternative is to use COM Interop and P/Invoke the SHFileOperation from the Windows shell32.dll.

Deleting a file using P/Invoke in C#:

I have written a class that provides methods to easily specify how the file should be sent to the recycle bin.  SHFileOperation can take flags, which I have put into an enum and commented.  These flags define how the operation should be executed.

  • FOF_SILENT – Do not show the “Recycling…” dialog.
  • FOF_NOCONFIRMATION – Do not prompt the use for confirmation of the operation.
  • FOF_ALLOWUNDO – This is the option that specifies that the file should be sent to the recycle bin.  It is added by default in each Send method.
  • FOF_SIMPLEPROGRESS – This option will surpress the display of the names of the files that are being deleted.
  • FOF_NOERRORUI – This option will suppress any errors encountered during the operation.
  • FOF_WANTNUKEWARNING – Adding this flag will allow a dialog to allow the user to cancel removing large files permanently, if they are too large for the recycle bin.

Any one of these flags can be added to change the operation.

Methods provided by the RecycleBin class:

  • bool Send(string path)
  • bool Send(string path, FileOperationFlags flags)
  • static bool SendSilent(string path)

The first will simply send a path to the bin while showing the dialog, but surpressing the confirmation.

The second will allow you to customize the flags for the file operation, the only flag set for you is FOF_ALLOWUNDO. Flags can be combined with the | operator. e.g. FOF_NOERRORUI | FOF_SIMPLEPROGRESS.

The third, and probably most commonly used, will surpress all errors and warnings, and not show the user any operation is in progress.

Code Sample:

SendToRecycleBin.zip [ZIP] [Visual Studio 2008] (43KB)

Update – 04/18/2010:  Updated sample for x64 compatibility.

Next Page »