Hi i have created windows services in Delphi. I have installed and started the Services. Every thing working fine. Even i can check it in Task Manager.My service is Running.
But the code which i included inside the OnExecute method is not working.
My whole Code:
unit MyService;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.SvcMgr, Vcl.Dialogs,
Vcl.ExtCtrls,Jira;
type
TJiraTestlink = class(TService)
procedure ServiceExecute(Sender: TService);
private
{ Private declarations }
public
function GetServiceController: TServiceController; override;
{ Public declarations }
end;
var
JiraTestlink: TJiraTestlink;
implementation
{$R *.DFM}
procedure ServiceController(CtrlCode: DWord); stdcall;
begin
JiraTestlink.Controller(CtrlCode);
end;
function TJiraTestlink.GetServiceController: TServiceController;
begin
Result := ServiceController;
end;
procedure TJiraTestlink.ServiceExecute(Sender: TService);
const
SecBetweenRuns = 10;
var
Count: Integer;
begin
Count := 0;
while not Terminated do
begin
Inc(Count);
if Count >= SecBetweenRuns then
begin
Count := 0;
{ place your service code here }
{ this is where the action happens }
ShowMessage(DateTimeToStr(Now));
end;
Sleep(1000);
ServiceThread.ProcessRequests(False);
end;
end;
end.
I am not sure where i did mistake. Any help will be appreciated. Thanks.
Since Vista, services are isolated and run in session 0, a non-interactive session. Interactive processes run in different sessions, starting from session 1 for the first logged on user.
This means that you cannot use your service to show UI. Calls to ShowMessage cannot work, by design, in a service.
You will need to find some other way to debug your service. For instance logging messages to a text file. Or OutputDebugString and a viewer like SysInternals DebugView which is capable of catching these debug strings from different sessions.
Related
I write this minimal example to test a strange behavior I found in Indy.
I have a TCP server and client, and I send 50 x 2MB buffers from client to server. If I open the Windows WiFi menu (the one you use to connect to a network), my TCP transfer is stopped for a few seconds and then continues. This is not good for my app, because I cannot use read timeouts.
Is something wrong with Indy? Or my code? Or Windows? Or is it something else? Can I fix it somehow?
I have uploaded a YouTube Video to demonstrate this issue in action.
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, IdTCPConnection,
IdTCPClient, IdBaseComponent, IdComponent, IdCustomTCPServer, IdTCPServer,
IdContext, IdIOHandlerSocket, IdGlobal;
type
TClientThread = class(TThread)
private
Socket: TIdIOHandlerSocket;
protected
procedure Execute; override;
public
constructor Create(ASocket: TIdIOHandlerSocket);
end;
TForm1 = class(TForm)
Memo1: TMemo;
Server: TIdTCPServer;
Client: TIdTCPClient;
BStart: TButton;
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure ServerExecute(AContext: TIdContext);
procedure BStartClick(Sender: TObject);
private
procedure LogEvent(var Msg: TMessage); message WM_USER;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
//---- Client -----------------------------------------------------------------
constructor TClientThread.Create(ASocket: TIdIOHandlerSocket);
begin
inherited Create;
Socket:= ASocket;
end;
procedure TClientThread.Execute;
var I, bSize: Integer;
Buff: TIdBytes;
begin
bSize:= 2000000;
SetLength(Buff, bSize+4);
Move(bSize, Buff[0], 4);
for I:= 1 to 50 do begin
Socket.Write(Buff, bSize+4);
end;
end;
//---- Server -----------------------------------------------------------------
procedure TForm1.ServerExecute(AContext: TIdContext);
var Cmd: Cardinal;
Buff: TIdBytes;
I: Integer;
procedure SendString(const AStr: String);
var Tmp: String;
begin
Tmp:= AStr;
SendMessage(Form1.Handle, WM_USER, WPARAM(#Tmp), 0);
end;
begin
SendString('Server START.');
I:= 0;
try
repeat
Cmd:= AContext.Connection.Socket.ReadUInt32(False);
if Cmd = $FFFFFFFF then SendString('Srv: keep alive')
else if Cmd <> 0 then begin
Inc(I);
SendString('Srv read '+IntToStr(I)+': '+IntToStr(Cmd)+' bytes');
if Length(Buff) < Cmd then SetLength(Buff, Cmd);
AContext.Connection.Socket.ReadBytes(Buff, Cmd, False);
SendString('Srv read done.');
end;
until Cmd = 0;
finally
AContext.Connection.Disconnect;
SendString('Server STOP.');
end;
end;
//---- Form -------------------------------------------------------------------
procedure TForm1.FormCreate(Sender: TObject);
begin
Server.Bindings.Clear;
Server.Bindings.Add.SetBinding(<the host>, <the port>);
Server.Active:= True;
Client.Host:= <a host>;
Client.Port:= <a port>;
Client.Connect;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Client.Disconnect;
Server.Active:= False;
end;
procedure TForm1.BStartClick(Sender: TObject);
begin
TClientThread.Create(Client.Socket);
end;
procedure TForm1.LogEvent(var Msg: TMessage);
begin
Memo1.Lines.Add(PString(Msg.WParam)^.Substring(0));
end;
end.
Edit:
I found that
the problem persist even with the NetCom7 library.
the blocking period it is greatly reduced (about 1s) if I try the app on another machine.
This is not an issue with Indy itself, or the underlying TCP connection, as evident by the fact that the TCP connection is not being cut off completely while the WiFi menu is open.
I would be more concerned over why the video shows your UI running so slowly in general. You have no sleeps in your code, nothing else running in your main UI thread, so your comms should be slamming through those 50 TCP messages in a blink, but it is not. You are sending a synchronous window message to the main UI thread between each TCP message read, and it is clear in the video that there is a delay in processing each of those messages. So, I have to assume that something external to Indy is blocking your main UI thread from handling window messages in a timely manner. Opening the WiFi menu is simply exacerbating that issue for a few seconds.
I imagine that if you were to drop a TTimer (which is a window message based timer) onto your Form, set its Interval to a short value, and have its OnTimer event display a counter on your UI, you would likely see those updates appear delayed, as well.
If you get rid of the SendString() calls inside of the TIdTCPServer's reading loop (or at least replace SendMessage() with PostMessage()) so that the loop is not being blocked by the UI at all, and then have your client send a Cmd=0 message (or disconnect) after sending its 50 Buff messages so that the reading loop terminates, you should see the Server STOP message appear in your UI very quickly. Transmitting ~100 MB of data should not take more than a few seconds on modern networking hardware.
Play a local video file (not in a web browser) and see if it hangs during your maneuver, too. That indicates the system is in general affected, not only the network.
I use DPC Latency Checker (Deferred Procedure Calls) to see if there are spikes of (typically) device drivers that take too long. Just like the manual explains: if you find spikes, try to disable single hardware components and repeat measuring. Sadly even the majority of hardware drivers are sloppy coded, and finding alternatives is a hard task.
I want load a image that will be the background of a maximized Form that stays in a dll.
The dll is called from a Vcl Form Application but have a trouble where not is possible load the background image on Form, the dll always crashes.
Thank you by you help.
===========================================================================
Executable
unit Unit2;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TForm2 = class(TForm)
btn1: TButton;
procedure btn1Click(Sender: TObject);
end;
var
Form2: TForm2;
implementation {$R *.dfm}
procedure LoadDLL;
type
TShowformPtr = procedure; stdcall;
var
HDLL: THandle;
Recv: TShowformPtr;
begin
HDLL := LoadLibrary('lib.dll');
if HDLL <> 0 then
begin
#Recv := GetProcAddress(HDLL, 'Recv');
if #Recv <> nil then
Recv;
end;
//FreeLibrary(HDLL);
end;
procedure TForm2.btn1Click(Sender: TObject);
begin
LoadDLL;
end;
end.
Dll
Main:
library Project2;
uses
SysUtils, Classes, Unit1, Unit2;
{$R *.res}
procedure Recv; stdcall;
begin
showform;
end;
exports
Recv;
begin
end.
Unit1 (Form):
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls;
type
TForm1 = class(TForm)
img1: TImage;
pnl1: TPanel;
procedure FormShow(Sender: TObject);
private
{ Private declarations }
procedure CreateParams(var Params: TCreateParams); override;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
Params.WndParent:= Application.Handle;
Params.ExStyle := Params.ExStyle or WS_EX_TOPMOST or WS_EX_TRANSPARENT;
Params.ExStyle := WS_EX_TRANSPARENT or WS_EX_TOPMOST;
end;
procedure TForm1.FormShow(Sender: TObject);
begin
brush.Style := bsclear;
img1.Picture.LoadFromFile(IncludeTrailingBackslash(GetCurrentDir) + 'background.bmp');
SetWindowPos(Form1.handle, HWND_TOPMOST, Form1.Left, Form1.Top, Form1.Width,
Form1.Height, 0);
ShowWindow(Application.handle, SW_HIDE);
pnl1.Top := (self.Height div 2) - (pnl1.Height div 2);
pnl1.Left := (self.Width div 2) - (pnl1.Width div 2);
end;
end.
Unit2:
unit Unit2;
interface
Uses
windows,
Unit1,
SysUtils;
procedure showform;
implementation
procedure showform;
begin
Form1 := TForm1.Create(Form1);
sleep(100);
Form1.Show;
Form1.Pnl1.Visible := True;
end;
end.
Your question has a lot of problems, so I would try to answer it as best I can, considering the lack of details.
You are using forms so you are building a VCL application. You need to let the IDE assign the VCL framework to your project.
This line is terribly wrong:
Form1 := TForm1.Create(Form1);
In rare circumstances show a from own itself. I would go and say that most probably this is why your application crashes. See this for details about forms in DLLs.
If you cannot properly debug your application put a beep before that line and one after (make a delay between them).
I think your question should be rather called "how to debug a Delphi project".
What you need to do is to get the exact line on which the program crashes. This will give you an insight of why the error/crash (by the way, you never shown the exact error message) appears.
Go check HadShi (recommended) or EurekaLog (buggy) or Smartinspect (I never tried it. Price is similar to the other two). Make sure that you are running in debug mode, the Integrated debugger is on (see IDE options) and that the debug information is present in your EXE/DLL.
PS: you can still debug your app without have one of the three loggers shown above. Just configure your project properly to run in Debug mode!
To debug the DLL see the 'Run->Parameters' menu. Define there a host application that will load your DLL. If the error is the DLL, the debugger will take control and put the cursor to the line of code that generated the crash.
I don't know what the final purpose/what is that you want to achieve. Because of this I must warn you that you might need to take into consideration these questions:
Do you need to use ShareMM?
Why are you building this as a DLL? Can't the application be written as a single EXE? Or two EXEs that communicate with each other?
i'm try to building a DataSnap Rest Application running as a windows service, but the wizard just have "Stand-alone VCL application", "Stand-alone console application" and "ISAPI dynamic link library"(i'm using Delphi XE2 enterprise). Someone can help me. Thanks.
It would be more convenient if the DataSnap REST wizard had the option to create a Windows Service (how else would you run an application server?), but you can work around it with a little fiddling.
The first time I ran in to this, I created a regular DataSnap REST server as a VCL application and a regular Windows service and copied the relevant portions from the REST server to the service. Just make sure the output directory for the service is the same as the directory for the VCL application.
The service's .dpr might look like this:
program Service;
uses
Vcl.SvcMgr,
Web.WebReq,
IdHTTPWebBrokerBridge,
WebModuleUnit1 in '..\GUI\WebModuleUnit1.pas' {WebModule1: TWebModule},
ServerMethodsUnit1 in '..\GUI\ServerMethodsUnit1.pas' {ServerMethods1: TDSServerModule},
ServerContainerUnit1 in '..\GUI\ServerContainerUnit1.pas' {ServerContainer1: TDataModule},
Unit1 in 'Unit1.pas' {Service1: TService};
{$R *.RES}
begin
if not Application.DelayInitialize or Application.Installing then
Application.Initialize;
if WebRequestHandler <> nil then
WebRequestHandler.WebModuleClass := WebModuleClass;
Application.CreateForm(TService1, Service1);
Application.Run;
end.
The service's main unit might look like this:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Classes,
Vcl.Graphics, Vcl.Controls, Vcl.SvcMgr, Vcl.Dialogs,
IdHTTPWebBrokerBridge, Web.HTTPApp;
type
TService1 = class(TService)
procedure ServiceCreate(Sender: TObject);
procedure ServiceExecute(Sender: TService);
procedure ServiceStart(Sender: TService; var Started: Boolean);
procedure ServiceStop(Sender: TService; var Stopped: Boolean);
private
FServer: TIdHTTPWebBrokerBridge;
procedure TerminateThreads;
public
function GetServiceController: TServiceController; override;
end;
var
Service1: TService1;
implementation
{$R *.DFM}
uses
//Datasnap.DSService; // XE2
Datasnap.DSSession; // XE3
procedure ServiceController(CtrlCode: DWord); stdcall;
begin
Service1.Controller(CtrlCode);
end;
{ TService1}
function TService1.GetServiceController: TServiceController;
begin
Result := ServiceController;
end;
procedure TService1.ServiceCreate(Sender: TObject);
begin
FServer := TIdHTTPWebBrokerBridge.Create(Self);
end;
procedure TService1.ServiceExecute(Sender: TService);
begin
while not Terminated do
begin
Sleep(1000);
ServiceThread.ProcessRequests(False);
end;
end;
procedure TService1.ServiceStart(Sender: TService; var Started: Boolean);
begin
if not FServer.Active then
begin
FServer.Bindings.Clear;
FServer.DefaultPort := 8080;
FServer.Active := True;
end;
end;
procedure TService1.ServiceStop(Sender: TService; var Stopped: Boolean);
begin
TerminateThreads;
FServer.Active := False;
FServer.Bindings.Clear;
ServiceThread.Terminate;
end;
procedure TService1.TerminateThreads;
begin
if TDSSessionManager.Instance <> nil then
TDSSessionManager.Instance.TerminateAllSessions;
end;
end.
I have a Delphi 6 application that works with the Skype API. I want to know when the Skype client has shut down even though my software did not launch it (so I don't have a process handle for it). This way I can know if the user shut down the Skype client I can get the process ID for the Skype client fairly easily, so is there a Windows API call or other technique that accepts a process ID where I can get a notification when the process (Skype client) has terminated?
If not, is there a WinApi call that I can use to poll Windows to see if the process ID is still valid, or does Windows reuse process IDs so there's a chance that I could end up with a process ID belonging to a recently launched process that is not the Skype client, which would invalidate my polling efforts?
Call OpenProcess to get a process handle. The SYNCHRONIZE access right will probably be enough. Then wait on the handle. Something like:
HANDLE hProcess = OpenProcess(SYNCHRONIZE, FALSE, pid);
WaitForSingleObject(hProcess, INFINITE);
CloseHandle(hProcess);
You can use the __InstanceDeletionEvent WMI intrinsic event to monitor the Win32_Process class and the filter by the ProcessId property, this event run in async mode in your code.
Check this sample code (Written in delphi XE2, but must work in delphi 6 without problems)
Note : You must import the Microsoft WMI Scripting V1.2 Library before to use it.
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, WbemScripting_TLB;
type
TWmiAsyncEvent = class
private
FWQL : string;
FSink : TSWbemSink;
FLocator : ISWbemLocator;
FServices : ISWbemServices;
procedure EventReceived(ASender: TObject; const objWbemObject: ISWbemObject; const objWbemAsyncContext: ISWbemNamedValueSet);
public
procedure Start;
constructor Create(Pid : DWORD);
Destructor Destroy;override;
end;
TFrmDemo = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
AsyncEvent : TWmiAsyncEvent;
public
{ Public declarations }
end;
var
FrmDemo: TFrmDemo;
implementation
{$R *.dfm}
uses
ActiveX;
{ TWmiAsyncEvent }
constructor TWmiAsyncEvent.Create(Pid: DWORD);
begin
inherited Create;
CoInitializeEx(nil, COINIT_MULTITHREADED);
FLocator := CoSWbemLocator.Create;
FServices := FLocator.ConnectServer('.', 'root\CIMV2', '', '', '', '', wbemConnectFlagUseMaxWait, nil);
FSink := TSWbemSink.Create(nil);
FSink.OnObjectReady := EventReceived;
//construct the WQL sentence with the pid to monitor
FWQL:=Format('Select * From __InstanceDeletionEvent Within 1 Where TargetInstance ISA "Win32_Process" And TargetInstance.ProcessId=%d',[Pid]);
end;
destructor TWmiAsyncEvent.Destroy;
begin
if FSink<>nil then
FSink.Cancel;
FLocator :=nil;
FServices :=nil;
FSink :=nil;
CoUninitialize;
inherited;
end;
procedure TWmiAsyncEvent.EventReceived(ASender: TObject;
const objWbemObject: ISWbemObject;
const objWbemAsyncContext: ISWbemNamedValueSet);
var
PropVal: OLEVariant;
begin
PropVal := objWbemObject;
//do something when the event is received.
ShowMessage(Format('The Application %s Pid %d was finished',[String(PropVal.TargetInstance.Name), Integer(PropVal.TargetInstance.ProcessId)]));
end;
procedure TWmiAsyncEvent.Start;
begin
FServices.ExecNotificationQueryAsync(FSink.DefaultInterface,FWQL,'WQL', 0, nil, nil);
end;
procedure TFrmDemo.FormCreate(Sender: TObject);
begin
//here you must pass the pid of the process
AsyncEvent:=TWmiAsyncEvent.Create(1852);
AsyncEvent.Start;
end;
procedure TFrmDemo.FormDestroy(Sender: TObject);
begin
AsyncEvent.Free;
end;
end.
For more info you can check this article Delphi and WMI Events
Windows does reuse process IDs, so do not rely on that by itself.
You can use EnumProcesses() to know which processes are currently running, then grab their filenames and process IDs, etc. See this example on MSDN.
How can I check if the control is fully initialized ?
Consider the following code (I know it's very bad practice to do this, please take it as an example)
type
TForm1 = class(TForm)
Memo1: TMemo;
private
procedure WndProc(var Message: TMessage); override;
public
{ Public declarations }
end;
procedure TForm1.WndProc(var Message: TMessage);
begin
{
I'd like to log the messages to the memo as soon
as it's possible so I need to find out how to
check if the memo box is ready to use; the following
code stuck the application, so that the form is not
even displayed. How would you fix this code except
"avoid using of component access in window proc" ?
}
if Assigned(Memo1) then
if Memo1.HandleAllocated then
Memo1.Lines.Add('Message: ' + IntToStr(Message.Msg));
inherited WndProc(Message);
end;
P.S. I know OutputDebugString :-)
Thanks!
Your question rather confused me. When you said:
log messages to the memo
What you mean is that you want to log messages to the form by writing text to the memo.
That approach is fraught with danger since when you write to the memo, the form gets sent messages which results in you writing to the memo and a stack overflow is the inevitable consequence.
I managed to make your idea sort of work by putting in re-entrancy protection. I also introduced a transient non-visual string list to capture any messages that are delivered before the control is ready to display them. Once you introduce this then you no longer need to worry about finding the precise earliest moment at which it is safe to add to the memo.
unit LoggingHack;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TLoggingForm = class(TForm)
Memo1: TMemo;
private
FLog: TStringList;
FLogging: Boolean;
protected
procedure WndProc(var Message: TMessage); override;
public
destructor Destroy; override;
end;
var
LoggingForm: TLoggingForm;
implementation
{$R *.dfm}
{ TLoggingForm }
destructor TLoggingForm.Destroy;
begin
FreeAndNil(FLog);
inherited;
end;
procedure TLoggingForm.WndProc(var Message: TMessage);
var
Msg: string;
begin
if not FLogging then begin
FLogging := True;
Try
Msg := IntToStr(Message.Msg);
if Assigned(Memo1) and Memo1.HandleAllocated then begin
if Assigned(FLog) then begin
Memo1.Lines.Assign(FLog);
FreeAndNil(FLog);
end;
Memo1.Lines.Add(Msg);
end else if not (csDestroying in ComponentState) then begin
if not Assigned(FLog) then begin
FLog := TStringList.Create;
end;
FLog.Add(Msg);
end;
Finally
FLogging := False;
End;
end;
inherited;
end;
end.
end;
The moral of the story is that you should use a more appropriate logging framework that does not interact with what you are trying to log.