MFC small example description

Posted by GreenMarch on Fri, 21 Jan 2022 20:01:05 +0100

catalogue

I Enum window EnumDialog

        1. Enumeration mode

        2. Find window

        3. Detect whether the current application exists

II Button activate ButtonActive

        1. principle

        2. realization

III Star viewer AsterPassword

        1. Principle

        2. realization

IV Image Explorer

        1. principle

        2. realization

V Simulate mouse button message MouseKeboardClick

        1. principle

        2. realization

Vi Screen magnifier

        1. principle

        2. realization

VII SCreenShot function SCreenShot

        1. principle

        2. realization

VIII ToolTips control prompt box

        1. principle

        2. realization

IX Translucent profiled window TransparentCtrl

        1. Text profile window

         2. Translucent profiled window

        3. Translucent profiled window with control display

        4. Fixed translucent special-shaped window

X Tray button TrayTip

        1. Adaptive window change

        2. Tray icon

Xi summary

I Enum window EnumDialog

        1. Enumeration mode

(1) method 1: GetWindow

	    HWND hChildWnd = ::GetWindow(GetDesktopWindow()->GetSafeHwnd(), GW_CHILD);
	    while (hChildWnd)
	    {
            //Do something

		    hChildWnd = ::GetWindow(hChildWnd, GW_HWNDNEXT);
	    }

(2) mode 2: EnumWindows/EnumChildWindows

        EnumWindows(EnumWindowsPro, (LPARAM)this);//Enumerate all current windows

        ... ...

        BOOL CALLBACK EnumWindowsPro(HWND hWnd, LPARAM lParam)
        {
            //Do something

	        return TRUE;
        }

        2. Find window

                 (1) FindWindow

        HWND hWnd = ::FindWindow(NULL, _T("Calculator"));//Class name, window title

                 (2) FindWindowEx

        //Parent window handle, child window handle, window class name, window title
        HWND hInputWnd = ::FindWindowEx(hWnd, NULL, _T("Window"), NULL);

        3. Detect whether the current application exists

(1) there are several methods to detect the existence of the current application, which are not described one by one here. Only one is introduced here, which meets the following functions: when it is detected that the application already exists, the program will pop up directly and put it on the top.

        TCHAR g_acProName[] = _T("{E91C1C60-DC70-460D-9299-FC67422ECA23}");
        HANDLE g_hValue = (HANDLE)9527;

        ... ...

        void CEnumDialogDlg::CheckProExist()
        {
	        HWND hWnd = nullptr;
	        EnumWindows(EnumExistPro, (LPARAM)&hWnd);
	        if (nullptr != hWnd)//The description window exists
	        {
		        AfxMessageBox(_T("A program is already running..."));
		        ::ShowWindow(hWnd, SW_NORMAL);
		        ::SetForegroundWindow(hWnd);

		        ExitProcess(0);
		        return;
	        }

	        ::SetProp(m_hWnd, g_acProName, g_hValue);//set a property
        }

        ... ...

        BOOL CALLBACK EnumExistPro(HWND hWnd, LPARAM lParam)
        {
	        HANDLE hValue = ::GetProp(hWnd, g_acProName);
	        if (g_hValue == hValue)//Get value 
	        {
		        *(HWND*)lParam = hWnd;//Pass window handle out
		        return FALSE;
	        }

	        return TRUE;
        }

II Button activate ButtonActive

        1. principle

We know that all controls in MFC are essentially window classes, so we can traverse all windows of the current program and activate the inactive windows. This example is only useful for programs developed by MFC. The reason is that many UI s are developed with DirectUI. We know that this interface essentially has only one window and controls are painted.

        2. realization

        EnumChildWindows(hWnd, EnumWindowsPro, NULL);

        ... ...

        BOOL CALLBACK EnumWindowsPro(HWND hWnd, LPARAM lParam)
        {
	        if (FALSE == IsWindowEnabled(hWnd))
	        {
		        EnableWindow(hWnd, TRUE);
	        }

	        return TRUE;
         }

III Star viewer AsterPassword

        1. Principle

The Edit class developed by MFC encapsulates the win32 native control Edit, and we know that the way for the native control to obtain the Edit text is SendMessage(hWnd, WM_GETTEXT, lrTextLen + 1, (LPARAM)pcBuf). Then we can obtain the password as long as we use WindowFromPoint to find the star Edit window.

The interface imitates spy + +. If you try QQ,YY will find that it has no effect at all (don't do bad things). The reason is that QQ uses its own DirectUI library and yy uses qt. If you are familiar with their interface library, you can also write the corresponding viewer.

        2. realization

	    HWND hWndParent = ::WindowFromPoint(pt);//Gets the handle of the window where the current point is located
	    CRect stRect = { 0 };
	    HWND hWndCurrent = ::GetWindow(hWndParent, GW_CHILD);//Gets the child window of the parent window
	    while (NULL != hWndCurrent)
	    {
		    ::GetWindowRect(hWndCurrent, &stRect);//Gets the size of the current window
		    if (::PtInRect(stRect, pt))//Detect whether the mouse position is within the current window
			    break;
		    else
			    hWndCurrent = ::GetWindow(hWndCurrent, GW_HWNDNEXT);//Gets the next sibling window of the current window
	    }


        ... ...

        LRESULT lrTextLen = ::SendMessage(hWnd, WM_GETTEXTLENGTH, 0, 0);//Get Edit text length

IV Image Explorer

        1. principle

Use ListCtrl to obtain the picture information of the folder, and then use PicCtrl to display each item in ListCtrl. gif format pictures are drawn by gdi +.

        2. realization

(1) normal picture drawing: directly draw with CImage

		CImage objImage;
		HRESULT hr = objImage.Load(lpFilePath);
		if (SUCCEEDED(hr))
		{
			CRect stPicRect = { 0 };
			GetDlgItem(IDC_STC_SHOW)->GetClientRect(stPicRect);//Gets the size of the picControl

			//Set picture coordinates
			CRect stImagePos = { 0 };
			AdjustImageRect(stImagePos, stPicRect, objImage.GetWidth(), objImage.GetHeight());

			//Start drawing
			CDC* pDC = CDC::FromHandle(::GetDC(GetDlgItem(IDC_STC_SHOW)->m_hWnd));

			pDC->FillRect(stPicRect, CBrush::FromHandle(::CreateSolidBrush(GetSysColor(COLOR_3DFACE))));//Background fill

			SetStretchBltMode(pDC->m_hDC, COLORONCOLOR);//Before calling strechblt, you need to set the device context to stretch mode
			objImage.StretchBlt(pDC->m_hDC, stImagePos.left, stImagePos.top,
				stImagePos.right - stImagePos.left, stImagePos.bottom - stImagePos.top, SRCCOPY);//Can stretch

			::ReleaseDC(GetDlgItem(IDC_STC_SHOW)->m_hWnd, pDC->m_hDC);
		}

(2) gif format picture drawing

        void CImageExploreDlg::DrawGif(LPCTSTR lpFilePath)
        {
	        if (nullptr == lpFilePath) return;
	

	        m_pGifImage = Gdiplus::Image::FromFile(lpFilePath);//Load picture
	        if (nullptr != m_pGifImage)
	        {
		        //Gets the size of the picControl
		        CRect stPicRect = { 0 };
		        GetDlgItem(IDC_STC_SHOW)->GetClientRect(stPicRect);

	    	    //Set picture coordinates
		        CRect stImagePos;
		        AdjustImageRect(stImagePos, stPicRect, m_pGifImage->GetWidth(), m_pGifImage->GetHeight());

		        //coordinate transformation 
		        m_stGifPos.X = (Gdiplus::REAL)stImagePos.left;
		        m_stGifPos.Y = (Gdiplus::REAL)stImagePos.top;
		        m_stGifPos.Width = (Gdiplus::REAL)stImagePos.Width();
		        m_stGifPos.Height = (Gdiplus::REAL)stImagePos.Height();

		        //draw
		        m_uiCurFrame = 0;
		        SetTimer(1, 100, NULL);
	        }
        }


        ... ...


        void CImageExploreDlg::OnTimer(UINT_PTR nIDEvent)
        {
	        PlayGif();

	        CDialogEx::OnTimer(nIDEvent);
        }

        ... ...


        void CImageExploreDlg::PlayGif()
        {
	        //Get PIC CONTROL size
	        CRect stPicRect = { 0 };
	        GetDlgItem(IDC_STC_SHOW)->GetClientRect(stPicRect);
    
	        //Get gif information
	        GUID acGuidBuf[MAX_PATH] = { 0 };
	        UINT uiCount = m_pGifImage->GetFrameDimensionsCount();//The dimension of the frame of the GIF file
	        m_pGifImage->GetFrameDimensionsList(acGuidBuf, uiCount);// Gets the GUID of the image frame
        	UINT uiFrameSize = m_pGifImage->GetFrameCount(&acGuidBuf[0]);//Total number of motion pictures

	        //draw
        	CDC* pDC = CDC::FromHandle(::GetDC(GetDlgItem(IDC_STC_SHOW)->m_hWnd));

        	Gdiplus::Graphics graphics(pDC->GetSafeHdc());
	        if (0 == m_uiCurFrame) pDC->FillRect(stPicRect, CBrush::FromHandle(::CreateSolidBrush(GetSysColor(COLOR_3DFACE))));//Background fill
	        graphics.DrawImage(m_pGifImage, m_stGifPos, 0, 0, (Gdiplus::REAL)m_pGifImage->GetWidth(), (Gdiplus::REAL)m_pGifImage->GetHeight(), Gdiplus::UnitPixel);
	        //Set gif next frame
	        GUID pageGuid = Gdiplus::FrameDimensionTime;
        	m_uiCurFrame = (++m_uiCurFrame) % uiFrameSize;
        	m_pGifImage->SelectActiveFrame(&pageGuid, m_uiCurFrame);//Get next frame

	        ::ReleaseDC(GetDlgItem(IDC_STC_SHOW)->m_hWnd, pDC->m_hDC);
        }

V Simulate mouse button message MouseKeboardClick

        1. principle

(1) Description: query MSDN, keybd_event and mouse_event is not recommended. Instead, SendInput is used to simulate the key and mouse messages.

(2) keyboard message: simulate the hotkey "Ctrl + Alt + P" of "Netease cloud music" to realize the "play / pause" function. First use

Spy + + obtains the window class name of "Netease cloud", then judges whether the program is open, and sends a keyboard message through SendInput

Drag and drop / pause function

(3) mouse message: when the mouse clicks a button, the mouse will appear on another button and click automatically to realize the click response message of another button.

        2. realization

(1) simulation of keyboard message: it should be explained here that if your computer is equipped with 360, you need to add the program to

360's trust zone. Otherwise, 360 will intercept the SendPut message, and the demo program will not see the effect.

        void CMouseKeyboardClickDlg::OnBnClickedBtnHotkey()
        {
	        HWND hWnd = ::FindWindow(_T("OrpheusBrowserHost"), NULL);//Find the window class of Netease cloud music with spy + +
	        if (!hWnd)
	        {
		        AfxMessageBox(_T("No Netease cloud music program found"));
		        return;
	        }
		
	        INPUT astKey[6] = { 0 };

	        astKey[0].type = astKey[1].type = astKey[2].type = astKey[3].type = astKey[4].type = astKey[5].type = INPUT_KEYBOARD;

	        //Control + Alt + P hotkey: play / pause
	        astKey[0].ki.wVk = astKey[5].ki.wVk = VK_CONTROL;
	        astKey[1].ki.wVk = astKey[4].ki.wVk = VK_MENU;
	        astKey[2].ki.wVk = astKey[3].ki.wVk = 'P';

	        astKey[3].ki.dwFlags = astKey[4].ki.dwFlags = astKey[5].ki.dwFlags = KEYEVENTF_KEYUP;

	        //Use system timestamp
	        astKey[0].ki.time = astKey[1].ki.time = astKey[2].ki.time = GetTickCount();
	        Sleep(100);
	        astKey[3].ki.time = astKey[4].ki.time = astKey[5].ki.time = GetTickCount();

	        SendInput(ARRAYSIZE(astKey), astKey, sizeof(INPUT));
        }

(2) simulation of mouse message

        void CMouseKeyboardClickDlg::OnBnClickedBtnMouse()
        {
	        //Simple test: if external is used exe.  You can use spy + + and:: FindWindow to get the window handle
	        INPUT astKey[2] = { 0 };
	        astKey[0].type = astKey[1].type = INPUT_MOUSE;

	        astKey[0].mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
	        astKey[1].mi.dwFlags = MOUSEEVENTF_LEFTUP;

	        //Reset mouse position
	        CRect stRect = { 0 };
	        ::GetWindowRect(GetDlgItem(IDC_BTN_TESTOBJ)->m_hWnd, &stRect);
	        ::SetCursorPos(stRect.left + (stRect.right - stRect.left) / 2, stRect.top + (stRect.bottom - stRect.top) / 2);

	        SendInput(ARRAYSIZE(astKey), astKey, sizeof(INPUT));
        }

        ... ...

        void CMouseKeyboardClickDlg::OnBnClickedBtnTestobj()
        {
	        // TODO: add control notification handler code here
	        AfxMessageBox(_T("The test button is clicked"));
        }

Vi Screen magnifier

        1. principle

The StretchBlt function is mainly used to scale the bitmap.

        2. realization

        void CScreenMagnifyDlg::StretchScreenToBitmap(CRect stShowRect, UINT uiZoom, CBitmap& bitmapShow)
        {
	        if (nullptr != bitmapShow.m_hObject) bitmapShow.DeleteObject();//If the bitmap exists, the resource is deleted and will be created later to avoid disclosure

	        CDC* pDC = CDC::FromHandle(::GetDC(NULL));

	        int iScreenX = GetSystemMetrics(SM_CXSCREEN);
	        int iScreenY = GetSystemMetrics(SM_CYSCREEN);

	        //=============Gets the bitmap of the entire screen
	        CDC screenDC;
	        CBitmap bitmapScreen;
	        screenDC.CreateCompatibleDC(pDC);
	        bitmapScreen.CreateCompatibleBitmap(pDC, iScreenX, iScreenY);
	        CBitmap* pOldScreen = screenDC.SelectObject(&bitmapScreen);
	        screenDC.BitBlt(0, 0, iScreenX, iScreenY, pDC, 0, 0, SRCCOPY);

	        //=============Gets the bitmap of the enlarged screen area
	        CDC memDC;
	        CRect stRect = { 0 };

	        //
	        int iWidth = stShowRect.Width() / uiZoom;
	        int iHeight = stShowRect.Height() / uiZoom;

	        //Get the enlarged screen coordinates
	        AdjustRect(iScreenX, iScreenY, iWidth, iHeight, stRect);

	        //Get bitmap
	        memDC.CreateCompatibleDC(pDC);
	        bitmapShow.CreateCompatibleBitmap(pDC, stShowRect.Width(), stShowRect.Height());
	        CBitmap* pOldMem = memDC.SelectObject(&bitmapShow);
	        SetStretchBltMode(memDC.m_hDC, COLORONCOLOR);//Set the device context to stretch mode
	        memDC.StretchBlt(0, 0, stShowRect.Width(), stShowRect.Height(), &screenDC, stRect.left, stRect.top, iWidth, iHeight, SRCCOPY);

	        //Release resources
	        screenDC.SelectObject(pOldScreen);
	        memDC.SelectObject(pOldMem);

	        bitmapScreen.DeleteObject();

	        ::ReleaseDC(NULL, pDC->m_hDC);
        }

VII SCreenShot function SCreenShot

        1. principle

Let's just talk about the principle of "custom screenshot". Create a full screen modal dialog box, copy the current screen bitmap to the dialog box, and obtain the areas that need screenshots in combination with the crittracker rubber band class, instead of the areas that do not need screenshots

gdi + draw a gray mask, so you can achieve the effect of QQ screenshot. Of course, only the core part is processed here. To achieve a complete effect, you only need to use WindowFromPoint to obtain the window area, and then add a Tooltip at the lower right of the screenshot. To realize its function is a complete QQ screenshot.

        2. realization

        void CCaptureDlg::DrawFrame()
        {
	        CDC* pDC = GetDC(); //CDC::FromHandle(::GetDC(NULL));

	        //Double buffering to avoid flicker
	        CDC BufferDC;
	        CBitmap bitBuffer;
	        BufferDC.CreateCompatibleDC(pDC);
	        bitBuffer.CreateCompatibleBitmap(pDC, m_iScreenX, m_iScreenY);
	        CBitmap* pOldBitBuffer = BufferDC.SelectObject(&bitBuffer);

	        CDC memDC;
	        memDC.CreateCompatibleDC(&BufferDC);
	        CBitmap* pOldBitMem = memDC.SelectObject(&m_ScreenBit);

	        BufferDC.BitBlt(0, 0, m_iScreenX, m_iScreenY, &memDC, 0, 0, SRCCOPY);//Copy bitmap on memory DC to window DC

	        if ((0 == m_rect.Width()) && (0 == m_rect.Height()))
	        {
		        m_objGdiplusDraw.DrawImage(&BufferDC, 0, 0, m_iScreenX, m_iScreenY,  0, 0, 8, 8 );//Mask the picture
	        }
	        else//Redraw while dragging the mouse
	        {
		        AdjustRect(m_rect);//Prevent dragging the rectangular box out of bounds. When the coordinates are negative, readjust the coordinates

		        CRect stRect = m_rect;
		        stRect.NormalizeRect();//Adjust rectangular coordinates; eg, press and hold the mouse to drag down, and the coordinates are normal; What if you hold down the mouse and drag it up? This function inverts the coordinates
	
		        CRectTracker::Draw(&BufferDC);//Draw a rectangle	
		        m_objGdiplusDraw.DrawImage(&BufferDC, 0, 0, m_iScreenX, stRect.top, 0, 0, 8, 8);//Upper region
		        m_objGdiplusDraw.DrawImage(&BufferDC, 0, stRect.top, stRect.left, stRect.Height(), 0, 0, 8, 8);//Left region
		        m_objGdiplusDraw.DrawImage(&BufferDC, stRect.right, stRect.top, m_iScreenX - stRect.right, stRect.Height(), 0, 0, 8, 8);//Right area
		        m_objGdiplusDraw.DrawImage(&BufferDC, 0, stRect.bottom, m_iScreenX, m_iScreenY - stRect.bottom, 0, 0, 8, 8);//Lower area
	        }

	        pDC->BitBlt(0, 0, m_iScreenX, m_iScreenY, &BufferDC, 0, 0, SRCCOPY);//Paste the contents of the memory DC on the screen DC at one time

	        memDC.SelectObject(pOldBitMem);
	        BufferDC.SelectObject(pOldBitBuffer);
	        ReleaseDC(pDC);
        }

VIII ToolTips control prompt box

        1. principle

MFC encapsulates a ctooltiptctrl class for us, which is specially used to provide "prompt box window" for controls and windows

        2. realization

(1) static addition: when the content of the window changes, the content of the prompt box remains unchanged.

	    CString strDirPath = _T("C:\\Program Files (x86)\\Tencent\\WeMeet\\2.8.8.403\\resource\\Default\\html");

	    //Static addition
	    if (!m_objToolTip.Create(this, TTS_ALWAYSTIP)) return FALSE;
	    ::SetWindowTheme(m_objToolTip.m_hWnd, _T(""), _T(""));//Compatible with different visual style information sets to solve the failure of SetTipTextColor
	    SetDlgItemText(IDC_EDIT_STATIC, strDirPath);

	    m_objToolTip.SetTipTextColor(RGB(99, 184, 255));
	    m_objToolTip.AddTool(GetDlgItem(IDC_EDIT_STATIC), strDirPath);
	    m_objToolTip.Activate(TRUE);


        ... ...


        BOOL CToolTipsDlg::PreTranslateMessage(MSG* pMsg)
        {
	        if (NULL != m_objToolTip.m_hWnd) m_objToolTip.RelayEvent(pMsg);//Transfer the mouse message to the prompt tool class for processing

	        return CDialogEx::PreTranslateMessage(pMsg);
        }

(2) dynamic addition: when the content of the window changes, the content of the prompt box changes.

	    if (!m_objToolTipAuto.Create(this)) return FALSE;
	    SetDlgItemText(IDC_EDIT_AUTO, strDirPath);
	    EnableToolTips(TRUE);//Main window allow prompt

        ... ...

        ON_NOTIFY_EX(TTN_NEEDTEXT, 0, &CToolTipsDlg::OnTtnNeedText)

        ... ...

        BOOL CToolTipsDlg::OnTtnNeedText(UINT id, NMHDR *pNMHDR, LRESULT *pResult)//Example given by msdn
        {
	        UNREFERENCED_PARAMETER(id);//No treatment

	        TOOLTIPTEXT *pTTT = (TOOLTIPTEXT *)pNMHDR;
	        UINT_PTR nID = pNMHDR->idFrom; //Get the target window ID, which may be HWND
	        BOOL bRet = FALSE;

	        if (pTTT->uFlags & TTF_IDISHWND) { //Indicates whether nID is HWND

		        bRet = TRUE;
	        	// idFrom is actually the HWND of the tool
	        	nID = ::GetDlgCtrlID((HWND)nID); //Get the ID value from HWND. Of course, you can also judge by HWND value
		        switch (nID)
		        {
		        case IDC_EDIT_AUTO:
			        m_strText.Empty();
			        GetDlgItemText(nID, m_strText);
			        pTTT->lpszText = (LPTSTR)(LPCTSTR)m_strText;
			        pTTT->hinst = AfxGetResourceHandle();
		        default:break;
	    	    }
	        }

        	*pResult = 0;

	        return bRet;
        }

IX Translucent profiled window TransparentCtrl

        1. Text profile window

(1) clipping region: if a window is placed in the clipping region, the part of the window that is not in the region will be clipped. Moreover, MFC also helps us encapsulate the CRgn class, which is very simple. For details, please refer to:

https://blog.csdn.net/qwdpoiguw/article/details/72898473

(2) path: the path is also a GDI object. Unlike brushes and brushes, MFC does not help us encapsulate the path, so we need to call the CDC API function for operation, and only a specific path function is useful when drawing the path, starting with beginPath and ending with endPath. The introduction of path mainly enriches the text display function. For details, please refer to

https://blog.csdn.net/love_xsq/article/details/53635163

(3) principle: first use the path to draw the font, then convert the path into an area, and finally put the window into the area, so that the special-shaped text window is formed.

(4) realization

        void CFontDlg::DrawFont()
        {
	        CRect stRect;
	        GetWindowRect(&stRect);

	        CDC* pDC = CDC::FromHandle(::GetDC(GetSafeHwnd()));
	        //Create font
	        CFont ojbFont;
	        ojbFont.CreateFont(
		100, 40, 0, 0, FW_HEAVY, TRUE, FALSE,
		0, ANSI_CHARSET, OUT_DEFAULT_PRECIS,
		CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
		DEFAULT_PITCH | FF_SWISS, _T("Song typeface"));

	        //transparent
	        pDC->SetBkMode(TRANSPARENT);
	        CFont *pOldFont = pDC->SelectObject(&ojbFont);
	        //Path drawing
	        pDC->BeginPath();
	        pDC->TextOut(0, 0, _T("I like watching the sunrise"));
	        pDC->EndPath();
	        pDC->SelectObject(pOldFont);

	        //Convert path to region
	        HRGN wndRgn = ::PathToRegion(pDC->m_hDC);
	        SetWindowRgn(wndRgn, TRUE);

	        ::ReleaseDC(GetSafeHwnd(), pDC->m_hDC);
        }

         2. Translucent profiled window

(1) precondition: ModifyStyleEx(0, WS_EX_LAYERED); Windows must be hierarchical

(2) function description: SetLayeredWindowAttributes sets the transparency of the entire window;

UpdateLayeredWindow can cut out the transparent area of the bitmap on the basis of setting the transparency of the window

(3) realization

            void CTransDlg::DrawTransDlg()
            {
	            ModifyStyleEx(0, WS_EX_LAYERED);//Set layered window

	            //Get window size
	            CRect stWindRect = { 0 };
	            GetWindowRect(&stWindRect);

	            //Get picture resources
	            //Image* pImage = Image::FromFile(_T(".\\res\\background.png"));
	            Image * pImage = CUtility::LoadImage(IDB_PNG2, _T("PNG"), AfxGetResourceHandle());
	            if (nullptr == pImage) return;

	            //Get picture size
	            int iWidth = pImage->GetWidth();
	            int iHeight = pImage->GetHeight();

	            //Modify window size
	            MoveWindow(stWindRect.left, stWindRect.top, iWidth, iHeight);

	            //mapping
	            CDC* pDC = CDC::FromHandle(::GetDC(GetSafeHwnd()));

	            //Memory DC select bitmap
	            CDC memDC;
	            CBitmap bmpMem;
	            memDC.CreateCompatibleDC(pDC);
	            bmpMem.CreateCompatibleBitmap(pDC, iWidth, iHeight);
	            CBitmap* pOldMem = memDC.SelectObject(&bmpMem);

	            //gdi + draw pictures
	            Graphics graphics(memDC);//Draw on memory DC
	            graphics.DrawImage(pImage, 0, 0, iWidth, iHeight);

	            //Update window parameter settings
	            POINT ptWinPos = { stWindRect.left, stWindRect.top };
	            POINT ptSrc = { 0, 0 };
	            SIZE sizeWindow = { iWidth, iHeight };
	            BLENDFUNCTION bf;
	            bf.BlendOp = AC_SRC_OVER;
	            bf.BlendFlags = 0;
	            bf.AlphaFormat = AC_SRC_ALPHA;
	            bf.SourceConstantAlpha = 254;

	            //update windows
	            UpdateLayeredWindow(pDC, &ptWinPos, &sizeWindow, &memDC, &ptSrc, 0, &bf, ULW_ALPHA);

	            //Release resources 
	            memDC.SelectObject(pOldMem);
	            DeleteObject(bmpMem);

	            ::ReleaseDC(GetSafeHwnd(), pDC->m_hDC);
            }

(4) question: there is a translucent special-shaped form, but the button control I put on the window is missing. What's going on? Look at the window drawing section. gdi + only draws translucent pictures, and then updates the window. Of course, you can only see translucent bitmaps. How do I see the buttons I drag and drop on the window?

        3. Translucent profiled window with control display

(1) continue the above problem: if we want to see the control we drag and drop, we can only draw the control on the bitmap before updating the window, so that we can see it! OK, according to this idea, we need to draw all the controls on the window before updating the window! I happen to see an example like this: https://www.codeproject.com/Articles/34158/Cool-Semi-transparent-and-Shaped-Dialogs-with-Stan.

(2) principle: use two windows, one original window A and one display window B. Create A window B of the same size as window A, paste window B on window A, and set window A to be fully transparent. Then through SendMessage: WM_PRINT takes A snapshot of all controls on window A and draws it on the bitmap of window B.

In this way, the original window A is used to process controls and window messages, and window B is used to display to the user. The reason why the user can see the behavior change of the fake control (eg, clicking the button will change) is that all the controls on the original window A are subclassed, and the control snapshot is refreshed to window B for display.

(3) realization

            void CImgDialogBase::Refresh(void)
            {
	            if( m_bIsRefreshing )
	            	return;

	            if( !IsWindow(m_hFakeWnd) )
		            return;

	            m_bIsRefreshing = TRUE;

	            RECT rc;
            	::GetWindowRect( m_hFakeWnd, &rc);
            	POINT ptSrc = { 0, 0};
	            POINT ptWinPos = { rc.left, rc.top};
            	SIZE szWin = { m_nWidth, m_nHeigh };
	            BLENDFUNCTION stBlend = { AC_SRC_OVER, 0, m_nAlpha, AC_SRC_ALPHA };
    

	            HDC hDC = ::GetDC(m_hFakeWnd);
	            HDC hdcMemory = ::CreateCompatibleDC(hDC);

	            BITMAPINFOHEADER stBmpInfoHeader = { 0 };   
	            int nBytesPerLine = ((m_nWidth * 32 + 31) & (~31)) >> 3;
	            stBmpInfoHeader.biSize = sizeof(BITMAPINFOHEADER);   
	            stBmpInfoHeader.biWidth = m_nWidth;   
	            stBmpInfoHeader.biHeight = m_nHeigh;   
	            stBmpInfoHeader.biPlanes = 1;   
	            stBmpInfoHeader.biBitCount = 32;   
	            stBmpInfoHeader.biCompression = BI_RGB;   
	            stBmpInfoHeader.biClrUsed = 0;   
	            stBmpInfoHeader.biSizeImage = nBytesPerLine * m_nHeigh;   

	            PVOID pvBits = NULL;   
	            HBITMAP hbmpMem = ::CreateDIBSection(NULL, (PBITMAPINFO)&stBmpInfoHeader, DIB_RGB_COLORS, &pvBits, NULL, 0);
	            ASSERT(hbmpMem != NULL);
	            memset( pvBits, 0, m_nWidth * 4 * m_nHeigh);
	            if(hbmpMem)   
	            {   
		            HGDIOBJ hbmpOld = ::SelectObject( hdcMemory, hbmpMem); 
		            Graphics graph(hdcMemory);
		
		            graph.SetSmoothingMode(SmoothingModeNone);
		
		            // Draw the background
	            	graph.DrawImage( m_pImage, 0, 0, m_nWidth, m_nHeigh);

	            	// On draw 
		            OnDraw(graph);
	        	
		            // Draw all the controls
		            HWND hwndChild = ::GetWindow( GetSafeHwnd(), GW_CHILD);  
		            while(hwndChild)   
		            {
			            DrawCtrl( graph, hDC, hwndChild);
			            hwndChild = ::GetWindow( hwndChild, GW_HWNDNEXT);   
		            }

		            // draw the caret
		            DrawCaret(graph);			
		
		            ::UpdateLayeredWindow( m_hFakeWnd
			                , hDC
			                , &ptWinPos
			                , &szWin
			                , hdcMemory
		                	, &ptSrc
			                , 0
		                	, &stBlend
			                , ULW_ALPHA
			                );

		                graph.ReleaseHDC(hdcMemory);
		                ::SelectObject( hdcMemory, hbmpOld);   
		                ::DeleteObject(hbmpMem); 
	            }

	
	            ::DeleteDC(hdcMemory);
	            ::DeleteDC(hDC);

	            m_bIsRefreshing = FALSE;
            }

(4) problem: there are also problems here, such as Edit has no cursor, Slider has no Slider, listCtrl scroll bar background is black, etc. the program specially adds a cursor for Edit. What should I do if the cursor position changes? Obviously, this design is also unreasonable. Users need to set a new shape state for controls that do not meet the requirements. What should I do?

        4. Fixed translucent special-shaped window

(1) continue the above question: is it possible to place the control of the original window A on the bitmap of window B and empty it, so that we can see the control of the original window. We will expand further according to this idea.

(2) principle: still use two windows, original window A and special-shaped window B. Cut out all the controls on window A, find their union C, and then find the complement between windows B and C. in this way, the control area of window A is buckled out on window B.

(3) realization

            void CMyDrawBase::OnDraw()
            {
	            if (!IsWindow(m_hFakeWnd)) return;

            	//Get window size
            	CRect stWindRect = { 0 };
            	::GetWindowRect(m_hFakeWnd, &stWindRect);

            	//mapping
            	CDC* pDC = CDC::FromHandle(::GetDC(m_hFakeWnd));

            	//Memory DC select bitmap
            	CDC memDC;
            	CBitmap bmpMem;
            	memDC.CreateCompatibleDC(pDC);
            	bmpMem.CreateCompatibleBitmap(pDC, m_nWidth, m_nHeight);
            	CBitmap* pOldMem = memDC.SelectObject(&bmpMem);

	            //gdi + draw pictures
            	Graphics graphics(memDC);//Draw on memory DC
	            graphics.DrawImage(m_pImage, 0, 0, m_nWidth, m_nHeight);
            
	            //Clipping Region 
            	CRgn objRgnCtrl, objRgnTemp, objRgnFakeWnd;
            	GetCtrlRgn(objRgnCtrl, objRgnTemp);//Gets the region union of all controls
            	objRgnFakeWnd.CreateRectRgn(0, 0, m_nWidth, m_nHeight);//Get fakewnd area
            	objRgnFakeWnd.CombineRgn(&objRgnFakeWnd, &objRgnCtrl, RGN_DIFF);//Remove the area of the control
	            pDC->SelectObject(&objRgnFakeWnd);

	            //Update window parameter settings
            	POINT ptWinPos = { stWindRect.left, stWindRect.top };
            	POINT ptSrc = { 0, 0 };
            	SIZE sizeWindow = { m_nWidth, m_nHeight };
            	BLENDFUNCTION bf;
            	bf.BlendOp = AC_SRC_OVER;
            	bf.BlendFlags = 0;
            	bf.AlphaFormat = AC_SRC_ALPHA;
            	bf.SourceConstantAlpha = m_iAlpha;

	            //update windows
	            ::UpdateLayeredWindow(m_hFakeWnd, pDC->m_hDC, &ptWinPos, &sizeWindow, memDC.m_hDC, &ptSrc, 0, &bf, ULW_ALPHA);

	            //Place window in Crop Region
	            ::SetWindowRgn(m_hWnd, objRgnCtrl, TRUE);
	            ::SetWindowRgn(m_hFakeWnd, objRgnFakeWnd, TRUE);
	
	            //Release resources 
            	memDC.SelectObject(pOldMem);
            	DeleteObject(bmpMem);

	            objRgnCtrl.DeleteObject();
	            objRgnTemp.DeleteObject();
	            objRgnFakeWnd.DeleteObject();

	            ::ReleaseDC(m_hFakeWnd, pDC->m_hDC);
            }

            void CMyDrawBase::GetCtrlRgn(CRgn& rgnDst, CRgn& rgnSrc)
            {
	            CRect stRect;
	            HWND hwndChild = ::GetWindow(m_hWnd, GW_CHILD);//Traverse all controls under the main window

	            //Gets the region of the first control
	            ::GetWindowRect(hwndChild, &stRect);
            	ScreenToClient(&stRect);
	            rgnDst.CreateRectRgnIndirect(stRect);

	            while (hwndChild)
            	{
	            	hwndChild = ::GetWindow(hwndChild, GW_HWNDNEXT);
	            	if (!IsWindow(hwndChild)) break;
	            	//Get the nth control area
	            	::GetWindowRect(hwndChild, &stRect);
	                ScreenToClient(&stRect);
		            rgnSrc.CreateRectRgnIndirect(stRect);
    
	            	rgnDst.CombineRgn(&rgnDst, &rgnSrc, RGN_OR);//Find the union of two regions
	            	rgnSrc.DeleteObject();//Be sure to delete it, or there will be a memory leak
	            }
            }

(4) renderings

X Tray button TrayTip

        1. Adaptive window change

(1) principle: record the position of the original window and all controls. When the window size changes, calculate the window scaling in OnSize, and then resize the controls through the scaling.

(2) realization

            int CTrayTilDlg::CalSize(float fSize)//Fix control coordinate values
            {
	            int iTemp = (int)fSize;
	            float fTemp = fSize - (float)iTemp;

	            if (0 >= fSize)
	            	return iTemp + (int)((fTemp > -m_fReferSize) ? 0 : -1.0);//
	            else
		            return iTemp + (int)((fTemp < m_fReferSize) ? 0 : 1.0);//
            }

            void CTrayTilDlg::ReSize(UINT uiType, int iX, int iY)
            {
            	if ((0 == m_stOldRect.Width()) || (0 == m_stOldRect.Height())) return;

            	float fZoomX = (float)(iX - m_stOldRect.Width()) / (float)m_stOldRect.Width();
	            float fZoomY = (float)(iY - m_stOldRect.Height()) / (float)m_stOldRect.Height();

	            HWND hChildWnd = ::GetWindow(GetSafeHwnd(), GW_CHILD);
	            while (hChildWnd)
	            {
	            	CRect stRect = m_mapCtrl.at(::GetDlgCtrlID(hChildWnd));//use. at will detect the serial number value and report an error, [] will not
	            	int iLeft = stRect.left + CalSize(stRect.left * fZoomX);
	            	int iTop = stRect.top + CalSize(stRect.top * fZoomY);
		            int iWidth = stRect.Width() + CalSize(stRect.Width() * fZoomX);
		            int iHeight = stRect.Height() + CalSize(stRect.Height() * fZoomY);
		
		            ::MoveWindow(hChildWnd, iLeft, iTop, iWidth, iHeight, FALSE);//Reset control position

		            hChildWnd = ::GetWindow(hChildWnd, GW_HWNDNEXT);
	            }
	            Invalidate(TRUE);//Redraw the entire window
            }
            

        2. Tray icon

(1) principle: while creating the tray icon, use the timer to replace the bitmap of the tray icon to achieve the effect of QQ flashing, and use WM_TRAY_MSG message response to achieve the effect of double clicking the pop-up window and right clicking the pop-up menu.

(2) realization

            void CTrayTilDlg::MyTrayIcon(DWORD dwMessage, DWORD dwIconIDd, LPCTSTR lpTipTitle)
            {
	            NOTIFYICONDATA stNotifyIcon = { 0 };

	            stNotifyIcon.cbSize = sizeof (NOTIFYICONDATA);
	            stNotifyIcon.hWnd = GetSafeHwnd();
	            stNotifyIcon.uID = IDR_MAINFRAME;//Note that after ADD, this value is used as a unique identifier, whether Delete or Modify


	            stNotifyIcon.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP | NIF_INFO;//NIF_INFO bubble prompt
	            stNotifyIcon.uCallbackMessage = WM_TRAY_MSG;//Set the response message of the callback function
	            stNotifyIcon.hIcon = LoadIcon(AfxGetInstanceHandle(), MAKEINTRESOURCE(dwIconIDd));//Get bitmap

	            if (nullptr != lpTipTitle)
              	{
		            _tcscpy_s(stNotifyIcon.szTip, ARRAYSIZE(stNotifyIcon.szTip), lpTipTitle);
	            }

	            _tcscpy_s(stNotifyIcon.szInfo, ARRAYSIZE(stNotifyIcon.szInfo), _T("This is a bubble tip"));
	            _tcscpy_s(stNotifyIcon.szInfoTitle, ARRAYSIZE(stNotifyIcon.szInfoTitle), _T("Bubble tip title"));

	            Shell_NotifyIcon(dwMessage, &stNotifyIcon);
            }

            ... ...

            void CTrayTilDlg::OnTimer(UINT_PTR nIDEvent)
            {
	            // TODO: add message handler code here and / or call default values
	            static BOOL bTrayIcon = TRUE;
            	bTrayIcon = !bTrayIcon;
	            DWORD dwIconID = (TRUE == bTrayIcon) ? IDI_ICON1 : IDR_MAINFRAME;

	            MyTrayIcon(NIM_MODIFY, dwIconID, NULL);

	            CDialogEx::OnTimer(nIDEvent);
            }

            ... ...

            LRESULT CTrayTilDlg::OnTrayIcon(WPARAM wParam, LPARAM lParam)
            {
            	UINT uiMessage = (UINT)lParam;

	            switch (uiMessage)
            	{
	            case WM_RBUTTONUP://Pop-up Menu 
	            {
	            	CMenu objMenu, *pobjMenu;
	            	objMenu.LoadMenu(IDR_MENU_TRAY);
		            pobjMenu = objMenu.GetSubMenu(0);

		            CPoint stPoint;
		            GetCursorPos(&stPoint);
		            pobjMenu->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, stPoint.x, stPoint.y, this);
	            }
		        break;
	            case WM_LBUTTONDBLCLK://Double click the popup
	            {
	            	KillTimer(1);
	            	ShowWindow(SW_SHOW);//Normal display window
		            MyTrayIcon(NIM_MODIFY, IDR_MAINFRAME, NULL);//Prevent tray icons from appearing transparent
	            }
	          	break;
	            default:
		            break;
	            }
	            return TRUE;
            }

Xi summary

          1. These examples are based on the tutorial of syc of VC post station. Some examples have been extended to be familiar with MFC programming

          2. Download address: https://download.csdn.net/download/zhoumin4576/20030100

Topics: Windows MFC