Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Delphi Unerlaubte Member-Variablen im Service? (https://www.delphipraxis.net/9314-unerlaubte-member-variablen-im-service.html)

Phoenix 23. Sep 2003 17:32


Unerlaubte Member-Variablen im Service?
 
Hallo,

folgendes Problem:

Ich habe ein Klasse und einige Methoden darauf. Eine dieser Methoden übergebe ich nun an den ServiceControlDispatcher um den Dienst zu starten. In dieser übergebenen Methode will bzw. muss ich aber weiterhin auf member-Variablen meines Objektes zugreifen können. In dem Augenblick in dem ich aber auf ein Element der Klasse zugreifen will (ausser einem anderen Methodenaufruf) schmiert mir der Service mit einer Zugriffsverletzung ab.

Etwas anschaulicher:

Delphi-Quellcode:
   PhService = class
      // Private declarations
      private
         m_name: string;
                  [...]
      private
         procedure doStart;
         procedure servicehandler(a_val: integer); stdcall;
         procedure serviceProc(a_dwargc: integer; a_lpszargv: PChar); stdcall;
      // Proteced declarations
      protected
         procedure service_main; virtual;         protected
         property myName: string read m_name write m_name;
      // Constructor / Destructor
                  [...]
      // Public declarations
      public
         procedure Start;
      public
         property Mode: integer read m_startupmode write m_startupmode;
         property Silent: boolean read m_silent write m_silent;
   end;
Delphi-Quellcode:
procedure PhService.doStart;
begin;
   doDebugOutput('doStart');
   m_dispatcher.lpServiceName := PChar(myName);
   m_dispatcher.lpServiceProc := @PhService.serviceProc; // <-- hier wird die kommende Methode übergeben
   doDebugOutput('Start Dispatcher');
   StartServiceCtrlDispatcher(m_dispatcher); // <-- hier die Übergabe und der Start der Methode
end;


// controls the service execution
//
procedure PhService.serviceProc(a_dwargc: integer; a_lpszargv: PChar);
var
   l_name: AnsiString;
begin
   doDebugOutput('begin ServiceProc ' + m_Name); // <-- hier wird auf die Membervariable zugegriffen: Exception!
   m_statushandle := RegisterServiceCtrlHandler(PChar(m_name),
                                                @PhService.ServiceHandler);
   doDebugOutput('has ServiceController');
   if (m_statusHandle <> 0) then
   begin
      ZeroMemory(@m_status, SizeOf(m_status));
      with m_status do
      begin
         dwServiceType := SERVICE_WIN32_OWN_PROCESS;
         dwCurrentState := SERVICE_START_PENDING;
         dwControlsAccepted := SERVICE_ACCEPT_STOP or
                               SERVICE_ACCEPT_PAUSE_CONTINUE;
         dwWaitHint := 1000;
      end; // of with m_status do
         [...]
end;
Ich kann mir das leider gar nicht erklären. Wie kann ich aus der übergebenen Methode jetzt ohne eine Speicherverletzung zu bekommen auf die Eigenschaften der Klasse zugreifen?

Phoenix 24. Sep 2003 06:46

Re: Unerlaubte Member-Variablen im Service?
 
*nochmalnachobenzupf* :wink:

Motzi 24. Sep 2003 07:37

Re: Unerlaubte Member-Variablen im Service?
 
Eine Objekt-Methode führt noch einen versteckten Parameter mehr mit - die Self-Referenz, aber von der weiß Windows natürlich nichts. Schau dir dazu mal die beiden Funktionen MakeObjectInstance und FreeObjectInstance an.

Phoenix 24. Sep 2003 13:48

Re: Unerlaubte Member-Variablen im Service?
 
Hi,

ich will a) kein Subclassing machen. IMHO müsste es da auch ne andere Methode geben, das hinzubekommen.

b) möchte ich möglichst vermeiden die Unit classes zu verwenden. Assarbad und Luckie werden mich töten, wenn mein Service dann wegen dieser blöden Sache hier wieder von 20k auf über 100k anwächst.

Also gibt es da noch ne andere Methode? Ich meine eine Klasse MUSS doch auf die eigenen Methoden und Bariablen zugreifen können, oder?

Motzi 24. Sep 2003 13:55

Re: Unerlaubte Member-Variablen im Service?
 
Das hat nicht unbedingt was mit SubClassing zu tun..

Das Problem ist eben, dass eine Methode einen versteckten Parameter mehr mit sich führt, von dem Windows allerdings keine Ahnung hat!

Aber ich werd mal weitersuchen...

Phoenix 24. Sep 2003 14:10

Re: Unerlaubte Member-Variablen im Service?
 
Ich hab grad ein wenig probiert: MakeObjectInstance kann nur mit eienr TWndMethod als Paramater etwas anfangen.

Die Service - Methode braucht jedoch andere Parameter als eine TWndMethod. Der Aufruf ist so also gar nicht kompatibel.

Ich brauche also definitiv eine andere Lösung.

sakura 24. Sep 2003 14:14

Re: Unerlaubte Member-Variablen im Service?
 
Das ist aus verschiedenen Gründen nicht möglich. Einerseits hast Du getrennte Speicherbereiche, andererseits übergibst Du die Adresse der Prozedur im Speicher, allerdings nicht die Adresse der Daten Deines Objektes. Du kannst 1000 Objekte vom Typ PhService haben, aber es wird nur eine Kopie jeder Methode dieser Klasse geben. Das heisst, wenn Du die Adresse der Methode bergibst, so steht diese völlig "leer im Raume" ohne jegliches Wissen über die Objektinstanz.

...:cat:...

Motzi 24. Sep 2003 14:20

Re: Unerlaubte Member-Variablen im Service?
 
Vielleicht hilft dir das weiter:
http://www.swissdelphicenter.ch/de/showcode.php?id=1671

Phoenix 24. Sep 2003 14:44

Re: Unerlaubte Member-Variablen im Service?
 
Äh, nicht ganz. Ist aber so wie es aussieht genau das, was ich brauche.

Ich verstehe nur den Code nicht, der da unten steht. Zuerst wird ein TMyMethod als procedure of object deklariert, später aber nie wieder verwendet.

Dann muss man der MakeProcInstance ein TMethod übergeben. Wie bekomme ich aus der serviceproc - Methode denn nun ein TMethod, um mir den Pointer erstellen zu lassen?

Phoenix 24. Sep 2003 17:15

Re: Unerlaubte Member-Variablen im Service?
 
Oki. Jetzt soweit:

Ich habe verstanden, das ich diese serviceproc via MakeProcInstance (wahrscheinlich am besten beim Instanzieren des Objektes)in einen Pointer auf einen Methodenzeiger verwandeln muss. Diesen übergebe ich dann.

Die Methode bekommt beim Aufruf durch de API-Funktion den self-parameter mit (wird ja in dem Pointer mit dem Objekt verbudnen) und kann somit auf die richtigen Variablen in der Klasse zugreifen. Hinterher muss ich den Speicher des neuen Methodenzeigers (also im Destruktor der Klasse) wieder freigeben.

Womit ich nach dem Codebeispiel im SDC noch Probleme habe ich der richtige Aufruf dieser Funktion. Diese erwartet als Parameter ein TMethod. Wie bekomme ich jetzt meine ServiceProc richtig an MakeProcInstance übergeben?

Hier der Code:
Delphi-Quellcode:
//Man legt einen ausführbaren Speicherbereich an, und schreibt in
// diesen 4 Maschinencodeanweisungen: 2 davon enthalten die beiden
// Pointer (als Konstanten, die in ein Register geschrieben werden,
// 1 den Aufruf der Methode, und 1 die Return-Anweisung. Den Zeiger
// auf den Speicherbereich kann man nun als normalen Funktionspointer
// verwenden, der die Methode für eine ganz bestimmte Instanz aufruft.

function MakeProcInstance(M: TMethod): Pointer;
begin
  // allocate memory
  GetMem(Result, 15);
  asm
    // MOV ECX,
    MOV BYTE PTR [EAX], $B9 
    MOV ECX, M.Data
    MOV DWORD PTR [EAX+$1], ECX
    // POP EDX
    MOV BYTE PTR [EAX+$5], $5A
    // PUSH ECX
    MOV BYTE PTR [EAX+$6], $51 
    // PUSH EDX
    MOV BYTE PTR [EAX+$7], $52 
    // MOV ECX,
    MOV BYTE PTR [EAX+$8], $B9 
    MOV ECX, M.Code
    MOV DWORD PTR [EAX+$9], ECX
    // JMP ECX
    MOV BYTE PTR [EAX+$D], $FF
    MOV BYTE PTR [EAX+$E], $E1 
  end;
end;


procedure FreeProcInstance(ProcInstance: Pointer);
begin
  // free memory
  FreeMem(ProcInstance, 15);
end;

Phoenix 24. Sep 2003 18:32

Re: Unerlaubte Member-Variablen im Service?
 
Hier die Lösung:
(was lange währt wird endilch gut heisst es doch? ;-) )

Delphi-Quellcode:
// constructor
//
constructor PhService.Create;
var
   t: TMethod;
begin
   t.Code := @PhService.serviceproc;
   t.Data := self;
   m_serviceProcInst := MakeProcInstance(t); // m_serviceProcInst ist ein normaler Pointer, der an die API übergeben wird
end;

// destructor
//
destructor PhService.Destroy;
begin
   FreeProcInstance(m_serviceProcInst); // aufräumen
   inherited;
end;

stoxx 17. Okt 2003 02:03

Re: Unerlaubte Member-Variablen im Service?
 
@ Phoenix & Motzi

ich stand gerade vor dem gleichen Problem. Und diese Lösung funktioniert perfekt !
Es war gut Phoenix, dass Du die Lösung noch gepostet hast, hätte ich auf die Schnelle nicht herausgefunden.

Dagegen ist diese Lösung regelrecht umständlich:
http://www.delphipraxis.net/internal...ht=enumwindows

Luckie 2. Aug 2007 11:57

Re: Unerlaubte Member-Variablen im Service?
 
Ich habe jetzt mal versucht das mit der API-Funktion EnumWindows umzusetzen:
Delphi-Quellcode:
program Project2;

{$APPTYPE CONSOLE}

uses
  Windows;

type
  TEnumWindows = class(TObject)
  private
    EnumProcInst: Pointer;
    function MakeProcInstance(M: TMethod): Pointer;
    procedure FreeProcInstance(ProcInstance: Pointer);
    function EnumWindows(hWnd: THandle; lp: LPARAM): Boolean; stdcall;
  public
    constructor Create;
    destructor Destroy; override;
  end;

{ TEnumWindows }

function TEnumWindows.MakeProcInstance(M: TMethod): Pointer;
begin
  // allocate memory
  GetMem(Result, 15);
  asm
    // MOV ECX,
    MOV BYTE PTR [EAX], $B9
    MOV ECX, M.Data
    MOV DWORD PTR [EAX+$1], ECX
    // POP EDX
    MOV BYTE PTR [EAX+$5], $5A
    // PUSH ECX
    MOV BYTE PTR [EAX+$6], $51
    // PUSH EDX
    MOV BYTE PTR [EAX+$7], $52
    // MOV ECX,
    MOV BYTE PTR [EAX+$8], $B9
    MOV ECX, M.Code
    MOV DWORD PTR [EAX+$9], ECX
    // JMP ECX
    MOV BYTE PTR [EAX+$D], $FF
    MOV BYTE PTR [EAX+$E], $E1
  end;
end;


procedure TEnumWindows.FreeProcInstance(ProcInstance: Pointer);
begin
  // free memory
  FreeMem(ProcInstance, 15);
end;

constructor TEnumWindows.Create;
var
  Method: TMethod;
begin
  Method.Code := @TEnumWindows.EnumWindows;
  Method.Data := Self;
  EnumProcInst := MakeProcInstance(Method);
  EnumWindows(Cardinal(EnumProcInst), 0);
end;

function TEnumWindows.EnumWindows(hWnd: THandle; lp: LPARAM): Boolean; stdcall;
begin
  Writeln(hWnd);
  Result := True;
end;


destructor TEnumWindows.Destroy;
begin
  FreeProcInstance(EnumProcInst);
  inherited;
end;

var
  EnumWnds: TEnumWindows;

begin
  EnumWnds := TEnumWindows.Create;
  EnumWnds.Free;
  Readln;
end.
Das scheint leider nicht ganz zu funktionieren, da ich nur ein Handle ausgegeben bekomme.

Phoenix 2. Aug 2007 12:39

Re: Unerlaubte Member-Variablen im Service?
 
Im Moment machst Du ja noch nichts mit der Klasse als solches. Probier mal, die EnumWIndowsProc direkt zu übergeben und schau ob Du dann mehrfach aufgerufen wirst. Es könnte sein, dass der ASM-Block den Rückgabewert nicht wieder an Windows zurück durchreicht - was Du aber brauchst..

Luckie 2. Aug 2007 12:47

Re: Unerlaubte Member-Variablen im Service?
 
Ich rufe es doch im Konstruktor auf.

Zitat:

Zitat von Phoenix
Probier mal, die EnumWIndowsProc direkt zu übergeben und schau ob Du dann mehrfach aufgerufen wirst. Es könnte sein, dass der ASM-Block den Rückgabewert nicht wieder an Windows zurück durchreicht - was Du aber brauchst..

Wie meinst du das?

SirThornberry 2. Aug 2007 12:49

Re: Unerlaubte Member-Variablen im Service?
 
@Luckie: Warum so kompliziert? nutze doch den lParam um die ObjectInstanz zu übergeben wenn du den lparam sowieso nicht nutzt. Und selbst wenn du den lparam nutzten willst kannst du dort auch einen Pointer auf einen Record übergeben der deinen eigentlichen lparam enthält und zustäzlich die Instanz.

Variante wenn lParam nicht benötigt wird
Delphi-Quellcode:
function GlobalEnumWindows(hWnd: THandle; lp: LPARAM): Boolean; stdcall;
begin
  TEnumWindows(lp).EnumWindows(hWnd, 0);
end;

constructor TEnumWindows.Create;
begin
  Windows.EnumWindows(@GlobalEnumWindows, Cardinal(Self));
end;

function TEnumWindows.EnumWindows(hWnd: THandle): Boolean; stdcall;
begin
  Writeln(hWnd);
  Result := True;
end;
Der Fehler bei dir lag daran das du im Create zwar "EnumWindows" aufrufst, aber da EnumWindows eine Methode deiner Klasse ist wird diese aufgerufen und nicht die EnumWindows-Funktion aus der Unit Windows
Anstelle von:
Delphi-Quellcode:
constructor TEnumWindows.Create;
var
  Method: TMethod;
begin
  Method.Code := @TEnumWindows.EnumWindows;
  Method.Data := Self;
  EnumProcInst := MakeProcInstance(Method);
  EnumWindows(Cardinal(EnumProcInst), 0);
end;
müsste es also mindestens so aussehen:
Delphi-Quellcode:
constructor TEnumWindows.Create;
var
  Method: TMethod;
begin
  Method.Code := @TEnumWindows.EnumWindows;
  Method.Data := Self;
  EnumProcInst := MakeProcInstance(Method);
  windows.EnumWindows(EnumProcInst, 0);
end;
ansonsten wird wie bereits erwähnt deine Methode aufgerufen und der erste Parameter ist dann nicht ein Handle sondern "Cardinal(EnumProcInst)".

Luckie 2. Aug 2007 13:09

Re: Unerlaubte Member-Variablen im Service?
 
:wall: :wall: :wall:

Ich habe es jetzt so:
Delphi-Quellcode:
type
  TEnumWindows = class(TObject)
  private
    EnumProcInst: Pointer;
    function MakeProcInstance(M: TMethod): Pointer;
    procedure FreeProcInstance(ProcInstance: Pointer);
    function EnumWindows(hWnd: THandle; lp: LPARAM): Boolean; stdcall;
  public
    constructor Create;
    destructor Destroy; override;
  end;

{ TEnumWindows }

function TEnumWindows.MakeProcInstance(M: TMethod): Pointer;
begin
  // allocate memory
  GetMem(Result, 15);
  asm
    // MOV ECX,
    MOV BYTE PTR [EAX], $B9
    MOV ECX, M.Data
    MOV DWORD PTR [EAX+$1], ECX
    // POP EDX
    MOV BYTE PTR [EAX+$5], $5A
    // PUSH ECX
    MOV BYTE PTR [EAX+$6], $51
    // PUSH EDX
    MOV BYTE PTR [EAX+$7], $52
    // MOV ECX,
    MOV BYTE PTR [EAX+$8], $B9
    MOV ECX, M.Code
    MOV DWORD PTR [EAX+$9], ECX
    // JMP ECX
    MOV BYTE PTR [EAX+$D], $FF
    MOV BYTE PTR [EAX+$E], $E1
  end;
end;


procedure TEnumWindows.FreeProcInstance(ProcInstance: Pointer);
begin
  // free memory
  FreeMem(ProcInstance, 15);
end;

constructor TEnumWindows.Create;
var
  Method: TMethod;
begin
  Method.Code := @TEnumWindows.EnumWindows;
  Method.Data := Self;
  EnumProcInst := MakeProcInstance(Method);
  Windows.EnumWindows(EnumProcInst, 0);
end;

function TEnumWindows.EnumWindows(hWnd: THandle; lp: LPARAM): Boolean; stdcall;
var
  Buffer: PChar;
  len: Integer;
begin
  if IsWindow(hWnd) and IsWindowVisible(hWnd) then
  begin
    len := SendMessage(hWnd, WM_GETTEXTLENGTH, 0, 0);
    if len > 0 then
    begin
      Buffer := GetMemory(len + 1);
      try
        SendMessage(hWnd, WM_GETTEXT, len, Integer(Buffer));
        Writeln(Buffer);
      finally
        FreeMemory(Buffer);
      end;
    end;
  end;
  Result := True;
end;

destructor TEnumWindows.Destroy;
begin
  FreeProcInstance(EnumProcInst);
  inherited;
end;
Aber irgendwas stimmt mit dem Speicher nicht. Entweder sind die Fenstertitel abgeschnitten oder es hängt noch Zeichensalat hinten dran. Aber die Routine hab eich schon tausendmal so geschrieben. Muss ich da noch was beachten, wenn die Callback eine Methode einer Klasse ist?

SirThornberry 2. Aug 2007 13:30

Re: Unerlaubte Member-Variablen im Service?
 
mach mal aus
Delphi-Quellcode:
SendMessage(hWnd, WM_GETTEXT, len, Integer(Buffer));
einfach
Delphi-Quellcode:
SendMessage(hWnd, WM_GETTEXT, len + 1, Integer(Buffer));
ansonsten fehlt dir die abschließende #0
Oder initialisiere den speicher vorher mit #0
[Edit]Tippfehler beseitigt[/Edit]

Luckie 2. Aug 2007 13:32

Re: Unerlaubte Member-Variablen im Service?
 
:wall: :wall: :wall:

Das ist heute nicht mein Tag. Ich gehe jetzt erstmal in eine Besprechung. Mal sehen, was ich da für einen Mist verzapfen kann. :?


Alle Zeitangaben in WEZ +1. Es ist jetzt 21:40 Uhr.

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz