Autor Beitrag
Steve1024
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 141

Windows 2K, XP, 7 & Server 2003 - 2008; Linux (Ubuntu, Fedora)
D7, D05, D06, D09, DXE
BeitragVerfasst: Mo 18.02.08 17:53 
Hi,

ich habe da mal wieder ein Problem :-)
Und zwar habe ich eine Anwendung geschrieben, die mehrere Messgeräte ansteuern soll. Nun habe ich aber das Problem, dass ich immer eine Access violation bekomme.

Der Aufbau der Anwendung ist wie folgt:
- Hauptprogramm lädt Treiber
- Jeder Treiber hat ein Status-Fentser in welchem die Ergebnisse angezeigt werden
( habe den Source mal zusätzlich angehängt )
- Jeder Treiber wird über eigene Threads angesteuert, um ein synchrones messen zu ermöglichen

Hat von euch vielleicht jemand eine Idee warum das nicht funktioniert??? Das merkwürdige daran ist, ist dass ich eine andere Anwendung habe, wo das gnaze funktioniert. Zusätzlich sogar ohne dass ich CriticalSection's benutze.

Wäre echt super wenn da jemand eine Idee hat (bin seit 2 wochen am verzweifeln)

Gruß Steve

PS: Bitte kompiliert alles und registriert die einzelnen .dll dateien mit regsvr32 (REG unter HKEY_CURRENT_USER) - Danke!
Einloggen, um Attachments anzusehen!
Narses
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Administrator
Beiträge: 10183
Erhaltene Danke: 1256

W10ent
TP3 .. D7pro .. D10.2CE
BeitragVerfasst: Mo 18.02.08 18:16 
Moin!

user profile iconSteve1024 hat folgendes geschrieben:
Jeder Treiber wird über eigene Threads angesteuert, um ein synchrones messen zu ermöglichen
Ohne den Code gesehen zu haben - in den Threads greifst du aber nicht schreibend auf VCL-Komponenten zu, oder? Wenn ja, dann ist hier der "Fehler", da die VCL nicht threadsave ist und es so zu unvorhersagbaren Effekten kommt (bis hin zum Crash). :idea:

cu
Narses

_________________
There are 10 types of people - those who understand binary and those who don´t.
Steve1024 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 141

Windows 2K, XP, 7 & Server 2003 - 2008; Linux (Ubuntu, Fedora)
D7, D05, D06, D09, DXE
BeitragVerfasst: Mo 18.02.08 18:46 
Doch das tu ich. Aber wie gesagt, ich habe ein Projekt bei dem funktioniert das ganze. Zusätzlich habe ich alle VCL Operationen in CriticalSection's eingebaut und in try except - leider ohne Erfolg :'(

Hast du denn einen anderen Vorschlag, das mit dem Status zu realisieren abgesehen von VCL?! - Für Tipps bin ich immer offen. - danke!
Narses
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Administrator
Beiträge: 10183
Erhaltene Danke: 1256

W10ent
TP3 .. D7pro .. D10.2CE
BeitragVerfasst: Mo 18.02.08 18:54 
Moin!

user profile iconSteve1024 hat folgendes geschrieben:
Doch das tu ich.
Dann ist hier der Fehler. :|

user profile iconSteve1024 hat folgendes geschrieben:
Aber wie gesagt, ich habe ein Projekt bei dem funktioniert das ganze.
Hm... :? empirische Informatik nach dem "funktioniert-doch-Prinzip"... :roll: normalerweise höre ich hier auf zu diskutieren. 8)

user profile iconSteve1024 hat folgendes geschrieben:
Zusätzlich habe ich alle VCL Operationen in CriticalSection's eingebaut und in try except - leider ohne Erfolg :'(

Hast du denn einen anderen Vorschlag,
Ja ;) du musst die Zugriff auf die VCL mit der Methode Synchronize() im Thread mit dem Hauptthread synchronisieren; an dieser Stelle hilft eine CS und try-except nix. :idea:

cu
Narses

_________________
There are 10 types of people - those who understand binary and those who don´t.
Steve1024 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 141

Windows 2K, XP, 7 & Server 2003 - 2008; Linux (Ubuntu, Fedora)
D7, D05, D06, D09, DXE
BeitragVerfasst: Mo 18.02.08 18:56 
Synchronisize habe ich auch schon versucht. Das Problem dabei ist aber, dass sich dann das ganze Programm aufhängt, da das "Application"-Objekt aus einer DLL auf ausgerufen wird und somit der MainThread auf diese Ausführung wartet (d.h. MainThread wartet auf Application und Application auf MainThread).
Narses
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Administrator
Beiträge: 10183
Erhaltene Danke: 1256

W10ent
TP3 .. D7pro .. D10.2CE
BeitragVerfasst: Mo 18.02.08 19:04 
Moin!

user profile iconSteve1024 hat folgendes geschrieben:
Synchronisize habe ich auch schon versucht.
Fein, dann hast du ja schon einen Ansatz. ;)

user profile iconSteve1024 hat folgendes geschrieben:
Das Problem dabei ist aber, dass sich dann das ganze Programm aufhängt, da das "Application"-Objekt aus einer DLL auf ausgerufen wird und somit der MainThread auf diese Ausführung wartet (d.h. MainThread wartet auf Application und Application auf MainThread).
Dann ist dein DLL-Konzept (bzw. das Interface dessen) wurstig. :nixweiss: Sorry, aber anders als per Synchronize()darfst du einfach nicht auf die VCL aus einem Thread heraus zugreifen. :mahn:

cu
Narses

_________________
There are 10 types of people - those who understand binary and those who don´t.
Steve1024 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 141

Windows 2K, XP, 7 & Server 2003 - 2008; Linux (Ubuntu, Fedora)
D7, D05, D06, D09, DXE
BeitragVerfasst: Mo 18.02.08 19:09 
Naja gut, das wäre zumindest ein Ansatz beim Anzeigen des Status. Aber auch wenn keine Anzeige von statten geht, sondern ich nur mit der Maus drüber fahre bekomme ich diese Access Violation. Die Fenster werden dabei aber vom MainThread erzeugt bzw. angezeigt.

Hast du dafür auch einen Idee?
Narses
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Administrator
Beiträge: 10183
Erhaltene Danke: 1256

W10ent
TP3 .. D7pro .. D10.2CE
BeitragVerfasst: Mo 18.02.08 19:22 
Moin!

user profile iconSteve1024 hat folgendes geschrieben:
Naja gut, das wäre zumindest ein Ansatz beim Anzeigen des Status. Aber auch wenn keine Anzeige von statten geht, sondern ich nur mit der Maus drüber fahre bekomme ich diese Access Violation. Die Fenster werden dabei aber vom MainThread erzeugt bzw. angezeigt.
Es spielt keine (so große) Rolle, wer das Fenster erzeugt hat, sondern ob es ausschließlich vom Hauptthread bearbeitet wird (was Synchronize sicherstellt). :idea:

user profile iconSteve1024 hat folgendes geschrieben:
Hast du dafür auch einen Idee?
Sorry, aber das geht zu tief in dein Programm-Konzept rein, da kann ich nix aus dem Handgelenk schütteln... :| Es sieht aber so aus, als ob du die GUI nicht sauber (genug?) von der Funktionalität getrennt hast. :nixweiss:

Wenn es nur um´s Lesen aus VCL-Kompos geht, könnte man das noch mit einer CS gekapselt durchgehen lassen, aber Probleme gibt es definitiv beim Schreiben aus einem anderen Thread, als dem Hauptthread, egal ob mit CS oder ohne oder sonstwas. :idea:

cu
Narses

_________________
There are 10 types of people - those who understand binary and those who don´t.
Steve1024 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 141

Windows 2K, XP, 7 & Server 2003 - 2008; Linux (Ubuntu, Fedora)
D7, D05, D06, D09, DXE
BeitragVerfasst: Mo 18.02.08 21:55 
Hallo... habe jetzt nochmals nachgeforscht....
nachdem ich ja alles out-gesourced habe (welches bei dem anderen Projekt nicht der Fall war) ist mir durch langes und mühsames stöbern die varibale "IsMultiThread" aufgefallen. Die in diesem Projekt nur in einer DLL auf TRUE gesetzt wird. Denn die BeginThread prozedur macht das (somit nur die bvmsdt.dll).

Weiss jemand wozu diese Variable da ist?? Ich habe die jetzt mal in jeder DLL beim init auf TRUE gesetzt...
Werde dann mal sehen ob es nun geht... könnte das sein?!

Werde meine Ergebnisse posten, ok?! - cu
Steve1024 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 141

Windows 2K, XP, 7 & Server 2003 - 2008; Linux (Ubuntu, Fedora)
D7, D05, D06, D09, DXE
BeitragVerfasst: Di 19.02.08 18:56 
So... also... nun funktioniert alles wunderbar.
Diese Variable ist scheinbahr für den Mem-Manager sehr wichtig. Naja, wie dem auch sei...

Nochmals danke an alle :-)
Motzi
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2931

XP Prof, Vista Business
D6, D2k5-D2k7 je Prof
BeitragVerfasst: Sa 23.02.08 16:45 
Auch wenn das Thema schon erledigt ist, nur nochmal kurz zur Klarstellung.
IsMultiThread macht die Speicherverwaltung threadsafe, daher sollte man immer BeginThread statt CreateThread verwenden (sonst wird diese Variable nicht auf true gesetzt und der MemoryManager weiß nicht, dass er eigentlich threadsafe arbeiten muss).

Der Zugriff auf VCL-Objekte muss immer im primären Thread stattfinden! Der Grund ist ganz einfach der, dass die VCL nicht threadsafe ist. Es ist daher egal ob du Critical Sections oder andere Synchronisationsmechanismen verwendest, auf das was intern in der VCL abläuft hast du keinen Einfluss! Die einfachste Möglichkeit ist "Synchronize" zu verwenden, was ich jedoch auch gerne mach ist mit SendMessage zu arbeiten. Die Message wird vom betroffenen Fenster verarbeitet (im primären Thread) und da SendMessage den aufrufenden Thread solange blockiert bis die Message verarbeitet wurde ist alles schön synchronisiert. Dieses Konzept ist sehr flexibel, so lassen sich zB auch Funktionszeiger übergeben etc. Bei Interesse kann ich ein paar Codeschnipsel zeigen die ich mal geschrieben hab.

Gruß, Motzi

_________________
gringo pussy cats - eef i see you i will pull your tail out by eets roots!
Steve1024 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 141

Windows 2K, XP, 7 & Server 2003 - 2008; Linux (Ubuntu, Fedora)
D7, D05, D06, D09, DXE
BeitragVerfasst: Sa 23.02.08 18:43 
Das wusste ich nicht (das mit dem SendMessage). Geht dann also auch PostMessage? Bei mir ist es sehr wichtig, dass der aufrufende Thread nicht gestoppt wird.

Wäre echt super wenn du dazu ein code schnipsel hättest. Würde mich echt interesieren...

Vielen Dank!
Motzi
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2931

XP Prof, Vista Business
D6, D2k5-D2k7 je Prof
BeitragVerfasst: Sa 23.02.08 19:23 
Es gibt zwei Gründe warum ich Synchronize nicht mag:
1) Synchronize benötigt TThread -> kein TThread - kein Synchronize. Ich verwende üblicherweise aber lieber BeginThread und musste mir daher ein anderes Konzept überlegen, das ähnlich arbeitet wie Synchronize.
2) Synchronize nimmt nur einen einzigen Parameter vom Typ TThreadMethod welcher als procedure of object deklariert ist, also eine Methode mit leerer Parameterliste. Wenn ich also eine Methode synchronisiert aufrufen will und dieser Methode Parameter mitgeben will, dann bin ich aufgeschmissen. Die einzige Möglichkeit ist alle potenziellen Parameter in Felder meiner Thread-Klasse abzulegen, damit ich in der Methode dann darauf zugreifen kann.

Ich hab mir daher folgende Unit geschrieben:
ausblenden volle Höhe Delphi-Quelltext
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:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
unit lqsThreadSync;

interface

uses
  Windows, Messages, Classes, SysUtils;

type
  PSyncMethod = ^TSyncMethod;
  TSyncMethod = procedure () of object;
  PSyncParamMethod = ^TSyncParamMethod;
  TSyncParamMethod = procedure (pData: Pointer) of object;
  
  TSyncWindow = class(TObject)
  protected
    class procedure SyncWndProc(var Msg: TMessage);
  public
    class procedure SyncMethodCall(const method: TSyncMethod); overload;
    class procedure SyncMethodCall(const method: TSyncParamMethod; param: Pointer); overload;
  end;

  function SyncWindow: HWND;

const
  WM_SYNCMETHODCALL = WM_USER + 1;
  WM_SYNCPARAMMETHODCALL = WM_USER + 2;
  
implementation

var
  _SyncWindow: HWND = 0;
  _PrimaryThreadID: DWord;

function SyncWindow: HWND;
begin
  if _SyncWindow = 0 then
  begin
    if GetCurrentThreadId = _PrimaryThreadID then
      _SyncWindow := AllocateHWnd(TSyncWindow.SyncWndProc)
    else
      raise Exception.Create('SyncWindow must be created in primary thread');
  end;

  Result := _SyncWindow;
end;


////////////////////////////////////////////////////////////////////////////////


{ TSyncWindow }

class procedure TSyncWindow.SyncMethodCall(const method: TSyncMethod);
begin
  SendMessage(SyncWindow, WM_SYNCMETHODCALL, DWord(@@method), 0);
end;


////////////////////////////////////////////////////////////////////////////////


class procedure TSyncWindow.SyncMethodCall(const method: TSyncParamMethod; param: Pointer);
begin
  SendMessage(SyncWindow, WM_SYNCPARAMMETHODCALL, DWord(@@method), DWord(param));
end;


////////////////////////////////////////////////////////////////////////////////


class procedure TSyncWindow.SyncWndProc(var Msg: TMessage);
begin
  Msg.Result := DefWindowProc(_SyncWindow, Msg.Msg, Msg.WParam, Msg.LParam);
  case Msg.Msg of
    WM_SYNCMETHODCALL : PSyncMethod(Msg.WParam)^();
    WM_SYNCPARAMMETHODCALL : PSyncParamMethod(Msg.WParam)^(Pointer(Msg.LParam));
  end;
end;


////////////////////////////////////////////////////////////////////////////////


initialization
  _PrimaryThreadID := GetCurrentThreadId;

finalization
  if _SyncWindow <> 0 then
    DeallocateHWnd(_SyncWindow);
end.


Über TSyncWindow.SyncMethodCall(..) kann ich so eine Methode im Kontext des primären Threads ausführen, wobei es zwei Möglichkeiten gibt - eine Methode ohne Parameter (so wie bei TThread.Synchronize) oder eine Methode mit einem Parameter vom Typ Pointer. Über diesen Pointer können alle möglichen beliebigen Konstrukte übergeben werden - die aufzurufende Methode muss natürlich wissen was für Daten das sind und wie sie damit umgehen muss.

Das Konzept ist sehr allgemein gehalten, du kannst aber natürlich direkt Messages an deine MainForm schicken sofern du sie dort korrekt behandelst. Oft ist es auch gar nicht notwendig irgendwelche Methoden auszuführen sondern will zB nur ein StatusUpdate machen. Das geht dann zB so:
ausblenden Schnipsel aus einem meiner alten Projekte
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
// SetMaxOverallProgress wird direkt aus einem abgespaltenen Thread aufgerufen -> wird also im Kontext dieses Threads ausgeführt -> Synchronisation notwenig
procedure TMainForm.SetMaxOverallProgress(processDataCount: DWord; fileListDataCount: DWord);
begin
  SendMessage(Handle, GM_SETMAXOVERALLPROGRESS, processDataCount, fileListDataCount);
end;

procedure TMainForm.GMSetMaxOverallProgress(var Msg: TMessage);
begin
  TotalProgressBar.Max := (
      Msg.WParam + // ProcessDataCount
      Msg.LParam + // FileListDataCount
    ) * 100;
end;

In diesem Fall war es wichtig, dass der Thread angehalten wird bis die Message verarbeitet wird. Während der Thread seine Arbeit erledigt werden jedoch immer wieder StatusUpdates gemacht, die den Thread nicht blockieren sollen - hier kann man PostMessage verwenden, muss jedoch sehr genau auf Nebeneffekte aufpassen und daher genau wissen was man macht (Stichwort Strings)!!!
ausblenden Schnipsel aus demselben Projekt
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
// Diese Methode wird ebenfalls direkt aus dem Thread heraus aufgerufen
procedure TMainForm.UpdateProcessDetails(const details: String; progress: DWord);
begin
  // "details" ist eine Variable vom Typ string -> Delphi kümmert sich selbst um die Speicherverwaltung dieser Strings per Referenzzähler.
  // Ich kann nicht einfach nur einen Zeiger auf diesen String übergeben, da der Referenzzähler sonst nicht erhöht wird und der String freigegeben wird
  // bevor die Message verarbeitet wird. Durch den expliziten Zugriff auf die Adresse des ersten Zeichen des Strings fügt der Compiler automatisch einen Aufruf von
  // UniqueString ein, wodurch ein nagelneuer String mit demselben Inhalt und einem Referenzzähler von 1 erzeugt wird.
  // Details dazu gibts in meinem String-Tutorial unter www.manuel-poeter.de
  PostMessage(Handle, GM_UPDATECURRENTPROCESS, Integer(@details[1]), progress);
end;

procedure TMainForm.GMUpdateCurrentProcess(var Msg: TMessage);
begin
  ProcessDetailsLabel.Caption := PChar(Msg.WParam);
  if Msg.LParam <> -1 then
  begin
    CurrentProgressBar.Position := Msg.LParam;
    TotalProgressBar.Position := ((FActionCount - 1) * 100) + DWord(Msg.LParam);
  end;
end;


Gruß, Motzi

_________________
gringo pussy cats - eef i see you i will pull your tail out by eets roots!