Problem mit Ausgabe: Wert nach setzen von ItemData am CTreeCtrl

pibr

Grünschnabel
C++/MFC - Problem mit Ausgabe: Wert nach setzen von ItemData am CTreeCtrl

Hallo zusammen,

bitte nicht von der Größe des Threads abschrecken lassen. Das Problem ist wahrscheinlich ganz klein, aber ich finde einfach die Lösung nicht, da ich wahrscheinlich immer noch nicht mit den Datentypen und dem casten klar komme.

1. Ich rufe meine Methode DrawNodes auf um im TreeView die ersten Knoten zu zeichnen. Jeder der Knoten bekommt als ItemData den Wert G und dann 1,2,3,4,5.... usw. Mein zweiter Node hat also als ItemData-Wert eine "G2".
Das wurde wie folgt erreicht:
Code:
  HTREEITEM hItem;
  hItem = this->InsertItem("Gruppiert nach Prio",3,3);
  this->SetItemData(hItem,(DWORD) "G2");

2. Nun nutze ich die Methode OnTvnSelchanged, um den geklickten Node und deren ItemDate-Wert abzuholen. Hier erst einmal die Methode. Darunter beschreibe ich dann was nun geschieht und welche Ausgabe (Fehlerhaft) ich erhalte.

Code:
  void CNavTree::OnTvnSelchanged(NMHDR *pNMHDR, LRESULT *pResult)
  {
  	LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
  	//Item-Klick auswerten
  	HTREEITEM hItem = pNMTreeView->itemNew.hItem;
  
  	LPCTSTR value;
  	value = (LPCTSTR) this->GetItemData(hItem);
  
  	//Test-Ausgabe
  	TRACE("\n");
  	TRACE("Click-Ausgabe Node-Wert: ");
  	TRACE(value);
  	TRACE("\n");
  
  	//für Prozedur-Name
  	_bstr_t m_ProcedureName;
  
  	if(this->GetChildItem(this->GetSelectedItem()) == NULL)
  	{
  
  		//Sub-Nodes zeichnen
  		if(value == "G2")
  		{
  		m_ProcedureName = "KENNY$PR_GETERRWITHPRIO";
  		}
  
  		//Nodes zeichnen
  		try
  		{
  			CRec* RS = new CRec();
  			//RS->Select(theApp.p_MyCN,m_stSQLString);
  			RS->SelectProcedure(theApp.p_MyCN,m_ProcedureName);
  
 			//Prüfen, ob über haupt Daten vorhanden sind, sonst else-Zweig
  			if(!(RS->pRS->BOF || RS->pRS->EndOfFile))
  			{
 				//Auf den ersten Datensatz navigieren
  				RS->pRS->MoveFirst();
  
 				//Mit Schleifen alle Daten bis zum Ende durchgehen
 				while(!RS->pRS->EndOfFile)
  				{
 		 		//Node Objekte deklarieren, um später
 					SetItemData ausführen zu können
 					HTREEITEM hItemSub;
 		 		//Icon und Wert aus Datenbank setzen
 		 		hItemSub = this->InsertItem((_bstr_t)RS->pRS->Fields->GetItem("VALUE")->Value,3,3,hItem);
  
 					//ID an Knoten (ItemData) setzen
 					_bstr_t NodeIdentValue;
 					NodeIdentValue = value;
 					NodeIdentValue += (_bstr_t)
 		 	 RS->pRS->Fields->GetItem("id")->Value;
  
 		 	 this->SetItemData(hItemSub,(DWORD) ((LPCTSTR) NodeIdentValue));
 		 		TRACE((LPCTSTR) this->GetItemData(hItemSub));
 					TRACE("\n");
  
 					//zum nächsten DS navigieren
 					RS->pRS->MoveNext();
  				}
  			}
  		//Es gibt keine Daten
  		else
  		AfxMessageBox("Es gibt keine Datensätze, die
  		angezeigt werden können");
  
  		//Pointer löschen
  		delete RS;
  		}
  	catch(_com_error err)
  	{
  		_bstr_t str(err.Description());
  		AfxMessageBox(str);
  	}
  
  
  	//Expandieren
  	this->Expand(this->GetSelectedItem(),TVE_EXPAND);
  
  	TRACE("Warte auf Click");
  	TRACE("\n");
  	TRACE("\n");
  }
  
  *pResult = 0;
  }

Nachdem ich also nun auf meinen Knoten (G2) gedrückt habe erscheint als Ausgabe (siehe Kommentar: //Test-Ausgabe im Source) folgendes:

Code:
  Click-Ausgabe Node-Wert: G2

Kurz darunter, frage ich ab ob der ItemData-Wert eine "G2" ist. Wenn ja (das ist ja der Fall) soll der Prozedur-Name gespeichert werden.Siehe:

Code:
  if(value == "G2")
   		{
   		m_ProcedureName = "KENNY$PR_GETERRWITHPRIO";
   		}

Darunter folgt dann eine Schleife, die nun alle Daten aus Oracle holen soll: Das ist der Source:

Code:
  //Node Objekte deklarieren, um später
 					SetItemData ausführen zu können
 					HTREEITEM hItemSub;
 		 		//Icon und Wert aus Datenbank setzen
 		 		hItemSub = this->InsertItem((_bstr_t)RS->pRS->Fields->GetItem("VALUE")->Value,3,3,hItem);
  
 					//ID an Knoten (ItemData) setzen
 					_bstr_t NodeIdentValue;
 					NodeIdentValue = value;
 		 		NodeIdentValue += (_bstr_t) RS->pRS->Fields->GetItem("id")->Value;
  
 		 	 this->SetItemData(hItemSub,(DWORD) ((LPCTSTR) NodeIdentValue));
 		 		TRACE((LPCTSTR) this->GetItemData(hItemSub));
 					TRACE("\n");
  
 					//zum nächsten DS navigieren
 					RS->pRS->MoveNext();

Wie man sieht deklariere ich ein hItemSub und mache ein InsertItem. Darunter deklariere ich dann die Var: NodeIdentValue. Sinn soll es sein mein "G2" und die Id des Wertes zusammenzufügen. Ich übergebe also den Wert aus value an die NodeIdentValue. Und dann mache ich ein Inkrement und setze die ID des Datensatzes hinten dran.

Dann setze ich nun meinen neuen ItemData-Wert und gebe mit Trace direkt den Wert von ItemDate für das aktuelle Node einfach im Output aus. Der Output sieht dann bis jetzt, wie folgt aus:

Code:
  Click-Ausgabe Node-Wert: G2
  G21
  G22
  G23
  G24
  G25
  Warte auf Click

Wenn die Methode am Ende ist, dann wird mit Trace noch "Warte auf Click" ausgegeben. So, und jetzt kommt es.

Ich drücke nun auf den ersten Sub-Node, der hat ja, wie man sehen kann den ItemData-Wert "G21". Jetzt würde man ja denken, dann wenn ich da draufklicke, dass dann in der Ausgabe:

Code:
  Click-Ausgabe Node-Wert: G21

erscheint. Aber genau das passiert eben nicht. Die Ausgabe sieht wie folgt aus:

Code:
  Click-Ausgabe Node-Wert: îþîþîþîþîþîþîþîþîþîþîþîþL
  First-chance exception at  0x77e9bbf3 in Kenny.exe: Microsoft C++ exception: _com_error @  0x0012f074.
  First-chance exception at 0x77e9bbf3 in Kenny.exe: Microsoft C++  exception: _com_error @ 0x0012f1d0.

Es wäre nett, wenn mir jemand sagen kann, warum beim zweiten Klick das hier erscheint:

Code:
  îþîþîþîþîþîþîþîþîþîþîþîþL

Vielen Dank im voraus für eure Mühe.
 
Zuletzt bearbeitet:
Re: C++/MFC - Problem mit Ausgabe: Wert nach setzen von ItemData am CTreeCtrl

Auf den ersten Blick denke ich, daß es damit zu tun hat, daß Du schon beim ersten Insert (G2) einen lokalen, konstanten String als Parameter übergibst, d.h. einen Zeiger darauf. Wenn die Funktion dann verlassen wird, ist dieser String nicht mehr gültig. Daß Du ihn später noch siehst ist Zufall, da dann einfach dieser (als frei gekennzeichnete) Speicherbereich noch nicht wieder überschrieben ist.
Ich würde vorschlagen, Du machst das so:
Code:
_bstr_t * pLabel;
         pLabel = new _bstr_t("G2");
         hItem = this->InsertItem("Gruppiert nach Prio",3,3);
        this->SetItemData(hItem,(DWORD) pLabel);
Du darfst später, wenn Du ein Item löschst, natürlich nicht vergessen, den erzeugten Zeiger mit delete wieder zu löschen.
Das gleiche machst Du mit Deinem NodeIdentValue:
Code:
   _bstr_t * pvalue;
   pvalue = (  _bstr_t *) this->GetItemData(hItem); 
    ....
      _bstr_t * pNodeIdentValue;
     pNodeIdentValue = new _bstr_t(*value);
   *pNodeIdentValue += (_bstr_t)RS->pRS->Fields->GetItem("id")->Value;
 this->SetItemData(hItemSub,(DWORD)pNodeIdentValue));
Mit new erzeugst Du Deine Strings auf dem Heap, wo sie so lange bleiben, bis Du sie mit delete wieder löschst. Das machst Du am Besten, wenn Du das Treeitem löschst (ChildItems nicht vergessen!)
 
Hallo jokey2,

so recht zufrieden bin ich mit der Antwort noch nicht. Nichts gegen dich, aber es erklärt immer noch nicht, warum die Ausgabe nicht hinhaut. Dein Weg beschreibt meiner Meinung nach eine saubere Programmierung, was den Speicher angeht, keine Frage.

Sprich, die Verwaltung der ItemData-Werte für ein Node übernimmt die CTreeCtrl-Klasse, das soll nicht mein Problem sein.

Meiner Meinung nach muss der Fehler am Datentyp und/oder am casten liegen. Denn G2 wird richtig erkannt, sonst würde er ja die SubNodes nicht zeichnen. Nur versuche ich dann den Wert G2 und die ID aus dem Datensatz zusammen zu fügen. Entweder liegt der Fehler da oder später weiter oben beim casten auf _bstr_t.

Ich habe teilweise auch andere Datentypen versucht, teilweise mit dem Ergebnis, das mir dann gar nichts mehr angezeigt wurde, bzw. ich statt G2 schon (also beim Hauptknoten-Click) diese Zeichen bekommen habe.

Für mich deuten diese Zeichen auf einen falschen Cast hin. Da bin ich mit ziemlich sicher. Und trotz alledem weiß ich nicht, wie ich es anders casten soll.

Leider bin ich ich auch an MFC und ADO gebunden, sonst würde ich die ganze Sache entweder mit qt oder direkt in Net bauen.
 
Der String "G2" wird vom Compiler als Konstante mit konstanter Adresse in dein Programm eingesetzt. Teilweise ist der Compiler so schlau, und ersetzt zwei gleiche char-String-Konstanten mit einer, weshalb der Vergleich wahrscheinlich zufällig klappt.

Pack doch einfach direkt eine Zahl rein, dann musst du dich nicht mit new/delete rumärgern.
 
Endurion hat gesagt.:
Der String "G2" wird vom Compiler als Konstante mit konstanter Adresse in dein Programm eingesetzt. Teilweise ist der Compiler so schlau, und ersetzt zwei gleiche char-String-Konstanten mit einer, weshalb der Vergleich wahrscheinlich zufällig klappt.

Pack doch einfach direkt eine Zahl rein, dann musst du dich nicht mit new/delete rumärgern.

mit ner reiner Zahl klappt leider nicht :-), weil ich nämlich nicht immer welche zur Verfügung habe.

Also meinst du auch, dass ich definitiv mit new und delete arbeiten muss? Na suppi, wat ein Aufwand
 
mmmhh, irgend etwas stimmt nicht.

Habe den ersten Knoten nun wie folgt gesetzt:

Code:
_bstr_t * pLabel;

pLabel = new _bstr_t("G2");

hItem = this->InsertItem("Gruppiert nach Prio",3,3);

this->SetItemData(hItem,(DWORD) pLabel); 

TRACE((LPCTSTR) this->GetItemData(hItem));

TRACE("\n");

Der ItemData-Wert hat:

Code:
È„/
 
Müsstest du da nicht erst auf bstr_t* casten, bevor du direkt auf LPCTSTR gehst?

Code:
TRACE( (LPCTSTR)(bstr_t*)( GetItemData( hItem ) );
Der bstr_t hat einen cast-operator auf const char*, das sollte dann so eigentlich klappen.

Warum benutzt du denn eigentlich bstr_t? Benötigst du Unicode?
 
Zurück