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