반응형

Var
  StartTick,Freq,EndTick : int64;
begin
  QueryPerformanceCounter(StartTick); // Windows 에 선언된 kernel32.dll API
  QueryPerformanceFrequency(Freq);
  { 측정할 코드.. }
  QueryPerformanceCounter(EndTick);
  Showmessage(Formatfloat('0.000000',(EndTick-StartTick)/Freq)+'초 소요됨');
end;



이 방법을 사용하여 16밀리초 미만의 시간도 잡아낼 수 있습니다. CPU의 성능 카운터값을 호출해오는 것으로 알고 있습니다. MSDN에 따르면 윈도95 / NT3.1부터 포함되어 있다고 합니다. 화면갱신시 fps 계산시에도 이 값을 역수로 만들면 현순간 기준 fps를 계산 가능합니다. 저는 실제 코딩에서는 Freq값이 변하지 않기에 프로시저가 아닌 함수로 포장해서 사용하는데 여러분 손에 익은 대로 사용하시기 바랍니다.

단점이 없는 것은 아닙니다. 거의 모든 시스템에서 이 코드가 정상작동하지만, 극소수의 퍼포먼스카운터 API를 미지원하는 시스템에서는 QueryPerformanceCounter 값이 0으로 잡힌다고 합니다. 필요한 경우 0으로 나누는 등의 문제를 막기 위해 failsafe한 안전망을 설치해두시는 편이 좋겠습니다.

반응형
반응형

만들면서 나중에 잊어버릴까 해서 일기 형식으로 남겨놓음.


소켓 통신은 String형식으로 전달해서 구조체로 가져올 수도 있으며, 문자 자체로 가져올 수 있음.



ServerSocketClientConnect 프로퍼티

반응형
반응형

작업하다 혹시 나중에 또 필요할 것 같아서 올려놓는다.


채팅방 기본 소켓 이용.


동작확인 여부 : O


출처 : 델마당 자료실


21_Chat_fix2.zip


반응형
반응형

function My_IP : String;

var
  WSAData: TWSAData;
  HostName, IPAddress: String;
  HostEnt: PHostEnt;
begin
  WSAStartup(2, WSAData);

  SetLength(HostName, 255);

  GetHostname(PChar(HostName), 255);

  SetLength(HostName, StrLen(PChar(HostName)));

  HostEnt := GetHostByName(PChar(HostName));

  with HostEnt^ do

  IPAddress := Format('%d.%d.%d.%d',[Byte(h_addr^[0]), Byte(h_addr^[1]),

  Byte(h_addr^[2]), Byte(h_addr^[3])]);

  WSACleanup;

  Result := IPAddress;
end;


uese절에 Winsock 추가

반응형
반응형

//DEL 6에는 STRICTDELEMITER 함수가 없다. 2007 이후부터 있음.
//문자열 분리 함수 기존에 ExtractStrings함수의 '" 의 문제점 수정
function ExtractStrings2(Separators, WhiteSpace: TSysCharSet; Content: PChar;
  Strings: TStrings): Integer;
var
  Head, Tail: PChar;
  EOS, InQuote: Boolean;
  QuoteChar: Char;
  Item: string;
begin
  Result := 0;
  if (Content = nil) or (Content^=#0) or (Strings = nil) then Exit;
  Tail := Content;
  InQuote := False;
  QuoteChar := #0;
  Strings.BeginUpdate;
  try
    repeat
      while Tail^ in WhiteSpace + [#13, #10] do Tail := StrNextChar(Tail);
      Head := Tail;
      while (InQuote and not (Tail^ in [QuoteChar, #0])) or
        not (Tail^ in Separators + [#0, #13, #10]) do Tail := StrNextChar(Tail);
      EOS := Tail^ = #0;
      if (Head <> Tail) and (Head^ <> #0) then
      begin
        if Strings <> nil then
        begin
          SetString(Item, Head, Tail - Head);
          Strings.Add(Item);
        end;
        Inc(Result);
      end;
      Tail := StrNextChar(Tail);
    until EOS;
  finally
    Strings.EndUpdate;
  end;
end;


------------

사용예제

ExtractStrings2(['|'],[],PChar(StrList.Text),StrList);

반응형
반응형

델마당에서 놀던중 넘 좋은 자료가있어 퍼왔습니다. TList 대신 TCollection을 이용하여 좀더 편리하게 리스트 관리를 할수있는 예제입니다.

 

[TCollection을 이용한 데이타 관리 Class]

 

uses
SysUtils, Classes;

 

type

TMyCollectionItem = class(TCollectionItem)
private
  FName: string;   
  FAge : integer;    

  procedure SetName(const AName: string);
  procedure SetAge(const AAge: integer);
public
  procedure AssignParameter(const AName: string; const AAge: integer); virtual;
published
  property Name: string read FName write SetName;
  property Age: integer read FAge write SetAge;
end;

 

TMyCollection = class(TCollection)
protected
  function GetItem(Index: integer): TMyCollectionItem; virtual;
  procedure SetItem(Index: integer; Value: TMyCollectionItem); virtual;
  function IndexOf(const AName: string): integer; virtual;
public
  constructor Create;
  function Add: TMyCollectionItem;
  procedure AddParameter(const AName: string; const AAge: integer);
  procedure DeleteParameter(const AName: string);

  property Items[ Index: integer ] : TMyCollectionItem read GetItem write SetItem;
end;

 

implementation

 

{ TMyCollectionItem }

 

procedure TMyCollectionItem.AssignParameter(const AName: string;
const AAge: integer);
begin
  Name := AName;
  Age := AAge;
end;

 

 

procedure TMyCollectionItem.SetAge(const AAge: integer);
begin
if FAge <> AAge then
  FAge := AAge;
end;

 

 

procedure TMyCollectionItem.SetName(const AName: string);
begin
if FName <> AName then
  FName := AName;
end;

 

{ TMyCollection }

 

function TMyCollection.Add: TMyCollectionItem;
begin
  Result := TMyCollectionItem(inherited Add);
end;

 

procedure TMyCollection.AddParameter(const AName: string;
const AAge: integer);

begin
  Add.AssignParameter(AName, AAge);
end;

 

constructor TMyCollection.Create;
begin

  inherited Create(TMyCollectionItem);
end;

 

procedure TMyCollection.DeleteParameter(const AName: string);
begin

  Items[ IndexOf(AName) ].Free;
end;

 

function TMyCollection.GetItem(Index: integer): TMyCollectionItem;
begin
  Result := TMyCollectionItem(inherited GetItem(Index));
end;

 

function TMyCollection.IndexOf(const AName: string): integer;
begin
for Result := 0 to Count - 1 do
  if Items[Result].Name = AName then
    exit;
  raise Exception.CreateFmt('Error: Parameter "%s" does not exist', [AName]);
end;

 

procedure TMyCollection.SetItem(Index: integer; Value: TMyCollectionItem);
begin
  inherited SetItem(Index, Value);
end;

 

[Class 사용 구문]

 

....

 

var
  MainForm: TMainForm;
  MyList : TMyCollection;

 

implementation

 

{$R *.DFM}

 

// 데이타 관리 클래스 생성

procedure TMainForm.FormCreate(Sender: TObject);
begin
  MyList := TMyCollection.Create;
end;

 

// 데이타 관리 클래스 파괴

procedure TMainForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  MyList.Free;
end;

 

// 데이타 추가
procedure AddData;
begin
  MyList.AddParameter('One', 1);
  MyList.AddParameter('Two', 2);
  MyList.AddParameter('Three', 3);
  MyList.AddParameter('Four', 4);
  MyList.AddParameter('Five', 5);
end;

 

// 데이타 리스트 출력

procedure ShowList;
var
  i : integer;
begin
  for i := 0 to MyList.Count - 1 do
    ShowMessage(MyList.Items[i].Name + ' : ' + IntToStr(MyList.Items[i].Age));
end;

 

// 레코드 삭제하는 함수다.
procedure DeleteData(Index : integer);
begin
  MyList.Items[Index].Free;
end;

반응형
반응형

Service Application 만들기

 

<             >

1. Service Application 이란?

2. 델파이에서의 Service Application 프로젝트 생성

3. Service Application 등록 등록해제

4. Service Application 기본 코드

5. Services Application 디버깅

* 문서 수정 내역

 

 

 

1. Service Application 이란?

Windows OS 에서의 Service 란 예전 DOS 시절의 램상주 프로그램처럼 시스템이 시작되고나서 자동으로 실행되는 것을 말한다.

 

프로그램 등을 자동으로 실행하는 방법은 몇가지가 더 있다.

첫번째가 윈도우 시작메뉴의 [시작]-[프로그램]-[시작 프로그램] 항목에 해당 프로그램의 아이콘을 넣어도 실행된다.

두번째는 레지스트리의 [\HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run] 에 프로그램을 등록해놔도 자동으로 실행된다.

 

하지만 이것은 Service Application 은 아니다.

이런 식으로 설정해놓은 경우에는 사용자가 콘솔이나 터미널에서 반드시 로그온을 해야만 구동되기 때문이다.

Service 는 로그인과 상관없이 시스템이 부팅되고나서 자동으로 실행되는 것을 말한다.

 

 

2. 델파이에서의 Service Application 프로젝트 생성

델파이에서 Service Application 을 생성하는 것은 상당히 쉽다.

델파이 메뉴에서 [File]-[New]-[Other] 에서 [Service Application]을 선택하면 자동으로 프로젝트가 생성된다.

 

 

 

[그림 1]

 

프로젝트가 생성되고 나서 최초 할 일은 해당 서비스의 명칭을 설정하는 것이다.

델파이 Object Inspector 를 보자.

 

[그림 2]

 

Name property :

[Name] 항목에는 서비스의 기능을 잘 설명하는 명칭으로 써넣자.

“Service1” 처럼 의미없는 명칭은 사용하지 말자.

명칭은 반드시 공백없이 입력하자.

) "CompanynameSqlDatabaseSpecialSomething"

[Name] 항목은 서비스의 등록과 함께 다음의 레지스트리에 키가 생성된다.

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services]

 

DisplayName property :

공백을 포함해서 사용자에게 친숙한 명칭을 입력한다.

) "Companyname SQL Database Special Something"

DisplayName property [제어판]-[관리도구]-[서비스]에 나오는 명칭이다.

 

 

 

[그림 3]

 

ServiceStartName & Password property :

property 는 우리가 만든 서비스가 특정 계정에서 실행되기를 원하는 경우에 해당 계정의 정보를 입력하는 곳이다.

ServiceStartName 에는 권한을 부여할 계정명과 Password 에는 해당 계정의 비밀번호를 입력한다.

ServiceStartName Service 가 인스톨되고나서 입력해도 된다.

ServiceStartName 를 입력하지 않으면 기본적으로 “Local System”으로 실행이 된다.

대부분의 경우에는 “Local System” 계정만으로도 충분하다.

하지만 Service 가 네트웍상의 공유폴더나 공유드라이브에 접근하고자 한다면 이 property 를 통해서 권한 계정을 설정해야 한다.

 

3. Service Application 의 등록 및 등록해제

Service Application 을 등록하고 등록해제하기 위해서는 레지스트리 [HKEY_LOCAL_MACHINE] 키에 내용을 쓰고 지워야 하기 때문에 일반 사용자나 제한된 사용자가 할 수 없는 권한을 필요로 하기 때문에 Administrator 권한의 계정이 필요하다.

 

service application 을 등록하기 위해서 명령행에 다음을 입력한다.

MyService.exe /install

 

성공적으로 설치가 되면 확인창이 보일것이다. 만약에 설치에 실패한다면 에러 메시지가 보일 것이다.

특히 충분한 권한을 가지고 있지 않은 경우에 설치에 실패할 수 있다.

 

만약 확인창을 보고 싶지 않다면 /silent 스위치를 사용한다.

MyService.exe /install /silent

 

service application 을 제거하려면 다음과 같이 명령행에 입력한다.

MyService.exe /uninstall

 

성공적으로 제거되면 확인창이 뜰것이다. 제거에 실패하면 에러 메시지를 보게 된다. 제거에 충분한 권한이 없는 경우 제거에 실패할 수 있다.

설치때와 마찬가지로 확인창을 보고싶지 않다면 /silent 스위치를 추가한다.

MyService.exe /uninstall /silent

 

[제어판]-[관리도구]-[서비스] 에서 등록된 해당 서비스를 선택하고 [시작]을 클릭하면 서비스가 시작된다.

[새로고침]을 눌러 서비스가 시작된 것을 확인할 수 있다.

또는 작업관리자의 프로세스 탭의 현재 실행중인 프로세스 목록에서 해당 서비스 프로세스가 실행된 것을 확인할 수 있다.

 

서비스 프로세스가 실행되는 동안은 실행파일을 이동하거나 삭제하거나 수정할 수 없음은 일반 어플리케이션과 동일하다.

 

name displayname property 를 변경하지 않는 한 서비스 어플리케이션을 교체하기 위해서 uninstall 을 할 필요는 없다. 중지해서 프로세스를 바꿔치기 하는 것 만으로 충분하다.

 

새로 생성된 서비스의 설명을 보았는가?

공백으로 되어 있을것이다. 델파이에서는 서비스의 설명을 위한 property 를 제공하지 않는다.

 

 

[그림 5]

 

service install 된 이후에 service 의 설명을 채워 넣어보자.

TService AfterInstall event 에서 TRegistry class 를 이용해서 쉽게 설명을 추가할 수 있다.

레지스트리의 [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\YourServiceName] 키 아래에 “Description”이라는 값으로 설명을 추가한다.

아래는 설명을 추가하는 샘플 코드이다.

procedure TMyTestServiceApp.ServiceAfterInstall(Sender: TService);

var

  Reg: TRegistry;

begin

  Reg := TRegistry.Create(KEY_READ or KEY_WRITE);

  try

    Reg.RootKey := HKEY_LOCAL_MACHINE;

    if Reg.OpenKey('\SYSTEM\CurrentControlSet\Services\' + Name, false) then

    begin

      Reg.WriteString('Description', 'This is a description for my fine Service Application.');

      Reg.CloseKey;

    end;

  finally

    Reg.Free;

  end;

end;

 

uses 절에 Registry 를 추가하는 것을 잊지 말자.

설명이 변경되었기 때문에 service uninstall 하고 다시 install 하자.

install 을 하고 나면 설명이 추가된 서비스를 보게 될 것이다.

 

service application uninstall 할때 위에서 추가한 레지스트리 키를 삭제할 필요는 없다.

왜냐하면 uninstall 할때에는 해당 서비스의 레지스트리 [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\YourServiceName] 키가 모두 삭제되기 때문이다.

 

 

4. Service Application 의 기본 코드

서비스의 등록과 제거, 실행시 이벤트의 순서는 다음과 같다.

 

[등록시]

ServiceCreate

ServiceBeforeInstall

ServiceAfterInstall

ServiceDestroy

 

[실행시]

ServiceCreate

ServiceStart

(ServiceExecute)

ServicePause – 일시중지

ServiceContinue

ServiceStop

ServiceDestroy

 

[등록해제시]

ServiceCreate

ServiceBeforeUninstall

ServiceAfterUninstall

ServiceDestroy

 

기본적으로 여러분의 서비스 코드를 넣는 두 군데 위치가 있다.

OnExecute 혹은 OnStart 이벤트.

 

 


[그림 4]

 

OnExecute method:

여러분이 서비스내에서 쓰레드처럼 사용하기를 원한다면 여기에 코드를 넣어라.

서비스는 자체 쓰레드를 가지고 있으며, 자체 쓰레드의 Execute 메쏘드가 바로 OnExecute 와 연결되어있다.

따라서 사용법도 OnExecute 메쏘드는 쓰레드의 Execute 메쏘드와 동일하다.

OnExecute 의 도움말에는 다음과 같이 나와있다.

서비스와 연결된 쓰레드가 시작될 때 일어난다.”

OnExecute 이벤트는 쓰레드와 밀접한 관계가 있다.

델파이 도움말에는 다음과 같이 나와있다.

“OnStart 이벤트에서 새 쓰레드를 생성할 수 없다면 여기에 정의하라.”

 

OnStart event:

별도의 쓰레드를 사용할 것이라면 TService.OnStart event 에서 쓰레드를 생성하고 실행하라.

OnStart 의 도움말에는 다음과 같이 나와있다.

“OnStartup OnExecute 이벤트 이전에 서비스가 처음 시작될 때 일어난다.”

이 이벤트는 서비스를 초기화하는데 사용될 수 있다.

 

어느 방법을 사용하느냐는 개인적인 문제이며 둘다 좋은 방법이다.

아래는 두가지 방법의 예이다.

 

Using OnExecute method

procedure TCompanySqlDatabaseSpecialSomething.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 }

      SomeProcedureInAnotherUnit;

 

    end;

    Sleep(1000);

    ServiceThread.ProcessRequests(False);

  end;

end;

 

우리는 서비스가 terminated 되거나, 머신이 셧다운 되거나, 서비스 어플릿으로부터 서비스가 중지될 때까지 while-do 루프를 돈다.

위의 예에서 “SomeProcedureInAnotherUnit” 프로시저는 매 10초마다 호출된다.

우리가 10초를 기다리기 위해서 Sleep(10000) 를 하지 않은 점에 주의해라.

서비스 제어 관리자(SCM)으로부터 보내온 제어 명령에 빠르게 대응하지 않길 바란다면 sleep(10000)을 사용해도 좋다.(사용하지 말라는 소리.)

 

대신에 우리는 멈추고자 하는 시간만큼 카운트를 세면서 1초짜리 sleep() 을 사용한다.

 

OnExecute 메쏘드를 사용하는데 있어 우리는 다음의 장점과 단점을 가진다.

장점 :

코드가 심플하다. 당신은 두번째 쓰레드를 생성할 필요가 없다.

서비스를 멈추고 다시 시작하는 것이 특별한 코드없이 자동으로 제어된다.

 

단점 :

SomeProcedureInAnotherUnit 은 종료하기 위해서 짧은(?) 시간을 가져야 한다. 하지만 그것은 대부분 몇초보다 많진 않다.

서비스 제어 관리자에서 서비스를 중지시켜보라. 다른 방식보다 즉각적이지 않고 시간이 많이 걸린다.

 

매 루프마다 그 안의 코드를 실행하는데 짧은 시간이 필요하다면 OnExecute 메쏘드는 유효하다.

만약 루프안의 코드가 실행하는데 긴 시간을 필요로 한다면, 여러분은 OnStart 이벤트안에서 두번째 쓰레드를 시작하는 것을 고려해야한다.

 

Using OnStart event

우선 여러분은 여러분이 만드는 서비스가 하기를 바라는 코드가 들어간 두번째 쓰레드 클래스를 정의하는 것이 필요하다.

일반적인 쓰레드 클래스를 생성하라.

쓰레드를 만드는 한가지 방법은 델파이 메뉴의 File-New-Other 에서 “Thread Object”를 선택하면 된다.

 

여러분이 TMyServiceThread 라 불리는 TThread 클래스를 만들었다고 치자.

이 쓰레드는 Execute 메쏘드가 terminate 될때 자동으로 해제될 것이다.

이제 쓰레드의 FreeOnTerminate 프라퍼티를 True 로 설정하자.

여러분의 서비스 유닛의 private 섹션에 다음과 같이 쓰레드를 정의하자.

private

    { Private declarations }

    MyServiceThread: TMyServiceThread;

 

이제 여러분은 다음과 같이 OnStart OnStop 이벤트를 생성하는 것이 필요하다.

procedure TCompanySqlDatabaseSpecialSomething.ServiceStart(

  Sender: TService; var Started: Boolean);

begin

  { Create an instance of the secondary thread where your service code is placed }

  MyServiceThread := TMyServiceThread.Create;

  { Set misc. properties you need (if any) in your thread }

  //MyServiceThread.Property1 := whatever;

  // and so on

  MyServiceThread.Resume;

end;

 

procedure TCompanySqlDatabaseSpecialSomething.ServiceStop(Sender: TService;

  var Stopped: Boolean);

begin

  MyServiceThread.Terminate;

end;

 

서비스의 OnStart 이벤트에서 두번째 쓰레드가 생성되었고 실행되었다.

쓰레드는 중지하라는 명령을 받을때까지 실행될 것이다.

이것은 쓰레드의 terminate 메쏘드를 호출하는 것에 의해 서비스의 OnStop 이벤트에서 처리된다.

Terminate 메쏘드를 호출하는 것이 쓰레드를 멈춘다는 것이 아님을 명심하라. 그것은 모두 쓰레드의 Terminated 프라퍼티를 true 로 설정하고 쓰레드가 쓰레드를 멈출 것인지 알기위해 짧은 시간에 이 프라퍼티를 체크한다.

그러면 쓰레드는 Execute 메쏘드를 탈출하는 것에 의해 멈춰진다.

 

아래는 매우 간단한 쓰레드 샘플코드이다.

unit MyServiceThreadUnit;

{ This thread frees itself when it terminates }

 

interface

 

uses

  Windows, Messages, SysUtils, Classes, Graphics;

 

type

  TMyServiceThread = class(TThread)

  private

    { Private declarations }

  protected

    procedure Execute; override;

  public

    constructor Create;

  end;

 

implementation

 

{ Important: Methods and properties of objects in visual components can only be

  used in a method called using Synchronize, for example,

 

      Synchronize(UpdateCaption);

 

  and UpdateCaption could look like,

 

    procedure TMyServiceThread.UpdateCaption;

    begin

      Form1.Caption := 'Updated in a thread';

    end; }

 

{ TMyServiceThread }

 

constructor TMyServiceThread.Create;

// Create the thread Suspended so that properties can be set before resuming the thread.

begin

  FreeOnTerminate := True;

  inherited Create(True);

end;

 

procedure TMyServiceThread.Execute;

const

  SecBetweenRuns = 10;

var

  Count: Integer;

begin

  { Place thread code here }

  while not Terminated do  // loop around until we should stop

  begin

    Inc(Count);

    if Count >= SecBetweenRuns then

    begin

      Count := 0;

 

      { place your service code here }

      { this is where the action happens }

      SomeProcedureInAnotherUnit;

 

    end;

    Sleep(1000);

  end;

end;

 

end.

 

두번째 쓰레드를 실행하기 위해서 OnStart 메쏘드를 사용하는 것에는 장점과 단점이 있다.

 

장점 :

코드가 좀 더 복잡해지고 여러분은 쓰레드에 대한 지식이 필요하다.

 

단점 :

서비스를 일시 중지하고 다시 시작하는 것이 자동으로 제어되지 않는다.

따라서 여러분은 OnPause OnContinue 이벤트를 정의하는 것이 필요하다.

그러나 Pause Continue 는 여러가지 문제를 야기할 수 있다.

특히 네트웍이나 파일, DB 접근시에 장애를 야기할 수 있다.

이 것을 막는 방법은 서비스의 AllowPause 프라퍼티를 false 로 설정하는 것이다.

 

어쨌거나 OnStart 메쏘드는 코드가 매 실행시에 작업을 마치는데 긴 시간이 걸린다면 잘 동작한다.

 

경고 :

우리가 알아야 할 몇 가지 이슈가 있다.

우선 나는 고백할게 하나있다.

나는 위에서 서비스가 종료될 때 스스로 해제하기 위해서 쓰레드의 FreeOnTerminate 프라퍼티를 true 로 설정하라고 말했다. 이것은 아주 잘못된 것이다.

사실 서비스 어플리케이션 내에서 쓰레드가 스스로 terminate 되도록 하는 것은 재앙을 야기하는 방법이다.

TService 를 벗어나기 전에 TService 클래스안에서 쓰레드를 terminate 하는 것이 좋다.

여러분이 쓰레드가 terminated 되기 전에 TService exit 하고 shutdown 한다면 그러면 쓰레드는 간단히 그것이 일하고 있는 중간에 강제종료된다. 물론 이것은 나쁜 방법이다.

그래서 여러분은 쓰레드의 FreeOnTerminate 프라퍼티를 false 로 설정하고 쓰레드가 종료될때까지 TService 안에서 기다려야 한다. 여러분은 쓰레드의 WaitFor 메쏘드를 사용하는 것에 의해서 이것을 할 수 있다.

 

다른 함정 :

서비스가 수동으로 중지될 때, OnStop 이벤트가 호출된다.(OnShutdown 은 호출되지 않는다.)

시스템이 Shut down 될때 OnShutdown 이벤트가 호출된다.(OnStop 는 호출되지 않는다.)

그래서 정확하게 해치우기 위해서 여러분은 OnStop 이벤트와 OnShutdown 이벤트 둘다를 정의해야한다.

 

우리는 그것을 아래와 같이 할 수 있다.

 

constructor TMyServiceThread.Create;

// Create the thread Suspended so that properties can be set before resuming the thread.

begin

  FreeOnTerminate := False;

  inherited Create(True);

end;

 

그리고 아래와 같이 :

type

  TCompanySqlDatabaseSpecialSomething = class(TService)

    procedure ServiceStart(Sender: TService; var Started: Boolean);

    procedure ServiceStop(Sender: TService; var Stopped: Boolean);

    procedure ServiceShutdown(Sender: TService);

  private

    { Private declarations }

    MyServiceThread: TMyServiceThread;

    procedure ServiceStopShutdown;

  public

    function GetServiceController: TServiceController; override;

    { Public declarations }

  end;

 

procedure TCompanySqlDatabaseSpecialSomething.ServiceStart(Sender: TService;

  var Started: Boolean);

begin

  // Allocate resources here that you need when the service is running

  { Create an instance of the secondary thread where your service code is placed }

  MyServiceThread := TMyServiceThread.Create;

  { Set misc. properties you need (if any) in your thread }

  //MyServiceThread.Property1 := whatever;

  // and so on

  MyServiceThread.Resume;

end;

 

procedure TCompanySqlDatabaseSpecialSomething.ServiceStop(Sender: TService;

  var Stopped: Boolean);

begin

  ServiceStopShutdown;

end;

 

procedure TCompanySqlDatabaseSpecialSomething.ServiceShutdown(

  Sender: TService);

begin

  ServiceStopShutdown;

end;

 

procedure TCompanySqlDatabaseSpecialSomething.ServiceStopShutdown;

begin

  // Deallocate resources here

  if Assigned(MyServiceThread) then

  begin

    // The TService must WaitFor the thread to finish (and free it)

    // otherwise the thread is simply killed when the TService ends.

    MyServiceThread.Terminate;

    MyServiceThread.WaitFor;

    FreeAndNil(MyServiceThread);

  end;

end;

 

 

5. Services Application 의 디버깅

여러분의 service application 을 디버깅하는 가장 쉬운 방법은 서비스가 실행될 때에 프로세스에 붙이는 것이다. 이것을 하기 위해서 델파이 메뉴의 Run/Attach To Process 를 클릭하고 가능한 프로세스 목록에서 서비스 어플리케이션을 선택하라.

몇몇 경우에 이것은 실패할 수도 있으며 부족한 권한에 기인한다. 만약 그게 일어났다면 여러분은 디버거와함께 동작하도록 여러분의 서비스를 활성화하기 위해서 서비스 제어 관리자를 사용할 수 있다.

 

1. 다음의 레지스트리 위치에서 Image File Execution Option 이라 불리는 키를 생성하라.

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion

2. 여러분의 서비스와 동일한 이름으로 subkey 를 생성하라(, MYSERV.EXE). subkey REG_SZ 종류로 디버거 이름을 추가하라. string 값으로 디버거의 full path 를 사용하라.

 

3. 관리도구의 Services 에서 여러분의 서비스를 선택하고 시작을 클릭하고 데스크탑과 상호작용하도록 허용되었는지 체크하라.

 

이것에 추가해서 아래 링크를 참조하라.

http://groups.google.dk/group/borland.public.delphi.nativeapi.win32/msg/13df743b00f57603?dmode=source&hl=da

 

또한 아래 주소로 가서 'NT Low Level Utilities' 를 다운로드하고 unitDebugService.pas 내의 TDebugServiceApplication 를 사용하라.

보기에 Colin Wilson 그것을 디버깅용으로만 사용한다.

http://www.wilsonc.demon.co.uk/delphi.htm

 

 

TService.LogMessage method 사용하기

여러분의 서비스를 실행하는 동안 문제가 발생한다면, 여러분은 나중에 윈도우의 이벤트 뷰어를 통해서 볼수 있도록 메시지를 저장하기 위해서 LogMessage 메쏘드를 사용할 수 있다.

여러분은 아래와 같이 간단하게 LogMessage 메쏘드를 호출한다.

 

LogMessage('Your message goes here SUCC', EVENTLOG_SUCCESS, 0, 1);

LogMessage('Your message goes here INFO', EVENTLOG_INFORMATION_TYPE, 0, 2);

LogMessage('Your message goes here WARN', EVENTLOG_WARNING_TYPE, 0, 3);

LogMessage('Your message goes here ERRO', EVENTLOG_ERROR_TYPE, 0, 4);

 

여러분이 적당하다고 생각되는 메시지 종류를 사용해라.

여러분이 이벤트 뷰어에서 메시지를 보게되면 다음과 같이 나올 것이다.

 

The description for Event ID ( 0 ) in Source ( MyService.exe ) cannot be

found. The local computer may not have the necessary registry information or

message DLL files to display messages from a remote computer. The following

information is part of the event: Your message goes here.

 

여러분은 우리가 LogMessage 로 기술한 메시지의 앞에 수 많은 의미없는 텍스트를 볼 수 있다.

만약 우리가 의미없는 텍스트를 없애고 우리의 메시지만 보이기를 원한다면 우리는 몇가지 특별한 코딩을 하는 것이 필요하다.

 



 

 

[참고 문헌]

1. http://www.tolderlund.eu/delphi/service/service.htm

 

 

* 문서 수정 내역

2009-11-27 : 김제성(4000king@4nb.co.kr) -> 최초 작성.

2010-01-12 : 김제성(4000king@4nb.co.kr) -> 배치파일 내용 업데이트.

반응형
반응형

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Edit1: TEdit;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

function GetComputerName: string;
var
  buffer: array[0..MAX_COMPUTERNAME_LENGTH + 1] of Char;
  Size: Cardinal;
begin
  Size := MAX_COMPUTERNAME_LENGTH + 1;
  Windows.GetComputerName(@buffer, Size);
  Result := StrPas(buffer);
end;

function GetUserFromWindows: string;
var UserName : string;
    UserNameLen : Dword;
begin
    UserNameLen := 255;
    SetLength(userName, UserNameLen) ;
    if GetUserName(PChar(UserName), UserNameLen) Then
      Result := Copy(UserName,1,UserNameLen - 1)
    else
      Result := 'Unknown';
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  Edit1.Text := GetComputerName;
  Edit2.Text := GetUserFromWindows;
end;

end.


반응형
반응형