|
Registriert seit: 7. Jun 2003 Ort: Mülheim an der Ruhr 436 Beiträge Delphi 10.3 Rio |
#6
Ok also wie gesagt hab ich mich mal an den Thread gewagt und die Rückgabe in eine VirtualTable gelegt.
1. Problem, ich habe zur schnellen Überprüfung einfach ein DBGrid benutzt, manchmal jedoch nicht immer kommt es zu der Meldung: Canvas erlaubt kein zeichnen, gestoppt wird in VCL.Graphics Zeile 4197 Da weiss ich gerade auch nicht warum der Fehler auftritt. 2. Ein weiteres Problem zu dem es kommen kann und da wird es richtig wild, (Ich hab zum Test das Create, Start des Thread auf einen Button gelegt, wenn dieser aber zu schnell hintereinander geklickt wird haut es mir ziemlich viele Exceptions um die Ohren, angefangen von Blob Fehler bis hin zu (Gitternetz irgendwas, müsste ich noch mal genau nachsehen.) Gut das kann ich schnell umgehen in dem ich einfach den Button so lange sperre wie der separate Thread läuft. In der späteren Hauptanwendung benutze ich ein PageControl, welches beim Seitenwechsel quasi den Thread starten soll um die GUI (Grid und weitere DBControls) mit den Werten zu füllen, da müsste ich mal sehen wie ich das unterbinde das die Seiten zu schnell gewechselt werden. Dann schmeiss ich mal den Code hier rein und hoffe mir kann da jemand auf die Sprünge helfen bezüglich obiger Probleme (Ich geh einfach mal davon aus, mein Thread hat einen Designfehler oder die Hauptanwendung oder beides ![]() Die Anwendung:
Delphi-Quellcode:
Der Thread für die SQL Querys:
unit fmView;
interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Data.DB, Vcl.StdCtrls, Vcl.ComCtrls, MemDS, VirtualTable, DBAccess, Uni, Vcl.Grids, Vcl.DBGrids, QueryThread; type TView = class(TForm) DBGrid1: TDBGrid; UniDataSource1: TUniDataSource; VirtualTable1: TVirtualTable; StatusBar1: TStatusBar; MSGLog: TMemo; Label1: TLabel; btnStart: TButton; edtConStr: TEdit; edtSqlTxt: TEdit; QrPB: TProgressBar; procedure btnStartClick(Sender: TObject); procedure FormDestroy(Sender: TObject); private { Private-Deklarationen } FSQLThrd: TSqlQueryThrd; procedure OnThreadStatusMsg( const ThreadStatusMsgPtr: PThreadStatusdMsg); procedure OnThreadQueryStarted( const ThreadQueryStartedMsgPtr: PThreadQueryStartMsg); procedure OnThreadQueryDone( const ThreadQueryDoneMsgPtr: PThreadQueryDoneMsg); procedure OnThreadQueryRecCount( const ThreadQueryRecCountMsgPtr: PThreadQueryRecCountMsg); public { Public-Deklarationen } protected procedure WndProc(var AMsg: TMessage); override; end; var View: TView; implementation {$R *.dfm} procedure TView.btnStartClick(Sender: TObject); begin // I know this will leak at the moment! FSQLThrd := TSqlQueryThrd.Create(self.Handle, edtConStr.Text, edtSqlTxt.Text, VirtualTable1); try FSQLThrd.FreeOnTerminate := false; FSQLThrd.Start; except on E: Exception do MSGLog.Lines.Text := E.Message; end; end; procedure TView.FormDestroy(Sender: TObject); begin if assigned(FSQLThrd) then begin FSQLThrd.Terminate; FSQLThrd.WaitFor; FSQLThrd.Free; end; end; procedure TView.OnThreadQueryStarted( const ThreadQueryStartedMsgPtr: PThreadQueryStartMsg); begin case ThreadQueryStartedMsgPtr^.Running of true: QrPB.State := pbsNormal; false: QrPB.State := pbsPaused; end; Dispose(ThreadQueryStartedMsgPtr); end; procedure TView.OnThreadQueryDone( const ThreadQueryDoneMsgPtr: PThreadQueryDoneMsg); begin case ThreadQueryDoneMsgPtr^.Done of true: begin QrPB.State := pbsPaused; try UniDataSource1.DataSet.DisableControls; UniDataSource1.DataSet := VirtualTable1; // exception // VCL.Graphics line 4197 // EInvalidOperation: Canvas erlaubt kein zeichnen! DBGrid1.DataSource := UniDataSource1; finally UniDataSource1.DataSet.EnableControls; end; end; false: QrPB.State := pbsNormal; end; Dispose(ThreadQueryDoneMsgPtr); end; procedure TView.OnThreadStatusMsg( const ThreadStatusMsgPtr: PThreadStatusdMsg); begin MSGLog.Lines.Add( string.Format('%s %s %s', [ ThreadStatusMsgPtr^.ExecTime, ThreadStatusMsgPtr^.MessageStr, ThreadStatusMsgPtr^.SQLStr ])); Dispose(ThreadStatusMsgPtr); end; procedure TView.OnThreadQueryRecCount( const ThreadQueryRecCountMsgPtr: PThreadQueryRecCountMsg); begin Statusbar1.Panels[0].Text := string.Format('RecCount: %s' , [IntToStr(ThreadQueryRecCountMsgPtr^.ICount)]); Dispose(ThreadQueryRecCountMsgPtr); end; procedure TView.WndProc(var AMsg: TMessage); begin with AMsg do begin case Msg of TS_THREAD_STATUS_AVAILABLE: OnThreadStatusMsg( PThreadStatusdMsg(WParam)); TS_THREAD_QUERY_STARTED: OnThreadQueryStarted( PThreadQueryStartMsg(WParam)); TS_THREAD_QUERY_DONE: OnThreadQueryDone( PThreadQueryDoneMsg(WParam)); TS_THREAD_QUERY_COUNT: OnThreadQueryRecCount( PThreadQueryRecCountMsg(WParam)); end; end; inherited; end; end.
Delphi-Quellcode:
unit QueryThread;
interface uses System.Classes, System.SysUtils, Winapi.Windows, Winapi.Messages, UniProvider, MySQLUniProvider, Data.DB, DBAccess, Uni, MemDS, MemData, VirtualTable; // messages to be send to the main thread const TS_THREAD_STATUS_AVAILABLE = WM_USER + 1; TS_THREAD_QUERY_STARTED = WM_USER +2; TS_THREAD_QUERY_DONE = WM_USER +3; TS_THREAD_QUERY_COUNT = WM_USER +4; type TThreadStatusMsg = record MessageStr: string; SQLStr: string; ExecTime: String; end; PThreadStatusdMsg = ^TThreadStatusMsg; TThreadQueryStartMsg = record Running: Boolean; end; PThreadQueryStartMsg = ^TThreadQueryStartMsg; TThreadQueryDoneMsg = record Done: Boolean; end; PThreadQueryDoneMsg = ^TThreadQueryDoneMsg; TThreadQueryRecCountMsg = record ICount: Int64; end; PThreadQueryRecCountMsg = ^TThreadQueryRecCountMsg; type TSqlQueryThrd = class(TThread) protected procedure Execute; override; procedure OnAfterFetchEvent(DataSet: TCustomDADataSet); procedure OnAfterOpen(DataSet: TDataSet); private FMainHandle: THandle; FSQLText: string; fStatusText: string; FUniDacConnection: TUniConnection; FUniDacSQLQuery: TUniQuery; FVirtualTable: TVirtualTable; ThreadStatusMsgPtr: PThreadStatusdMsg; ThreadQueryStartedMsgPtr: PThreadQueryStartMsg; ThreadQueryDoneMsgPtr: PThreadQueryDoneMsg; ThreadQueryRecCountMsgPtr: PThreadQueryRecCountMsg; function GetLongTime(aTime: TDatetime) : string; public constructor Create(aMainHandle: THandle; aConnectionStr: string; aSQL: string; aVTable: TVirtualTable); overload; destructor Destroy; override; end; implementation { TSqlQueryThrd } constructor TSqlQueryThrd.Create(aMainHandle: THandle; aConnectionStr, aSQL: string; aVTable: TVirtualTable); begin inherited Create(True); // main thread handle FMainHandle := aMainHandle; FUniDacConnection := TUniConnection.Create(nil); FUniDacConnection.ProviderName := 'MySQL'; FUniDacConnection.ConnectString := aConnectionStr; Assert(aConnectionStr <> '', 'Connection-String can not be empty!'); // test if connection is successful otherwise goodbye try try FUniDacConnection.Connect; except terminate; end; finally // can be disconnected the unidac query established a connection by itself if needed FUniDacConnection.Disconnect; end; FSQLText := aSQL; Assert(FSQLText <> '', 'SQL-Text can not be empty!'); FUniDacSQLQuery := TUniQuery.Create(nil); FUniDacSQLQuery.Connection := FUniDacConnection; FUniDacSQLQuery.FetchingAll; FUniDacSQLQuery.AfterFetch := OnAfterFetchEvent; FUniDacSQLQuery.AfterOpen := OnAfterOpen; FUniDacSQLQuery.SQL.Clear; FVirtualTable := aVTable; end; destructor TSqlQueryThrd.Destroy; begin if FUniDacConnection.Connected then FUniDacConnection.Disconnect; FUniDacConnection.Free; FUniDacSQLQuery.Free; inherited Destroy; end; procedure TSqlQueryThrd.Execute; begin NameThreadForDebugging('StormThread'); FUniDacSQLQuery.SQL.Text := FSQLText; // send the main thread some messages... New(ThreadQueryStartedMsgPtr); ThreadQueryStartedMsgPtr^.Running := True; if not(PostMessage(FMainHandle, TS_THREAD_QUERY_STARTED, integer(ThreadQueryStartedMsgPtr), 0)) then begin Dispose(ThreadQueryStartedMsgPtr); terminate; end; New(ThreadStatusMsgPtr); ThreadStatusMsgPtr^.MessageStr := 'Status: Query started'; ThreadStatusMsgPtr^.ExecTime := GetLongTime(Now); ThreadStatusMsgPtr^.SQLStr := ''; if not(PostMessage(FMainHandle, TS_THREAD_STATUS_AVAILABLE, integer(ThreadStatusMsgPtr), 0)) then begin Dispose(ThreadStatusMsgPtr); terminate; end; try try FUniDacSQLQuery.Execute; // Must be set here otherwise the virtualtable in the main thread // does not contain any data! FVirtualTable.Assign(FUniDacSQLQuery); except FUniDacSQLQuery.Close; FUniDacConnection.Close; terminate; end; finally FUniDacSQLQuery.Close; FUniDacConnection.Disconnect; terminate; end; end; function TSqlQueryThrd.GetLongTime(aTime: TDatetime): string; var formattedDate : string; begin Result :=''; try DateTimeToString(formattedDate, 'hh:nn:ss:zz', aTime); finally Result := FormattedDate; end; end; // special for unidac if that event is raised the query is done as written in the docu... procedure TSqlQueryThrd.OnAfterFetchEvent(DataSet: TCustomDADataSet); begin New(ThreadStatusMsgPtr); ThreadStatusMsgPtr^.MessageStr := 'Status: Query done'; ThreadStatusMsgPtr^.ExecTime := GetLongTime(Now); ThreadStatusMsgPtr^.SQLStr := '// ' + FSQLText; if not(PostMessage(FMainHandle, TS_THREAD_STATUS_AVAILABLE, integer(ThreadStatusMsgPtr), 0)) then begin Dispose(ThreadStatusMsgPtr); terminate; end; New(ThreadQueryDoneMsgPtr); ThreadQueryDoneMsgPtr^.Done := True; if not(PostMessage(FMainHandle, TS_THREAD_QUERY_DONE, integer(ThreadQueryDoneMsgPtr), 0)) then begin Dispose(ThreadQueryDoneMsgPtr); terminate; end; end; // this event is raised when the record count is available procedure TSqlQueryThrd.OnAfterOpen(DataSet: TDataSet); begin New(ThreadQueryRecCountMsgPtr); ThreadQueryRecCountMsgPtr^.ICount := FUniDacSQLQuery.RecordCount; if not(PostMessage(FMainHandle, TS_THREAD_QUERY_COUNT, integer(ThreadQueryRecCountMsgPtr), 0)) then begin Dispose(ThreadQueryRecCountMsgPtr); terminate; end; end; end. |
![]() |
Ansicht |
![]() |
![]() |
![]() |
ForumregelnEs ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.
BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus. Trackbacks are an
Pingbacks are an
Refbacks are aus
|
|
Nützliche Links |
Heutige Beiträge |
Sitemap |
Suchen |
Code-Library |
Wer ist online |
Alle Foren als gelesen markieren |
Gehe zu... |
LinkBack |
![]() |
![]() |