📄 svcproc.cxx
字号:
/*
* svcproc.cxx
*
* Service process implementation for Win95 and WinNT
*
* Portable Windows Library
*
* Copyright (c) 1993-1998 Equivalence Pty. Ltd.
*
* The contents of this file are subject to the Mozilla 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.mozilla.org/MPL/
*
* 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 is Portable Windows Library.
*
* The Initial Developer of the Original Code is Equivalence Pty. Ltd.
*
* Portions are Copyright (C) 1993 Free Software Foundation, Inc.
* All Rights Reserved.
*
* Contributor(s): ______________________________________.
*
* $Revision: 19426 $
* $Author: rjongbloed $
* $Date: 2008-02-09 03:19:51 +0000 (Sat, 09 Feb 2008) $
*/
#include <ptlib.h>
#include <winuser.h>
#include <winnls.h>
#include <shellapi.h>
#include <commdlg.h>
#include <process.h>
#include <signal.h>
#include <fcntl.h>
#include <io.h>
#pragma comment(lib,"advapi32.lib")
#pragma comment(lib,"user32.lib")
#pragma comment(lib,"comdlg32.lib")
#ifdef __USE_STL__
#include <fstream>
#else
#include <fstream.h>
#endif
#include <ptlib/svcproc.h>
#define new PNEW
#define UWM_SYSTRAY (WM_USER + 1)
#define ICON_RESID 1
#define SYSTRAY_ICON_ID 1
static HINSTANCE hInstance;
#define DATE_WIDTH 72
#define THREAD_WIDTH 80
#define LEVEL_WIDTH 32
#define PROTO_WIDTH 40
#define ACTION_WIDTH 48
enum {
SvcCmdTray,
SvcCmdNoTray,
SvcCmdVersion,
SvcCmdInstall,
SvcCmdRemove,
SvcCmdStart,
SvcCmdStop,
SvcCmdPause,
SvcCmdResume,
SvcCmdDeinstall,
SvcCmdNoWindow,
NumSvcCmds
};
static const char * const ServiceCommandNames[NumSvcCmds] = {
"Tray",
"NoTray",
"Version",
"Install",
"Remove",
"Start",
"Stop",
"Pause",
"Resume",
"Deinstall",
"NoWin"
};
static const char WindowLogOutput[] = "Window Log Output";
static const char DebuggerLogOutput[] = "Debugger Log Output";
class PNotifyIconData : public NOTIFYICONDATA {
public:
PNotifyIconData(HWND hWnd, UINT flags, const char * tip = NULL);
void Add() { Shell_NotifyIcon(NIM_ADD, this); }
void Delete() { Shell_NotifyIcon(NIM_DELETE, this); }
void Modify() { Shell_NotifyIcon(NIM_MODIFY, this); }
};
PNotifyIconData::PNotifyIconData(HWND window, UINT flags, const char * tip)
{
cbSize = sizeof(NOTIFYICONDATA);
hWnd = window;
uID = SYSTRAY_ICON_ID;
uFlags = flags;
if (tip != NULL) {
strncpy(szTip, tip, sizeof(szTip)-1);
szTip[sizeof(szTip)-1] = '\0';
uFlags |= NIF_TIP;
}
}
enum TrayIconRegistryCommand {
AddTrayIcon,
DelTrayIcon,
CheckTrayIcon
};
static PBoolean TrayIconRegistry(PServiceProcess * svc, TrayIconRegistryCommand cmd)
{
HKEY key;
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run",
0, KEY_ALL_ACCESS, &key) != ERROR_SUCCESS)
return PFalse;
DWORD err = 1;
DWORD type;
DWORD len;
PString str;
switch (cmd) {
case CheckTrayIcon :
err = RegQueryValueEx(key, svc->GetName(), 0, &type, NULL, &len);
break;
case AddTrayIcon :
str = "\"" + svc->GetFile() + "\" Tray";
err = RegSetValueEx(key, svc->GetName(), 0, REG_SZ,
(LPBYTE)(const char *)str, str.GetLength() + 1);
break;
case DelTrayIcon :
err = RegDeleteValue(key, (char *)(const char *)svc->GetName());
}
RegCloseKey(key);
return err == ERROR_SUCCESS;
}
///////////////////////////////////////////////////////////////////////////////
// PSystemLog
void PSystemLog::Output(Level level, const char * msg)
{
PServiceProcess & process = PServiceProcess::Current();
if (level > process.GetLogLevel())
return;
DWORD err = ::GetLastError();
if (process.isWin95 || process.controlWindow != NULL) {
static HANDLE mutex = CreateMutex(NULL, PFalse, NULL);
WaitForSingleObject(mutex, INFINITE);
ostream * out;
if (process.systemLogFileName == WindowLogOutput || process.systemLogFileName == DebuggerLogOutput)
out = new PStringStream;
else
out = new ofstream(process.systemLogFileName, ios::app);
PTime now;
*out << now.AsString("yyyy/MM/dd hh:mm:ss.uuu\t");
PThread * thread = PThread::Current();
if (thread == NULL)
*out << "ThreadID=0x"
<< setfill('0') << hex
<< setw(8) << GetCurrentThreadId()
<< setfill(' ') << dec;
else {
PString threadName = thread->GetThreadName();
if (threadName.GetLength() <= 23)
*out << setw(23) << threadName;
else
*out << threadName.Left(10) << "..." << threadName.Right(10);
}
*out << '\t';
if (level < 0)
*out << "Message";
else {
static const char * const levelName[4] = {
"Fatal error",
"Error",
"Warning",
"Info"
};
if (level < PARRAYSIZE(levelName))
*out << levelName[level];
else
*out << "Debug" << (level-Info);
}
*out << '\t' << msg;
if (level < Info && err != 0)
*out << " - error = " << err << endl;
else if (msg[0] == '\0' || msg[strlen(msg)-1] != '\n')
*out << endl;
if (process.systemLogFileName == WindowLogOutput)
process.DebugOutput(*(PStringStream*)out);
else if (process.systemLogFileName == DebuggerLogOutput)
OutputDebugStringA(*(PStringStream*)out);
delete out;
ReleaseMutex(mutex);
SetLastError(0);
}
else {
// Use event logging to log the error.
HANDLE hEventSource = RegisterEventSource(NULL, process.GetName());
if (hEventSource == NULL)
return;
PString threadName;
PThread * thread = PThread::Current();
if (thread != NULL)
threadName = thread->GetThreadName();
else
threadName.sprintf("%u", GetCurrentThreadId());
char thrdbuf[16];
if (threadName.IsEmpty())
sprintf(thrdbuf, "0x%08X", thread);
else {
strncpy(thrdbuf, threadName, sizeof(thrdbuf)-1);
thrdbuf[sizeof(thrdbuf)-1] = '\0';
}
char errbuf[25];
if (level > StdError && level < Info && err != 0)
::sprintf(errbuf, "Error code = %d", err);
else
errbuf[0] = '\0';
LPCTSTR strings[4];
strings[0] = thrdbuf;
strings[1] = msg;
strings[2] = errbuf;
strings[3] = level != Fatal ? "" : " Program aborted.";
static const WORD levelType[Info+1] = {
EVENTLOG_INFORMATION_TYPE,
EVENTLOG_ERROR_TYPE,
EVENTLOG_ERROR_TYPE,
EVENTLOG_WARNING_TYPE
};
ReportEvent(hEventSource, // handle of event source
(WORD)(level < Info ? levelType[level+1]
: EVENTLOG_INFORMATION_TYPE), // event type
(WORD)(level+1), // event category
0x1000, // event ID
NULL, // current user's SID
PARRAYSIZE(strings), // number of strings
0, // no bytes of raw data
strings, // array of error strings
NULL); // no raw data
DeregisterEventSource(hEventSource);
}
}
int PSystemLog::Buffer::overflow(int c)
{
if (pptr() >= epptr()) {
PMEMORY_IGNORE_ALLOCATIONS_FOR_SCOPE;
int ppos = pptr() - pbase();
char * newptr = string.GetPointer(string.GetSize() + 10);
setp(newptr, newptr + string.GetSize() - 1);
pbump(ppos);
}
if (c != EOF) {
*pptr() = (char)c;
pbump(1);
}
return 0;
}
int PSystemLog::Buffer::underflow()
{
return EOF;
}
int PSystemLog::Buffer::sync()
{
Level logLevel = log->logLevel;
#if PTRACING
if (log->width() != 0 &&(PTrace::GetOptions()&PTrace::SystemLogStream) != 0) {
// Trace system sets the ios stream width as the last thing it does before
// doing a flush, which gets us here. SO now we can get a PTRACE looking
// exactly like a PSYSTEMLOG of appropriate level.
unsigned traceLevel = log->width() -1 + PSystemLog::Warning;
log->width(0);
if (traceLevel >= PSystemLog::NumLogLevels)
traceLevel = PSystemLog::NumLogLevels-1;
logLevel = (Level)traceLevel;
}
#endif
PSystemLog::Output(logLevel, string);
PMEMORY_IGNORE_ALLOCATIONS_FOR_SCOPE;
string.SetSize(10);
char * base = string.GetPointer();
*base = '\0';
setp(base, base + string.GetSize() - 1);
return 0;
}
///////////////////////////////////////////////////////////////////////////////
// PServiceProcess
PServiceProcess::PServiceProcess(const char * manuf, const char * name,
WORD major, WORD minor, CodeStatus stat, WORD build)
: PProcess(manuf, name, major, minor, stat, build),
systemLogFileName(GetFile().GetDirectory() + GetName() + " Log.TXT")
{
controlWindow = debugWindow = NULL;
currentLogLevel = PSystemLog::Warning;
}
PServiceProcess & PServiceProcess::Current()
{
PServiceProcess & process = (PServiceProcess &)PProcess::Current();
PAssert(PIsDescendant(&process, PServiceProcess), "Not a service!");
return process;
}
const char * PServiceProcess::GetServiceDependencies() const
{
return "EventLog\0";
}
PBoolean PServiceProcess::IsServiceProcess() const
{
return PTrue;
}
static PBoolean IsServiceRunning(PServiceProcess * svc)
{
HANDLE hEvent = OpenEvent(EVENT_MODIFY_STATE, PFalse, svc->GetName());
if (hEvent == NULL)
return ::GetLastError() == ERROR_ACCESS_DENIED;
CloseHandle(hEvent);
return PTrue;
}
int PServiceProcess::_main(void * arg)
{
{
PMEMORY_IGNORE_ALLOCATIONS_FOR_SCOPE;
PSetErrorStream(new PSystemLog(PSystemLog::StdError));
#if PTRACING
PTrace::SetStream(new PSystemLog(PSystemLog::Debug3));
PTrace::ClearOptions(PTrace::FileAndLine);
PTrace::SetOptions(PTrace::SystemLogStream);
PTrace::SetLevel(4);
#endif
}
hInstance = (HINSTANCE)arg;
OSVERSIONINFO verinfo;
verinfo.dwOSVersionInfoSize = sizeof(verinfo);
GetVersionEx(&verinfo);
switch (verinfo.dwPlatformId) {
case VER_PLATFORM_WIN32_NT :
isWin95 = PFalse;
break;
case VER_PLATFORM_WIN32_WINDOWS :
isWin95 = PTrue;
break;
default :
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -