This post is to document ways of communicating with MAME. Since MAME has a built in method for sending data to external applications this article will concentrate on ways of sending data to MAME. For info on receiving data from MAME check out my
MAMEInterop SDK 1.1 and it's page on BYOAC
here. It includes a
Mame.dll that simplifies communication and includes sample code in C#, VB.NET, C++, Delphi and VB6.
The first method to send data to MAME is to use MAME's own output system's window to receive data. This seems like the most obvious choice since MAME already communicates to external apps this way albeit one-way.
The files of interest are
src/osd/windows/output.c and
src/osd/windows/output.h. The following example will modify MAME to receive a custom message for pausing and unpausing. You can view the full diff @
bezel_0129u2.zip although it is for a relatively old version of MAME.
First of all let's check out
output.h. You will notice MAME has some output messages defined.
#define OM_MAME_START TEXT("MAMEOutputStart")
#define OM_MAME_STOP TEXT("MAMEOutputStop")
#define OM_MAME_UPDATE_STATE TEXT("MAMEOutputUpdateState")
#define OM_MAME_REGISTER_CLIENT TEXT("MAMEOutputRegister")
#define OM_MAME_UNREGISTER_CLIENT TEXT("MAMEOutputUnregister")
#define OM_MAME_GET_ID_STRING TEXT("MAMEOutputGetIDString")
These are all the custom messages that can be sent to other applications. These messages are registered using the Win32 API call
RegisterWindowMessage(). Both the sending and receiving applications need to register these in order to communicate. So let's add our own custom message.
// IM_MAME_PAUSE: when received will pause mame.
// LPARAM = State, 1=Pause, 0=UnPause
#define IM_MAME_PAUSE TEXT("MAMEInputPause")
Here is our custom
IM_MAME_PAUSE message and we will use
lParam to tell it to pause or unpause.
Now let's take a look at
output.c where we will need to add code to register our message.
First of all we need a variable to storing the message id
static UINT im_mame_pause;
Then we need to register our message just as the input messages are registered
// HK: Add our pause input message
im_mame_pause = RegisterWindowMessage(IM_MAME_PAUSE);
assert(im_mame_pause != 0);
To send this custom message we need to register the same message and then we use
PostMessage() to MAME's output window. It will look like this
PostMessage(hWnd, im_mame_pause, null, pauseState);
Now we need to add some more code to
output.c to receive and process the message.
output_window_proc is MAME's Output Window's
WndProc function. Here we will add code to process the pause message
// HK: Pause message received
if (message == im_mame_pause)
mame_pause(machine, lparam);
Remember we use the
lParam parameter to tell MAME to pause or unpause.
lParam is the last parameter of the
PostMessage() Win32 API call.
------------------------------------------
NOTE: This is by no means the easiest way to pause MAME from an external application. I've found a much easier way by sending a built in message to MAME's main window instead of it's output window. Using this method requires no modification to MAME's source code.
PostMessage(hWnd, WM_USER_UI_TEMP_PAUSE, 1, null); // Pause MAME
PostMessage(hWnd, WM_USER_UI_TEMP_PAUSE, 0, null); // UnPause MAME
------------------------------------------
Which brings me to one more method for sending messages to MAME and that is it's main game window. You can use the Win32 API
FindWindow() with the class name of "MAME" to find it. MAME can have multiple windows so I actually recommend using
EnumWindows() and do a match for the window name starting with "MAME:" and the class name "MAME". Note that MAME's window title will have "MAME: [ROM_NAME]" so you cannot do an exact match of the window's title using
FindWindow() only it's class "MAME". Using
EnumWindows() you can do a partial match of "MAME:" and then you can even grab the ROM name from it.
Let's look at MAME's main
WndProc function located in
src\osd\windows\window.cThe function is called
winwindow_video_window_proc. As with our previous method instead of sending messages to MAME's output window we send them directly to MAME using
PostMessage() and process them in it's
WinProc. We can simply use
WM_USER + a custom offset. This is commonly used for custom messages.
Eg.
#define WM_USER_MY_CUSTOM_MESSAGE (WM_USER + 8)
Since MAME already uses custom messages we should make sure ours doesn't interfere with MAME's. MAME has the following custom messages defined.
#define WM_USER_FINISH_CREATE_WINDOW (WM_USER + 0)
#define WM_USER_SELF_TERMINATE (WM_USER + 1)
#define WM_USER_REDRAW (WM_USER + 2)
#define WM_USER_SET_FULLSCREEN (WM_USER + 3)
#define WM_USER_SET_MAXSIZE (WM_USER + 4)
#define WM_USER_SET_MINSIZE (WM_USER + 5)
#define WM_USER_UI_TEMP_PAUSE (WM_USER + 6)
#define WM_USER_EXEC_FUNC (WM_USER + 7)
So we can simply start our custom messages at
(WM_USER + 8 ). Then all we do is add our code to process the message in winwindow_video_window_proc.
case WM_USER_MY_CUSTOM_MESSAGE:
{
// Process wParam and/or lParam
break;
}
Now you can just use
PostMessage() to send MAME the message and use the
wParam and/or
lParam as custom parameters.
PostMessage(hWnd, WM_USER_MY_CUSTOM_MESSAGE, wParam, lParam);