mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-27 20:03:03 +01:00
1192 lines
32 KiB
C++
1192 lines
32 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 <stdio.h>
|
|
#include "../yvalve/gds_proto.h"
|
|
#include <stdlib.h>
|
|
#include <windows.h>
|
|
#include <shellapi.h>
|
|
#include <prsht.h>
|
|
#include <commctrl.h>
|
|
#ifdef TIME_WITH_SYS_TIME
|
|
# include <sys/time.h>
|
|
# include <time.h>
|
|
#else
|
|
# ifdef 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/server/os/win32/window.h"
|
|
#include "../remote/server/os/win32/chop_proto.h"
|
|
#include "../common/config/config.h"
|
|
#include "../common/classes/init.h"
|
|
#include "../common/os/path_utils.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);
|
|
|
|
THREAD_ENTRY_DECLARE start_and_watch_server(THREAD_ENTRY_PARAM);
|
|
THREAD_ENTRY_DECLARE swap_icons(THREAD_ENTRY_PARAM);
|
|
static void addTaskBarIcons(HINSTANCE hInstance, HWND hWnd, BOOL& bInTaskBar);
|
|
static 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::GlobalPtr<Firebird::string> service_name;
|
|
static Firebird::GlobalPtr<Firebird::string> remote_name;
|
|
static Firebird::GlobalPtr<Firebird::string> mutex_name;
|
|
// unsigned short shutdown_flag = FALSE;
|
|
static log_info* log_entry;
|
|
static Thread::Handle watcher_thd = 0;
|
|
static Thread::Handle swap_icons_thd = 0;
|
|
|
|
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.
|
|
*
|
|
**************************************/
|
|
|
|
strcpy(instance, FB_DEFAULT_INSTANCE);
|
|
|
|
service_flag = parse_args(lpszCmdLine);
|
|
|
|
service_name->printf(ISCGUARD_SERVICE, instance);
|
|
remote_name->printf(REMOTE_SERVICE, instance);
|
|
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 = static_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");
|
|
}
|
|
|
|
if (watcher_thd)
|
|
{
|
|
WaitForSingleObject(watcher_thd, 5000);
|
|
CloseHandle(watcher_thd);
|
|
}
|
|
}
|
|
else {
|
|
return WINDOW_main(0);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static bool parse_args(LPCSTR lpszArgs)
|
|
{
|
|
/**************************************
|
|
*
|
|
* 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;
|
|
bool delimited = false;
|
|
|
|
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':
|
|
delimited = false;
|
|
while (*p && *p == ' ')
|
|
p++;
|
|
if (*p && *p == '"')
|
|
{
|
|
p++;
|
|
delimited = true;
|
|
}
|
|
|
|
if (delimited)
|
|
{
|
|
char* pi = instance;
|
|
const char* pend = instance + sizeof(instance) - 1;
|
|
while (*p && *p != '"' && pi < pend) {
|
|
*pi++ = *p++;
|
|
}
|
|
*pi++ = '\0';
|
|
if (*p && *p == '"')
|
|
p++;
|
|
}
|
|
else
|
|
{
|
|
if (*p && *p != '-')
|
|
{
|
|
char* pi = instance;
|
|
const char* pend = instance + sizeof(instance) - 1;
|
|
while (*p && *p != ' ' && pi < pend) {
|
|
*pi++ = *p++;
|
|
}
|
|
*pi++ = '\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.
|
|
*
|
|
**************************************/
|
|
|
|
// If we're a service, don't create a window
|
|
if (service_flag)
|
|
{
|
|
try
|
|
{
|
|
Thread::start(start_and_watch_server, 0, THREAD_medium, &watcher_thd);
|
|
}
|
|
catch (const Firebird::Exception&)
|
|
{
|
|
// 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_ICONSTOP);
|
|
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 | MB_ICONSTOP);
|
|
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
|
|
try
|
|
{
|
|
Thread::start(start_and_watch_server, 0, THREAD_medium, NULL);
|
|
}
|
|
catch (const Firebird::Exception&)
|
|
{
|
|
// error starting server thread
|
|
char szMsgString[256];
|
|
LoadString(hInstance_gbl, IDS_CANT_START_THREAD, szMsgString, 256);
|
|
MessageBox(NULL, szMsgString, GUARDIAN_APP_LABEL, MB_OK | MB_ICONSTOP);
|
|
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 (swap_icons_thd)
|
|
{
|
|
CloseHandle(swap_icons_thd);
|
|
swap_icons_thd = 0;
|
|
};
|
|
}
|
|
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 UINT s_uTaskbarRestart;
|
|
|
|
hInstance = (HINSTANCE) GetWindowLongPtr(hWnd, GWLP_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++;
|
|
{ // scope
|
|
DWORD thr_exit = 0;
|
|
if (swap_icons_thd == 0 ||
|
|
!GetExitCodeThread(swap_icons_thd, &thr_exit) ||
|
|
thr_exit != STILL_ACTIVE)
|
|
{
|
|
Thread::start(swap_icons, hWnd, THREAD_medium, &swap_icons_thd);
|
|
}
|
|
} // scope
|
|
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:
|
|
s_uTaskbarRestart = RegisterWindowMessage("TaskbarCreated");
|
|
addTaskBarIcons(hInstance, hWnd, bInTaskBar);
|
|
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:
|
|
if (message == s_uTaskbarRestart)
|
|
addTaskBarIcons(hInstance, hWnd, bInTaskBar);
|
|
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 | MB_ICONSTOP);
|
|
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 | PSH_NOAPPLYNOW | PSH_NOCONTEXTHELP;
|
|
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 == (HWND) -1)
|
|
{
|
|
gds__log("Create property sheet window failed. Error code %d", GetLastError());
|
|
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) GetWindowLongPtr(hDlg, GWLP_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_NOTIFY:
|
|
switch (((LPNMHDR) lParam)->code)
|
|
{
|
|
case PSN_KILLACTIVE:
|
|
SetWindowLongPtr(hDlg, DWLP_MSGRESULT, FALSE);
|
|
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) GetWindowLongPtr(hWnd, GWLP_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))
|
|
SetClassLongPtr(hWnd, GCLP_HICON, (LONG_PTR) hIconAlert);
|
|
Sleep(500);
|
|
|
|
if (!Shell_NotifyIcon(NIM_MODIFY, &nidNormal))
|
|
SetClassLongPtr(hWnd, GCLP_HICON, (LONG_PTR) hIconNormal);
|
|
Sleep(500);
|
|
}
|
|
// Make sure that the icon is normal
|
|
if (!Shell_NotifyIcon(NIM_MODIFY, &nidNormal))
|
|
SetClassLongPtr(hWnd, GCLP_HICON, (LONG_PTR) hIconNormal);
|
|
|
|
if (hIconNormal)
|
|
DestroyIcon(hIconNormal);
|
|
if (hIconAlert)
|
|
DestroyIcon(hIconAlert);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void addTaskBarIcons(HINSTANCE hInstance, HWND hWnd, BOOL& bInTaskBar)
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static 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
|
|
*****************************************************************************/
|
|
const size_t BUFF_SIZE = 512;
|
|
char tmp_buff[BUFF_SIZE];
|
|
|
|
// 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 = static_cast<log_info*>(malloc(sizeof(log_info)));
|
|
memset(tmp, 0, sizeof(log_info));
|
|
|
|
#ifdef NOT_USED_OR_REPLACED
|
|
time_t ltime;
|
|
time(<ime);
|
|
const tm* today = localtime(<ime);
|
|
|
|
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 buffer[BUFF_SIZE];
|
|
char* act_buff[1];
|
|
act_buff[0] = buffer;
|
|
|
|
LoadString(hInstance_gbl, log_action + 1, tmp_buff, sizeof(tmp_buff));
|
|
sprintf(act_buff[0], "%s", buff);
|
|
|
|
LPTSTR 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));
|
|
|
|
const int len = static_cast<int>(MIN(BUFF_SIZE - 1, strlen(lpMsgBuf) - 1));
|
|
strncpy(act_buff[0], lpMsgBuf, len);
|
|
act_buff[0][len] = 0;
|
|
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);
|
|
}
|
|
DeregisterEventSource(hLog);
|
|
}
|
|
}
|
|
|
|
// Write to the Firebird log
|
|
if (*buff)
|
|
gds__log(buff);
|
|
}
|