Autor |
Beitrag |
alzaimar
      
Beiträge: 2889
Erhaltene Danke: 13
W2000, XP
D6E, BDS2006A, DevExpress
|
Verfasst: Di 29.11.05 23:52
Ich habe hier eine DLL, die mir COM-Objekte liefert. Irgendwo ist der Wurm drin und ich suche schon seit 2 Jahren, finde den Fehler einfach nicht. Neuschreiben geht nicht, weil 100.000 Zeilen. FastMM4, MemProof etc liefern keine Lösung.
Das Problem ist nun so eingekreist bzw. verlagert, das der Client beim Aufruf von CoUninitialize im Finalization-Teil von ComObjs auftritt, also nur dann, wenn das Programm sowieso terminiert. Nun, man soll ja nicht mauscheln, aber mittlerweile ist mir das sowas von egal, ich will nur diese dämlichen Fehler beim Beenden der Client-Anwendung unterdrücken, unterbinden, unsichtbar machen, mir ist das Schnurz!  ! Ich will nur, das mein Programm terminiert, und aus dem Speicher verschwindet.
Was kann man tun? Kann man einen Stub schreiben, der die Anwendung aufruft und diese dann einfach wegschnippelt (so, wie der Taskmanager, wenn man den Prozess abschiesst?)
Hilfe, helft einem armen alten Mann kurz vor Weihnachten,
Ich verspreche, demjenigen, der mir eine von mir aus total perverse Lösung  anbietet, das hier:  und ich werde ihn niemals nie  und immer  , wenn er mir übern Weg läuft und artig will ich auch sein und immer aufessen, aber bitte
heeeelllfttt mir!
Und mein Motto ändere ich auch, dann das hier, stimmt nicht mehr: 
_________________ Na denn, dann. Bis dann, denn.
|
|
digi_c
      
Beiträge: 1905
W98, XP
D7 PE, Lazarus, WinAVR
|
Verfasst: Mi 30.11.05 14:18
Hehe so einen Post mit soo vielen Smileys habe ich ja lange nciht mehr gesehen
Das hört sich so an als ob da Speicher durch das ActiveX freigegeben werden soll, der garnicht dem ActiveX gehört sondern z.B. deinem Programm.
Die COM hast du auch geschrieben? Mit Delphi oder MSC++?
Wäre Code möglich bei welcher Anweisung der Anwendung/COM es kracht?
|
|
alzaimar 
      
Beiträge: 2889
Erhaltene Danke: 13
W2000, XP
D6E, BDS2006A, DevExpress
|
Verfasst: Mi 30.11.05 14:32
Hi,
Danke für die Anteilnahme. Gestern abend war ich mit den Nerven am Ende. In meiner Verzeiflung hab ich dann diesen Post geschrieben.
Ich habe den COM-Server selbst gebastelt.Eventuell ist der Fehler im Server, aber eine andere Anwendung läuft wesentlich stabiler Die Zeile, bei der es kracht ist im Client in ComObj.Pas:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9:
| finalization begin OleUninitializing := True; ComClassManagerVar.Free; SafeCallErrorProc := nil; DispCallByIDProc := nil; VarDispProc := nil; if NeedToUninitialize then CoUninitialize; end; |
Da ich nicht genau weiss, was CoUninitialize macht, weiss ich natürlich auch nicht genau, wo der Fehler ist. Ich vermute aber, das irgendwelche COM-Objekte noch nicht freigegeben sind.
Nochwas passiert: Normalerweise ist es doch so, das COM die garbagecollection übernimmt, also:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7:
| Var Foo : IMyObject;
Begin Foo := MyServer.GetObject; Foo.Bar := 'Bla'; End; |
Und Foo wird automatisch freigegeben, gell? Leider ist das an einigen Stellen nicht so. Ich vermute, das COM mit den Refcounts durcheinander kommt, aber bis ich das finde, vergehen wieder Jahre. Und ich hab Frau und Kinder und will die aufwachsen sehen.
Hier ist so eine Code-Stelle, bei der die Garbagecollection nicht klappt. Ich ziehe mir eine Liste von Dingern (aTemplates) und schreibe die Beschreibung ('Description') sowie eine ID jedes Objektes (in aXLS kopiert) in die Items-Property einer ComboBox (von DevExpress, deshalb 'edTemplates.Properties.Items'. Kann es sein, das hier der RefCount durcheinandergerät?
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19:
| Var aTemplates : IdboObjects; clID, i : Integer; aXls : IdboObject; clCoil, aClass : IdboClass;
... With edTemplates.Properties.LookupItems do begin Clear; aTemplates := DM.cdb.dboObjects ['excelTemplate']; AddObject('(use default)', nil); for i:=0 to aTemplates.Count - 1 do begin axls := aTemplates.Items [i]; AddObject(aXLS.dboValue ['Description'].DisplayText, Pointer (aXLS.id)); End; End; edTemplates.ItemIndex := 0; end; |
Wenn ich den Fehler finde, ist das Produkt so gut, das man damit mal hausieren gehen könnte, aber so ist es auf Haufen Mist. Es ist eine objektorientierte Datenbank mit eigener Engine und optimalen Recherchemöglichkeiten (Zeig mir alle Objekte, deren 'Fuss' grün ist oder die ein M5-'Gewinde' haben).
Ich hab mir jetzt einen 'Sentinel' geschrieben, der einfach im Hintergrund läuft und auf eine Message wartet. Dann schiesst er den Prozess, der ihm die Message geschickt hat, einfach ab. Meine Schrott-Anwendung sucht im FormClose des Hauptformulares also nach dem Sentinel und schickt ihm die Message 'Töte mich' zusammen mit dem eigenen Handle. So merkt der User wenigstens nicht mehr, das mein Programm Müll ist.
Wie gesagt, an sich läuft es, nur beim Beenden knallt es weg. Aber so richtig.
_________________ Na denn, dann. Bis dann, denn.
|
|
digi_c
      
Beiträge: 1905
W98, XP
D7 PE, Lazarus, WinAVR
|
Verfasst: Do 01.12.05 10:05
Uff zu komplex für mich, da habe ich deutlich zu wenig Erfahrung mit COM :/
|
|
noidic
      
Beiträge: 851
Win 2000 Win XP Vista
D7 Ent, SharpDevelop 2.2
|
Verfasst: Do 01.12.05 10:24
Hi!
Ich hab zwar null Erfahrung mit eigenen COM-Servern, aber was mir bei ähnlichen Problemen mit MAPI geholfen hat, war vor das CoUninitialize ein Sleep von 100ms zu setzen. Vielleicht tuts das ja bei dem Problem auch?
Gruß
noidic
_________________ Bravery calls my name in the sound of the wind in the night...
|
|
alzaimar 
      
Beiträge: 2889
Erhaltene Danke: 13
W2000, XP
D6E, BDS2006A, DevExpress
|
Verfasst: Do 01.12.05 10:34
Probiere ich mal, danke
_________________ Na denn, dann. Bis dann, denn.
|
|
Horst_H
      
Beiträge: 1654
Erhaltene Danke: 244
WIN10,PuppyLinux
FreePascal,Lazarus
|
Verfasst: Do 01.12.05 11:32
Hallo,
Ich habe mal bestimmte Verzeichnisse gesucht:
msdn.microsoft.com/l...alfolderlocation.asp
Dort steht etwas:
Quelltext 1: 2: 3: 4: 5: 6: 7:
| Syntax
WINSHELLAPI HRESULT WINAPI SHGetSpecialFolderLocation( HWND hwndOwner, int nFolder, LPITEMIDLIST* ppidl ); |
und speziell:
Quelltext 1: 2: 3: 4: 5: 6:
| ppidl Pointer to a pointer to an item identifier list specifying the folder's location relative to the root of the namespace (the desktop). The calling application is responsible for freeing this pointer with the shell's IMalloc interface (see SHGetMalloc).in http://msdn.microsoft.com/library/en-us/wceappservices5/html/wce50lrfIMallocIUnknown.asp |
Vielleicht musst Du den Speicher selbst freigeben:
IMalloc
Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
| When to Implement
In general, you should not implement IMalloc, instead use the COM implementation, which is guaranteed to be thread-safe in managing task memory. :::::::::::::::::::::::: You get a pointer to the COM task allocator object's IMalloc through a call to the CoGetMalloc function. :::::::::::::::::::::::: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wceappservices5/html/wce50lrfcogetmalloc.asp |
Gruss Horst
|
|
alzaimar 
      
Beiträge: 2889
Erhaltene Danke: 13
W2000, XP
D6E, BDS2006A, DevExpress
|
Verfasst: Do 01.12.05 12:21
Hallo Horst, Ich alloziiere im Laufe einer Session ca. 10.000 Objekte, die Alle, bis auf ein Paar, wieder freigegeben werden. COM implementiert eine eigene Garbage Collection: Sobald ein COM-Objekt nicht mehr referenziert wird, wird der Speicher freigegeben.
_________________ Na denn, dann. Bis dann, denn.
|
|
opfer.der.genauigkeit
      
Beiträge: 754
Erhaltene Danke: 1
|
Verfasst: Do 01.12.05 13:05
Hallo alzaimar,
Delphi-Quelltext 1:
| aTemplates := DM.cdb.dboObjects ['excelTemplate']; |
aTemplates ist eine Referenz auf das Objekt in DM.cdb.dboObjects.
D.h. nachdem die Routine abgearbeitet wurde in der du das benutzt, wird das Objekt natürlich nicht freigegeben, da es ja zu dboObjects gehört. Höchstens wird aTemplates auf nil gesetzt.
Evtl. ist das aber nicht der Fall und deshalb wird RefCount auch nicht dekrementiert???
Bin mir nicht sicher. Normalerweise würde inc( RefCount ) ja nur beim Aufruf über einen Konstruktor oder eine Initialisierung erfolgen. Oder irre ich?
Setz aTemplates doch mal auf nil.
Is evtl. nur n Schuß ins Blaue.. aber bevor du garnix mehr als Alternative hast...
Edit:// Oder die Referenz hängt noch in der Kompo von DevExpress fest. Weiß ja nicht, wo du das dort eingehängt hat. Aber meine Erfahrung war immer, daß die meisten Komponenten von DevExpress die Verwaltung der Objekte selber übernehmen.
_________________ Stellen Sie sich bitte Zirkusmusik vor.
|
|
alzaimar 
      
Beiträge: 2889
Erhaltene Danke: 13
W2000, XP
D6E, BDS2006A, DevExpress
|
Verfasst: Do 01.12.05 15:49
Ich glaube nicht, das die DevEx Komponente das Interface 'festhält'. cdb.dboObjects['Something'] liefert eine Liste von IdboObject Objekten, analog zu einem ADO.Recordset. Diese Liste (und die dadrin enthaltenen IdboObject Objekte, und die in jedem IdboObject enthaltenen IdboValue-Objekte) wird dynamisch erzeugt. Sie enthält im geposteten Beispiel für jeses Objekt zwei Werte: 'Description' und 'ID'.
Ich verwende die LookupItems-Eigenschaft (TStrings) einer Combobox um sowohl die 'Description' als auch die 'ID' zu speichern. Wenn man dann einen Eintrag auswählt, habe ich sofort die korrespondierende ID.
Ich kann mir nicht vorstellen, wo das Interface noch gehalten wird, außer, Delphi generiert bei einem Typecast Müll, und zwar hier:
Delphi-Quelltext 1: 2: 3: 4: 5: 6:
| Var aXLS : IdboObject;
... Pointer(aXLS.ID); ... |
Ich sach ja, es ist zum 
_________________ Na denn, dann. Bis dann, denn.
|
|
opfer.der.genauigkeit
      
Beiträge: 754
Erhaltene Danke: 1
|
Verfasst: Do 01.12.05 16:26
Bevor du in deinem großen Projekt noch weiter debuggst, bau doch einen kleinen Testprototypen auf um die Vorgehensweise nachvollziehen zu können.
Evtl. liegt dort garnicht der Wurm, sondern ist nur ein Seiteneffekt.
//Edit: Alternativ kannst du mir ja den Quellcode schicken, damit ich mal ne Runde mitdebuggen kann. 
_________________ Stellen Sie sich bitte Zirkusmusik vor.
|
|
|