drawtext auf desktop



  • Hallo.
    Ich möchte Text auf das desktop schreiben und habe mir bissl was mit GDI gebaut.

    Fragen habe ich dabei einige, die vermutlich alle auf "wie geht das eigentlich" hinauslaufen:

    • mache ich das ganze richtig oder ist da viel doof?
    • im moment flimmert das ganze: man sieht die struktur refresh/draw
    • das refresh ist eigentlich unnötig, weil ich so und so alles bemale. allerdings habe ich dann probleme, dass die alte schrift natürlich sichtbar bleibt und man nichts mehr lesen kann

    Grundstruktur des Programms:

    • einige threads, die daten erzeugen

    • ein thread, der daten schreibt:

      • refresh
      • drawtext(daten)
      • print
      • sleep

    meine GDI-Wrapper-Klasse
    noch etwas ungeordnet, aber es funktioniert:

    struct desktop_handle_t
    {
    	desktop_handle_t()
    	{
    		auto desktop_handle = get_desktop_handle();
    
    		desktop.open(desktop_handle);
    		desktop_compatible.open(desktop.value);
    		desktop_bitmap.open(desktop);
    
    		select_src_into_compatible();
    	}
    
    	RECT get_print_rect() const
    	{
    		return desktop.get_rect();
    	}
    
    	HDC get_print_dc() const
    	{
    		return desktop_compatible.value;
    	}
    
    	void refresh()
    	{
    		desktop.clear();
    		copy_desktop_into_bitmap();
    	}
    
    	void print()
    	{
    		copy_compatible_into_desktop();
    	}
    
    	~desktop_handle_t()
    	{
    		try
    		{
    			backup_compatible();
    			desktop_bitmap.close();
    			desktop_compatible.close();
    			desktop.close();
    		}
    		catch (...)
    		{
    			;
    		}
    	}
    
    private:
    	void select_src_into_compatible()
    	{
    		my_assert(compatible_backup == nullptr);
    
    		compatible_backup = (HBITMAP)(SelectObject(desktop_compatible.value, desktop_bitmap.value));
    	}
    
    	void copy_desktop_into_bitmap()
    	{
    		auto dimensions = desktop.get_rect();
    		auto width = dimensions.right - dimensions.left;
    		auto height = dimensions.bottom - dimensions.top;
    
    		auto success = BitBlt(
    			desktop_compatible.value,
    			0,
    			0,
    			width,
    			height,
    			desktop.value,
    			0,
    			0,
    			SRCCOPY
    		);
    
    		if (success == 0)
    		{
    			throw my::platform::exception_t("copy_desktop_into_src: ");
    		}
    	}
    
    	void copy_compatible_into_desktop()
    	{
    		auto dimensions = desktop.get_rect();
    		auto width = dimensions.right - dimensions.left;
    		auto height = dimensions.bottom - dimensions.top;
    
    		auto success = BitBlt(
    			desktop.value,
    			0,
    			0,
    			width,
    			height,
    			desktop_compatible.value,
    			0,
    			0,
    			SRCCOPY
    		);
    
    		if (success == 0)
    		{
    			throw my::platform::exception_t("copy_compatible_into_desktop: ");
    		}
    	}
    
    	void backup_compatible()
    	{
    		if (compatible_backup != nullptr)
    		{
    			SelectObject(desktop_compatible.value, compatible_backup);
    			compatible_backup = nullptr;
    		}
    	}
    
    private:
    	static HWND get_desktop_handle()
    	{
    		auto program_handle = FindWindowA("ProgMan", NULL);
    
    		if (program_handle == nullptr)
    			throw my::platform::exception_t("progman not found");
    
    		auto shell_handle = FindWindowExA(program_handle, NULL, "SHELLDLL_DefView", NULL);
    
    		if (shell_handle == nullptr)
    			throw my::platform::exception_t("SHELLDLL_DefView not found");
    
    		auto desktop_handle_window = FindWindowExA(shell_handle, NULL, "SysListView32", NULL);
    
    		if (desktop_handle_window == nullptr)
    			throw my::platform::exception_t("desktop-HWND not found");
    
    		return desktop_handle_window;
    	}
    	
    	gdi::hdc desktop;
    	gdi::hdc_compatible desktop_compatible;
    	gdi::compatible_bitmap desktop_bitmap;
    	HBITMAP compatible_backup = nullptr;
    };
    

    gdi-wrapper-klassen sind dabei:

    namespace gdi
    {
    	struct hdc_compatible
    	{
    		hdc_compatible(const hdc_compatible&) = delete;
    		hdc_compatible(hdc_compatible&&) = delete;
    		hdc_compatible& operator=(const hdc_compatible&) = delete;
    		hdc_compatible& operator=(hdc_compatible&&) = delete;
    
    		hdc_compatible() : value(nullptr) {}
    		void open(HDC src)
    		{
    			close();
    
    			value = CreateCompatibleDC(src);
    
    			if (value == nullptr)
    			{
    				throw my::platform::exception_t("hdc_compatible: ");
    			}
    		}
    		void close()
    		{
    			if (value != nullptr)
    			{
    				DeleteDC(value);
    				value = nullptr;
    			}
    
    		}
    
    		~hdc_compatible() { close(); }
    
    		HDC value;
    	};
    	struct hdc
    	{
    		hdc(const hdc&) = delete;
    		hdc(hdc&&) = delete;
    		hdc& operator=(const hdc&) = delete;
    		hdc& operator=(hdc&&) = delete;
    
    		hdc() : value(nullptr), hwnd(nullptr) {}
    
    		void clear()
    		{
    			RECT content_rect = get_rect();
    			RedrawWindow(hwnd, &content_rect, NULL, RDW_NOERASE | RDW_INVALIDATE | RDW_UPDATENOW);
    		}
    
    		void open(HWND src)
    		{
    			close();
    
    			hwnd = src;
    			value = GetDC(hwnd);
    
    			if (value == nullptr)
    			{
    				throw my::platform::exception_t("hdc: ");
    			}
    
    			clear();
    		}
    		RECT get_rect() const
    		{
    			RECT ret_val;
    			GetWindowRect(hwnd, &ret_val);
    			return ret_val;
    		}
    		void close()
    		{
    			if (value != nullptr)
    			{
    				clear();
    				ReleaseDC(hwnd, value);
    				value = nullptr;
    			}
    		}
    		~hdc() { close(); }
    
    		HDC value;
    		HWND get_handle() const
    		{
    			return hwnd;
    		}
    	private:
    		HWND hwnd;
    	};
    	struct compatible_bitmap
    	{
    		compatible_bitmap(const compatible_bitmap&) = delete;
    		compatible_bitmap(compatible_bitmap&&) = delete;
    		compatible_bitmap& operator=(const compatible_bitmap&) = delete;
    		compatible_bitmap& operator=(compatible_bitmap&&) = delete;
    
    		compatible_bitmap() : value(nullptr) {}
    
    		void copy_from(HBITMAP src)
    		{
    			close();
    
    			value = (HBITMAP)CopyImage(src, IMAGE_BITMAP, 0, 0, 0);
    
    			if (value == nullptr)
    			{
    				throw my::platform::exception_t("compatible_bitmap (copy): ");
    			}
    		}
    
    		void open(hdc& src)
    		{
    			close();
    
    			auto dimensions = src.get_rect();
    			auto width = dimensions.right - dimensions.left;
    			auto height = dimensions.bottom - dimensions.top;
    
    			value = CreateCompatibleBitmap(src.value, width, height);
    
    			if (value == nullptr)
    			{
    				throw my::platform::exception_t("compatible_bitmap (open): ");
    			}
    		}
    
    		void close()
    		{
    			if (value != nullptr)
    			{
    				DeleteObject(value);
    				value = nullptr;
    			}
    		}
    		~compatible_bitmap() { close(); }
    
    		HBITMAP value;
    	};
    }
    

    wäre schön, wenn mal jemand mit etwas ahnung paar links posten könnte und/oder was dazu sagt

    danke im voraus 🙂


  • Mod

    Du kannst so nicht in ein fremdes Fenster schreiben.
    Das ist nie persitent.

    Du könntest eine DLL schreiben, den Desktop Window Thread hooken und WM_PAINT abfangen und nach Behandlung von WM_PAINT Deine Ausgabe starten.



  • @Martin-Richter
    dass es nicht persistent ist, ist schon klar.
    deshalb male ich es ja immer wieder neu.

    ich denke, ich muss 2 buffer haben und die abwechselnd beschreiben und dann in das compatible_hdc kopieren.
    aber ich dachte, ich frage erst mal, bevor ich (noch mehr) unfug mache 😉

    danke schon mal; aber dort möchte ich unbedingt hinmalen^^



  • Wenn du ein Popupfenster mit dem Stil WS_EX_LAYERED und Colorkey nimmst, erzielst du doch genau den gleichen Effekt? Mit dem Stil WS_EX_TOOLWINDOW wird auch kein Eintrag in der Taskleiste angezeigt.
    Und man hat keine Probleme, wenn der Nutzer ein Fenster drüberzieht. Außerdem kann man den Text (also das Fenster) als Nutzer auch noch selber platzieren.

    Ansonsten hat Petzold das mit LockWindowUpdate und GetDCEx mit entsprechenden Flags gemacht. Das klappte aber schon unter XP nicht (mehr?) gut.



  • WS_EX_LAYERED / WS_EX_TOOLWINDOW
    klingt an sich nicht schlecht; allerdings ist desktop selbst halt schon etwas praktischer: win+d funktioniert immer hin und zurück.

    noch mal: mein problem hat aber wohl eher mit dem malen an sich zu tun.
    und das war ja auch die frage: wie buffert man die "ausgabe" so, dass man das enstandene "bild" am ende einfach irgendwo reinswappen kann, damit die berechnungen zwischen dem "clear" und dem BitBlt entfallen (weil schon zuvor alles fertig).

    mein grundgerüst ist halt jetzt:

    clear();
    fill( ... data ..., compatible_dc )
    BitBlt( desktop, ....., compatible_dc, ... );
    

    ich hätte gern ein

    XYZ.clone();
    fill( ... data ..., XYZ);
    copy(XYZ, compatible_dc);
    clear();
    BitBlt( desktop, ....., compatible_dc, ... );
    

    oder gibt es keinen "buffered" weg hierfür?

    davon unabhängig werd ich mich aber trotzdem mal an eurem vorschlag versuchen; der scheint für mein problem alles in allem sinnvoller zu sein. danke 🙂



  • Du musst ja deinen Memory DC nicht jedesmal wegwerfen sondern einfach nichts neu in den Memory DC malen wenn es nichts neues zu malen gibt?



  • @Swordfish sagte in drawtext auf desktop:

    nicht wegwerfen

    (das problem des flackerns würde ja bleiben; auch wenn es nicht mehr so häufig wäre - aber wie schon zu anfang erwähnt: es ist nicht persistent -> ständig neu malen)

    das habe ich anfangs nicht gemacht; sondern einfach "drüber" geschrieben, wenn sich was geändert hat. und dann auch nur genau die stelle.
    nun habe ich damit aber nicht hinbekommen, den hintergrund zu behalten und win+d zeigt halt den leeren desktop weil mein programm ja nicht weiß, wann was im vordergrund gemacht wird.

    mein verständnis zur herangehensweise ist halt sehr beschränkt und ich war zu doof ein "how to" zu finden...

    deshalb noch mal
    init:
    ich habe nen DC (desktop), baue mir ein compatible dazu und hole mir den hintergrund aus dem desktop. dann schiebe ich die bitmap mittels selectobject ins compatible.

    loop:
    durchsichtig machen: SetBkMode(dc_compatible, TRANSPARENT);
    und text out: DrawTextA(dc_compatible, the_line.c_str(), -1, &paint_area, DT_LEFT | DT_TOP | DT_END_ELLIPSIS);

    select des compatible ins desktop

    so weit so gut; nun wird alter text aber nicht überschrieben sondern wegen des transparents kann man schon nach wenigen änderungen nicht mehr erkennen, was dort steht, weil der alte text einfach neuer hintergrund wird. das soll natürlich nicht so sein.

    jetzt mache ich entweder etwas falsch (weil ich doof bin) oder habe etwas falsch verstanden (weil ich nicht weiß, wie die herangehensweise ist) oder es geht einfach tatsächlich nicht (was ich mir nicht vorstellen kann).



  • Wie ich das sehe, sind der Quell- und Ziel-DC bei dir die gleichen. Du hast also nach dem ersten Zeichenvorgang keine Chance mehr, an den Originalhintergrund zu kommen.
    Wenn wirklich in den DC der Listview gemalt werden soll, hast du glaube ich keine andere Möglichkeit als die von @Martin-Richter genannte.
    Beim Blitten des Listview-DC und Schreiben in den Screen-DC müsste genau bestimmt werden, welcher Bereich wirklich frei von anderen Fenstern ist. Außerdem gibt es immer noch die Möglichkeit von dynamischen Inhalten, sei es eine Diashow oder eine Dragoperation.

    Mit einem Layered-Fenster habe ich das gestern mal probiert. Unschön ist dabei, dass in einen einfarbigen Hintergrund gezeichnet werden muss und die Texte dementsprechend aussehen. Der Vorteil ist, dass es auf jedem Hintergrund klappt.
    https://ibb.co/D4S1sNN



  • @yahendrik quell- und ziel-dc: naja; ich dachte, ich kann davor irgendwie das bmp speichern und dann das gespeicherte immer wieder als hintergrund nehmen.

    ansonsten werd ich die tage mal die "richtige" lösung versuchen - danke 🙂