[gelöst] CreateWindowEx() in einem ATL Control will nicht so recht...
-
Hallo zusammen,
hab ein kleines, eigenes MediaPlayer ActiveX Control gebastelt mit dem man einen Filtergraphen selbst aufbauen und Videos und Audio abspielen kann. Verwendet hab ich hierfür ATL.
Nun muss ich eine Art Overlay generieren. D.h. ich muss einen Text und Bilder ÜBER das laufende Video blenden/anzeigen können. Quasi eine Art "On Screen Display". Damit aber vom Video möglichst nix verloren geht, muss der Hintergrund des "Darübergelegten" transparent sein.
Ich hab ein paar Ansätze im Netz gefunden. Einer basiert auf einem extra Filter der direkt in den Bildstrom "reinzeichnet". Doch ich will nur ungern meinen Filter "aufschneiden" und vor den Video-Renderer einen extra Filter positionieren.
Ich hab mir deshalb gedacht, das muss auch anders gehen und hab mit Child-Frames experiementiert. Das funktioniert auch schon einigermaßen. Nur hab ich das Problem, dass mein Fenster keinen "echten" transparenten Hintergrund beherrscht.
In meiner Control-Klasse hab ich die "OnInitDialog" Methode:
LRESULT CMediaCtl::OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { // Initialize the COM library. HRESULT hr = CoInitialize(NULL); WTSRegisterSessionNotification(m_hWnd, NOTIFY_FOR_THIS_SESSION); ::OutputDebugString(L"MediaCtl:: onInit()\n"); InitDS(); SetupVideoWindow(); SetNotifications(); RECT rect; GetClientRect(&rect); rect.left = 0; rect.top = 0; rect.right = 320; rect.bottom = 240; CAxWindow ctrlCal; hChild = ctrlCal.Create(m_hWnd, rect, L"", WS_CHILD | WS_VISIBLE); ::ShowWindow(hChild,SW_SHOW); if (!::IsWindow(hChild)) { ::OutputDebugString(L"Error while creating decorator window"); } else { ::OutputDebugString(L"decorator window created..."); } return TRUE; }Darin erzeuge ich mein "hChild" Fenster (siehe Zeile 20+21). Das funktioniert prima. Das "Overlay" zeichne ich wie folgt (ebenfalls in der Control-Klasse):
LRESULT CMediaCtl::OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { PAINTSTRUCT ps1; HDC hDC1; /******************** * paint video window */ hDC1 = BeginPaint(&ps1); EndPaint(&ps1); /******************** * paint overlay window */ PAINTSTRUCT ps2; HDC hDC2; hDC2 = ::BeginPaint(hChild, &ps2); SetBkMode(hDC2,TRANSPARENT); RECT rc; //GetClientRect(&rc); // leave the background "untouched" !!! SetBkMode(hDC2,TRANSPARENT); rc.top = 100; rc.left = 100; rc.right = 200; rc.bottom = 200; HBRUSH hNewBrush = ::CreateSolidBrush( RGB(0, 255, 0) ); if (hNewBrush) { ::FillRect(hDC2, &rc, hNewBrush ); ::DrawText(hDC2,L"TEST",4,&rc,DT_CENTER); ::DeleteObject( hNewBrush ); } EndPaint(&ps2); ::BringWindowToTop(hChild); return 0; }Aber Die Fenster von CaxWindow sind offensichtlich nicht transparenz-fähig.
Wie man sieht habe ich in Zeile 22 der "OnPaint" Methode den Hintergrund auf Transparent gesetzt.Das hat auch für's erste funktioniert:
Das erste Video das ich auf meinem System öffne ist ja immer "hardware gerendert". Da funktioniert die Transparenz. Aber nur solange, bis ich ein anderes Fenster "über" das Video hinweg gezogen hab. Mit dem drüber hinwegziehen "wische" ich quasi die Transparenz weg. Der bis dato transparente Hintergrund wird schwarz. --> Sehr unschön.Bei jedem weiteren geöffneten Video (welches dann Software gerendert ist), hab ich erst gar keine Transparenz. Der Hintergrund ist hier gleich von vornherein schwarz.
Nun, ich hab gelesen dass man mit "CreateWindowEx(...)" den Style WS_EX_LAYERED einstellen kann und diese Fenster dann "transparent" sein können.
Nur irgendwie bin ich zu "doof" das in mein Projekt einzubauen. In sämtlichen Beispielen die ich im Netz gefunden hab, wird von einem anderen Konstrukt ausgegangen. Mit anderem "Konstrukt" mein ich: Kein ATL ActiveX Control sondern irgend was anderes (bin in C++ noch nicht so bewandert ... leider).Ich hab's unter anderem mit folgendem in der "OnInitDialog" Mthode probiert (also statt Zeile 20+21, das hier):
hChild = ::CreateWindowEx( WS_EX_LAYERED, L"", L"", WS_VISIBLE, rect.left, rect.top, rect.right, rect.bottom, m_hWnd, NULL, GetInstanceModule(NULL), &lParam);Aber meine Abfrage ob hChild ein Window ist, schlägt immer fehl.
Kann mich jemand auf den richtigen Weg weisen wie ich das mit CreateWindowEx und meinem Control hinbekomme?
Gruß
Alex
-
Weiß da keiner was oder hab ich irgendwas bei meiner Frage "falsch" gemacht?
Gruß
Alex
-
Hmm, *seltsam*
Hab gerade rausgefunden, dass wenn ich "SetBkMode" auskommentiere, das ganze nach wie vor gleich aussieht. Ist so als ob SetBkMode() keine Wirkung zeigt....
-
Okay, dann führe ich eben einen Monolog ... Auch recht.
Ich glaub ich hab jetzt verstanden was "SetBkMode" genau macht.
Ich hab die Sache mit dem Video abspielen erstmal auskommentiert und stattdessen in den Hauptdialog einen roten Hintergrund malen lassen. ÜBER diesen soll mein OnScrenDisplay angezeigt werden. Soweit so gut. Ich hab mal Screenshots gemacht.
So sieht's mit auskommentierter "SetBkMode" Zeile aus
So sieht's MIT "SetBkMode" Zeile aus
So wie ich das jetzt verstanden habe, bezieht sich die Funktion von SetBkMode nur auf den aktuellen Zeichenvorgang und wirkt sich qausi nur auf das Zeichnen und nicht auf das Fenster selbst aus. Dieses hat scheinbar _immer_ einen fixen Hintergrund. Ist das so korrekt?
Und so hätte ich's gerne. Ich hab das Bild in Paint etwas "manipuliert und mit einem etwas dunkleren Rot den Teil eingefärbt der von unten durch das darüberliegende Window durchzusehen sein sollte.
Hier ist der Code dazu:
LRESULT CMediaCtl::OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { // Initialize the COM library. HRESULT hr = CoInitialize(NULL); WTSRegisterSessionNotification(m_hWnd, NOTIFY_FOR_THIS_SESSION); ::OutputDebugString(L"MediaCtl:: onInit()\n"); InitDS(); // für's erste mal kein Video anzeigen... //SetupVideoWindow(); //SetNotifications(); RECT rect; GetClientRect(&rect); rect.left = 0; rect.top = 0; rect.right = 320; rect.bottom = 240; CAxWindow ctrlCal; hChild = ctrlCal.Create(m_hWnd, rect, L"", WS_CHILD | WS_VISIBLE); ::ShowWindow(hChild,SW_SHOW); if (!::IsWindow(hChild)) { ::OutputDebugString(L"Error while creating decorator window"); } else { ::OutputDebugString(L"decorator window created..."); } return TRUE; } ....... LRESULT CMediaCtl::OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { PAINTSTRUCT ps; HDC hDC; /******************** * paint overlay window */ hDC = ::BeginPaint(hChild, &ps); RECT rc; // leave the background "untouched" !!! //SetBkMode(hDC,TRANSPARENT); rc.top = 50; rc.left = 50; rc.right = 320-50; rc.bottom = 240-50; HBRUSH hNewBrush = ::CreateSolidBrush( RGB(0, 255, 0) ); if (hNewBrush) { ::FillRect(hDC, &rc, hNewBrush ); ::DrawText(hDC,L"TEST",4,&rc,DT_CENTER); ::DeleteObject( hNewBrush ); } EndPaint(&ps); //::BringWindowToTop(hChild); /******************** * paint video window */ hDC = BeginPaint(&ps); RECT rc2; rc2.top = 10; rc2.left = 10; rc2.right = 505-10; rc2.bottom = 369-10; HBRUSH hNewBrush2 = ::CreateSolidBrush( RGB(255, 0, 0) ); if (hNewBrush2) { ::FillRect(hDC, &rc2, hNewBrush2 ); ::DeleteObject( hNewBrush2 ); } EndPaint(&ps); return 0; }Meine Frage ist nun:
Woher und wie krieg ich ein Window das ich "über" das - ich nenn's mal Hauptfenster" legen kann und das einen durchsichtigen Hintergrund hat. Ist das mit CAxWindow überhaupt mäglich oder brauch ich da zwingend was anderes?
CreateWindowEx scheint das zu können (Stichwort WS_EX_LAYERED). Allerdings laut MSDN nicht als Child. Okay, als "Nicht-Child" müsste ich halt sicherstellen, dass es _immer_ über das Hauptfenster positioniert wird. Und ich hab noch keinen Plan wie ich das innerhalb meines Controls erzeugt bekomme, bzw. ob das ÜBERHAUPT geht (MFC in ATL?).
Würde mich also freuen wenn sich jemand erbarmt mir ein klein wenig Hilfestellung zu geben.
Gruß
Alex
-
Entweder macht hier keiner solche "kranken" Sachen oder alle sind in Urlaub

Wenigstens ein "hui, so eine Konstellatiopn ist so außergewöhnlich dass sich hier vermutlich keiner auskennt" hätte ich erwartet.
- Alex
-
Okay, der Monolog geht weiter ...
CreateWindowEx() funktioniert jetzt, ohne direkten Fehler:
// see: http://msdn2.microsoft.com/en-us/library/ms683199.aspx HINSTANCE hInstance = ::GetModuleHandle(L"MyOwnMediaControl"); // see: http://msdn2.microsoft.com/en-us/library/ms632680.aspx // for layered window see: http://msdn2.microsoft.com/en-us/library/ms997507.aspx hChild = ::CreateWindowEx( NULL, // DWORD dwExStyle CAxWindow::GetWndClassName(), // LPCTSTR lpClassName L"", // LPCTSTR lpWindowName WS_VISIBLE & ~WS_BORDER, // DWORD dwStyle 0, // int x 0, // int y 400, // int nWidth 300, // int nHeight NULL, // HWND hWndParent NULL, // HMENU hMenu hInstance, // HINSTANCE hInstance &lParam); // LPVOID lpParam ::ShowWindow(hChild,SW_SHOW); // Set WS_EX_LAYERED on this window ::SetWindowLong(hChild, GWL_EXSTYLE, ::GetWindowLong(hChild, GWL_EXSTYLE) | WS_EX_LAYERED); // Make this window 70% alpha ::SetLayeredWindowAttributes(hChild, 0, (255 * 70) / 100, LWA_ALPHA);Alpha-Blending funktioniert damit schon prima. Allerdings nicht über meinem eigenen DirectShow Video Fenster (ActiveMovieWindow). Über dem Winedows Media Player klappts allerdings. Muss noch rausfinden wo da jetzt der Unterschied ist.
Was noch gar nicht geht ist echte Hintergrund-Transparenz.
Aber ich denke das ist "andere" Probleme die ich in nem neuen Thread behandeln werde (zumindest versuch ich's. Sonst führ ich halt doch wieder einen Monolog).
- Alex