WinAPI - Zeichnen Problem



  • Moin,

    ich bastle gerade anhand eines NXT-Mindstorms einen Kopierer. Dieser Kopierer besteht aus drei Elementen
    - einem Scanner (scannt ein DinA5-Blatt mit einem Lichtsensor)
    - einem Drucker (bekommt später den Druck; funktioniert ähnlich wie der Scanner, nur dass er mit einem Stift, statt mit einem Lichtsensor arbeitet :p )
    - zu guter Letzt ein Interface auf dem Rechner, welches als Zwischenprogramm genutzt werden soll. Dieses Programm soll den Scan grafisch anzeigen, damit man in dieser Instanz schon sehen kann, ob es Fehler gab (bzw. man kann das Dokument auch bearbeiten...)

    Es ist noch nicht ganz fertig, aber die Daten lassen sich anzeigen. Nur das Problem ist, wenn ich zwei - drei mal ein neues zufälliges Feld generieren will, dann ist die optische Auswertung leicht fehlerhaft. Die Kästchen werden zB. nur noch in weiß mit schwarzer Umrandung gezeichnet.

    Jedenfalls hier der Code, der "main.cpp"

    #include <windows.h>
    #include <iostream>
    #include <cstdlib>
    #include <string>
    #include <stdio.h>
    #include <fstream>
    #include <sstream>
    #include <commctrl.h>
    #include <mmsystem.h>
    #include "resource.h"
    #include "define.h"
    #include "fields.h"
    #include "nxt.h"
    using namespace std;
    
    bool loadRandom = true;
    string parameteredFile = "";
    bool paintMode = false;
    
    string SaveFileRequester(HWND parentWindow){
        OPENFILENAME sfn ;
    	char syFile[100] ;
    	ZeroMemory( &sfn , sizeof( sfn));
    	sfn.lStructSize = sizeof ( sfn );
    	sfn.hwndOwner = parentWindow ;
    	sfn.lpstrFile = syFile;
    	sfn.lpstrFile[0] = '\0';
    	sfn.nMaxFile = sizeof(syFile);
    	sfn.lpstrFilter = "NXT-DATA\0*.nxl\0" ;
    	sfn.nFilterIndex = 1;
    	sfn.lpstrFileTitle = NULL ;
    	sfn.nMaxFileTitle = 0;
    	sfn.lpstrInitialDir = NULL;
        sfn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
    	if (GetSaveFileName( &sfn ) != true)
    	{
    		cout << "Saving file canceled, closing program in 10 secconds." << endl;
    	}
    
    	if(string(sfn.lpstrFile).find(".nxl") != string::npos){}else{
            string file = string(sfn.lpstrFile);
            file += ".nxl";
            strcpy(sfn.lpstrFile, file.c_str());
    	}
    
    	return std::string(sfn.lpstrFile);
    }
    
    std::string OpenFileRequester(std::string DefFilename, HWND ParentWnd){
        static const int STR_LEN = 2048;
    
        OPENFILENAME ofn;
        ZeroMemory(&ofn, sizeof(ofn));
    
        char strFile[STR_LEN] = { 0 };
        memcpy(strFile, DefFilename.c_str(), DefFilename.size());
    
        ofn.lStructSize = sizeof(ofn);
        ofn.lpstrFilter = "NXT-DATA\0*.nxl\0";
        ofn.lpstrFile   = strFile;
        ofn.nMaxFile    = STR_LEN;
        ofn.hwndOwner   = ParentWnd;
        ofn.Flags       = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
    
        if (!GetOpenFileName(&ofn))
            return "";
    
        return std::string(ofn.lpstrFile);
    }
    
    void createBitmap(const std::string& filename, unsigned int width, unsigned int height, DWORD* pixeldata){
        fstream fileStream(filename.c_str(), ios::binary | ios::out);
    
        if (!fileStream){
            return;
        }
    
        const int headeroffset = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
        const int scanlineWidth = ((width * 3) + 3) & ~3;
        const int padding = scanlineWidth - width * 3;
    
        const BITMAPFILEHEADER fileHeader =
        {
            ('B' | ('M' << 8)),
            headeroffset + height * scanlineWidth,
            0,
            0,
            headeroffset
        };
    
        const BITMAPINFOHEADER fileInfoHeader =
        {
            sizeof(BITMAPINFOHEADER),
            width,
            height * -1,
            1,
            24,
            BI_RGB,
            0, 0, 0, 0, 0
        };
    
        fileStream.write(reinterpret_cast<const char*>(&fileHeader), sizeof(fileHeader));
        fileStream.write(reinterpret_cast<const char*>(&fileInfoHeader), sizeof(fileInfoHeader));
    
        for(unsigned int y = 0; y < height; ++y)
        {
            for(unsigned int x = 0; x < width; ++x)
            {
                const DWORD pixel = *pixeldata++;
                const char bgr[] = {(char)pixel & 0xff,
                               (char)(pixel >> 8) & 0xff,
                               (char)(pixel >> 16) & 0xff};
    
                fileStream.write(reinterpret_cast<const char*>(bgr), sizeof(bgr));
            }
    
            const char filler[4] = {};
            fileStream.write(filler, padding);
        }
    
        fileStream.close();
    }
    
    class Fields{
        private:
        bool scannedFields[FIELDS];
        string file;
    
        public:
    
        void write(string path){
            fstream datei;
            datei.open(path.c_str(), ios::out);
            for(int i = 0; i < FIELDS; i++){
                string buf = "";
                if(scannedFields[i]){
                    buf = "1";
                }else{
                    buf = "0";
                }
                datei << buf;
            }
            datei.close();
        }
    
        void read(string path = "", bool invert = false){
            if(path == ""){
                path = this->file;
            }
    
            FILE *datei = fopen(path.c_str(), "r");
    
            int i=0;
    
            int c;
            if(datei != NULL){
                while((c=fgetc(datei)) != EOF){
                    if(c == '1'){
                        this->scannedFields[i] = true;
                    }else{
                        this->scannedFields[i] = false;
                    }
    
                    i++;
                }
            }
        }
    
        void randomFields(){
             srand( time( NULL ) );
            for(int i = 0; i < FIELDS; i++){
                int random = rand() % 100;
                if(random > 85)
                    scannedFields[i] = true;
                else
                    scannedFields[i] = false;
            }
        }
    
        Fields(){
            file = "log.nxl";
            randomFields();
        }
    
        bool getField(int i = 0){
            return scannedFields[i];
        }
    
        void setField(bool value, int i = 0){
            this->scannedFields[i] = value;
        }
    
        /*void bmpSave(){
            int x = 29;
            int y = 42;
    
            DWORD *data = new DWORD[FIELDS];
            float color = 0;
    
            DWORD *scan = data;
    
            for(int i = 0; i >= FIELDS; i++){
                if(scannedFields[i] == true){
                    color = 0x000000;
                }else{
                    color = 0xffffff;
                }
    
                memset(scan, (int)color, sizeof(DWORD));
            }
    
            createBitmap("log.bmp", x, y, data);
        }*/
    };
    
    class Mindstorm{
        private:
        Connection *connection;
        Filesystem *fs;
    
        public:
    
        Mindstorm(){
            connection = new Bluetooth();
            fs = new Filesystem(connection);
        }
    
        void setConnection(Connection *connection){
            this->connection = connection;
        }
    
        void setFilesystem(Filesystem *fs){
            this->fs = fs;
        }
    
        void print(){
         try{
            cout << "Try to connect to the NXT" << endl;
            connection->connect(40);
            cout << "Connected" << endl;
            fs->upload_file("log.nxl", "log.nxl");
            //upload.txt and download.txt should be the same
            connection->disconnect();
          }
    
          catch (Nxt_exception& e){
            //some error occurred - print it out
            cout << e.what() << endl;
            cout << "error code: " << e.error_code() << endl;
            cout << "error type: " << e.error_type() << endl;
            cout << e.who() << endl;
            connection->disconnect();
          }
        }
    
        void load(){
          try{
            cout << "Try to connect to the NXT" << endl;
            connection->connect(40);
            cout << "Connected" << endl;
            fs->download_file("log.nxl", "log.nxl");
            //upload.txt and download.txt should be the same
            connection->disconnect();
          }
    
          catch (Nxt_exception& e){
            //some error occurred - print it out
            cout << e.what() << endl;
            cout << "error code: " << e.error_code() << endl;
            cout << "error type: " << e.error_type() << endl;
            cout << e.who() << endl;
            connection->disconnect();
          }
        }
    };
    
    Mindstorm *printer = new Mindstorm();
    Mindstorm *scanner = new Mindstorm();
    Fields *field = new Fields();
    
    LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);
    LRESULT CALLBACK WindowProcedureConfig(HWND, UINT, WPARAM, LPARAM);
    HWND hwndConfig;
    /*  Make the class name into a global variable  */
    char szClassName[ ] = "CodeBlocksWindowsApp";
    
    int WINAPI WinMain (HINSTANCE hThisInstance,
                         HINSTANCE hPrevInstance,
                         LPSTR lpszArgument,
                         int nCmdShow){
        if(_argv[1]){
            field->read(_argv[1]);
        }
        HWND hwnd;               /* This is the handle for our window */
        MSG messages;            /* Here messages to the application are saved */
        WNDCLASSEX wincl;        /* Data structure for the windowclass */
        WNDCLASS wc;
    
        /* The Window structure */
        wincl.hInstance = hThisInstance;
        wincl.lpszClassName = szClassName;
        wincl.lpfnWndProc = WindowProcedure;      /* This function is called by windows */
        wincl.style = CS_DBLCLKS;                 /* Catch double-clicks */
        wincl.cbSize = sizeof (WNDCLASSEX);
    
        /* Use default icon and mouse-pointer */
        wincl.hIcon = LoadIcon (hThisInstance, MAKEINTRESOURCE(ID_ICON));
        wincl.hIconSm = LoadIcon (hThisInstance, MAKEINTRESOURCE(ID_ICON));
        wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
        wincl.lpszMenuName = MAKEINTRESOURCE(ID_MENU);                 /* No menu */
        wincl.cbClsExtra = 0;                      /* No extra bytes after the window class */
        wincl.cbWndExtra = 0;                      /* structure or the window instance */
        /* Use Windows's default colour as the background of the window */
        wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;
    
        /* Register the window class, and if it fails quit the program */
        if (!RegisterClassEx (&wincl))
            return 0;
    
        //wc.style = sizeof(WNDCLASS);
        wc.style = CS_HREDRAW | CS_VREDRAW;
        wc.cbClsExtra = 0;
        wc.cbWndExtra = 0;
        wc.lpfnWndProc = WindowProcedureConfig;
        wc.hInstance = hThisInstance;
        wc.hbrBackground = (HBRUSH) CreateSolidBrush (RGB ( 128, 128, 128));
        wc.hCursor = 0;
        wc.hIcon = 0;
        wc.lpszMenuName = NULL;
        wc.lpszClassName = "WinConf";
    
        RegisterClass(&wc);
    
        /* The class is registered, let's create the program*/
        hwnd = CreateWindowEx (
               0,                   /* Extended possibilites for variation */
               szClassName,         /* Classname */
               "NXT - Kopierer",       /* Title Text */
               WS_POPUPWINDOW|WS_CAPTION|WS_MINIMIZEBOX|WM_DROPFILES, /* default window */
               CW_USEDEFAULT,       /* Windows decides the position */
               CW_USEDEFAULT,       /* where the window ends up on the screen */
               WIDTH,                 /* The programs width */
               HEIGHT,                 /* and height in pixels */
               NULL,        /* The window is a child-window to desktop */
               NULL,                /* No menu */
               hThisInstance,       /* Program Instance handler */
               NULL                 /* No Window Creation data */
               );
    
        /* Make the window visible on the screen */
        ShowWindow (hwnd, nCmdShow);
    
        /*SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
                    SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOMOVE);*/
    
        int cx = GetSystemMetrics(SM_CXSCREEN);
        int cy = GetSystemMetrics(SM_CYSCREEN);
    
        int px = (cx - WIDTH)/2;
        int py = (cy - HEIGHT)/2;
    
        MoveWindow(hwnd, px, py, WIDTH, HEIGHT, true);
    
        UpdateWindow(hwnd);
        /* Run the message loop. It will run until GetMessage() returns 0 */
        while (GetMessage (&messages, NULL, 0, 0))
        {
            /* Translate virtual-key messages into character messages */
            TranslateMessage(&messages);
            /* Send message to WindowProcedure */
            DispatchMessage(&messages);
        }
    
        /* The program return-value is 0 - The value that PostQuitMessage() gave */
        return messages.wParam;
    }
    
    LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){
    
        static HWND hButton;
        static HWND hButton2;
        static HWND hButton3;
        static HWND hClose;
        static HWND hBMP;
        static POINT point;
        static int size;
    
        HDC         hdc ;
        PAINTSTRUCT ps ;
        RECT        rect ;
        switch (message)                  /* handle the messages */
        {
            case WM_CREATE:
            {
             point.x  = -1;
             point.y  = -1;
             hButton = CreateWindow(  "button",
                                      "Scannen",
                                      WS_CHILD | WS_VISIBLE|BS_PUSHBUTTON|BS_FLAT,
                                      0, 0, 0, 0,
                                      hwnd,
                                      NULL,
                                      ((LPCREATESTRUCT) lParam) -> hInstance,
                                      NULL);
             hButton2 = CreateWindow(  "button",
                                      "Erstelle Zufällig",
                                      WS_CHILD | WS_VISIBLE|BS_PUSHBUTTON|BS_FLAT,
                                      0, 0, 0, 0,
                                      hwnd,
                                      NULL,
                                      ((LPCREATESTRUCT) lParam) -> hInstance,
                                      NULL);
             hButton3 = CreateWindow(  "button",
                                      "Drucken",
                                      WS_CHILD | WS_VISIBLE|BS_PUSHBUTTON|BS_FLAT,
                                      0, 0, 0, 0,
                                      hwnd,
                                      NULL,
                                      ((LPCREATESTRUCT) lParam) -> hInstance,
                                      NULL);
             hClose = CreateWindow(  "button",
                                      "Beenden",
                                      WS_CHILD | WS_VISIBLE|BS_PUSHBUTTON|BS_FLAT,
                                      0, 0, 0, 0,
                                      hwnd,
                                      NULL,
                                      ((LPCREATESTRUCT) lParam) -> hInstance,
                                      NULL);
             hBMP = CreateWindow(  "button",
                                      "Bitmap erstellen",
                                      WS_CHILD | WS_VISIBLE|BS_PUSHBUTTON|BS_FLAT,
                                      0, 0, 0, 0,
                                      hwnd,
                                      NULL,
                                      ((LPCREATESTRUCT) lParam) -> hInstance,
                                      NULL);
             return 0;
            }
            case WM_SIZE:
            {
             MoveWindow(hButton, 10, HIWORD(lParam) - 30, 125, 22, TRUE);
             MoveWindow(hButton2, (WIDTH-125)/2, HIWORD(lParam) - 30, 125, 22, TRUE);
             MoveWindow(hButton3, WIDTH-135, HIWORD(lParam) - 30, 125, 22, TRUE);
             MoveWindow(hClose, 10, 5, 125, 22, TRUE);
             MoveWindow(hBMP, 10, 49, 125, 22, TRUE);
             SendMessage(hwnd, WM_PAINT, 0, 0);
    
             return 0;
            }
    
           case WM_COMMAND:
              {
    
                 switch(LOWORD(wParam)){
                         case ID_FILE_SCAN:{
                            scanner->load();
                            field->read();
                            SendMessage(hwnd, WM_PAINT, 0, 0);
                            break;
                         }
                         case ID_FILE_SAVE:{
                            field->write(SaveFileRequester(hwnd));
                            break;
    
                         }
                         case ID_FILE_EXIT:{
                            SendMessage(hwnd, WM_DESTROY, 0, 0);
                            break;
                         }
                         case ID_FILE_OPEN:{
                        field->read(OpenFileRequester("", hwnd));
                        SendMessage(hwnd, WM_PAINT, 0, 0);
                            break;
                        }
                         case ID_CONFIG_BLUETOOTH:{
                            hwndConfig = CreateWindow("WinConf",
                                     "Konfigurieren",
                                     WS_POPUPWINDOW|WS_CAPTION,
                                     100,
                                     100,
                                     500,
                                     400,
                                     hwnd,
                                     NULL,
                                     (HINSTANCE) GetWindowLong(hwnd, GWL_HINSTANCE),
                                     NULL);
                            ShowWindow(hwndConfig, SW_SHOW);
                            break;
                         }
                 }
                 if (lParam == (LPARAM)hButton)
                 {
                    if (HIWORD(wParam) == BN_CLICKED){
                       scanner->load();
                       field->read();
                       InvalidateRect(hwnd, NULL, true);
                       UpdateWindow(hwnd);
                    }
                 }
                 if(lParam == (LPARAM)hButton2){
                    if(HIWORD(wParam) == BN_CLICKED){
                        InvalidateRect(hwnd,NULL,true);
                        field->randomFields();
                        InvalidateRect(hwnd, NULL, true);
                        UpdateWindow(hwnd);
                    }
                 }
                 if(lParam == (LPARAM)hButton3){
                    if(HIWORD(wParam) == BN_CLICKED){
                        printer->print();
                    }
                 }
                 if(lParam == (LPARAM)hClose){
                    if(HIWORD(wParam) == BN_CLICKED){
                        SendMessage(hwnd, WM_DESTROY, 0, 0);
                    }
                 }
                 if(lParam == (LPARAM)hBMP){
                    if(HIWORD(wParam) == BN_CLICKED){
                        //field->bmpSave();
                    }
                 }
                 break;
              }
    
            case WM_LBUTTONDOWN:{
                if(paintMode == true){
                size = SCALE;
                int bw = (int)(0.28*WIDTH);
                int bh = 5;
                int rectWidth = 2 * size + 5;
    
                //Rectangle(hdc, j*size+(int)(0.28*WIDTH), i*size+5, j*size+size+(int)(0.28*WIDTH), i*size+size+5);
                point.x  = LOWORD(lParam);
                point.y  = HIWORD(lParam);
    
                int countX = (point.x-bw)/rectWidth;
                int countY = (point.y-bh)/rectWidth;
                int i = countX * countY;
                if(field->getField(i) == true){
                    field->setField(false, i);
                }else{
                    field->setField(true, i);
                }
                    string buffer = "";
                    buffer += point.x;
                    buffer += " - ";
                    buffer += point.y;
                    MessageBox(NULL, buffer.c_str(), buffer.c_str(), MB_OK);
                    InvalidateRect(hwnd, NULL, true);
                    UpdateWindow(hwnd);
                }
                break;
            }
            case WM_RBUTTONDOWN:{
                if(paintMode == false){
                    paintMode = true;
                    MessageBox(NULL, "Bearbeitungsmodus: Aktiv", "Bearbeitungsmodus", MB_OK);
                }else if(paintMode == true){
                    paintMode = false;
                    MessageBox(NULL, "Bearbeitungsmodus: Inaktiv", "Bearbeitungsmodus", MB_OK);
                }
                break;
            }
            case WM_PAINT:
                InvalidateRect(hwnd, NULL, true);
                hdc = BeginPaint (hwnd, &ps) ;
                hdc = GetDC(hwnd);
                GetClientRect(hwnd, &rect);
                size = SCALE;
    			for(int i = 0; i < 42; i++){
                    for(int j = 0; j < FIELDSINLINE; j++){
                        if(field->getField(i*FIELDSINLINE+j)){
                            SelectObject(hdc, CreatePen(PS_SOLID, 0, RGB(0, 0, 0)));
                            SelectObject(hdc, CreateSolidBrush(RGB(0, 0, 0)));
                        }else{
    
                            SelectObject(hdc, CreatePen(PS_SOLID, 0, RGB(255, 255, 255)));
                            SelectObject(hdc, CreateSolidBrush(RGB(255, 255, 255)));
                        }
    
                        Rectangle(hdc, j*size+(int)(0.28*WIDTH), i*size+5, j*size+size+(int)(0.28*WIDTH), i*size+size+5);
                    }
    			}
    			ReleaseDC(hwnd, hdc);
                EndPaint (hwnd, &ps);
                break;
            case WM_DESTROY:
                PostQuitMessage (0);       /* send a WM_QUIT to the message queue */
                break;
            default:                      /* for messages that we don't deal with */
                return DefWindowProc (hwnd, message, wParam, lParam);
            }
    
        return 0;
    }
    
    LRESULT CALLBACK WindowProcedureConfig (HWND hwndConfig, UINT uiMessage, WPARAM wParam, LPARAM lParam){
        switch(uiMessage)
        {
    
        default:
            return DefWindowProc (hwndConfig, uiMessage,
                wParam, lParam);
        }
    }
    

    Es geht hier hauptsächlich um die WM_PAINT-Methode, die ich immer dann aufrufe, wenn ich die Felder (Class Fields) überarbeitet habe.

    Falls benötigt, findet ihr hier nochmal das ganze Projekt.



  • Du hast ein Handel-Leak: der Rückgabewert von SelectObject muss gesichert werden und später wieder in den DC selcted werden. Hier muss dann der Rüchgabe werd an DeleteObject übergeben werden. GetDC RelseaDC sind unnötig. Ansonsten, was überrascht dich den dran, das du weiße Rechtecke mit schwarzen Rand bekommst? Das ist doch Bestandteil deines Codes.


Log in to reply