This article hopes to demonstrate:
- How to use P/Invoke to call unmanaged code.
- How to use Spy++ to log Windows messages and get
- How to implement
SendMessage() in C#.
- How to Interoperate with Windows Media Player.
Introduction to Win32 APIs
MSDN's definition of the Windows API is:
The Windows API (Application Programming Interface) consists of the functions, messages, data structures, data types, and statements you can use in creating applications that run under Microsoft Windows. The parts of the API you use most are code elements for calling API functions from Windows. These include procedure declarations (for the Windows functions), user-defined type definitions (for data structures passed to those functions), and constant declarations (for values passed to and returned from those functions).
In short, Windows APIs are DLLs that are embedded in the Microsoft Windows operating system. The advantage of using Windows APIs in your code is that development time and complexity may be reduced because they contain hundreds of useful functions already written. The disadvantage is that Windows APIs do not use managed code and have data types that are different from those used with Visual Studio .NET. While the .NET framework has wrapped a significant portion of the Win32 API into managed code, the ones that remain unmanaged and without a .NET equivalent may be called by managed code using the platform invoke service.
Introducing Platform Invocation Services
Platform Invocation Services (P/Invoke) is a mechanism by which managed code calls unmanaged functions that are implemented in a DLL.
When calling an unmanaged function, platform invoke must have the DLL file name and function or ordinal number. The
DLLImport attribute is used to specify the DLL location that contains the implementation of an external method and its parameters are used to specify specific behavior such as
CharSet. For more information on the syntax of the
DllImport attribute, see DllImportAttribute class at MSDN.
Table 1: Commonly used DLLs in the Win32 API
Description of its contents
Low-level operating system functions for memory management and resource handling.
Graphics Device Interface (GDI) functions for device output, such as those for drawing and font management.
Windows management functions for message handling, timers, menus, and communications.
Steps to call Win32 API using C#
- Import the
- Define functions by using
- Add code to call the Win32 API.
We are going to implement the
SendMessage() related Win32 API functions found in the User32.dll as illustrated in the Win32 class below.
public class Win32
public const int WM_COMMAND = 0x111;
public static extern int FindWindow(string strClassName,
public static extern int FindWindowEx(int hwndParent,
int hwndChildAfter, string strClassName, string strWindowName);
public static extern Int32 SendMessage(
int hWnd, int Msg, int wParam, [MarshalAs(UnmanagedType.LPStr)] string lParam);
public static extern Int32 SendMessage(
int hWnd, int Msg, int wParam, int lParam);
Windows Media Player Interoperability Conceptual Art
- Microsoft Visual Studio .NET
- Microsoft Spy++
- Windows Media Player
- Open Spy++ and press the Log Messages button, or press Ctrl + M.
- Open Microsoft Windows Media Player and arrange your windows so that Spy++ and Windows Media Player are both visible.
- Make sure Windows Media Player is not using the Auto Hide Menu bar feature and drag the Finder Tool to the outer Windows Media Player Window entitled “Windows Media Player”.
- Or -
If you know the handle of the Windows Media Player window, you may type it into the Handle box.
Next, proceed to the Messages tab and clear all selected messages. In the Messages to View list box, scroll down and select
WM_COMMAND and click on OK.
At this point, having only
WM_COMMAND selected will make the next step much easier.
Next, choose Play->Stop from the Windows Media Player Main Menu or simply press Ctrl+S for Spy++ to log the
<00001> 0023017A P WM_COMMAND wNotifyCode:0 (sent from a menu) wID:18809
The Messages view appears as shown below. Note that the first column contains the window handle, and the second column contains a message code. Message parameters and return values are on the right.
Double click on this entry to view additional Message Properties such as the
lParam hex values.
Repeating the above for the Start button yields:
<00001> 0023017A P WM_COMMAND wNotifyCode:0 (sent from a menu) wID:18808
Using P/Invoke and Spy++, it’s easy to extend functionality to include other options such as Eject, Repeat, or Volume control but for demonstration purposes, we shall only implement Start/Pause and Stop.
private System.Int32 iHandle;
private void btnStop_Click(object sender, System.EventArgs e)
Win32.SendMessage(iHandle, Win32.WM_COMMAND, 0x00004979, 0x00000000);
private void btnPlayPause_Click(object sender, System.EventArgs e)
Win32.SendMessage(iHandle, Win32.WM_COMMAND, 0x00004978, 0x00000000);
private void MainForm_Load(object sender, System.EventArgs e)
iHandle = Win32.FindWindow("WMPlayerApp", "Windows Media Player");
I had fun writing this article even though it’s not an original idea, but hopefully somebody out there will find it useful enough. Originally, I was thinking to dig deep into Windows Media player using
FindWindowEx() and start playing with
ATL:SysListView32 the “Current Playlist” to manipulate Windows Media Player Playlists on selected remote machines using WMI. If anyone has any other ideas or feature request, just let me know.
- Version 1.0 - February 08 2004 - Original submission.