www.pudn.com > shijiantongbu.zip > NetTimeIPC.pas
{ ************************************************************************
NetTime is copyrighted by Graham Mainwaring. Permission is hereby
granted to use, modify, redistribute and create derivative works
provided this attribution is not removed. I also request that if you
make any useful changes, please e-mail the diffs to graham@mhn.org
so that I can include them in an 'official' release.
************************************************************************ }
unit NetTimeIPC;
interface
uses Windows, Classes, SysUtils, NetTimeCommon, iswinnt, syncobjs;
type
TServerStatusBlock = record
Config: TServerConfigBlock;
Server: boolean;
Active: boolean;
Status: TSyncStatus;
LastUpdateTime: TDateTime;
end;
// Whenever you change this, you have to increment ProtocolVersion
// in NetTimeCommon.
TShareMemBlock = record
// Section that anyone can write to
G_MagicCookie: longword;
G_ProtocolVersion: longword;
G_ExitNowFlag: boolean;
// Section that the SERVER writes to
S_ServerPID: longword;
S_StatusProvidedSerial: integer;
S_AdviseStatusFlag: boolean;
S_Status: TServerStatusBlock;
S_LargeAdjFlag: boolean;
S_ServerTime, S_StationTime: TDateTime;
S_LastUpdateGood: boolean; // only valid when C_WantUpdateNow called
// Section that the CLIENT writes to
C_ClientPID: longword;
C_ClientStatusChangeFlag: boolean;
C_StatusWantedSerial: integer;
C_LargeAdjReplyFlag: boolean;
C_LargeAdjReplyResult: boolean;
C_SetConfigFlag: boolean;
C_Config: TServerConfigBlock;
C_SetServerFlag: boolean;
C_Server: boolean;
C_WantUpdateNowFlag: boolean;
end;
PShareMemBlock = ^TShareMemBlock;
TExitNowCallback = procedure of object;
TUpdateNowCallback = function: boolean of object;
TNetTimeIPC = class
protected
ShareMemHandle: THandle;
ShareMem: PShareMemBlock;
ExitNowCallback: TExitNowCallback;
protected
HaveKilled: boolean;
public
procedure InitResources; virtual;
procedure FreeResources; virtual;
function CheckServerRunning: boolean;
function CheckClientRunning: boolean;
procedure KillEverything;
constructor Create(const enb: TExitNowCallback);
destructor Destroy; override;
end;
TGetServerStatusCallback = function: TServerStatusBlock of object;
TSetConfigCallback = procedure(const cfg: TServerConfigBlock) of object;
TSetServerCallback = procedure(const srv: boolean) of object;
TNetTimeServerThread = class;
TNetTimeIPCServer = class(TNetTimeIPC)
private
MyThread: TNetTimeServerThread;
ClientEvent: THandle;
GetServerStatusCallback: TGetServerStatusCallback;
SetConfigCallback: TSetConfigCallback;
SetServerCallback: TSetServerCallback;
UpdateNowCallback: TUpdateNowCallback;
procedure ClientHello;
procedure ClientGoodbye;
procedure SetServer;
procedure SetConfig;
public
procedure InitResources; override;
procedure FreeResources; override;
function LargeAdjustWarn(const ServerTime, StationTime: TDateTime): boolean;
procedure AdviseStatus;
constructor Create(const gsb: TGetServerStatusCallback;
const scb: TSetConfigCallback; const ssb: TSetServerCallback;
const enb: TExitNowCallback; const unb: TUpdateNowCallback);
destructor Destroy; override;
end;
TNetTimeServerThread = class(TThread)
protected
MyOwner: TNetTimeIPCServer;
MyEvent: THandle;
procedure Execute; override;
public
constructor Create(const Owner: TNetTimeIPCServer;
const Suspended: boolean = false);
end;
TAdviseStatusCallback = procedure(const stat: TServerStatusBlock) of object;
TLargeAdjCallback = function(const ServerTime, StationTime: TDateTime): boolean of object;
TNetTimeClientThread = class;
TNetTimeIPCClient = class(TNetTimeIPC)
private
MyThread: TNetTimeClientThread;
ServerEvent: THandle;
AdviseStatusCallback: TAdviseStatusCallback;
LargeAdjCallback: TLargeAdjCallback;
procedure RetrieveStatus;
procedure DoLargeAdj;
public
procedure InitResources; override;
procedure FreeResources; override;
function GetServerStatus: TServerStatusBlock;
procedure SetConfig(const cfg: TServerConfigBlock);
procedure SetServer(const srv: boolean);
constructor Create(const asb: TAdviseStatusCallback;
const lab: TLargeAdjCallback; const enb: TExitNowCallback);
function UpdateNow: boolean;
destructor Destroy; override;
end;
TNetTimeClientThread = class(TThread)
protected
MyOwner: TNetTimeIPCClient;
MyEvent: THandle;
procedure Execute; override;
public
constructor Create(const Owner: TNetTimeIPCClient;
const Suspended: boolean = false);
end;
implementation
const
ShareMemName = 'NetTimeGHJM_ShareMem';
ServerEventName = 'NetTimeGHJM_ServerEvent';
ClientEventName = 'NetTimeGHJM_ServerEvent';
{ TNetTimeIPC }
procedure TNetTimeIPC.InitResources;
var
sa: TSecurityAttributes;
sd: TSecurityDescriptor;
sp: PSecurityAttributes;
ae: boolean;
begin
if IsWindowsNT then
begin
InitializeSecurityDescriptor(@sd,SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDACL(@sd,true,nil,false);
sa.nLength := sizeof(sa);
sa.lpSecurityDescriptor := @sd;
sa.bInheritHandle := false;
sp := @sa;
end
else
sp := nil;
ShareMemHandle := CreateFileMapping($ffffffff,sp,PAGE_READWRITE,0,
sizeof(TShareMemBlock),pchar(ShareMemName));
if ShareMemHandle = 0 then
raise exception.create('Could not open shared memory');
ae := (GetLastError = ERROR_ALREADY_EXISTS);
ShareMem := MapViewOfFile(ShareMemHandle,FILE_MAP_ALL_ACCESS,0,0,
sizeof(TShareMemBlock));
if ShareMem = nil then
raise exception.create('Could not map shared memory');
if not ae then
FillChar(ShareMem^,sizeof(TShareMemBlock),0);
end;
procedure TNetTimeIPC.FreeResources;
begin
if ShareMem <> nil then
begin
UnmapViewOfFile(ShareMem);
CloseHandle(ShareMemHandle);
ShareMem := nil;
end;
end;
function CheckProcessExists(const pid: longword): boolean;
var
ph: THandle;
er: longword;
begin
if pid = 0 then
begin
result := false;
exit;
end;
ph := OpenProcess(PROCESS_QUERY_INFORMATION,false,pid);
if ph = 0 then
begin
er := GetLastError;
if (er = ERROR_ACCESS_DENIED) or (er = ERROR_NETWORK_ACCESS_DENIED) or
(er = ERROR_EA_ACCESS_DENIED) then
result := true
else
result := false;
end
else
begin
result := true;
CloseHandle(ph);
end;
end;
function TNetTimeIPC.CheckServerRunning: boolean;
begin
if ShareMem = nil then
result := false
else
result := CheckProcessExists(ShareMem^.S_ServerPID);
end;
function TNetTimeIPC.CheckClientRunning: boolean;
begin
if ShareMem = nil then
result := false
else
result := CheckProcessExists(ShareMem^.C_ClientPID);
end;
procedure SignalEventByName(const Name: string);
var
EventHandle: THandle;
begin
EventHandle := OpenEvent(EVENT_ALL_ACCESS,false,pchar(Name));
if EventHandle <> 0 then
begin
SetEvent(EventHandle);
CloseHandle(EventHandle);
end;
end;
procedure TNetTimeIPC.KillEverything;
begin
if ShareMem <> nil then
begin
ShareMem^.G_ExitNowFlag := true;
SignalEventByName(ClientEventName);
SignalEventByName(ServerEventName);
end;
if Assigned(ExitNowCallback) then
ExitNowCallback;
end;
constructor TNetTimeIPC.Create(const enb: TExitNowCallback);
begin
inherited Create;
ExitNowCallback := enb;
HaveKilled := false;
ShareMem := nil;
end;
destructor TNetTimeIPC.Destroy;
begin
FreeResources;
inherited;
end;
{TNetTimeIPCServer}
procedure TNetTimeIPCServer.ClientHello;
begin
ClientEvent := OpenEvent(EVENT_ALL_ACCESS,false,ClientEventName);
end;
procedure TNetTimeIPCServer.ClientGoodbye;
begin
ClientEvent := 0;
end;
function TNetTimeIPCServer.LargeAdjustWarn(const ServerTime, StationTime: TDateTime): boolean;
begin
if (ClientEvent = 0) or (ShareMem = nil) or (not CheckClientRunning) then
begin
result := true;
exit;
end;
ShareMem^.S_ServerTime := ServerTime;
ShareMem^.S_StationTime := StationTime;
ShareMem^.C_LargeAdjReplyFlag := false;
ShareMem^.S_LargeAdjFlag := true;
SetEvent(ClientEvent);
repeat
Sleep(IPCSleepTime);
until ShareMem^.C_LargeAdjReplyFlag;
ShareMem^.C_LargeAdjReplyFlag := false;
result := ShareMem^.C_LargeAdjReplyResult;
end;
procedure TNetTimeIPCServer.AdviseStatus;
begin
if ShareMem = nil then
exit;
ShareMem^.S_Status := GetServerStatusCallback;
ShareMem^.S_AdviseStatusFlag := true;
if (ClientEvent <> 0) then
SetEvent(ClientEvent);
end;
procedure TNetTimeIPCServer.InitResources;
begin
inherited;
ShareMem^.G_MagicCookie := MagicCookie;
ShareMem^.G_ProtocolVersion := ProtocolVersion;
ShareMem^.S_ServerPID := GetCurrentProcessID;
MyThread := TNetTimeServerThread.Create(Self);
end;
procedure TNetTimeIPCServer.FreeResources;
begin
if ShareMem <> nil then
ShareMem^.S_ServerPID := 0;
if MyThread <> nil then
begin
MyThread.Terminate;
SetEvent(MyThread.MyEvent);
MyThread.WaitFor;
MyThread.Free;
MyThread := nil;
end;
inherited;
end;
constructor TNetTimeIPCServer.Create(const gsb: TGetServerStatusCallback;
const scb: TSetConfigCallback; const ssb: TSetServerCallback;
const enb: TExitNowCallback; const unb: TUpdateNowCallback);
begin
inherited Create(enb);
MyThread := nil;
GetServerStatusCallback := gsb;
SetConfigCallback := scb;
SetServerCallback := ssb;
UpdateNowCallback := unb;
end;
destructor TNetTimeIPCServer.Destroy;
begin
FreeResources;
inherited;
end;
procedure TNetTimeIPCServer.SetServer;
begin
if ShareMem <> nil then
SetServerCallback(ShareMem^.C_Server);
end;
procedure TNetTimeIPCServer.SetConfig;
begin
if ShareMem <> nil then
SetConfigCallback(ShareMem^.C_Config);
end;
{ TNetTimeServerThread }
constructor TNetTimeServerThread.Create(const Owner: TNetTimeIPCServer;
const Suspended: boolean = false);
var
sa: TSecurityAttributes;
sd: TSecurityDescriptor;
sp: PSecurityAttributes;
begin
inherited Create(true);
MyOwner := Owner;
if IsWindowsNT then
begin
InitializeSecurityDescriptor(@sd,SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDACL(@sd,true,nil,false);
sa.nLength := sizeof(sa);
sa.lpSecurityDescriptor := @sd;
sa.bInheritHandle := false;
sp := @sa;
end
else
sp := nil;
MyEvent := CreateEvent(sp,true,false,ServerEventName);
if MyEvent = 0 then
raise exception.create('Could not create server event');
if not Suspended then
Resume;
end;
procedure TNetTimeServerThread.Execute;
begin
repeat
ResetEvent(MyEvent);
WaitForSingleObject(MyEvent,INFINITE);
if MyOwner.ShareMem <> nil then
with MyOwner.ShareMem^ do
begin
if (C_StatusWantedSerial > S_StatusProvidedSerial) then
begin
S_Status := MyOwner.GetServerStatusCallback;
S_StatusProvidedSerial := C_StatusWantedSerial;
end;
if C_SetConfigFlag then
begin
MyOwner.SetConfig;
C_SetConfigFlag := false;
end;
if C_SetServerFlag then
begin
MyOwner.SetServer;
C_SetServerFlag := false;
end;
if C_ClientStatusChangeFlag then
begin
if C_ClientPID <> 0 then
MyOwner.ClientHello
else
MyOwner.ClientGoodbye;
C_ClientStatusChangeFlag := false;
end;
if C_WantUpdateNowFlag then
begin
if Assigned(MyOwner.UpdateNowCallback) then
S_LastUpdateGood := MyOwner.UpdateNowCallback;
C_WantUpdateNowFlag := false;
end;
if G_ExitNowFlag then
begin
if not MyOwner.HaveKilled then
begin
MyOwner.HaveKilled := true;
if Assigned(MyOwner.ExitNowCallback) then
Synchronize(MyOwner.ExitNowCallback);
end;
end;
end;
until Terminated;
end;
{ TNetTimeIPCClient }
function TNetTimeIPCClient.GetServerStatus: TServerStatusBlock;
var
sws: integer;
crit: TCriticalSection;
begin
if ShareMem = nil then
raise exception.create('Shared memory not mapped');
if not CheckServerRunning then
raise exception.create('Server died');
ShareMem^.S_AdviseStatusFlag := false;
crit := TCriticalSection.Create;
try
crit.Acquire;
sws := ShareMem^.C_StatusWantedSerial + 1;
ShareMem^.C_StatusWantedSerial := sws;
crit.Release;
finally
crit.Free;
end;
SetEvent(ServerEvent);
repeat
Sleep(IPCSleepTime);
until ShareMem^.S_StatusProvidedSerial >= sws;
result := ShareMem^.S_Status;
end;
procedure TNetTimeIPCClient.RetrieveStatus;
begin
if ShareMem = nil then
raise exception.create('Shared memory not mapped');
AdviseStatusCallback(ShareMem^.S_Status);
end;
procedure TNetTimeIPCClient.DoLargeAdj;
begin
if ShareMem = nil then
raise exception.create('Shared memory not mapped');
ShareMem^.C_LargeAdjReplyResult := LargeAdjCallback(ShareMem^.S_ServerTime,
ShareMem^.S_StationTime);
Sharemem^.C_LargeAdjReplyFlag := true;
end;
procedure TNetTimeIPCClient.SetConfig(const cfg: TServerConfigBlock);
begin
if ShareMem = nil then
raise exception.create('Shared memory not mapped');
if not CheckServerRunning then
raise exception.create('Server died');
ShareMem^.C_Config := cfg;
ShareMem^.C_SetConfigFlag := true;
SetEvent(ServerEvent);
repeat
Sleep(IPCSleepTime);
until ShareMem^.C_SetConfigFlag = false;
end;
procedure TNetTimeIPCClient.SetServer(const srv: boolean);
begin
if ShareMem = nil then
raise exception.create('Shared memory not mapped');
if not CheckServerRunning then
raise exception.create('Server died');
ShareMem^.C_Server := srv;
ShareMem^.C_SetServerFlag := true;
SetEvent(ServerEvent);
repeat
Sleep(IPCSleepTime);
until ShareMem^.C_SetServerFlag = false;
end;
function TNetTimeIPCClient.UpdateNow: boolean;
begin
if ShareMem = nil then
raise exception.create('Shared memory not mapped');
if not CheckServerRunning then
raise exception.create('Server died');
ShareMem^.C_WantUpdateNowFlag := true;
SetEvent(ServerEvent);
repeat
Sleep(IPCSleepTime);
until ShareMem^.C_WantUpdateNowFlag = false;
result := ShareMem^.S_LastUpdateGood;
end;
procedure TNetTimeIPCClient.InitResources;
begin
inherited;
if (ShareMem^.G_MagicCookie <> MagicCookie) or
(ShareMem^.G_ProtocolVersion <> ProtocolVersion) then
raise exception.create('Could not connect to server: Server is running a different version of NetTime.');
ServerEvent := OpenEvent(EVENT_ALL_ACCESS,false,ClientEventName);
if ServerEvent = 0 then
raise exception.create('Could not open server event: error '+inttostr(GetLastError));
MyThread := TNetTimeClientThread.Create(Self);
ShareMem^.C_ClientPID := GetCurrentProcessID;
ShareMem^.C_ClientStatusChangeFlag := true;
SetEvent(ServerEvent);
end;
procedure TNetTimeIPCClient.FreeResources;
begin
if ShareMem <> nil then
begin
ShareMem^.C_ClientPID := 0;
ShareMem^.C_ClientStatusChangeFlag := true;
if ServerEvent <> 0 then
SetEvent(ServerEvent);
end;
if MyThread <> nil then
begin
MyThread.Terminate;
SetEvent(MyThread.MyEvent);
MyThread.WaitFor;
MyThread.Free;
MyThread := nil;
end;
inherited;
end;
constructor TNetTimeIPCClient.Create(const asb: TAdviseStatusCallback;
const lab: TLargeAdjCallback; const enb: TExitNowCallback);
begin
inherited Create(enb);
AdviseStatusCallback := asb;
LargeAdjCallback := lab;
ServerEvent := 0;
MyThread := nil;
end;
destructor TNetTimeIPCClient.Destroy;
begin
if ShareMem <> nil then
begin
ShareMem^.C_ClientPID := 0;
ShareMem^.C_ClientStatusChangeFlag := true;
SetEvent(ServerEvent);
end;
if ServerEvent <> 0 then
CloseHandle(ServerEvent);
MyThread.Terminate;
if MyThread.MyEvent <> 0 then
SetEvent(MyThread.MyEvent);
inherited;
end;
{ TNetTimeClientThread }
constructor TNetTimeClientThread.Create(const Owner: TNetTimeIPCClient;
const Suspended: boolean = false);
var
{
sa: TSecurityAttributes;
sd: TSecurityDescriptor;
}
sp: PSecurityAttributes;
begin
inherited Create(true);
MyOwner := Owner;
// if IsWindowsNT then
// begin
// InitializeSecurityDescriptor(@sd,SECURITY_DESCRIPTOR_REVISION);
// sa.nLength := sizeof(sa);
// sa.lpSecurityDescriptor := @sd;
// sa.bInheritHandle := false;
// sp := @sa;
// end
// else
sp := nil;
MyEvent := CreateEvent(sp,true,false,ClientEventName);
if MyEvent = 0 then
raise exception.create('Could not create client event');
if not Suspended then
Resume;
end;
procedure TNetTimeClientThread.Execute;
begin
repeat
ResetEvent(MyEvent);
WaitForSingleObject(MyEvent,INFINITE);
if MyOwner.ShareMem <> nil then
with MyOwner.ShareMem^ do
begin
if S_AdviseStatusFlag then
begin
Synchronize(MyOwner.RetrieveStatus);
S_AdviseStatusFlag := false;
end;
if S_LargeAdjFlag then
begin
Synchronize(MyOwner.DoLargeAdj);
S_LargeAdjFlag := false;
end;
if G_ExitNowFlag then
begin
if not MyOwner.HaveKilled then
begin
MyOwner.HaveKilled := true;
if Assigned(MyOwner.ExitNowCallback) then
Synchronize(MyOwner.ExitNowCallback);
end;
end;
end;
until Terminated;
end;
end.