[GDI+] Zugriffsverletzung beim Versuch Resourcen freizugeben



  • Hallo liebe C++ Community,

    ich bin C++ Anfänger, habe allerdings in AutoIt (Scriptsprache) schon mal mit GDI+ gearbeitet. In meinem Sourcecode habe ich Images und Fonts geladen und möchte diese beim Beenden des Programms wieder freigeben. Damit ich die Resourcen nur 1 mal laden muss und sie dann in verschiedenen Funktionen benutzen kann, habe ich einen globalen Pointer darauf gesetzt. Wenn ich meinen Quellcode kompiliere und danach die Anwendung teste, funktioniert alles super, bis ich das Fenster schließe, denn genau dann erscheint die Fehlermeldung "Unbehandelte Ausnahme bei 0x..... in MeinProjekt.exe: 0x......: Zugriffsverletzung beim Lesen an Position 0x.....".

    Danach wird auf folgende Funktion verwiesen (GdiPlusBase.h):

    void (operator delete)(void* in_pVoid)
        {
           DllExports::GdipFree(in_pVoid);
        }
    

    Nun zu meinem Quellcode. Ich habe den groben Ablauf mal herausgefiltert (⚠ Anfänger):

    Image *einBild; //Pointer
    
    void eineAndereFunktion()
    {
    // hier zeichne ich das Bild etc...
    }
    
    int Main()
    {
    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
    Image dasBild(L"Bild.bmp");
    einBild=&dasBild;
    // Hier werden noch andere Funktionen aufgerufen, in denen ich das Bild benutze...
    eineAndereFunktion();
    // Nun versuche ich die Resourcen freizugeben
    delete &dasBild, einBild;
    GdiplusShutdown(gdiplusToken);
    }
    

    Nur woran liegt der Fehler? Wo wird hier ein Zugriff verletzt? 😞

    mfG
    Developer30



  • Hi probier mal das hier.

    Erzeugen:

    Image test(L"C:\test.jpg");
    

    Löschen:

    DeleteObject(&test);
    

    Du musst aber auch wieder die Graphics Instanz frei geben.
    Erzeugen:

    Graphics grafik(...);
    

    Löschen:

    grafik.ReleaseHDC(hdc);
    

    so hab ich es zumindest in meinen alten Programmen gemacht.

    Gruß
    Ombre



  • Warum deletest du dasBild, es wurde doch nicht mit new erzeugt!?



  • dot schrieb:

    Warum deletest du dasBild, es wurde doch nicht mit new erzeugt!?

    ja ich muss die Resource ja irgendwie freigeben. Ob/Dass delete nur für Objekte mit new ist, wusste ich nicht.

    @Ombre: hm ja ich arbeite mit einem Buffer, der sieht ungefähr so aus:

    hdc = GetDC(hWnd);
       hdcBuffer = CreateCompatibleDC(hdc);
       bmpBuffer = CreateCompatibleBitmap(hdc, iWidth, iHeight);
       SelectObject(hdcBuffer, bmpBuffer);
    

    und den gebe ich so wieder frei:

    DeleteDC(hdcBuffer);	
    DeleteObject(bmpBuffer);
    

    keine Ahnung wieso, aber DeleteObject ändert nichts an der Fehlermeldung. Ich habe auch noch ein SolidBrush, eine FontFamily und eine Font drin, die ich auf die gleiche Weise wie die Images behandle.



  • so, ich habe meinen Projektquellcode mal auf das wesentliche reduziert, Fehler kommt immernoch.

    #pragma comment(lib, "gdiplus.lib")
    #include <windows.h>
    #include <GdiPlus.h>
    using namespace Gdiplus;
    
    LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);
    
    char szClassName[ ] = "WindowClass";
    int iWidth=200;
    int iHeight=200;
    
    // GDI+
    HDC hdc, hdcBuffer;
    HBITMAP bmpBuffer;
    Image *einBild;
    
    void OnPaint(HDC hdc) // Draw-Funktion
    {
    	Graphics graphics(hdcBuffer);
    	graphics.Clear(Color(0,0,0));
    	graphics.DrawImage(einBild,10, 0);
    	BitBlt(hdc, 0, 0, iWidth, iHeight, hdcBuffer, 0, 0, SRCCOPY);
    	graphics.ReleaseHDC(hdcBuffer);
    }
    
    LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
    
    INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, PSTR, INT iCmdShow)
    {
       HWND                hWnd;
       MSG                 msg;
       WNDCLASS            wndClass;
       GdiplusStartupInput gdiplusStartupInput;
       ULONG_PTR           gdiplusToken;
    
       wndClass.style          = CS_HREDRAW | CS_VREDRAW;
       wndClass.lpfnWndProc    = WndProc;
       wndClass.cbClsExtra     = 0;
       wndClass.cbWndExtra     = 0;
       wndClass.hInstance      = hInstance;
       wndClass.hIcon          = LoadIcon(NULL, IDI_APPLICATION);
       wndClass.hCursor        = LoadCursor(NULL, IDC_ARROW);
       wndClass.hbrBackground  = (HBRUSH)GetStockObject(WHITE_BRUSH);
       wndClass.lpszMenuName   = NULL;
       wndClass.lpszClassName  = TEXT("title");
    
       RegisterClass(&wndClass);
    
       hWnd = CreateWindow(
          TEXT("title"),   // window class name
          TEXT("title"),  // window caption
          WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX,      // window style
          CW_USEDEFAULT,            // initial x position
          CW_USEDEFAULT,            // initial y position
          iWidth,			  // initial x size
          iHeight,            // initial y size
          NULL,                     // parent window handle
          NULL,                     // window menu handle
          hInstance,                // program instance handle
          NULL);                    // creation parameters
    
       // Initialize GDI+.
       GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
       hdc = GetDC(hWnd);
       hdcBuffer = CreateCompatibleDC(hdc);
       bmpBuffer = CreateCompatibleBitmap(hdc, iWidth, iHeight);
       SelectObject(hdcBuffer, bmpBuffer);
    
       // Lade das Bild
       Image dasBild(L"..\\logo.bmp");
       einBild=&dasBild;
    
       ShowWindow(hWnd, iCmdShow);
       UpdateWindow(hWnd);
    
       while(GetMessage(&msg, NULL, 0, 0))
       {
          TranslateMessage(&msg);
          DispatchMessage(&msg);
       }
       // Resourcen freigeben
       DeleteObject(&dasBild);  
       DeleteDC(hdcBuffer);	
       DeleteObject(bmpBuffer);
       GdiplusShutdown(gdiplusToken);
       return msg.wParam;
    }  // WinMain
    
    LRESULT CALLBACK WndProc(HWND hWnd, UINT message, 
       WPARAM wParam, LPARAM lParam)
    {
       HDC          hdc;
       PAINTSTRUCT  ps;
       switch(message)
       {
    	case WM_PAINT:
    		hdc = BeginPaint(hWnd, &ps);
    		OnPaint(hdc);
    		EndPaint(hWnd, &ps);
    		return 0;
    	case WM_DESTROY:
    		PostQuitMessage(0);
    		return 0;
    	default:
    		return DefWindowProc(hWnd, message, wParam, lParam);
       }
    } // WndProc
    


  • Du hast dort eine lokale Variable Image dasBild(...); - wenn die Klasse vernünftig gestaltet ist, hat die einen Destruktor, der sich darum kümmert, die von ihr angeforderten Speicherbereiche wieder freizugeben. Also ist es auch nicht notwendig, diese Variable per delete oder DeleteObject() wieder freizugeben.



  • gibt es in GDI+ dafür einen Destruktor? (ja gibt es)
    was noch komisch ist, ist dass keine Fehlermeldung kommt, wenn ich folgendes von WinMain nach OnPaint verschiebe:

    Image dasBild(L"..\\logo.bmp");
    einBild=&dasBild;
    

    wenn ich das mache, muss ich natürlich DeleteObject in Zeile 82 rauskommentieren...

    Edit:
    Hier der Destruktor

    inline 
    Image::~Image()
    {
        DllExports::GdipDisposeImage(nativeImage);
    }
    

    aber irgendwas stimmt nicht, weil sobald GdiplusShutdown aufgerufen wird, kommt der Fehler 😕



  • Hmmm wenn der Fehler mit GdiplusShutdown zusammen hängt, kann es fast nur daran liegen, das nicht alle erzeugten Objekte wieder gelöscht werden.

    Siehe die MSDN:

    ... , and you must delete all of your GDI+ objects (or have them go out of scope) before you call GdiplusShutdown.

    Also hat ein Objekt noch Gültigkeit und wurde nicht gelöscht.

    Hoffe das konnte helfen.
    Ombre



  • ok ich habe den Fehler gefunden.
    so habe ich nun das Bild erstellt:

    Image* einBild = new Image(L"..\\logo.bmp");
    

    und dann kann man es auch mit delete wieder freigeben:

    delete einBild;
    

    danke an alle, die sich mitbeteiligt haben 👍

    lg
    Developer30


Anmelden zum Antworten