![]() |
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:
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?
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; |
Re: Unerlaubte Member-Variablen im Service?
*nochmalnachobenzupf* :wink:
|
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.
|
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? |
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... |
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. |
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:... |
Re: Unerlaubte Member-Variablen im Service?
|
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? |
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; |
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; |
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: ![]() |
Re: Unerlaubte Member-Variablen im Service?
Ich habe jetzt mal versucht das mit der API-Funktion EnumWindows umzusetzen:
Delphi-Quellcode:
Das scheint leider nicht ganz zu funktionieren, da ich nur ein Handle ausgegeben bekomme.
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. |
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..
|
Re: Unerlaubte Member-Variablen im Service?
Ich rufe es doch im Konstruktor auf.
Zitat:
|
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:
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
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; Anstelle von:
Delphi-Quellcode:
müsste es also mindestens so aussehen:
constructor TEnumWindows.Create;
var Method: TMethod; begin Method.Code := @TEnumWindows.EnumWindows; Method.Data := Self; EnumProcInst := MakeProcInstance(Method); EnumWindows(Cardinal(EnumProcInst), 0); end;
Delphi-Quellcode:
ansonsten wird wie bereits erwähnt deine Methode aufgerufen und der erste Parameter ist dann nicht ein Handle sondern "Cardinal(EnumProcInst)".
constructor TEnumWindows.Create;
var Method: TMethod; begin Method.Code := @TEnumWindows.EnumWindows; Method.Data := Self; EnumProcInst := MakeProcInstance(Method); windows.EnumWindows(EnumProcInst, 0); end; |
Re: Unerlaubte Member-Variablen im Service?
:wall: :wall: :wall:
Ich habe es jetzt so:
Delphi-Quellcode:
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?
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; |
Re: Unerlaubte Member-Variablen im Service?
mach mal aus
Delphi-Quellcode:
einfach
SendMessage(hWnd, WM_GETTEXT, len, Integer(Buffer));
Delphi-Quellcode:
ansonsten fehlt dir die abschließende #0
SendMessage(hWnd, WM_GETTEXT, len + 1, Integer(Buffer));
Oder initialisiere den speicher vorher mit #0 [Edit]Tippfehler beseitigt[/Edit] |
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