"The shell stopped unexpectedly and Explorer.exe was restarted."
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
| interface
uses
Windows, Messages, SysUtils, Classes,
IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, IdHTTP;
type
TGetThread = class(TThread)
private
HTTP: TIdHTTP;
public
Result: Byte;
constructor Create(AOwner: TComponent);
procedure Execute; override;
end;
implementation
constructor TGetThread.Create(AOwner: TComponent);
begin
inherited Create(False);
HTTP := TIdHTTP.Create(AOwner);
Result := 0;
end;
procedure TGetThread.Execute;
var
HTTPData: String;
begin
try
HTTPData := HTTP.Get('http://www.tweakers.net/');
if Pos('1', HTTPData) > 0 then Result := 1 else Result := 0;
finally
HTTP.Free;
end;
end;
end. |
"The shell stopped unexpectedly and Explorer.exe was restarted."
1
2
3
4
5
6
7
8
9
10
| procedure TMainForm.Button1Click(Sender: TObject);
begin
GetThread := TGetThread.Create(Self);
with GetThread do begin
OnTerminate := GetThreadTerminate;
Priority := tpNormal;
FreeOnTerminate := True;
Resume;
end;
end; |
En de OnTerminate event:
1
2
3
4
5
6
7
8
9
10
11
| procedure TMainForm.GetThreadTerminate(Sender: TObject);
begin
with GetThread do begin
if Result = 1 then begin
StatusBar.Panels.Items[0].Text := 'OK';
end else begin
StatusBar.Panels.Items[0].Text := 'ERROR';
end;
end;
GetThread := nil;
end; |
"The shell stopped unexpectedly and Explorer.exe was restarted."
Dit is nog vager:
Nadat ik zelf weet wanneer de OnTerminate van de Thread had moeten plaats vinden doe ik dit:
1
2
3
4
| procedure TMainForm.Button2Click(Sender: TObject);
begin
ShowMessage('hallo');
end; |
Dan triggerd hij toch de OnTerminate vlak voor de MessageBox.
Daarom dacht ik dat het iets met die DLL te maken moet hebben.
"The shell stopped unexpectedly and Explorer.exe was restarted."
Verwijderd
B.t.w.: maak je wel gebruik van de Unit ShareMem ?
TIdHTTP heeft GEEN eigen thread, daarom doe ik het zo.Op woensdag 20 februari 2002 09:57 schreef hvdberg het volgende:
Allereerst maak je je form uit een ander proces eigenaar van het thread in een DLL. Dit is onzinnig. Waarschijnlijk draait dat HTTP component ook zijn eigen thread en lijkt me dus een beetje onzinnig om deze te doen.
B.t.w.: maak je wel gebruik van de Unit ShareMem ?
Anders zou die hele MainForm + de applicatie die de DLL aanroept even vast lopen als die IdHTTP zijn data ophaalt.
ShareMem heb ik toch niet nodig, omdat de applicatie die de DLL aanroept niks met de inhoud van de DLL heeft te maken. De applicatie die hem aanroept is niet eens een Delphi applicatie.
"The shell stopped unexpectedly and Explorer.exe was restarted."
Verwijderd
Ok... da's dan duidelijk maar waarom maak je het form dan eigenaar van het thread (via je constructor). TThread stamt namelijk helemaal niet af van TComponent en dus is de AOwner: TComponent constructie onzin (je gebruikt hem volgens mij niet eens, je owner).Op woensdag 20 februari 2002 10:10 schreef jelmervos het volgende:
[..]
TIdHTTP heeft GEEN eigen thread, daarom doe ik het zo.
Anders zou die hele MainForm + de applicatie die de DLL aanroept even vast lopen als die IdHTTP zijn data ophaalt.
ShareMem heb ik toch niet nodig, omdat de applicatie die de DLL aanroept niks met de inhoud van de DLL heeft te maken. De applicatie die hem aanroept is niet eens een Delphi applicatie.
In ieder geval heb ik even een projectgroep gemaakt met daarin een DLL en een EXE. De DLL bevat het thread (TMyThread) en één geëxporteerde functie (DoThreading), deze functie geeft een TThread object terug en benodigd een TNotifyEvent als parameter (OnTerminate). De EXE bevat een form met twee buttons (Start en Stop).
Hier volgt de code voor de DLL (Project1)
1
2
3
4
5
6
7
8
9
10
11
| library Project1; uses SysUtils, Classes, Unit1 in 'Unit1.pas'; exports DoThreading; end. |
Hier volgt de code voor Unit1 van Project1.dll
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
| unit Unit1;
interface
uses
Classes;
type
TMyThread = class(TThread)
private
FMyInt64: Int64;
protected
procedure Execute; override;
constructor Create(const CreateSuspended: Boolean);
end;
function DoThreading(const OnTerminateProc: TNotifyEvent): TThread; stdcall;
implementation
function DoThreading;
begin
Result := TMyThread.Create(True);
with Result do
try
FreeOnTerminate := True;
OnTerminate := OnTerminateProc;
Resume;
except
raise;
end;
end;
constructor TMyThread.Create(const CreateSuspended: BOolean);
begin
inherited Create(CreateSuspended);
FMyInt64 := 0;
end;
procedure TMyThread.Execute;
begin
while not Terminated do
inc(FMyInt64);
end;
end. |
De code voor project2 (de EXE) is niet zo van belang behalve van Unit2 (Form2). Hier volgt de code van Unit2.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
| unit Unit2;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TForm2 = class(TForm)
Button1: TButton;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
FThread: TThread;
procedure ThreadTerminated(Sender: TObject);
public
{ Public declarations }
end;
function DoThreading(const OnTerminateProc: TNotifyEvent): TThread; stdcall;
var
Form2: TForm2;
implementation
{$R *.DFM}
procedure TForm2.Button1Click(Sender: TObject);
begin
FThread:=DoThreading(ThreadTerminated);
end;
procedure TForm2.ThreadTerminated(Sender: TObject);
begin
ShowMessage('Thread beëindigd');
end;
procedure TForm2.Button2Click(Sender: TObject);
begin
FThread.Terminate;
end;
function DoThreading; external 'Project1.dll';
end. |
Deze code heb ik getest en deze werkt goed. D.m.v. Start-knop (Button1) start het thread. D.m.v. Knop2 wordt het thread gestopt. Doordat het thread gestopt wordt (Terminate) wordt het OnTerminate-event afgevuurd (Form2.ThreadTerminated).
Suc6!
Het is bij mij wel het geval dat ik de aanroepende applicatie niet ken, laat staan bewerken. Mijn DLL export 1 functie die de aanroepende applicatie gebruikt.
"The shell stopped unexpectedly and Explorer.exe was restarted."
1
| Self.OnTerminate(Self); |
Dan wordt de OnTerminatie event goed getriggerd en wordt deze uitgevoerd in de MainForm.
Maar da's niet echt een prettige oplossing denk ik.
"The shell stopped unexpectedly and Explorer.exe was restarted."
Verwijderd
Inderdaad niet om de volgende 2 redenen:Op woensdag 20 februari 2002 11:26 schreef jelmervos het volgende:
Oja, het werkt allemaal wel goed als ik aan het einde vande Execute van de Thread dit doe:
code:
1 Self.OnTerminate(Self);
Dan wordt de OnTerminatie event goed getriggerd en wordt deze uitgevoerd in de MainForm.
Maar da's niet echt een prettige oplossing denk ik.
1) Je controleert niet (wordt niet uit stukje code duidelijk) of OnTerminate wel ge-assigned is, dus daarvan maken:
1
2
| if Assigned(OnTerminate) then OnTerminate(Self); |
2) Je vuurt nu zelf het event af terwijl het best zo kan zijn dat ook het basis (TThread) object ook nog eens het OnTerminate-event afvuurt. Dus daar kun je beter van maken:
1
2
3
| if Assigned(OnTerminate) then OnTerminate(Self); OnTerminate := nil; |
Hierdoor zal je event uitgevoerd worden en niet nog een keer door de basis TThread.
PS. Netjes is deze manier allerminst!
Oke, da's idd de perfect functie voor dit probleem.Op woensdag 20 februari 2002 11:41 schreef hvdberg het volgende:
[..]
PS. Netjes is deze manier allerminst!
Maar je hebt gelijk, het is niet netjes. Maar ik wilde het gewoon testen, en het bleek idd zo te zijn dat het zo wel werkt.
Nu de grote vraag? Waarom triggerd die TThread zelf niet die event na de Execute? Alles lijkt immers goed te werken?
Overigens wordt hij wel degelijk getriggerd, maar da's pas als ik bijvoorbeeld een ShowMessage (zie paar posts hierboven) doe in de MainForm? Waarom???
"The shell stopped unexpectedly and Explorer.exe was restarted."
"The shell stopped unexpectedly and Explorer.exe was restarted."
Verwijderd
Als je een dll gebruikt, hoe haal je dan de class uit die dll?
Maar alles wat je ziet is alleen binnen de DLL hoor. De aanroepende applicatie roept 1 funtie aan en die export mijn DLL dus.
Met de thread wil ik voorkomen dat de boel niet klem zit als hij een HTTP request doet.
"The shell stopped unexpectedly and Explorer.exe was restarted."
Verwijderd
De aanroepende applicatie is in die geval Winamp, het is namelijk een Winamp plugin.
"The shell stopped unexpectedly and Explorer.exe was restarted."
Verwijderd
Run->Parameters, selecteer WinAmp.exe als Host Application en ge kunt debuggen.
B.t.w. Heb je stdcall achter je functie gezet? Dit maakt namelijk behoorlijk uit wat betreft parameters! (zie mijn voorbeeld).
Maar het lost zo nog niks op natuurlijk. Verder zie ik als enige verschil met jou code en die van mij, dat jij een functie in je thread unit hebt die de thread maakt e.d. Ik doe dat gewoon in de MainForm unit, maakt dat veel uit?
Daardoor is er dus ook nergens plaats voor een stdcall achter een method.
"The shell stopped unexpectedly and Explorer.exe was restarted."
Verwijderd
Moet niet uitmaken. Stdcall zet je achter de functie welke je exporteert.Op donderdag 21 februari 2002 10:45 schreef jelmervos het volgende:
Heb Winamp aan mijn DLL gehangen voor debuggen, perfect!
Maar het lost zo nog niks op natuurlijk. Verder zie ik als enige verschil met jou code en die van mij, dat jij een functie in je thread unit hebt die de thread maakt e.d. Ik doe dat gewoon in de MainForm unit, maakt dat veel uit?
Daardoor is er dus ook nergens plaats voor een stdcall achter een method.
Maar ik denk niet dat dit veel uit gaat maken. Natuurlijk kun je wel tot op VCL TThread niveau debuggen door deze Unit (classes) aan je project (pas-file) toe te voegen.
Ik zit nu nog steeds met deze oplossing: aan het eind van de Execute procedure de OnTerminate van de thread 'handmatig' aanroepen.
Het is wel zo dat de thread PRIMA werkt in een gewone applicatie.
"The shell stopped unexpectedly and Explorer.exe was restarted."
Verwijderd
Most developers using Synchronize will not need to take any special action to make their code work with Delphi 6. Developers of a projects which do not use TApplication (such as ActiveX libraries), however, will need to call this manually or use something other than Synchronize.
Het gaat hier om een DLL applicatie die door Winamp wordt gebruikt als plugin.
"The shell stopped unexpectedly and Explorer.exe was restarted."
Verwijderd
Delphi 5 source code:
1
2
3
4
5
6
7
8
9
10
11
| procedure TThread.CallOnTerminate;
begin
{ Hier wordt de OnTerminate procedure dus aangeroepen }
if Assigned(FOnTerminate) then FOnTerminate(Self);
end;
procedure TThread.DoTerminate;
begin
{ En hier wordt gebruik gemaakt van Synchronize }
if Assigned(FOnTerminate) then Synchronize(CallOnTerminate);
end; |
Ik weet niet zeker of dit in D6 ook zo is, maar ik denk het wel. En doordat gebruik gemaakt wordt van Synchronize geeft dit problemen bij jou, omdat jij geen gebruik maakt van TApplication [zie de link die ik gaf]
Maar wat is dat de oplossing, want die Synchronize werkt dus niet. Dat verklaart ook waarom de thread wel werkt in een gewone applicatie.
"The shell stopped unexpectedly and Explorer.exe was restarted."
"The shell stopped unexpectedly and Explorer.exe was restarted."
Verwijderd
- CheckSynchronize aanroepen in de main-thread als de applicatie idle wordt, zodat de Synchronize werkt. Dit is denk ik niet zo simpel.
- OnTerminate aanroepen zonder Synchronize. Dit is wat je nu doet, geloof ik. Probleem is misschien dat de OnTerminate 2x aangeroepen wordt [2e mislukt omdat Synchronize niet werkt] Maar die 2e komt dus wel in een lijst terecht; en als om wat voor reden [patch van Borland :)] dan ook CheckSynchronize wordt aangeroepen wordt OnTerminate dus 2x aangeroepen.
- Zelf een nieuw event maken. Die aanroepen zonder Synchronize. Probleem is misschien dat je nu op moet letten wat je in die OnTerminate doet.
- Met PostMessage een message versturen naar jouw TMainForm. Dit is de veiligste oplossing.
"The shell stopped unexpectedly and Explorer.exe was restarted."