i have an Windows Service made in Delphi 6 that connects to a progress openedge database and transfer the information to a MySQL database one time a day. The problem is, when i start the service, it works fine, but if i update the progress database, in the next day the service still showing the first values (same values). Its like i have an database image and it dont get updated. If i restart the service, they will get the new values, but in the next day will happen the same thing.
For database connection i use TDatabase class.
Anyone knows how to fix this?
This is the code:
unit svcMain;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, SvcMgr, Dialogs,
ExtCtrls, AppEvnts, DateUtils, inifiles;
type
TsvcAgraria = class(TService)
timAcao: TTimer;
procedure timAcaoTimer(Sender: TObject);
procedure ServicePause(Sender: TService; var Paused: Boolean);
procedure ServiceStart(Sender: TService; var Started: Boolean);
procedure ServiceStop(Sender: TService; var Stopped: Boolean);
procedure ServiceContinue(Sender: TService; var Continued: Boolean);
function RightPad(S: string; Ch: Char; Len: Integer): string;
private
{ Private declarations }
procedure Gera_log(ctipo,msgtxt:string);
procedure Replica_Dados();
procedure Verifica_Ini();
public
function GetServiceController: TServiceController; override;
{ Public declarations }
end;
var
svcAgraria: TsvcAgraria;
lsup1,lsup2,lsupexc,ltudook:boolean;
cont_registros:integer;
teste,loteini,lotefim,tLooping:string;
implementation
uses UdtmRamal;
{$R *.DFM}
procedure ServiceController(CtrlCode: DWord); stdcall;
begin
svcAgraria.Controller(CtrlCode);
end;
function TsvcAgraria.GetServiceController: TServiceController;
begin
Result := ServiceController;
end;
procedure TsvcAgraria.ServiceStart(Sender: TService; var Started: Boolean);
begin
Gera_Log('INFO','Iniciado o Serviço Windows para Replicação de Ramais.');
timAcao.Enabled := Started;
end;
procedure TsvcAgraria.ServiceStop(Sender: TService; var Stopped: Boolean);
begin
timAcao.Enabled := not(Stopped);
Gera_Log('INFO','Finalizado o Serviço Windows para Replicação de Ramais.');
end;
procedure TsvcAgraria.ServicePause(Sender: TService; var Paused: Boolean);
begin
timAcao.Enabled := not(Paused);
Gera_Log('INFO','Serviço Pausado.');
end;
procedure TsvcAgraria.ServiceContinue(Sender: TService;
var Continued: Boolean);
begin
timAcao.Enabled := Continued;
Gera_Log('INFO','Serviço em Execução.');
end;
//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
procedure TsvcAgraria.timAcaoTimer(Sender: TObject);
var tagora:string;
begin
Verifica_Ini();
tagora := RightPad(IntToStr(HourOf(Now)), '0', 2) + ':' + RightPad(IntToStr(MinuteOf(Now)), '0', 2);
//Gera_Log('INFO','Valores à comparar: var "' + tagora + '", ini "' + tLooping + '".');
if (tLooping = tagora) then
begin
Replica_Dados();
end;
end;
//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
procedure TsvcAgraria.Replica_Dados();
var
_validaprof:integer;
begin
Gera_Log('INFO','--- Iniciando a Replicação de Dados para a Intranet...');
/////////////////////////////////////////////////
try
dtmAtualizaRamal.DbMyRamal.Connected:=true;
dtmAtualizaRamal.ConectDBHR.Connected:=true;
if (dtmAtualizaRamal.ConectDBHR.Connected and dtmAtualizaRamal.DbMyRamal.Connected) then
begin
/* BUSINESS LOGIC */
end;
except
on E: Exception do Gera_Log('ERRO',E.Message);
end;
Gera_Log('INFO','--- Desconectar Banco.');
dtmAtualizaRamal.DbMyRamal.Connected:=false;
dtmAtualizaRamal.ConectDBHR.Connected:=false;
Gera_Log('INFO','--- Final da Replicação de Dados para a Intranet.');
end;
function TsvcAgraria.RightPad(S: string; Ch: Char; Len: Integer): string;
var
RestLen: Integer;
begin
Result := S;
RestLen := Len - Length(s);
if RestLen < 1 then Exit;
Result := StringOfChar(Ch, RestLen) + S;
end;
procedure TsvcAgraria.Gera_log(ctipo,msgtxt:string);
var
LogFileName,LogData:string;
LogFile:TextFile;
begin
LogFileName := 'c:\datasul\temp\scvRamais.log.txt';
AssignFile(LogFile, LogFileName);
//either create an error log file, or append to an existing one
if FileExists(LogFileName) then
Append(LogFile)
else
Rewrite(LogFile);
try
//add the current date/time and the exception message to the log
LogData := Format('%s : %s : %s',[DateTimeToStr(Now),ctipo,msgtxt]) ;
WriteLn(LogFile,LogData) ;
finally
CloseFile(LogFile);
end;
end;
end.
Related
The code below, via...
procedure TTimetellServiceServerMonitor.ServiceDebugLog(const AMsg: String);
const cDebugLogFile = 'd:\temp\service.log';
... outputs this debug info showing that we go through the OnCreate several times (I added the - - descriptions):
- testsvcserverMonitor /install -
S 1802 servicecreate
S 1802 AfterInstall
- start from services app -
S 1741 servicecreate
S 1741 servicestart
S 1741 MonitorThread.Start
- stop from services app -
S 1741 servicestop
- testsvcserverMonitor /uninstall -
S 1336 servicecreate
S 1336 beforeuninstall
I assign a random tag value to the service in its OnCreate and you can see that these are different.
Why does this happen, is there a bug, should I prevent it and how?
(Windows 32 bit, Delphi 10.4.2. Sydney)
.pas code:
unit USvcServerMonitor;
interface
uses
WinApi.Windows, WinApi.Messages, System.SysUtils, System.Classes, Vcl.SvcMgr, WinApi.WinSvc;
type
TMonitorServiceThread = class(TThread) // Worker thread
private
FCheckLiveEvery,
FLastLiveCheck : TDateTime;
public
procedure Execute; override;
end;
type
TApplicationMonitor = class(TService)
procedure ServiceBeforeUninstall(Sender: TService);
procedure ServiceCreate(Sender: TObject);
procedure ServiceStop(Sender: TService; var Stopped: Boolean);
// procedure ServiceExecute(Sender: TService); Not necessary, WorkerThread does the work
procedure ServiceStart(Sender: TService; var Started: Boolean);
procedure ServiceAfterInstall(Sender: TService);
private
procedure ServiceDebugLog(const AMsg: String);
public
function GetServiceController: TServiceController; override;
end;
var
MonitorThread : TMonitorServiceThread;
ApplicationMonitor: TApplicationMonitor;
implementation
{$R *.DFM}
procedure ServiceController(CtrlCode: DWord); stdcall;
begin
ApplicationMonitor.Controller(CtrlCode);
end;
function TApplicationMonitor.GetServiceController: TServiceController;
begin
Result := ServiceController;
end;
procedure TApplicationMonitor.ServiceAfterInstall(Sender: TService);
begin
ServiceDebugLog('AfterInstall');
// StartType is stAuto, but start manually after install
end;
procedure TApplicationMonitor.ServiceBeforeUninstall(Sender: TService);
begin
ServiceDebugLog('beforeuninstall');
end;
procedure TApplicationMonitor.ServiceCreate(Sender: TObject);
begin
Self.Tag := 1000 + Random(1000); // For debugging
ServiceDebugLog('servicecreate');
end;
procedure TApplicationMonitor.ServiceStart(Sender: TService; var Started: Boolean);
begin
ServiceDebugLog('servicestart');
MonitorThread := TMonitorServiceThread.Create(true); // Suspended
ServiceDebugLog('MonitorThread.Start');
MonitorThread.Start;
Started := true;
end;
procedure TApplicationMonitor.ServiceDebugLog(const AMsg: String);
// Quick-n-dirty debugging routine
const cDebugLogFile = 'd:\temp\service.log';
var t: textfile;
begin
if not fileexists(cDebugLogFile) then
begin
assignfile(t,cDebugLogFile);
Rewrite(t);
end
else
begin
assignfile(t,cDebugLogFile);
Append(T);
end;
writeln(T,'S ' + Inttostr(self.Tag) + ' ' + AMsg);
closefile(t);
end;
procedure TApplicationMonitor.ServiceStop(Sender: TService; var Stopped: Boolean);
begin
ServiceDebugLog('servicestop');
MonitorThread.Terminate;
Sleep(100);
MonitorThread.Free;
Sleep(100);
Stopped := True;
end;
{ TMonitorServiceThread }
procedure TMonitorServiceThread.Execute;
begin
inherited;
FLastLiveCheck := Now;
FCheckLiveEvery := 1;
while not Terminated do
begin
try
if (FCheckLiveEvery > 0) and (Now-FLastLiveCheck > FCheckLiveEvery/1440) then
begin
// Do some checks
FLastLiveCheck := Now;
end;
Sleep(500);
finally
end;
end;
end;
end.
.dfm file:
object ApplicationMonitor: TApplicationMonitor
Tag = 123
OldCreateOrder = False
OnCreate = ServiceCreate
AllowPause = False
DisplayName = 'Test Application Monitor Service'
AfterInstall = ServiceAfterInstall
BeforeUninstall = ServiceBeforeUninstall
OnStart = ServiceStart
OnStop = ServiceStop
Height = 250
Width = 400
end
TService is derived from TDataModule, so OnCreate will be called when the TService instance is created. That obviously happens when the service is going to be started, but also when it is installed and uninstalled.
So, no, it is not a bug and you also should not prevent it.
Perhaps it is just that your expectations are wrong?
Related to this question: Should "Library path" point to the source files of packages?
Fabricio Araujo suggested that is not necessary to set the 'search path' for each new project by creating a 'Default Project Option'. How can this be done in Delphi XE7?
Prompted by your q, and more for amusement than anything else, I decided to try
writing an IDE-plugin that would provide a way to store some preferred project
settings somewhere and allow you to apply them to the current project.
To use, prepare and save a sample .Ini file containing your preferred settings in the format shown below (it's important to get the project option names right, see below for how to do find them out), then compile the unit below into a new package and install it in the IDE. Its gui will pop up when you subsequently open a project.
The settings in the .Ini are loaded into a ValueList editor and pressing the
[Return] key in one of the values will apply it to the project.
Interestingly, the names the IDE uses for the Project seetings are the same in XE7
as they are in D7. Iow, the XE7 IDE uses these internally rather than the names
which appear in the .DProj XML file. You can get a full list of them by clicking the GetOptions button.
As usual when working with the IDE OTA services, the code has to include
a fair amount of "baggage".
Tested in D7 and XE7.
Sample Ini File:
[settings]
OutputDir=Somewhere
UnitOutputDir=Somewhere else
UnitDir=$(DELPHI)
ObjDir=$(DELPHI)
SrcDir=$(DELPHI)
ResDir=$(DELPHI)
Code:
unit ProjectOptionsXE7u;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, ToolsAPI, Grids, ValEdit, IniFiles;
type
TProjectOptionsForm = class(TForm)
Panel1: TPanel;
Memo1: TMemo;
ValueListEditor1: TValueListEditor;
btnGetOptions: TButton;
procedure btnGetOptionsClick(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure ValueListEditor1KeyPress(Sender: TObject; var Key: Char);
private
function GetCurrentProject: IOTAProject;
procedure GetOptionsFromIni;
procedure UpdateOptionValue;
public
Services: IOTAServices;
ProjectGroup : IOTAProjectGroup;
Project: IOTAProject;
Options : IOTAProjectOptions;
ModServices: IOTAModuleServices;
Module: IOTAModule;
NotifierIndex: Integer;
Ini : TMemIniFile;
IsSetUp : Boolean;
SetUpCount : Integer;
procedure GetProjectOptions;
procedure SetUp;
end;
var
ProjectOptionsForm: TProjectOptionsForm;
procedure Register;
implementation
{$R *.dfm}
type
TIdeNotifier = class(TNotifierObject, IOTANotifier, IOTAIDENotifier)
protected
procedure AfterCompile(Succeeded: Boolean);
procedure BeforeCompile(const Project: IOTAProject; var Cancel: Boolean);
procedure FileNotification(NotifyCode: TOTAFileNotification;
const FileName: string; var Cancel: Boolean);
end;
procedure Register;
begin
ProjectOptionsForm:= TProjectOptionsForm.Create(Nil);
ProjectOptionsForm.Services := BorlandIDEServices as IOTAServices;
ProjectOptionsForm.NotifierIndex := ProjectOptionsForm.Services.AddNotifier(TIdeNotifier.Create);
end;
procedure CloseDown;
begin
ProjectOptionsForm.Services.RemoveNotifier(ProjectOptionsForm.NotifierIndex);
ProjectOptionsForm.Close;
ProjectOptionsForm.Free;
end;
procedure TIdeNotifier.AfterCompile(Succeeded: Boolean);
begin
end;
procedure TIdeNotifier.BeforeCompile(const Project: IOTAProject; var Cancel: Boolean);
begin
end;
procedure TIdeNotifier.FileNotification(NotifyCode: TOTAFileNotification;
const FileName: string; var Cancel: Boolean);
begin
if NotifyCode in [ofnProjectDesktopLoad, ofnActiveProjectChanged] then begin
ProjectOptionsForm.Show;
// ProjectOptionsForm.Memo1.Lines.Add('Got notification');
ProjectOptionsForm.SetUp;
// ProjectOptionsForm.Memo1.Lines.Add('after GetProjectOptions');
end;
end;
procedure TProjectOptionsForm.btnGetOptionsClick(Sender: TObject);
var
KeyName,
Value,
S : String;
V : Variant;
i : Integer;
begin
GetProjectOptions;
ValueListEditor1.Strings.Clear;
for i := Low(Options.GetOptionNames) to High(Options.GetOptionNames) do begin
try
KeyName := Options.GetOptionNames[i].Name;
if CompareText(KeyName, 'HeapSize') = 0 then
NoOp;
V := Options.Values[KeyName];
if not VarIsEmpty(V) then
Value := VarToStr(V)
else
Value := '';
ValueListEditor1.InsertRow(KeyName, Value, True);
except
// Reading some CPP-related settings cause exceptions
S := '***Error ' + KeyName; // + ': ' + IntToStr(Options.Values[KeyName].Kind);
Memo1.Lines.Add(S);
end;
end;
end;
procedure TProjectOptionsForm.FormDestroy(Sender: TObject);
begin
Ini.Free;
end;
procedure TProjectOptionsForm.GetOptionsFromIni;
var
i : Integer;
KeyName : String;
TL : TStringList;
begin
ValueListEditor1.Strings.Clear;
TL := TStringList.Create;
try
Ini.ReadSection('Settings', TL);
Assert(TL.Count > 0);
for i := 0 to TL.Count - 1 do begin
KeyName := TL[i];
ValueListEditor1.InsertRow(KeyName, Ini.ReadString('Settings', KeyName, ''), True);
end;
finally
TL.Free;
end;
end;
procedure TProjectOptionsForm.FormCreate(Sender: TObject);
var
IniFileName : String;
begin
IniFileName := 'd:\aaad7\ota\ProjectOptions.Ini'; // <beware of hard-code path
Ini := TMemIniFile.Create(IniFileName);
GetOptionsFromIni;
end;
function TProjectOptionsForm.GetCurrentProject: IOTAProject;
var
i: Integer;
begin
Result := nil;
ModServices := BorlandIDEServices as IOTAModuleServices;
for i := 0 to ModServices.ModuleCount - 1 do
begin
Module := ModServices.Modules[i];
if Supports(Module, IOTAProjectGroup, ProjectGroup) then begin
Result := ProjectGroup.ActiveProject;
Options := Result.ProjectOptions;
Exit;
end
else if Supports(Module, IOTAProject, Project) then
begin // In the case of unbound packages, return the 1st
if Result = nil then begin
Result := Project;
Options := Result.ProjectOptions;
end;
end;
end;
end;
procedure TProjectOptionsForm.GetProjectOptions;
begin
Assert(Project <> Nil, 'Project');
Options := Project.ProjectOptions;
end;
procedure TProjectOptionsForm.SetUp;
begin
Project := GetCurrentProject;
GetProjectOptions;
Inc(SetUpCount);
Caption := 'Setup done ' + IntToStr(SetUpCount);
IsSetUp := True;
end;
procedure TProjectOptionsForm.UpdateOptionValue;
var
Rect : TGridRect;
S : String;
KeyName,
Value : String;
Row,
Col : Integer;
begin
if Options = Nil then
Exit;
Rect := ValueListEditor1.Selection;
// S := 'left: %d top: %d right: %d, bottom: %d';
// S := Format(S, [Rect.Left, Rect.Top, Rect.Right, Rect.Bottom]);
// Memo1.Lines.Add(S);
Row := Rect.Bottom;
Col := Rect.Left - 1;
KeyName := ValueListEditor1.Cells[Col, Row];
Value := ValueListEditor1.Values[KeyName];
Options.SetOptionValue(KeyName, Value);
Options.ModifiedState := True;
Module.Save(False, False);
end;
procedure TProjectOptionsForm.ValueListEditor1KeyPress(Sender: TObject; var
Key: Char);
begin
if Key = #13 then
UpdateOptionValue;
end;
initialization
finalization
CloseDown;
end.
How this can be done in Delphi XE7?
It cannot. This functionality was removed I'm not sure exactly when, but it has not been present for some considerable time.
What you can do is:
Create a new project.
Change its settings however you please.
Save this modified project template to some central location.
Whenever you make a new project, do so by copying this project template.
You can integrate this process into the IDE by saving your modified project template into the Object Repository. Add a project to the repository with Project | Add to Repository.
I'm using Delphi XE, I have the following code for my program and DLL:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, superobject,
OtlCommon, OtlCollections, OtlParallel;
type
TForm1 = class(TForm)
btnStart: TButton;
btnStop: TButton;
procedure btnStartClick(Sender: TObject);
procedure btnStopClick(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure FormCreate(Sender: TObject);
private
FLogger : IOmniBackgroundWorker;
FPipeline: IOmniPipeline;
FLogFile: TextFile;
strict protected
procedure Async_Log(const workItem: IOmniWorkItem);
procedure Async_Files(const input, output: IOmniBlockingCollection);
procedure Async_Parse(const input: TOmniValue; var output: TOmniValue);
procedure Async_JSON(const input, output: IOmniBlockingCollection);
end;
var
Form1: TForm1;
function GetJSON(AData: PChar): ISuperObject; stdcall; external 'my.dll';
implementation
uses OtlTask, IOUtils;
{$R *.dfm}
function GetJSON_local(AData: PChar): ISuperObject;
var
a: ISuperObject;
sl: TStringList;
begin
sl := TStringList.Create;
try
sl.Text := StrPas(AData);
Result := SO();
Result.O['array'] := SA([]);
a := SO;
a.S['item1'] := sl[14];
Result.A['array'].Add(a);
a := nil;
a := SO;
a.S['item2'] := sl[15];
Result.A['array'].Add(a);
finally
sl.Free;
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
var
s: string;
begin
// log
s := ExtractFilePath(Application.ExeName) + 'Logs';
if not TDirectory.Exists(s) then TDirectory.CreateDirectory(s);
s := Format(s+'\%s.txt', [FormatDateTime('yyyy-mm-dd_hh-nn-ss', Now)]);
AssignFile(FLogFile, s);
Rewrite(FLogFile);
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
CloseFile(FLogFile);
end;
procedure TForm1.Async_Log(const workItem: IOmniWorkItem);
begin
WriteLn(FLogFile, workItem.Data.AsString);
end;
procedure TForm1.Async_Files(const input, output: IOmniBlockingCollection);
var
f: string;
begin
while not input.IsCompleted do begin
for f in TDirectory.GetFiles(ExtractFilePath(Application.ExeName), '*.txt') do
output.TryAdd(f); // output as FileName
Sleep(1000);
end;
end;
procedure TForm1.Async_Parse(const input: TOmniValue; var output: TOmniValue);
var
sl: TStringList;
begin
sl := TStringList.Create;
try
sl.LoadFromFile(input.AsString);
// output := GetJSON_local(PChar(sl.Text)); // output as ISuperObject --- local function
output := GetJSON(PChar(sl.Text)); // output as ISuperObject --- DLL function
finally
sl.Free;
end;
FLogger.Schedule(FLogger.CreateWorkItem(Format('%s - File processed: %s', [DateTimeToStr(Now), input.AsString])));
end;
procedure TForm1.Async_JSON(const input, output: IOmniBlockingCollection);
var
value: TOmniValue;
JSON: ISuperObject;
begin
for value in input do begin
if value.IsException then begin
FLogger.Schedule(FLogger.CreateWorkItem(value.AsException.Message));
value.AsException.Free;
end
else begin
JSON := value.AsInterface as ISuperObject;
FLogger.Schedule(FLogger.CreateWorkItem(JSON.AsString));
end;
end;
end;
//
procedure TForm1.btnStartClick(Sender: TObject);
begin
btnStart.Enabled := False;
FLogger := Parallel.BackgroundWorker.NumTasks(1).Execute(Async_Log);
FPipeline := Parallel.Pipeline
.Stage(Async_Files)
.Stage(Async_Parse)
.Stage(Async_JSON)
.Run;
end;
procedure TForm1.btnStopClick(Sender: TObject);
begin
if Assigned(FPipeline) and Assigned(FLogger) then begin
FPipeline.Input.CompleteAdding;
FPipeline := nil;
FLogger.Terminate(INFINITE);
FLogger := nil;
end;
btnStart.Enabled := True;
end;
end.
// DLL code
library my;
uses
SysUtils,
Classes, superobject;
function GetJSON(AData: PChar): ISuperObject; stdcall;
var
a: ISuperObject;
sl: TStringList;
begin
sl := TStringList.Create;
try
sl.Text := StrPas(AData);
Result := SO();
Result.O['array'] := SA([]);
a := SO;
a.S['item1'] := sl[14];
Result.A['array'].Add(a);
a := nil;
a := SO;
a.S['item2'] := sl[15];
Result.A['array'].Add(a);
finally
sl.Free;
end;
end;
exports
GetJSON;
begin
end.
When I try to run with debugging my code, after a few calls of the dll GetJSON function i get the following error:
"Project test_OTL_SO.exe raised exception class EAccessViolation with message 'Access violation at address 005A2F8A in module 'my.dll'. Write of address 00610754'."
However, this issue does not occur when I use the same local function GetJSON_local.
Could anyone suggest what am I doing wrong here?
EDIT: (solution)
I write this code for my DLL:
procedure GetJSON_(const AData: PChar; out Output: WideString); stdcall;
var
json, a: ISuperObject;
sl: TStringList;
begin
sl := TStringList.Create;
try
sl.Text := AData;
json := SO();
json.O['array'] := SA([]);
a := SO;
a.S['item1'] := sl[14];
json.A['array'].Add(a);
a := nil;
a := SO;
a.S['item2'] := sl[15];
json.A['array'].Add(a);
Output := json.AsString;
finally
sl.Free;
end;
end;
and changed the code of Async_Parse procedure:
procedure TForm1.Async_Parse(const input: TOmniValue; var output: TOmniValue);
var
sl: TStringList;
ws: WideString;
begin
sl := TStringList.Create;
try
sl.LoadFromFile(input.AsString);
GetJSON_(PChar(sl.Text), ws); // DLL procedure
output := SO(ws); // output as ISuperObject
finally
sl.Free;
end;
FLogger.Schedule(FLogger.CreateWorkItem(Format('%s - File processed: %s', [DateTimeToStr(Now), input.AsString])));
end;
The problem is your passing of ISuperObject interfaces across a module boundary. Although interfaces can be safely used that way, the methods of the interface are not safe. Some of the methods of the interface accept, or return, strings, objects, etc. That is, types that are not safe for interop.
Some examples of methods that are not safe:
function GetEnumerator: TSuperEnumerator; // TSuperEnumerator is a class
function GetS(const path: SOString): SOString; // returns a Delphi string
function SaveTo(stream: TStream; indent: boolean = false;
escape: boolean = true): integer; overload; // TStream is a class
function AsArray: TSuperArray; // TSuperArray is a class
// etc.
You should serialize the JSON to text, and pass that text between your modules.
I need to create server and client programs with synapse using UDP protocol.
I have created the server program to listen to any coming messages like this
procedure TForm1.Timer1Timer(Sender: TObject);
var
resive:string;
begin
InitSocket;
resive:=UDPResiveSocket.RecvPacket(1000);
if resive<>'' then Memo1.Lines.Add('>' + resive);
DeInitSocket;
end;
procedure TForm1.InitSocket;
begin
if UDPResiveSocket <> nil then
DeInitSocket;
UDPResiveSocket := TUDPBlockSocket.Create;
UDPResiveSocket.CreateSocket;
UDPResiveSocket.Bind('0.0.0.0','22401');
UDPResiveSocket.AddMulticast('234.5.6.7');
UDPResiveSocket.MulticastTTL := 1;
end;
procedure TForm1.DeInitSocket;
begin
UDPResiveSocket.CloseSocket;
UDPResiveSocket.Free;
UDPResiveSocket := nil;
end;
So i get all incoming messages.
But i want to send a response from the source of this messages.
How can i do that? Does my method is good for server/client?
My UDP Echo client / server code. First the server:
unit UE_Server;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils,
// synapse
blcksock;
type
{ TUEServerThread }
TUEServerThread = class(TThread)
protected
procedure Execute; override;
end;
TUEServer = class
private
FUEServerThread: TUEServerThread;
function GetRunning: Boolean;
public
procedure Stop;
procedure Start;
property Running: Boolean read GetRunning;
end;
implementation
{ TUEServer }
function TUEServer.GetRunning: Boolean;
begin
Result := FUEServerThread <> nil;
end;
procedure TUEServer.Start;
begin
FUEServerThread := TUEServerThread.Create(False);
end;
procedure TUEServer.Stop;
begin
if FUEServerThread <> nil then
begin
FUEServerThread.Terminate;
FUEServerThread.WaitFor;
FreeAndNil(FUEServerThread);
end;
end;
{ TUEServerThread }
procedure TUEServerThread.Execute;
var
Socket: TUDPBlockSocket;
Buffer: string;
Size: Integer;
begin
Socket := TUDPBlockSocket.Create;
try
Socket.Bind('0.0.0.0', '7');
try
if Socket.LastError <> 0 then
begin
raise Exception.CreateFmt('Bind failed with error code %d', [Socket.LastError]);
Exit;
end;
while not Terminated do
begin
// wait one second for new packet
Buffer := Socket.RecvPacket(1000);
if Socket.LastError = 0 then
begin
// just send the same packet back
Socket.SendString(Buffer);
end;
// minimal sleep
if Buffer = '' then
Sleep(10);
end;
finally
Socket.CloseSocket;
end;
finally
Socket.Free;
end;
end;
end.
Then the client:
unit UE_Client;
{$mode objfpc}{$H+}
interface
uses
{$IFDEF WINDOWS}Windows,{$ENDIF}Classes, SysUtils, DateUtils,
// synapse
blcksock;
const
cReceiveTimeout = 2000;
cBatchSize = 100;
type
{ TUEClient }
TUEClient = class
private
FSocket: TUDPBlockSocket;
FResponseTime: Int64;
public
constructor Create;
destructor Destroy; override;
procedure Disconnect;
function Connect(const Address: string): Boolean;
function SendEcho(const Message: string): string;
property ReponseTime: Int64 read FResponseTime;
end;
{ TUEAnalyzer }
{ TUEAnalyzerThread }
TUEAnalyzerThread = class(TThread)
private
FAddress: string;
FBatchDelay: Cardinal;
FDropedPackets: Cardinal;
FAverageResponse: Extended;
FCriticalSection: TRTLCriticalSection;
function GetAverageResponse: Extended;
function GetDropedPackets: Cardinal;
protected
procedure Execute; override;
public
destructor Destroy; override;
constructor Create(const Address: string; const BatchDelay: Cardinal);
property DropedPackets: Cardinal read GetDropedPackets;
property AverageResponse: Extended read GetAverageResponse;
end;
TUEAnalyzer = class
private
FAddress: string;
FBatchDelay: Cardinal;
FAnalyzerThread: TUEAnalyzerThread;
function GetAverageResponse: Extended;
function GetDropedPackets: Cardinal;
function GetRunning: Boolean;
public
procedure StopAnalyzer;
procedure StartAnalyzer;
property Running: Boolean read GetRunning;
property Address: string read FAddress write FAddress;
property DropedPackets: Cardinal read GetDropedPackets;
property AverageResponse: Extended read GetAverageResponse;
property BatchDelay: Cardinal read FBatchDelay write FBatchDelay;
end;
implementation
{ TUEAnalyzerThread }
function TUEAnalyzerThread.GetAverageResponse: Extended;
begin
EnterCriticalsection(FCriticalSection);
try
Result := FAverageResponse;
finally
LeaveCriticalsection(FCriticalSection);
end;
end;
function TUEAnalyzerThread.GetDropedPackets: Cardinal;
begin
EnterCriticalsection(FCriticalSection);
try
Result := FDropedPackets;
finally
LeaveCriticalsection(FCriticalSection);
end;
end;
procedure TUEAnalyzerThread.Execute;
var
UEClient: TUEClient;
Connected: Boolean;
SendString: string;
SendCounter: Int64;
SumResponse: Cardinal;
SumDropedPackets: Cardinal;
begin
UEClient := TUEClient.Create;
try
Connected := UEClient.Connect(FAddress);
try
if not Connected then
begin
raise Exception.CreateFmt('Could not connect UPD client to address %s', [FAddress]);
Exit;
end;
SumDropedPackets := 0;
FAverageResponse := 0;
FDropedPackets := 0;
SumResponse := 0;
SendCounter := 1;
while not Terminated do
begin
SendString := IntToStr(SendCounter);
if not (UEClient.SendEcho(SendString) = SendString) then
Inc(SumDropedPackets);
Inc(SumResponse, UEClient.ReponseTime);
Inc(SendCounter);
if (SendCounter mod cBatchSize) = 0 then
begin
EnterCriticalsection(FCriticalSection);
try
FAverageResponse := SumResponse / cBatchSize;
FDropedPackets := SumDropedPackets;
finally
LeaveCriticalsection(FCriticalSection);
end;
// sleep for specified batch time
Sleep(FBatchDelay * 1000);
SumDropedPackets := 0;
SumResponse := 0;
end;
// minimal sleep
Sleep(10);
end;
finally
UEClient.Disconnect;
end;
finally
UEClient.Free;
end;
end;
destructor TUEAnalyzerThread.Destroy;
begin
{$IFDEF MSWINDOWS}
DeleteCriticalSection(FCriticalSection)
{$ELSE}
DoneCriticalSection(FCriticalSection)
{$ENDIF};
inherited Destroy;
end;
constructor TUEAnalyzerThread.Create(const Address: string; const BatchDelay: Cardinal);
begin
{$IFDEF MSWINDOWS}
InitializeCriticalSection(FCriticalSection)
{$ELSE}
InitCriticalSection(FCriticalSection)
{$ENDIF};
FBatchDelay := BatchDelay;
FreeOnTerminate := True;
FAddress := Address;
inherited Create(False);
end;
{ TUEAnalyzer }
procedure TUEAnalyzer.StartAnalyzer;
begin
FAnalyzerThread := TUEAnalyzerThread.Create(FAddress, FBatchDelay);
end;
function TUEAnalyzer.GetRunning: Boolean;
begin
Result := FAnalyzerThread <> nil;
end;
function TUEAnalyzer.GetAverageResponse: Extended;
begin
Result := FAnalyzerThread.AverageResponse;
end;
function TUEAnalyzer.GetDropedPackets: Cardinal;
begin
Result := FAnalyzerThread.DropedPackets;
end;
procedure TUEAnalyzer.StopAnalyzer;
begin
if Running then
begin
FAnalyzerThread.Terminate;
FAnalyzerThread := nil;
end;
end;
{ TUEClient }
constructor TUEClient.Create;
begin
FSocket := TUDPBlockSocket.Create;
end;
destructor TUEClient.Destroy;
begin
FreeAndNil(FSocket);
inherited Destroy;
end;
procedure TUEClient.Disconnect;
begin
FSocket.CloseSocket;
end;
function TUEClient.Connect(const Address: string): Boolean;
begin
FSocket.Connect(Address, '7');
Result := FSocket.LastError = 0;
end;
function TUEClient.SendEcho(const Message: string): string;
var
StartTime: TDateTime;
begin
Result := '';
StartTime := Now;
FSocket.SendString(Message);
if FSocket.LastError = 0 then
begin
Result := FSocket.RecvPacket(cReceiveTimeout);
FResponseTime := MilliSecondsBetween(Now, StartTime);
if FSocket.LastError <> 0 then
begin
FResponseTime := -1;
Result := '';
end;
end;
end;
end.
The code is written in free pascal, but works equally well in Delphi. The client unit is actually a line analyzer that calculates average response times and dropped packets. It is ideal to check the quality of your internet line to a certain server. You put the echo server to the server part and client on the client side.
Simple client-server in two program
client send two string "Hello world" and "exit"
server wait for client message and stop after client send "exit"
write on free pascal(Lazarus)
client
unit Unit1;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
//ADD
blcksock;
type
{ TForm1 }
TForm1 = class(TForm)
Button1: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
private
procedure OnStatus(Sender: TObject; Reason: THookSocketReason; const Value: string );
{ private declarations }
public
{ public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.lfm}
{ TForm1 }
procedure TForm1.OnStatus(Sender: TObject; Reason: THookSocketReason; const Value: string);
var
sReason : String;
begin
case Reason of
HR_ResolvingBegin : sReason := 'HR_ResolvingBegin';
HR_ResolvingEnd : sReason := 'HR_ResolvingEnd';
HR_SocketCreate : sReason := 'HR_SocketCreate';
HR_SocketClose : sReason := 'HR_SocketClose';
HR_Bind : sReason := 'HR_Bind';
HR_Connect : sReason := 'HR_Connect';
HR_CanRead : sReason := 'HR_CanRead';
HR_CanWrite : sReason := 'HR_CanWrite';
HR_Listen : sReason := 'HR_Listen';
HR_Accept : sReason := 'HR_Accept';
HR_ReadCount : sReason := 'HR_ReadCount';
HR_WriteCount : sReason := 'HR_WriteCount';
HR_Wait : sReason := 'HR_Wait';
HR_Error : sReason := 'HR_Error';
end;
Memo1.Lines.Add( sReason + ': ' + Value );
end;
procedure TForm1.Button1Click(Sender: TObject);
var
UDP: TUDPBlockSocket;
s:string;
begin
UDP := TUDPBlockSocket.Create;
try
UDP.OnStatus := #OnStatus;
//send to server
s:='Hello world from client';
UDP.Connect( '127.0.0.1', '12345' );
UDP.SendString('------'+s+'--------');
memo1.Append(s);
//for server stop send string "exit"
s:='exit';
UDP.SendString(s);
memo1.Append('---');
memo1.Append(s);
memo1.Append('---');
UDP.CloseSocket;
finally
UDP.Free;
end;
end;
end.
SERVER
unit Unit1;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
//ADD
blcksock;
type
{ TForm1 }
TForm1 = class(TForm)
Button1: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
private
procedure OnStatus(Sender: TObject; Reason: THookSocketReason; const Value: string );
{ private declarations }
public
{ public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.lfm}
{ TForm1 }
procedure TForm1.OnStatus(Sender: TObject; Reason: THookSocketReason; const Value: string);
var
sReason : String;
begin
case Reason of
HR_ResolvingBegin : sReason := 'HR_ResolvingBegin';
HR_ResolvingEnd : sReason := 'HR_ResolvingEnd';
HR_SocketCreate : sReason := 'HR_SocketCreate';
HR_SocketClose : sReason := 'HR_SocketClose';
HR_Bind : sReason := 'HR_Bind';
HR_Connect : sReason := 'HR_Connect';
HR_CanRead : sReason := 'HR_CanRead';
HR_CanWrite : sReason := 'HR_CanWrite';
HR_Listen : sReason := 'HR_Listen';
HR_Accept : sReason := 'HR_Accept';
HR_ReadCount : sReason := 'HR_ReadCount';
HR_WriteCount : sReason := 'HR_WriteCount';
HR_Wait : sReason := 'HR_Wait';
HR_Error : sReason := 'HR_Error';
end;
Memo1.Append( sReason + ': ' + Value );
end;
procedure TForm1.Button1Click(Sender: TObject);
var
Sock:TUDPBlockSocket;
size:integer;
buf:string;
begin
Sock:=TUDPBlockSocket.Create;
try
//On status show error and other
//enable on status if you can more seen
//sock.OnStatus := #OnStatus;
sock.CreateSocket;
//create server
sock.bind('127.0.0.1','12345');
//send string to this server in this program(not client)
sock.Connect( '127.0.0.1', '12345' );
sock.SendString('test send string to sever');
if sock.LastError<>0 then exit;
//shutdown while client send "exit"
while buf<>'exit' do
begin
//get data client
buf := sock.RecvPacket(1000);
Memo1.Append(buf);
sleep(1);
end;
sock.CloseSocket;
finally
sock.free;
end;
end;
end.
I need a program to overwrite the file, but sometimes some process is lock it. How to check which process locks a file, and how to unlock it? What functions should I use?
I found on the Internet such a code, but it doesn't work me.
unit proc;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls, StdCtrls, ComCtrls, Grids, ValEdit, MTSUtilsUnit, TLHelp32,
Menus, PsAPI;
type
TApp = class
fPID: Integer;
fPArentPID: Integer;
fPIDName: string;
fThread: Integer;
fDLLName: TStringList;
fDLLPath: TStringList;
fDescription: string;
end;
TForm2 = class(TForm)
StatusBar1: TStatusBar;
Panel1: TPanel;
Panel2: TPanel;
Panel3: TPanel;
Panel4: TPanel;
Splitter2: TSplitter;
Edit1: TEdit;
Button1: TButton;
Label1: TLabel;
RichEdit1: TRichEdit;
PopupMenu1: TPopupMenu;
kill1: TMenuItem;
StringGrid1: TStringGrid;
function GetApps(AppName: string): TStringList;
function GetInfo(PID: Integer): string;
function Kill(PID: Integer): Boolean;
procedure kill1Click(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure StringGrid1SelectCell(Sender: TObject; ACol, ARow: Integer;
var CanSelect: Boolean);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form2: TForm2;
ApplicationList: TStringList;
row: Integer;
implementation
{$R *.dfm}
function TForm2.Kill(PID: Integer): Boolean;
var fHandle: THandle;
begin
fHandle := OpenProcess(PROCESS_TERMINATE, BOOL(0), PID);
if TerminateProcess(fHandle, 0) then
Result := True
else
Result := False;
CloseHandle(fHandle);
end;
procedure TForm2.kill1Click(Sender: TObject);
var i: Integer;
fApp: TApp;
begin
if Kill(StrToInt(StringGrid1.Cells[1, row])) then
begin
ApplicationList.Delete(row);
StringGrid1.RowCount := ApplicationList.Count;
for i := 1 to ApplicationList.Count - 1 do
begin
fApp := TApp(ApplicationList.Objects[i]);
Form2.StringGrid1.Cells[0,i] := fApp.fPIDName;
Form2.StringGrid1.Cells[1,i] := IntToStr(fApp.fPID);
end;
MessageBox(0, 'Terminate successfully', 'Kill', MB_ICONINFORMATION or MB_OK);
end
else
MessageBox(0, 'Could not terminate process', 'Kill', MB_ICONINFORMATION or MB_OK);
end;
procedure TForm2.StringGrid1SelectCell(Sender: TObject; ACol, ARow: Integer;
var CanSelect: Boolean);
var fApp: TApp;
begin
row := ARow;
RichEdit1.Lines.Clear();
if ApplicationList.Count >= row then
begin
fApp := TApp(ApplicationList.Objects[row]);
RichEdit1.Lines.Add(fApp.fDescription);
end;
end;
procedure TForm2.Button1Click(Sender: TObject);
var i: Integer;
fApp: TApp;
sItem: string;
CanSelect: Boolean;
begin
for i := 0 to ApplicationList.Count - 1 do
begin
fApp := TApp(ApplicationList.Objects[i]);
FreeAndNil(fApp.fDLLName);
FreeAndNil(fApp.fDLLPath);
FreeAndNil(fApp);
end;
FreeAndNil(ApplicationList);
ApplicationList := GetApps(Edit1.Text);
StringGrid1.RowCount := ApplicationList.Count;
for i := 0 to ApplicationList.Count - 1 do
begin
fApp := TApp(ApplicationList.Objects[i]);
StringGrid1.Cells[0,i] := fApp.fPIDName;
StringGrid1.Cells[1,i] := IntToStr(fApp.fPID);
end;
StringGrid1.OnSelectCell(Self, 0, 1, CanSelect);
end;
procedure TForm2.FormCreate(Sender: TObject);
begin
StringGrid1.Cells[0,0] := 'Name';
StringGrid1.Cells[1,0] := 'PID';
end;
function TForm2.GetInfo(PID: Integer): string;
var fHandle: THandle;
fModule: TModuleEntry32;
sInfo: string;
begin
Result := '';
sInfo := 'DLL Name: %s'#13#10 +
'DLL Path: %s'#13#10 +
'ModuleId: %d'#13#10;
fHandle := CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, PID);
if fHandle <> INVALID_HANDLE_VALUE then
if Module32First(fHandle, fModule) then
repeat
if SameText(ExtractFileExt(fModule.szModule), '.dll') then
begin
sInfo := Format(sInfo, [fModule.szModule, fModule.szExePath, fModule.th32ModuleID]);
Result := Result + sInfo;
end;
until not Module32Next(fHandle, fModule);
end;
function TForm2.GetApps(AppName: string): TStringList;
var fHandle: THandle;
fModHandle: THandle;
fProcess: TProcessEntry32;
fModule: TMODULEENTRY32;
App: TApp;
i: Integer;
IsDLL: Boolean;
IsProcess: Boolean;
fDesc: string;
sPath: string;
begin
IsDLL := False;
IsProcess := False;
Result := TStringList.Create();
Result.Clear();
fDesc := 'DLL Name: %s'#13#10 +
'DLL Path: %s'#13#10 +
'ModuleId: %d'#13#10;
fHandle := CreateToolHelp32SnapShot(TH32CS_SNAPPROCESS, 0);
fProcess.dwSize := SizeOf(fProcess);
IsProcess := Process32First(fHandle, fProcess);
while IsProcess do
begin
App := TApp.Create();
App.fDLLName := TStringList.Create();
App.fDLLPath := TStringList.Create();
fModHandle := CreateToolHelp32SnapShot(TH32CS_SNAPMODULE, fProcess.th32ProcessID);
IsDLL := Module32First(fModHandle, fModule);
while IsDLL do
begin
if Edit1.Text <> '' then
sPath := fModule.szModule
else
sPath := ExtractFileExt(fModule.szModule);
if SameText(sPath, Edit1.Text + '.dll') then
begin
App.fPID := fProcess.th32ProcessID;
App.fPIDName := fProcess.szExeFile;
App.fDLLName.Add(fModule.szModule);
App.fDLLPath.Add(fModule.szExePath);
App.fDescription := App.fDescription +
Format(fDesc, [fModule.szModule, fModule.szExePath, fModule.th32ModuleID]);
end;
IsDLL := Module32Next(fModHandle, fModule)
end;
if App.fDLLName.Count > 0 then
Result.AddObject(IntToStr(App.fPID), App);
IsProcess := Process32Next(fHandle, fProcess);
end;
CloseHandle(fHandle);
Result.Count;
end;
end.
You should not unlock the file yourself this will lead to lost data! Leave it to the user and instead show an error and explaining which process holds open the file.
This solution here will help you to do so:
http://www.remkoweijnen.nl/blog/2011/01/03/cannot-access-files-but-need-the-origin
Check out Process Explorer. It will show you which processes have which files opened, and will allow you to close individual files.