DLL Init, Timer läuft nicht an
Hi,
ich habe eine DLL gemacht, die beim Init eine Form erzeugt und einen Timer startet:
Delphi-Quellcode:
Wenn die DLL nun von eine Test-VCL Form von Delphi geladen wird und das Init aufgerufen wird, wird das Timer Event von StartUpDelay ausgelöst.
function InitDLL(Callback : TCallback):Boolean; stdcall;
begin try //plugin gets loaded, create Form if Not Assigned(frMain) then frMain := TfrMain.Create(NIL); frMain.AddLog('plugin got init'); //start startupdelay frMain.StartStartUpDelay(StartDelay); finally Result := Assigned(frMain); end; end; end; procedure TfrMain.StartStartUpDelay(Interval:Integer); begin //start startup delay StartUpDelay.Interval := Interval; StartUpDelay.Enabled := True; end; Wenn die DLL aber von einer C-Console-App (VS2008) geladen wird, wird das Timer Event nicht ausgelöst!? Erst wenn ich die Form anzeige wird das Event ausgelöst:
Delphi-Quellcode:
Sobald Showmodal aufgerufen wird spingt der Code in die StartUpDelay Timer Routine.
procedure ShowForm();
begin if Assigned(frMain) then begin frMain.ShowModal; end; end; Woran kann das liegen? |
Re: DLL Init, Timer läuft nicht an
Darum nutzt man keine VCL in einer DLL.
- der Timer wird über die VCL gesteuert - in deiner DLL hast du keine Nachrichtenbehandlung (Windows-Messages) eingebaut - die Nachrichtenschleife der EXE behandelt alle Nachichten und leitet sie an die DLL weiter - eine C-EXE hat zwar (vermutlich) eine Nachrichtenschleife, aber diese behandelt garantiert keine Delphi-Ereignisse Wenn unbedingt VCL in DLL, - dann erstelle und behandle diese in einem eigenem Thread und arbeite in diesem Thread die Nachrichten ab - und es darf keine Interation zwischen den beiden VCLs (EXE und DLL) geben, denn die VCL ist nicht threadsicher |
Re: DLL Init, Timer läuft nicht an
Zitat:
- Es gibt in diesem Fall absolut nichts verwerfliches daran in diesem Fall die VCL in der DLL zu verwenden. - Das der Timer über die VCL gesteuert wird ist nur bedingt richtig. Der Timer ist wohl Teil der VCL (je nach dem wie man VCL definiert) aber letztendlich wird er über das Windows-Nachrichtensystem gesteuert. - Das die Nachrichtenschleife der Exe alle Nachrichten behandelt und diese an die DLL weiter leitet ist nur bedingt richtig. Eine Nachrichtenschleife behandelt alle Nachrichten des Threads in der die Schleife läuft. Da der Timer im Kontext des Hauptthreads der Exe erstellt wurde, werden die Nachrichten aller Fenster (und nichts anderes empfängt die Timernachrichten in der Timerklasse) dieses Threads, inklusive derer aus der DLL verarbeitet. Ebenso würde aber auch eine Nachrichtenschleife innerhalb der DLL die Nachrichten für die Fenster in der EXE verarbeiten sofern diese im gleichen Threadkontext laufen. - Eine Nachrichtenschleife in einem C-Programm verarbeitet garantiert auch die Nachrichten welche für Fenster sind die aus Programmen/Programmteilen anderer programmiersprachlicher Herkunft sind. Die Begründung warum es nicht funktioniert liegt hierin: Zitat:
|
Re: DLL Init, Timer läuft nicht an
Es gibt aber auch eine Menge delphieigener Messages, welche direkt in Delphis Nachrichtenschleife behandelt werden
und Solche werden nicht von einer "fremden" Nachrichtenschleife behandelt. |
Re: DLL Init, Timer läuft nicht an
Hast du dich mal mit Nachrichtenschleifen beschäftigt? Zumindest alle Punkt die ich von dir zitiert habe trafen in diesem konkreten Beispiel nicht zu. Und dem Fragesteller ging es nicht darum was allgemein irgendwann mal sein kann sondern er wollte wissen warum im konkreten Fall der Timer seinen Dienst nicht verrichtet. Und da waren die von dir genannten Punkte schlichtweg falsch.
Aber auch die letzte Aussage würde ich so nicht stehen lassen. Ich bin der Meinung (bin mir hier aber nicht zu 100% sicher) das nicht die Nachrichtenschleife spezielle Messages verarbeitet. Vielmehr holt die Nachrichtenschleife nur die Nachrichten aus der Nachrichtenwarteschlange ab und leitet diese durch den Aufruf von Windowsfunktionen an die entsprechende registrierten Nachrichtenfunktionen weiter. So sieht übrigens eine Nachrichtenschleife in C aus:
Code:
In Delphi sieht sie übrigens nicht groß anders aus. Es werden die gleichen Funktionen aufgerufen. Rein der Syntax der Programmiersprache unterscheidet sich.
while (GetMessage(&Message, NULL, 0,0))
{ TranslateMessage(&Message); DispatchMessage(&Message); } |
Re: DLL Init, Timer läuft nicht an
Danke euch beiden erst einmal!
Gibt es zu dem Thread/Message abarbeiten ein Beispiel. Werde warscheinlich im Thread mit GetMessage ein Polling machen müssen und die WM_TIMER dann per PostMessage an mein HWND weiterleiten!? |
Re: DLL Init, Timer läuft nicht an
Zitat:
Delphi-Quellcode:
var
Msg: TMsg; begin while ProcessMessage(Msg) do {loop}; function TApplication.ProcessMessage(var Msg: TMsg): Boolean; var Handled: Boolean; Unicode: Boolean; MsgExists: Boolean; begin Result := False; MsgExists := PeekMessage(Msg, 0, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE); if MsgExists or PeekMessage(Msg, 0, 0, 0, PM_NOREMOVE) then begin Unicode := (Msg.hwnd = 0) or IsWindowUnicode(Msg.hwnd); if not MsgExists then begin if Unicode then MsgExists := PeekMessageW(Msg, 0, 0, 0, PM_REMOVE) else MsgExists := PeekMessageA(Msg, 0, 0, 0, PM_REMOVE); end; if MsgExists then begin Result := True; if Msg.Message <> WM_QUIT then begin Handled := False; if Assigned(FOnMessage) then FOnMessage(Msg, Handled); if not IsPreProcessMessage(Msg) and not IsHintMsg(Msg) and not Handled and not IsMDIMsg(Msg) and not IsKeyMsg(Msg) and not IsDlgMsg(Msg) then begin TranslateMessage(Msg); if Unicode then DispatchMessageW(Msg) else DispatchMessageA(Msg); end; end else begin {$IF DEFINED(CLR)} if Assigned(FOnShutDown) then FOnShutDown(self); DoneApplication; {$IFEND} FTerminate := True; end; end; end; end; |
Re: DLL Init, Timer läuft nicht an
Sorry, aber auch da sehe ich keine wirkliche Sonderbehandlung. Es wird die Message lediglich für das Application-Object etc. bereit gestellt. Aber eine besondere Behandlung einzelner Messages sehe ich hier nicht. Aber das ist auch egal. Mir ging es nur darum einige Aussagen richtig zu stellen damit der Fragestellende nicht den Eindruck bekommt wirklich alles falsch gemacht zu haben wenn im konkreten Fall der einzige "Fehler" ist keine Nachrichtenschleife zu haben.
|
Re: DLL Init, Timer läuft nicht an
Für den Timer selber könnten diese Befehle entscheidend sein ... man weiß ja nicht was da intern noch alles passiert.
Delphi-Quellcode:
Und dann wird der Timer nicht einzeln verwaltet.
if Assigned(FOnMessage) then FOnMessage(Msg, Handled);
if not IsPreProcessMessage(Msg) and ... and not Handled Ein Timer ohne Form/Owner muß nicht das Selbe sein, wie Einer mit Form/Owner ... hier kann es auch sein, daß schon bei dessen Erstellung etwas schief läuft, also z.B. wärend die Form erstellt/verwaltet wird. Der Code vom TTimer selber ist noch relativ primitiv und dürfte so vermutlich auch in der C-Nachrichtenschleife behandelt werden, aber das TComponent hinter dem TTimer verbirgt 'ne Menge unberechenbaren VCL-Code. Wie gesagt, der einfachste und sicherste Weg die VCL innerhalb einer "autonomen" DLL zu verwalten, wäre diese VCL in einem separaten Thread und mit eigener Nachrichtenschleife laufen zu lassen. |
Re: DLL Init, Timer läuft nicht an
Zitat:
Im Execute vom Thread dann wie am besten die Nachrichten abarbeiten? Tut leid, aber ich steh momentan voll auf dem Schlauch... :roll: Danke! |
Re: DLL Init, Timer läuft nicht an
Der Timer funktioniert nur mit Nachrichtenschleife. Diese wird unter anderem ausgeführt bei einem YourForm.ShowModal. Damit der Timer wie gewünscht funktioniert brauchst du also eine Nachrichtenschleife.
Himitsus Vorschlag verstehe ich wie folgt. Erstelle im C-Programm einen Thread. Dieser Thread startet/lädt die Dll und innerhalb der DLL lässt du dann auch die Nachrichtenschleife laufen. |
Re: DLL Init, Timer läuft nicht an
Zitat:
Wenn die DLL geladen/initialisiert wird, innerhalb der DLL einen Thread erzeugen, darin die Form erstellen und dann einfach ein (im Thread.Execute)
Delphi-Quellcode:
Und nicht vergessen Zugriffe auf diese VCL unbedingt mit diesem Thread zu syncronisieren.
MyForm := TMyForm.Create(nil);
try while not Terminated do //while aktiv{oder "not Beenden"} do begin Application.ProcessMessages; Sleep(10); end; finally MyForm.Free; end; |
Re: DLL Init, Timer läuft nicht an
Ich glaube so wird das nicht funktionieren. Wie du bereits angemerkt hast ist die VCL nicht Threadsave. Und das heißt konkret das die VCL (Application.ProcessMessage etc.) nur ordentlich arbeitet wenn es in dem Thread arbeitet in dem das Modul geladen wurde. Der Thread muss also außerhalb der DLL erstellt werden so dass, das Application-Object und die Initialization-Abschnitte innerhalb der DLL bereits in diesem Thread ausgeführt werden (oder mit anderen Worten: Die DLL darf muss "denken" das sie im einzigen Thread läuft)
|
Re: DLL Init, Timer läuft nicht an
Es geht!
Delphi-Quellcode:
Das ProcessMessages lasse ich aber nach dem Startup Delay wieder aus.
procedure TMainThread.Execute;
begin try //plugin gets loaded, create Form if Not Assigned(frMain) then frMain := TfrMain.Create(NIL); //start startupdelay frMain.StartStartUpDelay(StartDelay); while not Terminated do //while aktiv{oder "not Beenden"} do begin if frMain.StartUpDelay.Enabled then Application.ProcessMessages; Sleep(1); //fix 100% cpu bug end; finally if Assigned(frMain) then FreeAndNil(frMain); end; end; Danke! |
Re: DLL Init, Timer läuft nicht an
Das ganze Gekrampfe spart man sich, wenn man einfach per Hand einen Timer anfordert, und darin die Option eines Callbacks nutzt.
|
Re: DLL Init, Timer läuft nicht an
SetTimer geht bei mir nicht, da ich ja die WM_TIMER nicht bekomme....
Habe jetzt noch ein Problem! Der Timer wird nun ausgelöst, aber wenn ich die Form einmal gezeigt hatte mit ShowModal dann gibt es den Error: 5 ' Zugriff verweigert' wenn ich ein FreeAndNil(frMain) mache:
Delphi-Quellcode:
Callstack:
procedure TmyThread.Terminate;
begin inherited Terminate; PostMessage(FHWND, WM_QUIT , 0,0); end; procedure TmyThread.Execute; var msg:Tmsg; begin try //plugin gets loaded, create Form if Not Assigned(frMain) then frMain := TfrMain.Create(NIL); frMain.AddLog('plugin got init'); finally //todo end; FHWND:=allocatehwnd(WndProc); //hier ist jetzt dein Windowhandle für MMAudio try //[weitere Initialisierungen] while getMessage(msg,0,0,0) do //warten auf Message DispatchMessage(msg); //verteilen auf das entsprechende Fenster (gibt hier allerdings eh nur eins) finally if Assigned(frMain) then FreeAndNil(frMain); Deallocatehwnd(FHWND); end; end; procedure TmyThread.wndProc(var Msg:TMessage); begin Dispatch(msg); //verteilen auf die Methoden mit der entsprechenden Message -->MMInDone end; Zitat:
|
Re: DLL Init, Timer läuft nicht an
Zitat:
Genau: SetTimer+WM_TIMER :zwinker: |
Re: DLL Init, Timer läuft nicht an
Das habe ich schon kapiert,
jedoch behebt das mir leider nicht den Fehler wenn ich die Form anzeige. :( |
Re: DLL Init, Timer läuft nicht an
Zitat:
Habe im DLL Aufruf direkt auf die Form zugegriffen und das Showmodal ausgeführt. Sende nun mit PostThreadMessage eine Nachricht und der Thread führt dann das Showmodal aus. Dann gibt es keine Fehlermeldung mehr! :angel: |
Re: DLL Init, Timer läuft nicht an
Zitat:
|
Re: DLL Init, Timer läuft nicht an
Werd's mir nocheinmal genauer ansehen!
Aber zuerst habe ich noch eine Frage zu dem ShowModal: Ich sende ja nun den Thread, dass er die Form anzeigen soll. somit wird der DLL aufruf ja sofort wieder beendet, da ja nur PostThreadMessage gemacht wird. Somit ist es nicht Modal. Wie kann man das noch am besten machen, dass die DLL im ShowSettings stehen bleibt? |
Re: DLL Init, Timer läuft nicht an
Du könntest TThread.Syncronize in der DLL nutzen, um mit dem Thread zu synchronisieren.
|
Re: DLL Init, Timer läuft nicht an
Hab's mit Synchronize versucht, da kommt wieder der Zugriff-Verweigert Fehler:
Delphi-Quellcode:
EDIT:
MyMainThread : TMyMainThread;
procedure ShowSettings(Handle: Integer); begin if Assigned(MyMainThread) then MyMainThread.Synchronize(MyMainThread, MyMainThread.ShowDLLForm); // PostThreadMessage(MyMainThread.ThreadID, WM_SHOWFORM, Handle, 0); end; procedure TMyMainThread.ShowDLLForm; begin if Assigned(frMain) then frMain.ShowModal; end; Auch habe ich noch eine anderes Problem: Ich starte ja nun einen Thread, dieser macht mir die frMain Form. Nun arbeite ich mit einem HID-Deivce. Wenn nun das Gerät abgesteckt wird bleibt mir die DLL bei Checkin stehen:
Delphi-Quellcode:
// method CheckIn hands a checked out HidDevice back in
procedure TJvHidDeviceController.CheckIn(var HidDev: TJvHidDevice); begin if HidDev <> nil then begin HidDev.StopThread; //<----- hier bleibt es stehen HidDev.CloseFile; HidDev.CloseFileEx(omhRead); HidDev.CloseFileEx(omhWrite); if HidDev.IsPluggedIn then begin HidDev.FIsCheckedOut := False; Dec(FNumCheckedOutDevices); Inc(FNumCheckedInDevices); end else HidDev.Free; HidDev := nil; end; end;
Delphi-Quellcode:
Wenn ich die EXE beende, die die DLL geladen hatte läuft es erst weiter.
procedure TJvHidDevice.StopThread;
begin if Assigned(FDataThread) then begin FDataThread.Terminate; FDataThread.WaitFor; <<----schätze also hier das es stehen bleibt FDataThread.Free; FDataThread := nil; end; end; Wiso wird das jetzt blockiert? |
Re: DLL Init, Timer läuft nicht an
Will ja nicht pushen, aber ich werde noch Wahnsinnig! :wall:
Das SetTimer geht nicht, auch nicht per Callback. Dies wird erst ausgelöst wenn die Form angezeigt wird! Der Ablauf ist nun so: Die DLL wird geladen, INIT-Funktion wird aufgerufen -> Thread wird erstellt -> Execute -> frMain (Form wird erzeugt):
Delphi-Quellcode:
//funktion was ausgelöst wird wenn das Device entfernt wird:
procedure TMyMainThread.Execute;
var msg:Tmsg; begin try InitFinished := False; if Not Assigned(frMain) then frMain := TfrMain.Create(NIL); frMain.AddLog('plugin got init'); InitFinished := True; while not Terminated do begin Sleep(1); if PeekMessage(msg, 0, 0, 0, PM_REMOVE) then //warten auf Message then begin case msg.message of WM_SHOWFORM: begin if IsWindow(msg.wParam) then if Application.Handle <> msg.wParam then Application.Handle := msg.wParam; if NOT frMain.Showing then frMain.Show; end; WM_STARTUP: begin if Assigned(frMain) then frMain.Start_StartUp(StartDelay); end; WM_STOP: begin if Assigned(frMain) then begin frMain.AddLog('stopped plugin'); if ((Assigned(frMain.HIDDevice)) and (not frMain.HIDDevice.IsPluggedIn)) then frMain.JvHidDeviceController.CheckIn(frMain.HIDDevice); if Assigned(frMain.JvHidDeviceController) then FreeAndNil(frMain.JvHidDeviceController); end; end; WM_QUIT: Terminate; else begin //TranslateMessage(msg); DispatchMessage(msg); end; end; end; end; finally if Assigned(frMain) then begin if frMain.Showing then frMain.Close; FreeAndNil(frMain); end; end; end;
Delphi-Quellcode:
Nun bleibt meine DLL in frMain.JvHidDeviceController.CheckIn(frMain.HIDDev ice); stehen!
procedure TfrMain.JvHidDeviceRemoval(HidDev: TJvHidDevice);
begin AddLog('Device got removed: ' + HIDDevice.ProductName); if ((Assigned(HIDDevice)) and (NOT HIDDevice.IsPluggedIn)) then begin if ((HidDev.Attributes.VendorID = VendorID) AND (HidDev.Attributes.ProductID = ProductID) AND (HidDev.ProductName = ProductName )) then begin try PostThreadMessage(MyMainThread.ThreadID, WM_STOP, 0, 0); OutputDebugString(PWideChar('Device got disconnected')); finally //todo end; end; end; end; Wenn das Device geöffnet wird, wird für HIDDevice ein "WorkerThread" erzeugt. Dieser will bei CheckIn gestoppt werden. Und bei Thread Waitfor wartet man für immer, ausser die EXE wird beendet, dann kommt der Debugger sofort aus dem TThread.Waitfor zurück. Irgendwie kommen immer wieder neue Probleme mit DLL + Form auf... |
Re: DLL Init, Timer läuft nicht an
Zitat:
Zitat:
Ich bin mir aber irgendwie sicher, schon mal Timer in einem non-Form Projekt genutzt zu haben :gruebel: Vielleicht wäre es noch ein Weg, das Fenster ganz von Hand via WinAPI, also non-VCL, zu erstellen. Damit umgeht man zumindest das dumme Gehampel mit den VCL-Interna, sprich Threads und das ganze Gelöt. Oder aber du nimmst CreateTimerQueue + CreateTimerQueueTimer (das neue Gegenstück zum mittlerweile obsoleten MMTimer setTimerEvent()). Da spricht zumindest kein Parameter von irgendwelchen Fensterhandles, und statt "Messages" les ich da ausschließlich "Callback" :). Ist zwar erstmal mit Reindenken verbunden, aber man spart sich den ganzen Krampf den du bisher veranstalten musstest - NUR um einen Timer zu haben. |
Re: DLL Init, Timer läuft nicht an
Ja, hatte ich auch schon mal in einer DLL.
So eine Class mit Start(Interval), Stop usw. Gibt hier irgendwo ein Beispiel dafür. Da hatte es auch funktioniert, aber nun durch die Verwendung der DLL mit der Console bin ich auf diese Probleme gestoßen. |
Re: DLL Init, Timer läuft nicht an
Zu diesem leidigen Thema nochmal:
Mit der D6DLLSynchronizer.pas kann ich nun den SubThread beenden! :) Jedoch habe ich noch Probleme mit dem Anzeigen der Form mit ShowModal.
Delphi-Quellcode:
Wenn ich das nun ausführe wir mein Fenster angezeigt.
procedure ShowSettings(Handle: Integer);
begin if Assigned(MyMainThread) then begin if Handle = 0 then Handle := GetActiveWindow; Application.Handle := Handle; TThread.Synchronize(MyMainThread, MyMainThread.FormShowModal); Application.Handle := 0; end; end; Wenn ich nun aber FreeAndNil(frMain) beim beenden durchführe bekomme ich den Error: EOSError Code 5 (Zugriff verweigert). Habe leider keine Ahnung warum dieser kommt und wo dieser Auftritt. Wie kann man das noch lösen? :gruebel: |
Alle Zeitangaben in WEZ +1. Es ist jetzt 20:24 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz