Microsoft Corporation
October 1999
Summary: This article explains the changes made in Microsoft® DirectPlay® version 7.0 that allow the DirecPlayLobby to support a number of ripple launch scenarios. (7 printed pages)
New changes made in Microsoft® DirectPlay® version 7.0 allow the DirecPlayLobby to support a number of ripple launch scenarios. In order to support these ripple launch scenarios, a few modifications have been made to the DirectPlayLobby. DirectPlayLobby supports numerous interprocess communication (IPC) facilities. The IPC connections were established using the process ID of the application being launched. This was accomplished by generated shared object names, which included the process ID of the application being launched in the shared object name. See Figure 1.
Figure 1. Normal Lobby IPC Setup
A ripple launch is where one application is required to launch another application. In this scenario, trying to establish the IPC link between the DirectPlayLobby in the Lobby Client AND the DirectPlayLobby in the lobbied application would not work. The reason is because the Lobby Client would try to establish communication on shared objects using the launcher process ID, whereas the application that was launched by the ripple launcher would try to establish communication on shared objects using its process ID. See Figure 2.
Figure 2. Unsuccessful Lobby IPC Launch
In order to alleviate this problem when using a launcher application, a command line switch is passed to the launched application informing it of the identity of the IPC objects. See Figure 3.
Figure 3. Successful Lobby IPC Launch
These changes to DirectPlayLobby will be completely transparent to existing applications or non-protected ones. The original functionality will operate the same. For those games relying on setup to create the Lobby registry entries, a new value will need to be added to the DirectPlayLobby game key, "Launcher" with the name of the launching exe (with one example, it's the game exe). The game exe name (value "File") is changed to gameexename.icd (where gameexename is the name of the original exe for the game, with an .icd extension instead of an .exe extension).
This alleviates the problems that occur in a ripple launch scenario. This design puts numerous requirements on the ripple launching application and the registration of the game for lobbying. Details follow.
RegisterApplication now accepts both LPDPAPPLICATIONDESC and LPDPAPPLICATIONDESC2 in the lpAppDesc field.
HRESULT RegisterApplication(
DWORD dwFlags,
LPVOID lpAppDesc
);
You will notice that the DPAPPLICATIONDESC2 structure has an additional field for specifying lpszAppLauncherName. This is the name of the binary that should be launched instead of the application. The application launcher should reside in the same directory as the actual binary. The application name still needs to be provided and is used in the case of WaitForConnectionSettings to find the running application on the system.
typedef struct {
DWORD dwSize;
DWORD dwFlags;
union {
LPSTR lpszApplicationNameA;
LPWSTR lpszApplicationName;
}
GUID guidApplication;
union {
LPSTR lpszFilenameA;
LPWSTR lpszFilename;
}
union {
LPSTR lpszCommandLineA;
LPWSTR lpszCommandLine;
}
union {
LPSTR lpszPathA;
LPWSTR lpszPath;
}
union {
LPSTR lpszCurrentDirectoryA;
LPWSTR lpszCurrentDirectory;
}
LPSTR lpszDescriptionA;
LPWSTR lpszDescriptionW;
} DPAPPLICATIONDESC, FAR *LPDPAPPLICATIONDESC;
typedef struct DPAPPLICATIONDESC2
{
DWORD dwSize;
DWORD dwFlags;
union
{
LPSTR lpszApplicationNameA;
LPWSTR lpszApplicationName;
};
GUID guidApplication;
union
{
LPSTR lpszFilenameA;
LPWSTR lpszFilename;
};
union
{
LPSTR lpszCommandLineA;
LPWSTR lpszCommandLine;
};
union
{
LPSTR lpszPathA;
LPWSTR lpszPath;
};
union
{
LPSTR lpszCurrentDirectoryA;
LPWSTR lpszCurrentDirectory;
};
LPSTR lpszDescriptionA;
LPWSTR lpszDescriptionW;
union
{
LPSTR lpszAppLauncherNameA;
LPWSTR lpszAppLauncherName;
};
} DPAPPLICATIONDESC2, *LPDPAPPLICATIONDESC2;
DPAPPLICATIONDESC dpad;
...
dpad.dwSize = sizeof (DPAPPLICATIONDESC);
dpad.dwFlags = 0;
dpad.lpszApplicationNameA= "MyApp";
dpad.guidApplication = {12345678-...};
dpad.lpszFilenameA= "myapp.exe";
dpad.lpszCommandLineA= "/lobbied";
dpad.lpszPathA= "C:\\games";
dpad.lpszCurrentDirectoryA= "C:\\games";
dpad.lpszDescriptionA= "My Application";
dpad.lpszDescriptionW= L"My Application";
hr = lpDPLobby3A->RegisterApplication(0, &dpad);
...
"C:\games\myapp.exe /lobbied" will be executed when RunApplication is called with the {12345678-...} GUID.
DPAPPLICATIONDESC2 dpad2;
...
dpad2.dwSize = sizeof (DPAPPLICATIONDESC2);
dpad2.dwFlags = 0;
dpad2.lpszApplicationNameA= "MyApp";
dpad2.guidApplication = {12345678-...};
dpad2.lpszFilenameA= "myapp.exe";
dpad2.lpszCommandLineA= "/lobbied";
dpad2.lpszPathA= "C:\\games";
dpad2.lpszCurrentDirectoryA= "C:\\games";
dpad2.lpszDescriptionA= "My Application";
dpad2.lpszDescriptionW= L"My Application";
dpad2.lpszAppLauncherNameA= "launcher.exe";
hr = lpDPLobby3A->RegisterApplication(0, &dpad2);
...
"C:\games\launcher.exe /dplay_ipc_guid:{12345678-...} /lobbied" will be executed when RunApplication is called with the {12345678-...} GUID. LAUNCHER.EXE must pass all command line parameters to MYAPP.EXE.
A launcher application must pass on the command line parameters with which it is provided to the application being launched. The launcher application must reside in the same directory as the application being launched.
An application that wishes to be launchable through a launcher application should silently ignore any command line parameters it does not understand. Such an application should always call GetConnectionSettings at least once before using WaitForConnectionSettings.
//
// ripple.c : example ripple launcher. Win32 Console Application
//
#include <windows.h>
#include "stdio.h"
int main(int argc, char* argv[])
{
PROCESS_INFORMATION ProcInfo;
STARTUPINFO StartupInfo;
BOOL bCreated;
LPTSTR lpCommandLine;
printf("Launcher application\n");
lpCommandLine = GetCommandLineA();
// Strip EXE name from command line, pass rest to ripple launched app.
while(*lpCommandLine && *lpCommandLine!= ' ') lpCommandLine++;
printf("passing command line: %s\n",lpCommandLine);
memset(&StartupInfo,0,sizeof(StartupInfo));
StartupInfo.cb = sizeof(StartupInfo);
bCreated=CreateProcessA(
"duel.exe", // the program we are ripple launching.
lpCommandLine,
NULL,
NULL,
TRUE,
0,
NULL,
NULL,
&StartupInfo,
&ProcInfo);
if(bCreated){
printf("Waiting for application to exit\n");
WaitForSingleObject(ProcInfo.hThread, INFINITE);
} else {
printf("Failed to create process\n");
}
printf("launcher application has left the building...\n");
return 0;
}