mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-27 18:03:04 +01:00
1171 lines
33 KiB
C++
1171 lines
33 KiB
C++
/*
|
|
* The contents of this file are subject to the Interbase Public
|
|
* License Version 1.0 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy
|
|
* of the License at http://www.Inprise.com/IPL.html
|
|
*
|
|
* Software distributed under the License is distributed on an
|
|
* "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
|
|
* or implied. See the License for the specific language governing
|
|
* rights and limitations under the License.
|
|
*
|
|
* The Original Code was created by Inprise Corporation
|
|
* and its predecessors. Portions created by Inprise Corporation are
|
|
* Copyright (C) Inprise Corporation.
|
|
*
|
|
* All Rights Reserved.
|
|
* Contributor(s): ______________________________________.
|
|
*/
|
|
#include "firebird.h"
|
|
#include "../jrd/common.h"
|
|
#include <stdio.h>
|
|
#include "../jrd/gds_proto.h"
|
|
#include <stdlib.h>
|
|
#include <windows.h>
|
|
#include <shellapi.h>
|
|
#include <prsht.h>
|
|
#include <commctrl.h>
|
|
#if TIME_WITH_SYS_TIME
|
|
# include <sys/time.h>
|
|
# include <time.h>
|
|
#else
|
|
# if HAVE_SYS_TIME_H
|
|
# include <sys/time.h>
|
|
# else
|
|
# include <time.h>
|
|
# endif
|
|
#endif
|
|
#include "../iscguard/iscguard.rh"
|
|
#include "../iscguard/iscguard.h"
|
|
#include "../iscguard/cntlg_proto.h"
|
|
#include "../utilities/install/install_nt.h"
|
|
#include "../remote/os/win32/window.h"
|
|
#include "../remote/os/win32/chop_proto.h"
|
|
#include "../common/config/config.h"
|
|
|
|
#ifdef WIN_NT
|
|
#include <process.h> /* _beginthread */
|
|
#endif
|
|
|
|
/* Startup Configuration Entry point for regcfg.exe. */
|
|
//#define SVC_CONFIG 4
|
|
//#define REGCFG_ENTRYPOINT "LaunchInstReg"
|
|
//#define REGCFG_DLL "REGCFG.DLL"
|
|
typedef void (__cdecl * LPFNREGCFG) (char *, short);
|
|
|
|
// Define an array of dword pairs,
|
|
// where the first of each pair is the control ID,
|
|
// and the second is the context ID for a help topic,
|
|
// which is used in the help file.
|
|
static DWORD aMenuHelpIDs[] = {
|
|
IDC_VERSION, ibs_guard_version,
|
|
IDC_LOG, ibs_guard_log,
|
|
IDC_LOCATION, ibs_server_directory,
|
|
0, 0
|
|
};
|
|
|
|
/* Function prototypes */
|
|
static LRESULT CALLBACK WindowFunc(HWND, UINT, WPARAM, LPARAM);
|
|
static THREAD_ENTRY_DECLARE WINDOW_main(THREAD_ENTRY_PARAM);
|
|
#ifdef NOT_USED_OR_REPLACED
|
|
static void StartGuardian(HWND);
|
|
#endif
|
|
static bool parse_args(LPCSTR, TEXT*);
|
|
#ifdef NOT_USED_OR_REPLACED
|
|
static void HelpCmd(HWND, HINSTANCE, WPARAM);
|
|
#endif
|
|
//char* ChopFileName (char*, char*, long);
|
|
|
|
THREAD_ENTRY_DECLARE start_and_watch_server(THREAD_ENTRY_PARAM);
|
|
THREAD_ENTRY_DECLARE swap_icons(THREAD_ENTRY_PARAM);
|
|
void write_log(int, const char*);
|
|
|
|
HWND DisplayPropSheet(HWND, HINSTANCE);
|
|
LRESULT CALLBACK GeneralPage(HWND, UINT, WPARAM, LPARAM);
|
|
|
|
|
|
HINSTANCE hInstance_gbl;
|
|
HWND hPSDlg, hWndGbl;
|
|
static int nRestarts = 0; /* the number of times the server was restarted */
|
|
static bool service_flag = true;
|
|
static TEXT instance[MAXPATHLEN];
|
|
static Firebird::string* service_name = NULL;
|
|
static Firebird::string* remote_name = NULL;
|
|
static Firebird::string* mutex_name = NULL;
|
|
/* unsigned short shutdown_flag = FALSE; */
|
|
static log_info* log_entry;
|
|
|
|
|
|
int WINAPI WinMain(
|
|
HINSTANCE hInstance,
|
|
HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow)
|
|
{
|
|
/**************************************
|
|
*
|
|
* m a i n
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* The main routine for Windows based server guardian.
|
|
*
|
|
**************************************/
|
|
|
|
OSVERSIONINFO OsVersionInfo;
|
|
|
|
/* need to set the sizeof this structure for NT to work */
|
|
OsVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
|
|
|
|
GetVersionEx((LPOSVERSIONINFO) & OsVersionInfo);
|
|
|
|
/* service_flag is TRUE for NT false for 95 */
|
|
service_flag = (OsVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT);
|
|
|
|
if (service_flag) {
|
|
strcpy(instance, DEFAULT_INSTANCE);
|
|
service_flag = parse_args(lpszCmdLine, instance);
|
|
MemoryPool& pool = *getDefaultMemoryPool();
|
|
service_name = FB_NEW(pool) Firebird::string(pool);
|
|
service_name->printf(ISCGUARD_SERVICE, instance);
|
|
remote_name = FB_NEW(pool) Firebird::string(pool);
|
|
remote_name->printf(REMOTE_SERVICE, instance);
|
|
mutex_name = FB_NEW(pool) Firebird::string(pool);
|
|
mutex_name->printf(GUARDIAN_MUTEX, instance);
|
|
}
|
|
|
|
/* set the global HINSTANCE as we need it in WINDOW_main */
|
|
hInstance_gbl = hInstance;
|
|
|
|
/* allocate space for the event list */
|
|
log_entry =
|
|
reinterpret_cast<log_info*>(malloc(sizeof(log_info)));
|
|
log_entry->next = NULL;
|
|
|
|
/* since the flag is set we run as a service */
|
|
if (service_flag) {
|
|
CNTL_init(WINDOW_main, instance);
|
|
|
|
const SERVICE_TABLE_ENTRY service_table[] = {
|
|
{const_cast<char*>(service_name->c_str()), CNTL_main_thread},
|
|
{NULL, NULL}
|
|
};
|
|
|
|
// BRS There is a error in MinGW (3.1.0) headers
|
|
// the parameter of StartServiceCtrlDispatcher is declared const in msvc headers
|
|
#if defined(MINGW)
|
|
if (!StartServiceCtrlDispatcher(const_cast<SERVICE_TABLE_ENTRY*>(service_table)))
|
|
#else
|
|
if (!StartServiceCtrlDispatcher(service_table))
|
|
#endif
|
|
{
|
|
if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
|
|
CNTL_shutdown_service("StartServiceCtrlDispatcher failed");
|
|
}
|
|
}
|
|
else {
|
|
return (WINDOW_main(0));
|
|
}
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
static bool parse_args(LPCSTR lpszArgs, TEXT* instance)
|
|
{
|
|
/**************************************
|
|
*
|
|
* p a r s e _ a r g s
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* WinMain gives us a stupid command string, not
|
|
* a cool argv. Parse through the string and
|
|
* set the options.
|
|
* Returns
|
|
* A value of true or false depending on if -s is specified.
|
|
* CVC: Service is the default for NT, use -a for application.
|
|
*
|
|
*
|
|
**************************************/
|
|
bool is_service = true;
|
|
|
|
for (const char* p = lpszArgs; *p; p++) {
|
|
if (*p++ == '-') {
|
|
char c;
|
|
while (c = *p++) {
|
|
switch (UPPER(c)) {
|
|
case 'A':
|
|
is_service = false;
|
|
break;
|
|
|
|
case 'S':
|
|
while (*p && *p == ' ')
|
|
p++;
|
|
if (*p && *p != '-') {
|
|
while (*p && *p != ' ') {
|
|
*instance++ = *p++;
|
|
}
|
|
*instance++ = '\0';
|
|
}
|
|
break;
|
|
|
|
default:
|
|
is_service = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return is_service;
|
|
}
|
|
|
|
static THREAD_ENTRY_DECLARE WINDOW_main(THREAD_ENTRY_PARAM)
|
|
{
|
|
/**************************************
|
|
*
|
|
* W I N D O W _ m a i n
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
*
|
|
* This function is where the actual service code starts.
|
|
* Do all the window init stuff, then fork off a thread for starting
|
|
* the server.
|
|
*
|
|
**************************************/
|
|
|
|
unsigned long thread_id = 0;
|
|
|
|
/* If we're a service, don't create a window */
|
|
if (service_flag) {
|
|
gds__thread_start(start_and_watch_server, 0,
|
|
THREAD_medium, 0, &thread_id);
|
|
|
|
if (thread_id == (DWORD) -1) {
|
|
/* error starting server thread */
|
|
char szMsgString[256];
|
|
LoadString(hInstance_gbl, IDS_CANT_START_THREAD, szMsgString, 256);
|
|
gds__log(szMsgString);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Make sure that there is only 1 instance of the guardian running */
|
|
HWND hWnd = FindWindow(GUARDIAN_CLASS_NAME, GUARDIAN_APP_NAME);
|
|
if (hWnd) {
|
|
char szMsgString[256];
|
|
LoadString(hInstance_gbl, IDS_ALREADYSTARTED, szMsgString, 256);
|
|
MessageBox(NULL, szMsgString, GUARDIAN_APP_LABEL, MB_OK | MB_ICONHAND);
|
|
gds__log(szMsgString);
|
|
return 0;
|
|
}
|
|
|
|
/* initialize main window */
|
|
WNDCLASS wcl;
|
|
wcl.hInstance = hInstance_gbl;
|
|
wcl.lpszClassName = GUARDIAN_CLASS_NAME;
|
|
wcl.lpfnWndProc = WindowFunc;
|
|
wcl.style = 0;
|
|
wcl.hIcon = LoadIcon(hInstance_gbl, MAKEINTRESOURCE(IDI_IBGUARD));
|
|
wcl.hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
wcl.lpszMenuName = NULL;
|
|
wcl.cbClsExtra = 0;
|
|
wcl.cbWndExtra = 0;
|
|
wcl.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
|
|
|
|
if (!RegisterClass(&wcl)) {
|
|
char szMsgString[256];
|
|
LoadString(hInstance_gbl, IDS_REGERROR, szMsgString, 256);
|
|
MessageBox(NULL, szMsgString, GUARDIAN_APP_LABEL, MB_OK);
|
|
return 0;
|
|
}
|
|
|
|
hWnd = CreateWindowEx(0,
|
|
GUARDIAN_CLASS_NAME,
|
|
GUARDIAN_APP_NAME,
|
|
WS_DLGFRAME | WS_SYSMENU | WS_MINIMIZEBOX,
|
|
CW_USEDEFAULT,
|
|
CW_USEDEFAULT,
|
|
CW_USEDEFAULT,
|
|
CW_USEDEFAULT,
|
|
HWND_DESKTOP, NULL, hInstance_gbl, NULL);
|
|
|
|
/* Save the window handle for the thread */
|
|
hWndGbl = hWnd;
|
|
|
|
/* begin a new thread for calling the start_and_watch_server */
|
|
gds__thread_start(start_and_watch_server, 0,
|
|
THREAD_medium, 0, &thread_id);
|
|
|
|
if (thread_id == (DWORD) -1) {
|
|
/* error starting server thread */
|
|
char szMsgString[256];
|
|
LoadString(hInstance_gbl, IDS_CANT_START_THREAD, szMsgString, 256);
|
|
MessageBox(NULL, szMsgString, GUARDIAN_APP_LABEL, MB_OK);
|
|
gds__log(szMsgString);
|
|
DestroyWindow(hWnd);
|
|
return 0;
|
|
}
|
|
|
|
SendMessage(hWnd, WM_COMMAND, IDM_CANCEL, 0);
|
|
UpdateWindow(hWnd);
|
|
|
|
MSG message;
|
|
while (GetMessage(&message, NULL, 0, 0)) {
|
|
if (hPSDlg) { /* If property sheet dialog is open */
|
|
/* Check if the message is property sheet dialog specific */
|
|
BOOL bPSMsg = PropSheet_IsDialogMessage(hPSDlg, &message);
|
|
|
|
/* Check if the property sheet dialog is still valid, if not destroy it */
|
|
if (!PropSheet_GetCurrentPageHwnd(hPSDlg)) {
|
|
DestroyWindow(hPSDlg);
|
|
hPSDlg = NULL;
|
|
}
|
|
if (bPSMsg)
|
|
continue;
|
|
}
|
|
TranslateMessage(&message);
|
|
DispatchMessage(&message);
|
|
}
|
|
return message.wParam;
|
|
}
|
|
|
|
|
|
static LRESULT CALLBACK WindowFunc(
|
|
HWND hWnd,
|
|
UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
/**************************************
|
|
*
|
|
* W i n d o w _ F u n c
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
*
|
|
* This function is where the windowing action takes place.
|
|
* Handle the various messages which come here from GetMessage.
|
|
*
|
|
**************************************/
|
|
|
|
static BOOL bInTaskBar = FALSE;
|
|
static bool bStartup = false;
|
|
static HINSTANCE hInstance = NULL;
|
|
static unsigned long thread_id;
|
|
|
|
hInstance = (HINSTANCE) GetWindowLong(hWnd, GWL_HINSTANCE);
|
|
switch (message) {
|
|
case WM_CLOSE:
|
|
{
|
|
/* Clean up memory for log_entry */
|
|
while (log_entry->next) {
|
|
log_info* tmp = log_entry->next;
|
|
free(log_entry);
|
|
log_entry = tmp;
|
|
}
|
|
free(log_entry);
|
|
DestroyWindow(hWnd);
|
|
}
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
switch (wParam) {
|
|
case IDM_CANCEL:
|
|
ShowWindow(hWnd, bInTaskBar ? SW_HIDE : SW_MINIMIZE);
|
|
return TRUE;
|
|
|
|
case IDM_OPENPOPUP:
|
|
{
|
|
/* The SetForegroundWindow() has to be called because our window
|
|
does not become the Foreground one (inspite of clicking on
|
|
the icon). This is so because the icon is painted on the task
|
|
bar and is not the same as a minimized window.
|
|
*/
|
|
SetForegroundWindow(hWnd);
|
|
|
|
HMENU hPopup = CreatePopupMenu();
|
|
char szMsgString[256];
|
|
LoadString(hInstance, IDS_SVRPROPERTIES, szMsgString, 256);
|
|
AppendMenu(hPopup, MF_STRING, IDM_SVRPROPERTIES, szMsgString);
|
|
LoadString(hInstance, IDS_SHUTDOWN, szMsgString, 256);
|
|
AppendMenu(hPopup, MF_STRING, IDM_SHUTDOWN, szMsgString);
|
|
LoadString(hInstance, IDS_PROPERTIES, szMsgString, 256);
|
|
AppendMenu(hPopup, MF_STRING, IDM_PROPERTIES, szMsgString);
|
|
SetMenuDefaultItem(hPopup, IDM_PROPERTIES, FALSE);
|
|
|
|
POINT curPos;
|
|
GetCursorPos(&curPos);
|
|
TrackPopupMenu(hPopup, TPM_LEFTALIGN | TPM_RIGHTBUTTON,
|
|
curPos.x, curPos.y, 0, hWnd, NULL);
|
|
DestroyMenu(hPopup);
|
|
return TRUE;
|
|
}
|
|
|
|
case IDM_SHUTDOWN:
|
|
{
|
|
HWND hTmpWnd = FindWindow(szClassName, szWindowName);
|
|
PostMessage(hTmpWnd, WM_COMMAND, (WPARAM) IDM_SHUTDOWN, 0);
|
|
}
|
|
return TRUE;
|
|
|
|
case IDM_PROPERTIES:
|
|
if (!hPSDlg)
|
|
hPSDlg = DisplayPropSheet(hWnd, hInstance);
|
|
else
|
|
SetForegroundWindow(hPSDlg);
|
|
return TRUE;
|
|
|
|
case IDM_INTRSVRPROPERTIES:
|
|
return TRUE;
|
|
|
|
case IDM_SVRPROPERTIES:
|
|
{
|
|
HWND hTmpWnd = FindWindow(szClassName, szWindowName);
|
|
PostMessage(hTmpWnd, WM_COMMAND, (WPARAM) IDM_PROPERTIES, 0);
|
|
}
|
|
return TRUE;
|
|
}
|
|
break;
|
|
|
|
case WM_SWITCHICONS:
|
|
nRestarts++;
|
|
gds__thread_start(swap_icons, hWnd,
|
|
THREAD_medium, 0, &thread_id);
|
|
break;
|
|
|
|
case ON_NOTIFYICON:
|
|
if (bStartup) {
|
|
SendMessage(hWnd, WM_COMMAND, 0, 0);
|
|
return TRUE;
|
|
}
|
|
switch (lParam) {
|
|
case WM_LBUTTONDOWN:
|
|
break;
|
|
|
|
case WM_LBUTTONDBLCLK:
|
|
PostMessage(hWnd, WM_COMMAND, (WPARAM) IDM_PROPERTIES, 0);
|
|
break;
|
|
|
|
case WM_RBUTTONUP:
|
|
PostMessage(hWnd, WM_COMMAND, (WPARAM) IDM_OPENPOPUP, 0);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case WM_CREATE:
|
|
if (!service_flag) {
|
|
HICON hIcon = (HICON) LoadImage(hInstance, MAKEINTRESOURCE(IDI_IBGUARD),
|
|
IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR);
|
|
|
|
NOTIFYICONDATA nid;
|
|
nid.cbSize = sizeof(NOTIFYICONDATA);
|
|
nid.hWnd = hWnd;
|
|
nid.uID = IDI_IBGUARD;
|
|
nid.uFlags = NIF_TIP | NIF_ICON | NIF_MESSAGE;
|
|
nid.uCallbackMessage = ON_NOTIFYICON;
|
|
nid.hIcon = hIcon;
|
|
lstrcpy(nid.szTip, GUARDIAN_APP_LABEL);
|
|
|
|
/* This will be true if we are using the explorer interface */
|
|
bInTaskBar = Shell_NotifyIcon(NIM_ADD, &nid);
|
|
|
|
if (hIcon)
|
|
DestroyIcon(hIcon);
|
|
|
|
/* This will be true if we are using the program manager interface */
|
|
if (!bInTaskBar) {
|
|
char szMsgString[256];
|
|
HMENU hSysMenu = GetSystemMenu(hWnd, FALSE);
|
|
DeleteMenu(hSysMenu, SC_RESTORE, MF_BYCOMMAND);
|
|
AppendMenu(hSysMenu, MF_SEPARATOR, 0, NULL);
|
|
LoadString(hInstance, IDS_SVRPROPERTIES, szMsgString, 256);
|
|
AppendMenu(hSysMenu, MF_STRING, IDM_SVRPROPERTIES,
|
|
szMsgString);
|
|
LoadString(hInstance, IDS_SHUTDOWN, szMsgString, 256);
|
|
AppendMenu(hSysMenu, MF_STRING, IDM_SHUTDOWN, szMsgString);
|
|
LoadString(hInstance, IDS_PROPERTIES, szMsgString, 256);
|
|
AppendMenu(hSysMenu, MF_STRING, IDM_PROPERTIES, szMsgString);
|
|
DestroyMenu(hSysMenu);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_QUERYOPEN:
|
|
if (!bInTaskBar)
|
|
return FALSE;
|
|
return DefWindowProc(hWnd, message, wParam, lParam);
|
|
|
|
case WM_SYSCOMMAND:
|
|
if (!bInTaskBar)
|
|
switch (wParam) {
|
|
case SC_RESTORE:
|
|
return TRUE;
|
|
|
|
case IDM_SHUTDOWN:
|
|
{
|
|
HWND hTmpWnd = FindWindow(szClassName, szWindowName);
|
|
PostMessage(hTmpWnd, WM_COMMAND, (WPARAM) IDM_SHUTDOWN,
|
|
0);
|
|
}
|
|
return TRUE;
|
|
|
|
case IDM_PROPERTIES:
|
|
if (!hPSDlg)
|
|
hPSDlg = DisplayPropSheet(hWnd, hInstance);
|
|
else
|
|
SetFocus(hPSDlg);
|
|
return TRUE;
|
|
|
|
case IDM_SVRPROPERTIES:
|
|
{
|
|
HWND hTmpWnd = FindWindow(szClassName, szWindowName);
|
|
PostMessage(hTmpWnd, WM_COMMAND, (WPARAM) IDM_PROPERTIES,
|
|
0);
|
|
}
|
|
return TRUE;
|
|
}
|
|
return DefWindowProc(hWnd, message, wParam, lParam);
|
|
|
|
case WM_DESTROY:
|
|
if (bInTaskBar) {
|
|
NOTIFYICONDATA nid;
|
|
|
|
nid.cbSize = sizeof(NOTIFYICONDATA);
|
|
nid.hWnd = hWnd;
|
|
nid.uID = IDI_IBGUARD;
|
|
nid.uFlags = 0;
|
|
Shell_NotifyIcon(NIM_DELETE, &nid);
|
|
}
|
|
PostQuitMessage(0);
|
|
break;
|
|
|
|
default:
|
|
return DefWindowProc(hWnd, message, wParam, lParam);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
THREAD_ENTRY_DECLARE start_and_watch_server(THREAD_ENTRY_PARAM)
|
|
{
|
|
/**************************************
|
|
*
|
|
* s t a r t _ a n d _ w a t c h _ s e r v e r
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
*
|
|
* This function is where the server process is created and
|
|
* the thread waits for this process to exit.
|
|
*
|
|
**************************************/
|
|
Firebird::ContextPoolHolder threadContext(getDefaultMemoryPool());
|
|
|
|
HANDLE procHandle = NULL;
|
|
bool done = true;
|
|
const UINT error_mode = SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX |
|
|
SEM_NOOPENFILEERRORBOX | SEM_NOALIGNMENTFAULTEXCEPT;
|
|
SC_HANDLE hScManager = 0, hService = 0;
|
|
|
|
/* get the guardian startup information */
|
|
const short option = Config::getGuardianOption();
|
|
|
|
char prefix_buffer[MAXPATHLEN];
|
|
GetModuleFileName(NULL, prefix_buffer, sizeof(prefix_buffer));
|
|
Firebird::PathName path = prefix_buffer;
|
|
path = path.substr(0, path.rfind(PathUtils::dir_sep) + 1) + FBSERVER;
|
|
path = "\"" + path + "\"";
|
|
Firebird::PathName prog_name = path + " -a -n";
|
|
|
|
/* if the guardian is set to FOREVER then set the error mode */
|
|
UINT old_error_mode = 0;
|
|
if (option == START_FOREVER)
|
|
old_error_mode = SetErrorMode(error_mode);
|
|
|
|
/* Spawn the new process */
|
|
do {
|
|
SERVICE_STATUS ServiceStatus;
|
|
char out_buf[1024];
|
|
BOOL success;
|
|
int error = 0;
|
|
|
|
if (service_flag) {
|
|
if (hService) {
|
|
while ((QueryServiceStatus(hService, &ServiceStatus) == TRUE)
|
|
&& (ServiceStatus.dwCurrentState != SERVICE_STOPPED))
|
|
{
|
|
Sleep(500);
|
|
}
|
|
}
|
|
|
|
procHandle = CreateMutex(NULL, FALSE, mutex_name->c_str());
|
|
|
|
/* start as a service. If the service can not be found or
|
|
fails to start, close the handle to the mutex and set
|
|
success = FALSE */
|
|
if (!hScManager)
|
|
hScManager = OpenSCManager(NULL, NULL, GENERIC_READ);
|
|
if (!hService)
|
|
hService =
|
|
OpenService(hScManager, remote_name->c_str(),
|
|
GENERIC_READ | GENERIC_EXECUTE);
|
|
success = StartService(hService, 0, NULL);
|
|
if (success != TRUE)
|
|
error = GetLastError();
|
|
/* if the server is already running, then inform it that it should
|
|
* open the guardian mutex so that it may be governed. */
|
|
if ((!error) || (error == ERROR_SERVICE_ALREADY_RUNNING)) {
|
|
/* Make sure that it is actually ready to receive commands.
|
|
* If we were the one who started it, then it will need a few
|
|
* seconds to get ready. */
|
|
while ((QueryServiceStatus(hService, &ServiceStatus) == TRUE)
|
|
&& (ServiceStatus.dwCurrentState != SERVICE_RUNNING))
|
|
Sleep(500);
|
|
ControlService(hService, SERVICE_CREATE_GUARDIAN_MUTEX,
|
|
&ServiceStatus);
|
|
success = TRUE;
|
|
}
|
|
}
|
|
else {
|
|
HWND hTmpWnd = FindWindow(szClassName, szWindowName);
|
|
if (hTmpWnd == NULL) {
|
|
STARTUPINFO si;
|
|
SECURITY_ATTRIBUTES sa;
|
|
PROCESS_INFORMATION pi;
|
|
ZeroMemory(&si, sizeof(si));
|
|
si.cb = sizeof(si);
|
|
sa.nLength = sizeof(sa);
|
|
sa.lpSecurityDescriptor = NULL;
|
|
sa.bInheritHandle = TRUE;
|
|
success = CreateProcess(NULL, const_cast<char*>(prog_name.c_str()),
|
|
&sa, NULL, FALSE, 0,
|
|
NULL, NULL, &si, &pi);
|
|
if (success != TRUE)
|
|
error = GetLastError();
|
|
|
|
procHandle = pi.hProcess;
|
|
/* TMN: 04 Aug 2000 - closed the handle that previously leaked. */
|
|
CloseHandle(pi.hThread);
|
|
}
|
|
else {
|
|
SendMessage(hTmpWnd, WM_COMMAND, (WPARAM) IDM_GUARDED, 0);
|
|
DWORD server_pid;
|
|
GetWindowThreadProcessId(hTmpWnd, &server_pid);
|
|
procHandle = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION,
|
|
FALSE, server_pid);
|
|
if (procHandle == NULL) {
|
|
error = GetLastError();
|
|
success = FALSE;
|
|
}
|
|
else {
|
|
success = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (success != TRUE) {
|
|
/* error creating new process */
|
|
char szMsgString[256];
|
|
LoadString(hInstance_gbl, IDS_CANT_START_THREAD, szMsgString, 256);
|
|
sprintf(out_buf, "%s : %s errno : %d", path.c_str(), szMsgString, error);
|
|
write_log(IDS_CANT_START_THREAD, out_buf);
|
|
|
|
if (service_flag) {
|
|
SERVICE_STATUS status_info;
|
|
/* wait a second to get the mutex handle (just in case) and
|
|
then close it
|
|
*/
|
|
WaitForSingleObject(procHandle, 1000);
|
|
CloseHandle(procHandle);
|
|
hService = OpenService(hScManager, remote_name->c_str(),
|
|
GENERIC_READ | GENERIC_EXECUTE);
|
|
ControlService(hService, SERVICE_CONTROL_STOP, &status_info);
|
|
CloseServiceHandle(hScManager);
|
|
CloseServiceHandle(hService);
|
|
CNTL_stop_service(service_name->c_str());
|
|
}
|
|
else {
|
|
MessageBox(NULL, out_buf, NULL, MB_OK);
|
|
PostMessage(hWndGbl, WM_CLOSE, 0, 0);
|
|
}
|
|
return 0;
|
|
}
|
|
else {
|
|
char szMsgString[256];
|
|
LoadString(hInstance_gbl, IDS_STARTING_GUARD, szMsgString, 256);
|
|
sprintf(out_buf, "%s: %s\n", szMsgString, path.c_str());
|
|
write_log(IDS_LOG_START, out_buf);
|
|
}
|
|
|
|
/* wait for process to terminate */
|
|
DWORD exit_status;
|
|
if (service_flag) {
|
|
while (WaitForSingleObject(procHandle, 500) == WAIT_OBJECT_0) {
|
|
ReleaseMutex(procHandle);
|
|
Sleep(100);
|
|
}
|
|
|
|
const int ret_val = WaitForSingleObject(procHandle, INFINITE);
|
|
if (ret_val == WAIT_ABANDONED)
|
|
exit_status = CRASHED;
|
|
else if (ret_val == WAIT_OBJECT_0)
|
|
exit_status = NORMAL_EXIT;
|
|
|
|
CloseHandle(procHandle);
|
|
}
|
|
else {
|
|
while (WaitForSingleObject(procHandle, INFINITE) == WAIT_FAILED);
|
|
GetExitCodeProcess(procHandle, &exit_status);
|
|
CloseHandle(procHandle);
|
|
}
|
|
|
|
if (exit_status != NORMAL_EXIT) {
|
|
/* check for startup error */
|
|
if (exit_status == STARTUP_ERROR) {
|
|
char szMsgString[256];
|
|
LoadString(hInstance_gbl, IDS_STARTUP_ERROR, szMsgString, 256);
|
|
sprintf(out_buf, "%s: %s (%lu)\n", path.c_str(), szMsgString,
|
|
exit_status);
|
|
write_log(IDS_STARTUP_ERROR, out_buf);
|
|
done = true;
|
|
|
|
}
|
|
else {
|
|
char szMsgString[256];
|
|
LoadString(hInstance_gbl, IDS_ABNORMAL_TERM, szMsgString, 256);
|
|
sprintf(out_buf, "%s: %s (%lu)\n", path.c_str(), szMsgString,
|
|
exit_status);
|
|
write_log(IDS_LOG_TERM, out_buf);
|
|
|
|
/* switch the icons if the server restarted */
|
|
if (!service_flag)
|
|
PostMessage(hWndGbl, WM_SWITCHICONS, 0, 0);
|
|
if (option == START_FOREVER)
|
|
done = false;
|
|
}
|
|
}
|
|
else {
|
|
/* Normal shutdown - ie: via ibmgr - don't restart the server */
|
|
char szMsgString[256];
|
|
LoadString(hInstance_gbl, IDS_NORMAL_TERM, szMsgString, 256);
|
|
sprintf(out_buf, "%s: %s\n", path.c_str(), szMsgString);
|
|
write_log(IDS_LOG_STOP, out_buf);
|
|
done = true;
|
|
}
|
|
|
|
if (option == START_ONCE)
|
|
done = true;
|
|
} while (!done);
|
|
|
|
|
|
/* If on WINNT */
|
|
if (service_flag) {
|
|
CloseServiceHandle(hScManager);
|
|
CloseServiceHandle(hService);
|
|
CNTL_stop_service(service_name->c_str());
|
|
}
|
|
else
|
|
PostMessage(hWndGbl, WM_CLOSE, 0, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
HWND DisplayPropSheet(HWND hParentWnd, HINSTANCE hInst)
|
|
{
|
|
/******************************************************************************
|
|
*
|
|
* D i s p l a y P r o p S h e e t
|
|
*
|
|
******************************************************************************
|
|
*
|
|
* Input: hParentWnd - Handle to the main window of this application
|
|
*
|
|
* Return: Handle to the Property sheet dialog if successful
|
|
* NULL if error in displaying property sheet
|
|
*
|
|
* Description: This function initializes the page(s) of the property sheet,
|
|
* and then calls the PropertySheet() function to display it.
|
|
*****************************************************************************/
|
|
PROPSHEETPAGE PSPages[1];
|
|
HINSTANCE hInstance = hInst;
|
|
|
|
PSPages[0].dwSize = sizeof(PROPSHEETPAGE);
|
|
PSPages[0].dwFlags = PSP_USETITLE;
|
|
PSPages[0].hInstance = hInstance;
|
|
PSPages[0].pszTemplate = MAKEINTRESOURCE(IDD_PROPSHEET);
|
|
PSPages[0].pszTitle = MAKEINTRESOURCE(IDS_PROP_TITLE);
|
|
PSPages[0].pfnDlgProc = (DLGPROC) GeneralPage;
|
|
PSPages[0].pfnCallback = NULL;
|
|
|
|
PROPSHEETHEADER PSHdr;
|
|
PSHdr.dwSize = sizeof(PROPSHEETHEADER);
|
|
PSHdr.dwFlags = PSH_PROPTITLE | PSH_PROPSHEETPAGE |
|
|
PSH_USEICONID | PSH_MODELESS;
|
|
PSHdr.hwndParent = hParentWnd;
|
|
PSHdr.hInstance = hInstance;
|
|
PSHdr.pszIcon = MAKEINTRESOURCE(IDI_IBGUARD);
|
|
PSHdr.pszCaption = (LPSTR) GUARDIAN_APP_LABEL;
|
|
PSHdr.nPages = FB_NELEM(PSPages);
|
|
PSHdr.nStartPage = 0;
|
|
PSHdr.ppsp = (LPCPROPSHEETPAGE) & PSPages;
|
|
PSHdr.pfnCallback = NULL;
|
|
|
|
hPSDlg = (HWND) PropertySheet(&PSHdr);
|
|
|
|
if (hPSDlg <= 0)
|
|
hPSDlg = NULL;
|
|
return hPSDlg;
|
|
}
|
|
|
|
|
|
LRESULT CALLBACK GeneralPage(HWND hDlg, UINT unMsg, WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
/******************************************************************************
|
|
*
|
|
* G e n e r a l P a g e
|
|
*
|
|
******************************************************************************
|
|
*
|
|
* Input: hDlg - Handle to the page dialog
|
|
* unMsg - Message ID
|
|
* wParam - WPARAM message parameter
|
|
* lParam - LPARAM message parameter
|
|
*
|
|
* Return: FALSE if message is not processed
|
|
* TRUE if message is processed here
|
|
*
|
|
* Description: This is the window procedure for the "General" page dialog
|
|
* of the property sheet dialog box. All the Property Sheet
|
|
* related events are passed as WM_NOTIFY messages and they
|
|
* are identified within the LPARAM which will be pointer to
|
|
* the NMDR structure
|
|
*****************************************************************************/
|
|
HINSTANCE hInstance = (HINSTANCE) GetWindowLong(hDlg, GWL_HINSTANCE);
|
|
|
|
switch (unMsg) {
|
|
case WM_INITDIALOG:
|
|
{
|
|
char szText[256];
|
|
char szWindowText[MAXPATHLEN];
|
|
char szFullPath[MAXPATHLEN];
|
|
int index = 0;
|
|
const int NCOLS = 3;
|
|
|
|
/* Display the number of times the server has been started by
|
|
this session of the guardian
|
|
*/
|
|
SetDlgItemInt(hDlg, IDC_RESTARTS, nRestarts, FALSE);
|
|
|
|
/* get the path to the exe.
|
|
Make sure that it is null terminated */
|
|
GetModuleFileName(hInstance, szWindowText, sizeof(szWindowText));
|
|
char* pszPtr = strrchr(szWindowText, '\\');
|
|
*(pszPtr + 1) = 0x00;
|
|
|
|
ChopFileName(szWindowText, szWindowText, 38);
|
|
SetDlgItemText(hDlg, IDC_LOCATION, szWindowText);
|
|
|
|
/* Get version information from the application */
|
|
GetModuleFileName(hInstance, szFullPath, sizeof(szFullPath));
|
|
DWORD dwVerHnd;
|
|
const DWORD dwVerInfoSize = GetFileVersionInfoSize(szFullPath, &dwVerHnd);
|
|
if (dwVerInfoSize) {
|
|
/* If we were able to get the information, process it: */
|
|
UINT cchVer = 25;
|
|
LPSTR lszVer = NULL;
|
|
|
|
HANDLE hMem = GlobalAlloc(GMEM_MOVEABLE, dwVerInfoSize);
|
|
LPVOID lpvMem = GlobalLock(hMem);
|
|
GetFileVersionInfo(szFullPath, dwVerHnd, dwVerInfoSize,
|
|
lpvMem);
|
|
if (VerQueryValue
|
|
(lpvMem, "\\StringFileInfo\\040904E4\\FileVersion",
|
|
reinterpret_cast < void **>(&lszVer), &cchVer))
|
|
SetDlgItemText(hDlg, IDC_VERSION, lszVer);
|
|
else
|
|
SetDlgItemText(hDlg, IDC_VERSION, "N/A");
|
|
GlobalUnlock(hMem);
|
|
GlobalFree(hMem);
|
|
}
|
|
|
|
/* Create the columns Action, Date, Time for the listbox */
|
|
HWND hWndLog = GetDlgItem(hDlg, IDC_LOG);
|
|
LV_COLUMN lvC;
|
|
lvC.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
|
|
lvC.fmt = LVCFMT_LEFT; // left-align column
|
|
lvC.pszText = szText;
|
|
|
|
for (index = 0; index < NCOLS; index++) {
|
|
/* NOTE: IDS_ACTION = 220
|
|
IDS_DATE = 230
|
|
IDS_TIME = 240
|
|
*/
|
|
lvC.iSubItem = index;
|
|
lvC.cx = 85;
|
|
LoadString(hInstance, IDS_ACTION + (index * 10), szText,
|
|
sizeof(szText));
|
|
ListView_InsertColumn(hWndLog, index, &lvC);
|
|
}
|
|
|
|
log_info* liTemp = log_entry->next;
|
|
LV_ITEM lvI;
|
|
lvI.cchTextMax = sizeof(liTemp->log_action);
|
|
lvI.mask = LVIF_TEXT;
|
|
for (index = 0; liTemp->log_action;
|
|
index++, liTemp = liTemp->next)
|
|
{
|
|
lvI.iItem = index;
|
|
lvI.iSubItem = 0;
|
|
lvI.pszText = liTemp->log_action;
|
|
ListView_InsertItem(hWndLog, &lvI);
|
|
ListView_SetItemText(hWndLog, index, 0, lvI.pszText);
|
|
|
|
lvI.iSubItem = 1;
|
|
lvI.pszText = liTemp->log_date;
|
|
ListView_InsertItem(hWndLog, &lvI);
|
|
ListView_SetItemText(hWndLog, index, 1, lvI.pszText);
|
|
|
|
lvI.iSubItem = 2;
|
|
lvI.pszText = liTemp->log_time;
|
|
ListView_InsertItem(hWndLog, &lvI);
|
|
ListView_SetItemText(hWndLog, index, 2, lvI.pszText);
|
|
}
|
|
}
|
|
break;
|
|
case WM_HELP:
|
|
{
|
|
LPHELPINFO lphi = (LPHELPINFO) lParam;
|
|
if (lphi->iContextType == HELPINFO_WINDOW) // must be for a control
|
|
WinHelp((HWND) lphi->hItemHandle, GUARDIAN_HELP_FILE,
|
|
HELP_WM_HELP, (DWORD) (LPVOID) aMenuHelpIDs);
|
|
return TRUE;
|
|
}
|
|
|
|
case WM_CONTEXTMENU:
|
|
WinHelp((HWND) wParam, GUARDIAN_HELP_FILE, HELP_CONTEXTMENU,
|
|
(DWORD) (LPVOID) aMenuHelpIDs);
|
|
return TRUE;
|
|
break;
|
|
case WM_NOTIFY:
|
|
switch (((LPNMHDR) lParam)->code) {
|
|
case PSN_KILLACTIVE:
|
|
SetWindowLong(hDlg, DWL_MSGRESULT, FALSE);
|
|
break;
|
|
case PSN_HELP:
|
|
#ifdef NOT_USED_OR_REPLACED
|
|
/* HelpCmd(hDlg, hInstance, ibsp_Server_Information_Properties); */
|
|
#endif
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
THREAD_ENTRY_DECLARE swap_icons(THREAD_ENTRY_PARAM param)
|
|
{
|
|
/******************************************************************************
|
|
*
|
|
* S w a p I c o n s
|
|
*
|
|
******************************************************************************
|
|
*
|
|
* Description: Animates the icon if the server restarted
|
|
*****************************************************************************/
|
|
Firebird::ContextPoolHolder threadContext(getDefaultMemoryPool());
|
|
|
|
HWND hWnd = static_cast<HWND>(param);
|
|
HINSTANCE hInstance = (HINSTANCE) GetWindowLong(hWnd, GWL_HINSTANCE);
|
|
HICON hIconNormal = (HICON) LoadImage(hInstance,
|
|
MAKEINTRESOURCE(IDI_IBGUARD),
|
|
IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR);
|
|
|
|
HICON hIconAlert = (HICON) LoadImage(hInstance,
|
|
MAKEINTRESOURCE(IDI_IBGUARDALRT),
|
|
IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR);
|
|
|
|
NOTIFYICONDATA nidNormal;
|
|
nidNormal.cbSize = sizeof(NOTIFYICONDATA);
|
|
nidNormal.hWnd = hWnd;
|
|
nidNormal.uID = IDI_IBGUARD;
|
|
nidNormal.uFlags = NIF_ICON;
|
|
nidNormal.hIcon = hIconNormal;
|
|
|
|
NOTIFYICONDATA nidAlert;
|
|
nidAlert.cbSize = sizeof(NOTIFYICONDATA);
|
|
nidAlert.hWnd = hWnd;
|
|
nidAlert.uID = IDI_IBGUARD;
|
|
nidAlert.uFlags = NIF_ICON;
|
|
nidAlert.hIcon = hIconAlert;
|
|
|
|
/* Animate until the property sheet is displayed. Once the property sheet is
|
|
displayed, stop animating the icon
|
|
*/
|
|
while (!hPSDlg) {
|
|
if (!Shell_NotifyIcon(NIM_MODIFY, &nidAlert))
|
|
SetClassLong(hWnd, GCL_HICON, (long) hIconAlert);
|
|
Sleep(500);
|
|
|
|
if (!Shell_NotifyIcon(NIM_MODIFY, &nidNormal))
|
|
SetClassLong(hWnd, GCL_HICON, (long) hIconNormal);
|
|
Sleep(500);
|
|
}
|
|
/* Make sure that the icon is normal */
|
|
if (!Shell_NotifyIcon(NIM_MODIFY, &nidNormal))
|
|
SetClassLong(hWnd, GCL_HICON, (long) hIconNormal);
|
|
|
|
if (hIconNormal)
|
|
DestroyIcon(hIconNormal);
|
|
if (hIconAlert)
|
|
DestroyIcon(hIconAlert);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
void write_log(int log_action, const char* buff)
|
|
{
|
|
/******************************************************************************
|
|
*
|
|
* w r i t e _ l o g
|
|
*
|
|
******************************************************************************
|
|
*
|
|
* Description: Writes the guardian information to either the Windows 95
|
|
* property sheet structure (log_entry) or to the Windows NT
|
|
* Event Log
|
|
*****************************************************************************/
|
|
char tmp_buff[128];
|
|
|
|
time_t ltime;
|
|
time(<ime);
|
|
const tm* today = localtime(<ime);
|
|
|
|
/* Move to the end of the log_entry list */
|
|
log_info* log_temp = log_entry;
|
|
while (log_temp->next)
|
|
log_temp = log_temp->next;
|
|
|
|
log_info* tmp =
|
|
reinterpret_cast<log_info*>(malloc(sizeof(log_info)));
|
|
memset(tmp, 0, sizeof(log_info));
|
|
|
|
#ifdef NOT_USED_OR_REPLACED
|
|
sprintf(tmp->log_time, "%02d:%02d", today->tm_hour, today->tm_min);
|
|
sprintf(tmp->log_date, "%02d/%02d/%02d",
|
|
today->tm_mon + 1, today->tm_mday, today->tm_year % 100);
|
|
#else
|
|
/* TMN: Fixed this after bug-report. Should it really force */
|
|
/* 24hr format in e.g US, where they use AM/PM wharts? */
|
|
GetTimeFormat(LOCALE_USER_DEFAULT,
|
|
TIME_NOSECONDS | TIME_FORCE24HOURFORMAT, NULL, NULL,
|
|
tmp->log_time, sizeof(tmp->log_time));
|
|
GetDateFormat(LOCALE_USER_DEFAULT, DATE_SHORTDATE, NULL, NULL,
|
|
tmp->log_date, sizeof(tmp->log_date));
|
|
#endif
|
|
|
|
if (log_action >= IDS_LOG_START && log_action <= IDS_LOG_TERM) {
|
|
/* Only Windows 95 needs this information since it goes in the property sheet */
|
|
LoadString(hInstance_gbl, log_action, tmp_buff,
|
|
sizeof(tmp->log_action));
|
|
sprintf(tmp->log_action, "%s", tmp_buff);
|
|
tmp->next = NULL;
|
|
log_temp->next = tmp;
|
|
}
|
|
|
|
if (service_flag) { /* on NT */
|
|
HANDLE hLog = RegisterEventSource(NULL, service_name->c_str());
|
|
if (!hLog)
|
|
gds__log("Error opening Windows NT Event Log");
|
|
else {
|
|
char* act_buff[1]; // CVC: Where is this deallocated?
|
|
act_buff[0] = (char*) malloc(sizeof(tmp_buff));
|
|
LoadString(hInstance_gbl, log_action + 1, tmp_buff,
|
|
sizeof(tmp_buff));
|
|
sprintf(act_buff[0], "%s", buff);
|
|
|
|
LPVOID lpMsgBuf;
|
|
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
FORMAT_MESSAGE_ARGUMENT_ARRAY |
|
|
FORMAT_MESSAGE_FROM_STRING,
|
|
tmp_buff, 0, 0, (LPTSTR) &lpMsgBuf, 0,
|
|
reinterpret_cast<va_list*>(act_buff));
|
|
strncpy(act_buff[0], (LPTSTR) lpMsgBuf,
|
|
strlen(static_cast<const char*>(lpMsgBuf)) - 1);
|
|
LocalFree(lpMsgBuf);
|
|
WORD wLogType;
|
|
|
|
switch (log_action) {
|
|
case IDS_LOG_START:
|
|
case IDS_LOG_STOP:
|
|
wLogType = EVENTLOG_INFORMATION_TYPE;
|
|
break;
|
|
default:
|
|
wLogType = EVENTLOG_ERROR_TYPE;
|
|
}
|
|
|
|
if (!ReportEvent
|
|
(hLog, wLogType, 0, log_action + 1, NULL, 1, 0,
|
|
const_cast<const char**>(act_buff), NULL))
|
|
{
|
|
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
FORMAT_MESSAGE_FROM_SYSTEM |
|
|
FORMAT_MESSAGE_IGNORE_INSERTS, NULL,
|
|
GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
|
|
(LPTSTR) & lpMsgBuf, 0, NULL);
|
|
gds__log("Unable to update NT Event Log.\n\tOS Message: %s",
|
|
lpMsgBuf);
|
|
LocalFree(lpMsgBuf);
|
|
}
|
|
// CVC: free(act_buff[0]); ???
|
|
DeregisterEventSource(hLog);
|
|
}
|
|
}
|
|
|
|
/* Write to the Firebird log */
|
|
if (*buff)
|
|
gds__log(buff);
|
|
}
|
|
|
|
|
|
#ifdef NOT_USED_OR_REPLACED
|
|
void HelpCmd(HWND hWnd, HINSTANCE hInst, WPARAM wId)
|
|
{
|
|
/****************************************************************
|
|
*
|
|
* H e l p C m d
|
|
*
|
|
****************************************************************
|
|
*
|
|
* Input: hWnd - Handle of dialog box from which help was
|
|
* invoked.
|
|
* hInst - Instance of application.
|
|
* nId - The help message Id.
|
|
*
|
|
* Description: Invoke the Windows Help facility with context of nId.
|
|
*
|
|
*****************************************************************/
|
|
char szPathFileName[1024 + 256 + 1];
|
|
|
|
GetModuleFileName(hInst, szPathFileName, sizeof(szPathFileName));
|
|
|
|
/* Show hour glass cursor */
|
|
HCURSOR hOldCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
|
|
|
|
strcpy(strrchr(szPathFileName, '\\') + 1, "fbserver.hlp");
|
|
WinHelp(hWnd, szPathFileName, HELP_CONTEXT, wId);
|
|
|
|
/* Restore old cursor */
|
|
SetCursor(hOldCursor);
|
|
|
|
return;
|
|
}
|
|
#endif
|