Variablen werden unbemerkt verändert



  • Hallo!

    Ich bin gerade dabei einen native Viewer für mein Fotoalbum Virtual Photo Organizer zu schreiben. Beim Bildbetrachtungsfenster bin ich auf ein paar Probleme gestoßen. Alle HWNDs sind bei mir Teile von Klassen, die WndProc s sind dabei static. Ich habe zwei private int Vars (PbClientWidth und PbClientHeight) um die Dimensionen eines Fensters zu speichern, d.h. diese Variablen werden nur in einer WndProc in der WM_SIZE message verändert. Wenn ich diese Variablen nachher in einer von WM_PAINT aufgerufenen Methode auslese haben sie andere Werte als ihnen bei WM_SIZE zugewiesen wurden.

    Hier ist der zugehörige Code:
    PhotoViewer.h

    #ifndef PHOTO_VIEWER_H
    #define PHOTO_VIEWER_H
    #pragma once
    
    #include "Photo.h"
    #include "Collection.h"
    #include "Bitmap.h"
    
    #define MAXZOOM 48
    
    class PhotoViewer
    {
    public:
    	PhotoViewer(Collection<Photo *> *photos, int index, HINSTANCE hInstance);
    	~PhotoViewer(void);
    	void Show();
    	static LRESULT CALLBACK ParentWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
    	static LRESULT CALLBACK PhotoBoxWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
    private:
    	static PhotoViewer *pThis;
    	HWND hParent, hPhotoBox, hInfoPane;
    	HINSTANCE hInst;
    	Collection<Photo *> *Photos;
    	Bitmap *Image;
    	RECT ImgRect;
    	HCURSOR hDefault, hCross;
    	POINT MousePos;
    	bool Zooming;
    	int ZoomLevel;
    	int PbClientWidth, PbClientHeight, SbWidth, SbHeight;
    	bool VSbVisible;
    	bool HSbVisible;
    	int CurrIndex;
    	void LoadPhoto();
    	void DrawPhoto(HDC hdc);
    	void NextPhoto();
    	void PrevPhoto();
    	bool SetCursorIcon();
    	void ZoomIn();
    	void ZoomOut();
    	RECT GetZoomedRect();
    	static INT_PTR CALLBACK DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
    };
    
    #endif
    

    PhotoViewer.cpp

    #include "StdAfx.h"
    #include "PhotoViewer.h"
    #include "Resource.h"
    
    #define PANEWIDTH 443
    #define PANEHEIGHT 154
    #define SBHEIGHT 17
    
    PhotoViewer::PhotoViewer(Collection<Photo *> *photos, int index, HINSTANCE hInstance) {
    	Photos = photos;
    	CurrIndex = index;
    	hInst = hInstance;
    	Zooming = false;
    	ZoomLevel = 0;
    	hParent = NULL;
    	hPhotoBox = NULL;
    	hInfoPane = NULL;
    	Image = NULL;
    	PbClientHeight = 0;
    	PbClientWidth = 0;
    	HSbVisible = false;
    	VSbVisible = false;
    	SbWidth = 0;
    	SbHeight = 0;
    	hDefault = LoadCursor(NULL, IDC_ARROW);
    	hCross = LoadCursor(NULL, IDC_CROSS);
    }
    
    PhotoViewer::~PhotoViewer(void) {
    	if (hInfoPane)
    		DestroyWindow(hInfoPane);
    	if (hPhotoBox)
    		DestroyWindow(hPhotoBox);
    	if (hParent)
    		DestroyWindow(hParent);
    	if (Image)
    		delete Image;
    }
    
    void PhotoViewer::Show() {
    	LoadPhoto();
    	hParent = CreateWindow(C_PHOTOVIEWER, "", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, PANEWIDTH, 500, NULL, NULL, hInst, NULL);
    	SetWindowLongPtr(hParent, GWLP_USERDATA, (LONG_PTR) this);
    	hPhotoBox = CreateWindow(C_PHOTOBOX, "", WS_VISIBLE | WS_CHILD | WS_VSCROLL | WS_HSCROLL, 0, 0, PANEWIDTH, 100, hParent, NULL, hInst, (LPVOID) this);
    	hInfoPane = CreateDialogParam(hInst, MAKEINTRESOURCE(IDD_PVIEWERPANE), hParent, this->DlgProc, (LPARAM) this);
    	ShowWindow(hParent, SW_SHOW);
    }
    
    LRESULT CALLBACK PhotoViewer::ParentWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
    	PhotoViewer *pv = (PhotoViewer *) GetWindowLongPtr(hWnd, GWLP_USERDATA);
    	PAINTSTRUCT ps;
    	HDC hdc;
    
    	switch (message) {
    		case WM_PAINT:
    			hdc = BeginPaint(hWnd, &ps);
    			EndPaint(hWnd, &ps);
    			break;
    		case WM_SIZE: {
    				int cx = LOWORD(lParam);
    				int cy = HIWORD(lParam) - PANEHEIGHT;
    				MoveWindow(pv->hPhotoBox, 0, 0, cx, cy, TRUE);
    				//MoveWindow(pv->hInfoPane, (cx - PANEWIDTH) / 2, cy, PANEWIDTH, PANEHEIGHT, TRUE);  
    			}
    			break;
    		case WM_CLOSE:
    			delete pv;
    			return DefWindowProc(hWnd, message, wParam, lParam);                   
    		default: return DefWindowProc(hWnd, message, wParam, lParam);
    	}
    	return 0;
    }
    
    LRESULT CALLBACK PhotoViewer::PhotoBoxWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
    	PhotoViewer *pv = (PhotoViewer *) GetWindowLongPtr(hWnd, GWLP_USERDATA);
    	PAINTSTRUCT ps;
    	HDC hdc;
    
    	switch (message) {
    		case WM_CREATE:
    			SetWindowLongPtr(hWnd, GWLP_USERDATA, lParam);
    			break;
    		case WM_PAINT:
    			hdc = BeginPaint(hWnd, &ps);
    			pv->DrawPhoto(hdc);
    			EndPaint(hWnd, &ps);
    			break;
    		case WM_SIZE:
    			pv->PbClientWidth = LOWORD(lParam);
    			pv->PbClientHeight = HIWORD(lParam);
    			break;
    		case WM_LBUTTONUP:
    			if (pv->SetCursorIcon())
    				pv->ZoomIn();
    			break;
    		case WM_RBUTTONUP:
    			if (pv->SetCursorIcon())
    				pv->ZoomOut();
    			break;
    		case WM_MOUSEMOVE:
    			pv->MousePos.x = LOWORD(lParam);
    			pv->MousePos.y = HIWORD(lParam);
    			pv->SetCursorIcon();
    			break;
    		default: return DefWindowProc(hWnd, message, wParam, lParam);
    	}
    	return 0;
    }
    
    void PhotoViewer::DrawPhoto(HDC hdc) {
    	if (PbClientWidth == 0 || PbClientHeight == 0)
    		return;
    	if (!Zooming) {
    		if ((Image->GetWidth() > PbClientWidth) || (Image->GetHeight() > PbClientHeight)) {
    			// photo is larger than screen
    			double ratio = Image->GetHeight() / Image->GetHeight();
    			if (ratio < (PbClientHeight / PbClientWidth)) {
    				// width is constant
    				int height = (Image->GetHeight() * PbClientWidth) / Image->GetWidth();
    				ImgRect.left = 0;
    				ImgRect.top = (PbClientHeight - height) / 2;
    				ImgRect.right = PbClientWidth;
    				ImgRect.bottom = ImgRect.top + height;
    			} else {
    				// height is constant
    				int width = (Image->GetWidth() * PbClientHeight) / Image->GetHeight();
    				ImgRect.top = 0;
    				ImgRect.left = (PbClientWidth - width) / 2;
    				ImgRect.right = ImgRect.left + width;
    				ImgRect.bottom = PbClientHeight;
    			}
    		} else {
    			// just calculate offset
    			ImgRect.top = (PbClientHeight - Image->GetHeight()) / 2;
    			ImgRect.left = (PbClientWidth - Image->GetWidth()) / 2;
    			ImgRect.right = ImgRect.left + Image->GetWidth();
    			ImgRect.bottom = ImgRect.top + Image->GetHeight();
    		}
    	} else {
    		// draw zoomed version
    		RECT area = this->GetZoomedRect();
    		// check if image is still smaller than client area
    		int width = Image->GetWidth() * ZoomLevel;
    		int height = Image->GetHeight() * ZoomLevel;
    		if (width < PbClientWidth) {
    			ImgRect.left = (PbClientWidth - width) / 2;
    			ImgRect.right = ImgRect.left + width;
    		} else {
    			ImgRect.left = 0;
    			ImgRect.right = PbClientWidth;
    		}
    		if (height < PbClientHeight) {
    			ImgRect.top = (PbClientHeight - height) / 2;
    			ImgRect.bottom = ImgRect.top + height;
    		} else {
    			ImgRect.top = 0;
    			ImgRect.bottom = PbClientHeight;
    		}
    	}
    	RECT drawArea;
    	drawArea.left = 0;
    	drawArea.top = 0;
    	drawArea.right = PbClientWidth;
    	drawArea.bottom = PbClientHeight;
    	Image->Draw(hdc, &ImgRect, &drawArea);
    }
    

    Ich bin dankbar für jeden Lösungsansatz.

    *EDIT: unnötiger Code entfernt
    *EDIT 2: Wer den vollständigen Code braucht: http://tommazzo.com/misc/PhotoViewer.cpp



  • Warum postest du hier soviel Code der nix mit dem Problem zu tun hat? Oder kann man da nix mehr minimieren?



  • Du hast Recht. Ich habe den Code, der sicher nichts mit dem Problem zu tun hat jetzt entfernt.



  • Kein Ahnung ob das mit deinem Problem zu tun hat aber diese zeile kommt mir ziemlich sinnlos vor.

    double ratio = Image->GetHeight() / Image->GetHeight();
    

    und bei der folgenden Zeile

    if (ratio < (PbClientHeight / PbClientWidth)) {
    

    wirst du PbClientHeight und PbClientWidth nach double casten müssen um ein vernünftiges Ergebnis zu bekommen.
    Hoffe das hilft dir weiter.
    Kurt



  • Die beiden Stellen haben zwar wirklich nichts mit dem Problem zu tun, aber trotzdem danke für den Hinweis (das mit ratio war ein kleiner Tippfehler meinerseits).

    Was das eigentliche Problem angeht, so bin ich der Lösung schon näher gekommen. Ich habe einen statischen This Pointer in die Klasse eingefügt, der im ctor initialisiert wird. Wenn ich nun das Ergebnis von GetWindowLongPtr(hPhotoBox, GWLP_USERDATA) in einen PhotoViewer Pointer caste, stimmt die Memoryadresse nicht mit der des statischen This Pointers überein. Folglich muss ich bei CreateWindow() den this Pointer irgendwie falsch übergeben, sodass er nicht korrekt gespeichert wird. Die Frage ist nun, wie übergebe ich ihn richtig?



  • Problem gelöst!

    Geholfen hat mir hierbei ein Eintrag in Raymond Chens Blog.
    Dank Mr. Chen weiß ich nun, dass (im Gegensatz zu Dialogen) bei WM_CREATE messages von HWNDs in lParam eine LPCREATESTRUCT drinnensteckt und nicht einfach der Parameter, der bei CreateWindow() übergeben wurde.
    Alles was schlussendlich nötig ist um die Klasse zum funktionieren zu bringen ist folgende Änderung bei WM_CREATE in PhotoBoxWndProc():

    case WM_CREATE: {
    			LPCREATESTRUCT create = (LPCREATESTRUCT) lParam;
    			SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG) create->lpCreateParams);
    			}
    			break;
    

    Danke an alle, die mir helfen wollten!


Anmelden zum Antworten