Einen Bereich in die Zwischenablage kopieren



  • Hallo,
    ich versuche gerade ein Benutzerdefiniertes Format, als Bereich, in die Zwischenablage zu kopieren, bzw. dieses Format, als Bereich, wieder aus der Zwischenablage herraus zu holen.
    Leider schlugen bisher alle versuche fehl, die ich gestartet habe.

    ich hab mich mal grob an dieses Beispiel auf Code Project gehalten.

    Das kopieren eines einzelnen Objektes (Benbutzerdefinertes Format) klappt problemlos.

    jedoch wenn ich das ganze als bereich kopieren will klappt es nicht.

    //-----------------------------------------------------------------------------------------------------------------------
    //				Einfügen der Daten in die Zwischenablage
    void				CDlg::OnCopyToClipBoard				(HWND hWnd)
    {
    	//
    	CWeekdayWorkInfo cbWDWInf[EOLines_h];//EOLines_h = 7 btw. 0 - 6 Mo. - So.
    	memset(&cbWDWInf,0,EOLines_h);
    
    	//--------------------------------------------------------------------------------------------------------------
    	//	cbWDWInf wird mit Wochentagsinformationen gefüllt
    
    	//--------------------------------------------------------------------------------------------------------------
    	//										Zwischenablage öffnen
    	if(OpenClipboard(hWnd))
    	{
    
    		HGLOBAL clipbuffer;
    		EmptyClipboard();
    
    		CWeekdayWorkInfo *buffer = new CWeekdayWorkInfo[EOLines_h];
    
    		for(int i = 0; i < EOLines_h; i++)
    			memset(buffer[i],0,sizeof(CWeekdayWorkInfo));
    
    		int iSize = (sizeof(CWeekdayWorkInfo) * EOLines_h);
    		clipbuffer = GlobalAlloc(GHND |GMEM_SHARE, iSize);
    
    		buffer	= (CWeekdayWorkInfo*)GlobalLock(clipbuffer);
    
    		for(int ix = 0; ix < EOLines_h; ix++)
    			//memcpy(buffer[ix],cbWDWInf[ix],sizeof(CWeekdayWorkInfo));// hier wird clipbuffer gesprengt
    			//buffer[ix] = cbWDWInf[ix]; hier wird clipbuffer nicht gesprengt, es kommt beim Abruf der Daten firlefanz raus
    
    		GlobalUnlock(clipbuffer);
    		SetClipboardData(m_cbFormat,clipbuffer);// Abbruch... clipbuffer wird gesprengt
    		CloseClipboard();
    	}
    }
    
    //-----------------------------------------------------------------------------------------------------------------------
    //				Abruf der Daten aus der Zwischenablage
    void				CDlg::OnInsertFromClipBoard			(HWND hWnd)
    {	if(IsClipboardFormatAvailable(m_cbFormat))
    	{
    		if ( OpenClipboard(hWnd) ) 
    		{
    			HANDLE hData = GetClipboardData(m_cbFormat);
    
    			CWeekdayWorkInfo *buffer = new CWeekdayWorkInfo[EOLines_h];
    
    			for(int i = 0; i < EOLines_h; i++)
    				memset(buffer[i],0,sizeof(CWeekdayWorkInfo));
    
    			buffer	= (CWeekdayWorkInfo*)GlobalLock(hData);
    
    			GlobalUnlock( hData );
    			CloseClipboard();
    
    		}
    
    	}
    }
    

    kann mir dabei jemand helfen? weis jemand was ich falsch mache?



  • Hi,

    durch deinen Code steige ich noch nicht so ganz durch. Der Umgang mit dem Clipboard scheint mir auf den ersten Blick korrekt (ich gehe davon aus, dass dein Format m_cbFormat korrekt registriert ist?). Das davor und danach verstehe ich aber nicht 😕

    In Zeile 6 erzeugst du ein Array aus irgendeiner Klasse oder Struktur, und in der Zeile danach schreibst du per memset() EOLines_h-mal eine Null, überbügelst also die Inhalte zumindest des ersten Arrayelements. Warum? Und hat die Klasse keinen Konstruktor, der für eine korrekte Initialisierung sorgt?

    In Zeile 21 machst du das gleiche noch mal, nur auf dem Heap. Und diesen Zeiger verwendest du in Zeile 29 als Rückgabewert aus GlobalLock(). Damit wirst du den heapbasierten Block verlieren -> Speicherleck.

    In Zeilen 23/24 kommt wieder dein memset(), diesmal per Schleife für alle Einträge. Abgesehen davon, dass es nix nutzt wegen der anderweitigen Verwendung des Zeigers: nimm besser einen Konstruktor zur Initialisierung.

    Zeile 26: Vorsicht, ein Array aus n Einträgen mag größer sein als n einzelne Elemente. Stelle sicher, dass der Compiler hier keine Alignment-Bytes einschiebt! Das mag schon der Grund sein, warum das Clipboard allergisch reagiert. Hast du geprüft, dass du nicht über die Grenze des geblockten Bereiches hinaus schreibst?



  • hi,
    erstmal danke für deine Antwort... 😉
    die zwischenablage ist korrekt registriert. 🙄

    Also,
    Ich habe eine Dialog, der als Stundenzettel (was in der woche gearbeitet wurde) als Liste, aufgebaut.
    Eine Zeile für jeden Wochentag, mit Spalten für informationen über diesen tag.
    CWeekdayWorkInfo ist der hintergrund für diesen Dialog und fungiert als Bereich.
    Die größe des Bereiches ist 7. für jeden Wochentag (Mo.- So. (0-6)) ein Element.

    In dieser klasse sind weiter gefüllte Bereiche, mit informationen über die arbeit eines Wochentages. (zB. Arbeitszeit anfang/ende, Ort/Straße, Tätigkeit...) Diese Klassen internen Bereiche, stellen die Spalten dar.

    Markiere ich jetzt diese Zeilen (Wochentage) oder einzelne Spalten daraus, will ich eben diese Elemente kopieren, bzw. in die Zwischenablage verschieben.....

    zu dem Code...
    es ist das resultat von verzweifelten versuchen, Windows das zu verklickern, was ich will. Man sieht halt irgendwann den wald vor lauter bäumen nicht mehr... 🕶

    Ich hab mich gestern, nach deiner Antwort, nochmal mit meinem programm auseinder gesetzt. ich denke das mein aller erster versuch gar nicht so falsch war.
    der sah wie folgt aus.

    //-----------------------------------------------------------------------------------------------------------------------
    //              Einfügen der Daten in die Zwischenablage
    void				CDlg::OnCopyToClipBoard				(HWND hWnd)
    {
    	CWeekdayWorkInfo		cbWDWInf[EOLines_h];
    
    	//--------------------------------------------------------------------------------------------------------------
    	//	cbWDWInf wird mit Wochentagsinformationen gefüllt, alle zu kopierenden Werte werden korrekt eingetragen
    	//
    
    	if(OpenClipboard(hWnd))
    	{
    
    		HGLOBAL clipbuffer;
    		EmptyClipboard();
    
    		CWeekdayWorkInfo *buffer = new CWeekdayWorkInfo[EOLines_h];
    
    		clipbuffer	= GlobalAlloc(GHND, sizeof(CWeekdayWorkInfo[EOLines_h]));
    		buffer		= (CWeekdayWorkInfo*)GlobalLock(clipbuffer);
    
    		//------------------------------------------------------------------------------------------------------
    		//	Es wird der Bereich cbWDWInf korrekt durchlaufen, in buffer wird mir beim Debugen immer nur
    		//	das erste Element eingetragen, kein weiters.
    		for(int ix = 0; ix < EOLines_h; ix++)
    			buffer[ix] = cbWDWInf[ix];
    
    		GlobalUnlock(clipbuffer);
    		SetClipboardData(m_cbFormat,clipbuffer);
    		CloseClipboard();
    	}
    }
    //-----------------------------------------------------------------------------------------------------------------------
    //              Abruf der Daten aus der Zwischenablage
    void				Dlg::OnInsertFromClipBoard			(HWND hWnd)
    {	
    	CWeekdayWorkInfo		cbWDWInf[EOLines_h];
    
    	if(IsClipboardFormatAvailable(m_cbFormat))
    	{
    		if ( OpenClipboard(hWnd) ) 
    		{
    			HANDLE hData = GetClipboardData(m_cbFormat);
    
     			//-----------------------------------------------------------------------------------------------
    			//	buffer wird als Bereich, ohne Grenzüberschreitung, durchlaufen, jedoch wird mir immer nur
    			//	das erste element, beim Debugen, bei buffer[i] angezeigt.
    			CWeekdayWorkInfo *buffer = (CWeekdayWorkInfo*)GlobalLock(hData);
    
    			for (int i = 0; i < EOLines_h; i++)
    			{
    				//---------------------------------------------------------------------------------------
    				// Im Datumsbereich der Klasse, wird ein Element angezeigt. Im Element stehen aber keine Werte
    				if(((stdDate*)buffer[i].GetDate())->second.size() > 0)
    					cbWDWInf[i].SetDate(buffer[i].GetDate());//Prog. Krash, es sind keine Werte zum abrufen da 
    			}
    		}
    	}
    }
    

    Was passiert.

    Wenn ich in meinem Programm, einige Zeilen & Spalten kopiere, als Beispiel.
    Mo. - Mi. Datum, Arbeitszeit- Anfang & Ende.

    wird in der Funktion OnCopyToClipBoard, die Variable buffer immer nur mit dem ersten Element belegt. Es stehen in diesem ersten Element die Spaltenwerte aber korrekt drin.

    unter der Funktion OnInsertFromClipBoard wird die Variable buffer als Wochentagsbereich anerkannt und überschreitet nicht die Puffergrenze. aber auch hier steht immer nur das erste element drinn. Die größer der Internen Bereiche (Spalten Datum...) stimmen auch, aber in den Bereichen stehen die werte nicht drin.
    es wird mir also nur das erste Element (Mo. (0)) und davon die Hälfte kopiert.

    entweder ist der Speicher den ich zuweise nicht groß genug, oder ich muss dafür sorgen das die Spaltenwerte in den zugewiesenen Speicher kommen.
    aber wie?



  • Poste auch noch die Klassendefinition von CWeekdayWorkInfo - und den Kopierzuweisungsoperator, wenn du einen definiert hast.

    Was sagt denn der Debugger zu deiner Kopieraktion in den Zeilen 25 und 26 - welche Speicherbereiche werden angesprochen, und wie oft wird der Kopierzuweisungsoperator aufgerufen?



  • es würde jetzt den Rahmen sprengen, wenn ich die ganze CWeekdayWorkInfo hier poste, deshalb nur ein ausschnitt daraus. es sind aber alle anderen funktionen bzw. memory Variablen so aufgebaut wie die, auf die ich jetzt eingehe.

    zu zeile 25/26 (beim Debuggen)
    schrieb ich ja schon 🙄 , dass die Variable buffer, das seltsame verhalten hat, nur das erste element anzuzeigen. egal bei welchem durchlauf die Schleife gerade ist. die Variable cbWDWInf zeigt mir alle Wochentage als bereich an. Die zu kopierenden Elemente Stehen auch korrekt in der Variable cbWDWInf.

    Durch die Member Variable m_iRowIndex, sehe ich welches element gerade drann sein sollte.

    also die klasse CWeekdayWorkInfo sieht wie folgt aus.

    //CWeekdayWorkInfo.h
    	//	Bereich bis 
    	typedef std::map <int, SYSTEMTIME> stdTo;
    	typedef std::map <int, SYSTEMTIME>::iterator iterTo;
    	//	Bereich von 
    	typedef std::map <int, SYSTEMTIME> stdFrom;
    	typedef std::map <int, SYSTEMTIME>::iterator iterFrom;
    	//	Bereich Datum 
    	typedef std::map <int, CCtrlDateTime> stdDate;
    	typedef std::map <int, CCtrlDateTime>::iterator iterDate;
    
    class CWeekdayWorkInfo 
    {
    
    public:
    	CWeekdayWorkInfo();
    	CWeekdayWorkInfo(BOOL bRefresh);
    	virtual ~CWeekdayWorkInfo(void);
    
    	void			SetTo(int iIndex, SYSTEMTIME *pTime);
    	void			SetFrom(int iIndex, SYSTEMTIME *pTime);
    	void			SetDate(int iIndex, CCtrlDateTime  *pDateTime);
    
    	stdTo			*GetTo();
    	stdFrom			*GetFrom();
    	stdDate			*GetDate();
    
    	//--------------------------------------------------------------------------------------------------------------
    	//										Klassen Kopieren
    	CWeekdayWorkInfo& operator =			(CWeekdayWorkInfo&);
    			operator		CWeekdayWorkInfo&	();
    
    private:
    	stdTo			m_To;//Arbeitszeit bis
    	stdFrom			m_From;//Arbeitszeit von
    	stdDate			m_Date;//Datum der ausgeführten Tätigkeit
    
    	short			m_iRowIndex;//Identifizierungsnummer der Datenbankzeile 1 - 7 Mo.-So.
    
    };
    

    --------------------------------------------------------------------------------------------------------

    //CWeekdayWorkInfo.cpp
    
    void			CWeekdayWorkInfo::SetRowIndex		(short iRowIndex)
    {	/**************************************************************************************************************************************
    															SET ROW INDEX
    
    							Setzt den Zeilenwert, der mit diesem Wocheneintag in der Datenbank in verbindung steht.
    							Standartmäsig wird dieser Wert auf WDWI_DEFROWINDEX (-1) gesetzt.
    							Andernfals, ist dieser wert die eindeutige Identifizierungsnr der Datenbankzeile.
    
    		- in
    			-	 iRowIndex		Integer wert der die Datenbank Zeile bestimmt, die mit dieser
    								Wochentags Arbeistinformation in verbindung steht.
    
    	***************************************************************************************************************************************/
    
    	m_iRowIndex = iRowIndex;
    }
    short			CWeekdayWorkInfo::GetRowIndex		()const
    {	/**************************************************************************************************************************************
    															GET ROW INDEX
    
    					gibt den Zeilenwert, der mit diesem Wocheneintag in der Datenbank in verbindung steht zurück.
    					Standartmäsig wird dieser Wert auf WDWI_DEFROWINDEX (-1) gesetzt.
    					Andernfals, ist dieser wert die eindeutige Identifizierungsnr der Datenbankzeile.
    
    			- return
    				-	Gibt den Integer wert der Datenbankzeile zurück, deren Werte in dieser Wochentags Arbeitsinformation stehen.
    
    	**************************************************************************************************************************************/
    
    	return m_iRowIndex;
    }
    
    /***********************************************************************************************************************************
    												Spalten Elemente
    											Werte an Bereiche Übergeben
    ************************************************************************************************************************************/
    
    void			CWeekdayWorkInfo::SetFrom			(int iIndex, SYSTEMTIME *pTime)
    {	/*********************************************************************************************
    											SET FROM
    				Setzt den Wert der Arbeitszeit, von wann an dem Bezeichneten Tag gearbeitet
    				wurde
    			- in
    				- iIndex Wert der das Element Identifiziert
    				- pTime Zeiger auf eine System Zeit Strucktur, in dem der Arbeitszeitbeginn
    				  vermerkt ist
    
    	*********************************************************************************************/
    
    	iterFrom mC = m_From.find(iIndex);
    
    	if(mC != m_From.end())
    	{
    
    		if((BOOL)pTime)
    			mC->second = *pTime;
    		else
    			m_From.erase(iIndex);
    	}
    	else
    		m_From.insert(std::make_pair(iIndex,*pTime));
    
    }
    
    void			CWeekdayWorkInfo::SetTo				(int iIndex, SYSTEMTIME *pTime)
    {	/*********************************************************************************************
    											SET TO
    				Setzt den Wert der Arbeitszeit, bis wann an dem Bezeichneten Tag gearbeitet
    				wurde
    			- in
    				- iIndex Wert der das Element Identifiziert
    				- pTime Zeiger auf eine System Zeit Strucktur, in dem das Arbeitszeitende
    				  vermerkt ist
    
    	*********************************************************************************************/
    
    	iterTo mC = m_To.find(iIndex);
    
    	if(mC != m_To.end())
    	{
    
    		if((BOOL)pTime)
    			mC->second = *pTime;
    		else
    			m_To.erase(iIndex);
    	}
    	else
    		m_To.insert(std::make_pair(iIndex,*pTime));
    
    }
    void			CWeekdayWorkInfo::SetDate			(int iIndex, CCtrlDateTime *pDateTime)
    {	/*********************************************************************************************
    											SET DATE
    				Setzt den Wert des Arbeitsdatums, an welchem Tag gearbeitet wurde, auf
    				welches Datum sich die Angaben in dieser Klasse beziehen
    			- in
    				- iIndex Wert der das Element Identifiziert
    				- pDateTime Zeiger auf eine CCtrlDateTime Klasse in dem das Arbeistdatum 
    				  vermerkt ist
    
    	*********************************************************************************************/
    
    	iterDate mC = m_Date.find(iIndex);
    
    	if(mC != m_Date.end())
    	{
    
    		if((BOOL)pDateTime)
    			mC->second = *pDateTime;
    		else
    			m_Date.erase(iIndex);
    	}
    	else
    		m_Date.insert(std::make_pair(iIndex,*pDateTime));
    
    }
    /***********************************************************************************************************************************
    											Spalten Elemente
    									Zeiger Auf Bereicher der Klasse
    ************************************************************************************************************************************/
    
    stdTo			*CWeekdayWorkInfo::GetTo				()
    {	/*********************************************************************************************
    											GET TO
    				Gibt den Bereich in dem der Wert der Arbeitszeit, bis wann an dem Bezeichneten 
    				Tag gearbeitet wurde zurück
    
    			- return
    				- Zeiger auf den stdTo Bereich
    
    	*********************************************************************************************/
    	return &m_To;
    
    }
    stdFrom			*CWeekdayWorkInfo::GetFrom			()
    {	/*********************************************************************************************
    											GET FROM
    				gibt den Bereich in dem der Wert der Arbeitszeit, von wann an dem Bezeichneten 
    				Tag gearbeitet wurde, zurück.
    
    			- return
    				- Zeiger auf den stdFrom Bereich
    
    	*********************************************************************************************/
    	return &m_From;
    
    }
    stdDate			*CWeekdayWorkInfo::GetDate			()
    {	/*********************************************************************************************
    											GET DATE
    				gibt den Bereich, in dem der Wert des Arbeitsdatums, an welchem Tag gearbeitet 
    				wurde, auf welches Datum sich die Angaben in dieser Klasse beziehen, zurück
    
    			- return
    				- Zeiger auf den stdDate Bereich
    
    	*********************************************************************************************/
    
    	return &m_Date;
    }
    
    /******************************************************************************************************************************************
    
    													OPERATOREN
    
    ******************************************************************************************************************************************/
    CWeekdayWorkInfo	&CWeekdayWorkInfo::operator	=					(CWeekdayWorkInfo &WDWInf)
    {	/**************************************************************************************************************************************
    													OPERATOR =
    				ermöglicht das Kopieren, der werte, einer CWeekdayWorkInfo Klasse über das = Zeichen, in diese Klasse
    					- in
    						-	WDWInf	eine Variable der CWeekdayWorkInfo Klasse, dessen Werte, auf diese Klasse übertragen werden sollen
    	**************************************************************************************************************************************/
    	m_iRowIndex = WDWInf.GetRowIndex();
    
    	//--------------------------------------------------------------------------------------------------------------
    	//										Zeitliche Angaben 
    	memcpy(&m_Date,(stdDate*)WDWInf.GetDate(),sizeof(stdDate));
    	memcpy(&m_From,(stdFrom*)WDWInf.GetFrom(),sizeof(stdFrom));
    	memcpy(&m_To,(stdTo*)WDWInf.GetTo(),sizeof(stdTo));
    	memcpy(&m_Hours,(stdHours*)WDWInf.GetHours(),sizeof(stdHours));
    
    	//--------------------------------------------------------------------------------------------------------------
    	//									Arbeits und Orts beschreibung
    	memcpy(&m_Description,(stdDescription*)WDWInf.GetDescription(),sizeof(stdDescription));
    	memcpy(&m_Location,(stdLocation*)WDWInf.GetLocation(),sizeof(stdLocation));
    	memcpy(&m_Report,(stdReport*)WDWInf.GetReport(),sizeof(stdReport));
    
    	//--------------------------------------------------------------------------------------------------------------
    	//									Eigene Aufwendungen / Übernachtung
    	memcpy(&m_DrivingTime,(stdDrivingTime*)WDWInf.GetDrivingTime(),sizeof(stdDrivingTime));
    	memcpy(&m_Assembly,(stdAssembly*)WDWInf.GetAssembly(),sizeof(stdAssembly));
    	memcpy(&m_PrivateCar,(stdPrivateCar*)WDWInf.GetPrivateCar(),sizeof(stdPrivateCar));
    
    	//--------------------------------------------------------------------------------------------------------------
    	//									NICHT SICHTBARE ELEMENTE
    	//
    	//								Arbeitgeber Relavante Angaben
    	memcpy(&m_WorkBreak,(stdWorkBreak*)WDWInf.GetWorkBreak(),sizeof(stdWorkBreak));
    	memcpy(&m_CW,(stdCW*)WDWInf.GetCalendarWeek(),sizeof(stdCW));
    	memcpy(&m_CustomId,(stdCustomId*)WDWInf.GetCustomId(),sizeof(stdCustomId));
    	//--------------------------------------------------------------------------------------------------------------
    	//								Rechnungs Relavante Angaben
    	memcpy(&m_AccNum,(stdAccNum*)WDWInf.GetAccountNumber(),sizeof(stdAccNum));
    	memcpy(&m_AccTyped,(stdAccTyped*)WDWInf.GetAccountTyped(),sizeof(stdAccTyped));
    	memcpy(&m_AccRemind,(stdAccRemind*)WDWInf.GetAccountReminder(),sizeof(stdAccRemind));
    	memcpy(&m_AccStatus,(stdAccStatus*)WDWInf.GetAccountNumber(),sizeof(stdAccStatus));
    
    	return *this;
    }
    					CWeekdayWorkInfo::operator	CWeekdayWorkInfo&	()
    {	/**************************************************************************************************************************************
    													= OPERATOR
    				ermöglicht das Kopieren, der werte, dieser Klasse über das = Zeichen, in eine andere CWeekdayWorkInfo Klasse
    
    				return
    					-	Werte dieser Klasse.
    	**************************************************************************************************************************************/
    
    	return 	*this;
    }
    


  • Oh, das Board ist wieder on... 🙂

    Eigentlich hab ich ja nur nach dem Kopierzuweisungsoperator gefragt... 🙄 der Rest ist eher uninteressant.
    Also, das letzte Element deiner Klasse ist ein 16bit breites short, also wird ein Element des Arrays mindestens 2 Bytes größer als ein einzelnes Klassenelement groß ist. Stelle sicher, dass dein Array von n Einträgen groß genug ist, damit auch n Einträge reinpassen.

    Zweitens ist mir aufgefallen, dass der Kopierzuweisungsoperator massivst mit memcpy() arbeitet. Das ist okay, solange du alle Implementierungsdetails kennst, aber gefährlich, wenn du Klassen kopierst, die vom Compiler mit Eigenintelligenz versehen sind. Es ist besser, wenn du auch alle Member mit deren jeweiligen Kopierzuweisungsoperatoren kopierst. Stelle sicher, dass wirklich eine korrekte Kopie durchgeführt wird.

    Oh, und dein Speicherleck zwischen Zeilen 17 und 20 gibts immer noch 😉


Anmelden zum Antworten