时钟.exe

【投稿赢 iPhone 17】「我的第一个开源项目」故事征集:用代码换C位出道! 10w+人浏览 1.7k人参与

这里写自定义目录标题

花了3天做了一个小时钟。。。中途无数次想放弃。。。
最终还是做出来了,哈哈哈哈哈啊哈哈哈~~~~~~

在这里插入图片描述

与大家分享一下源码。一起学习。

basewin.h

template <class DERIVED_TYPE> 
class BaseWindow
{
public:
    static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        DERIVED_TYPE *pThis = NULL;

        if (uMsg == WM_NCCREATE)
        {
            CREATESTRUCT* pCreate = (CREATESTRUCT*)lParam;
            pThis = (DERIVED_TYPE*)pCreate->lpCreateParams;
            SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pThis);

            pThis->m_hwnd = hwnd;
        }
        else
        {
            pThis = (DERIVED_TYPE*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
        }
        if (pThis)
        {
            return pThis->HandleMessage(uMsg, wParam, lParam);
        }
        else
        {
            return DefWindowProc(hwnd, uMsg, wParam, lParam);
        }
    }

    BaseWindow() : m_hwnd(NULL) { }

    BOOL Create(
        PCWSTR lpWindowName,
        DWORD dwStyle,
        DWORD dwExStyle = 0,
        int x = CW_USEDEFAULT,
        int y = CW_USEDEFAULT,
        int nWidth = CW_USEDEFAULT,
        int nHeight = CW_USEDEFAULT,
        HWND hWndParent = 0,
        HMENU hMenu = 0
        )
    {
        WNDCLASSW wc = {0};

        wc.lpfnWndProc   = DERIVED_TYPE::WindowProc;
        wc.hInstance     = GetModuleHandle(NULL);
        wc.lpszClassName = ClassName();

         if (!RegisterClassW(&wc)) // 显式调用 RegisterClassW
         {
             return FALSE;
         }

        m_hwnd = CreateWindowExW(
            dwExStyle, ClassName(), lpWindowName, dwStyle, x, y,
            nWidth, nHeight, hWndParent, hMenu, GetModuleHandle(NULL), this
            );

        return (m_hwnd ? TRUE : FALSE);
    }

    HWND Window() const { return m_hwnd; }

protected:

    virtual PCWSTR  ClassName() const = 0;
    virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) = 0;

    HWND m_hwnd;
};

scene.h

#pragma once

#include <d2d1.h>
#include <dwrite.h>
#include <atlbase.h>
#include <atlcomcli.h>

class GraphicsScene 
{
protected:
    // D2D Resources
    CComPtr<ID2D1Factory>           m_pFactory;
    CComPtr<ID2D1HwndRenderTarget>  m_pRenderTarget;

    float   m_fScaleX;
    float   m_fScaleY;

public: 
    CAdapt<CComPtr<IDWriteFactory>> m_pDWriteFactory;

    // Derived class must implement these methods.
    virtual HRESULT CreateDeviceIndependentResources() = 0;
    virtual void    DiscardDeviceIndependentResources() = 0;
    virtual HRESULT CreateDeviceDependentResources() = 0;
    virtual void    DiscardDeviceDependentResources() = 0;
    virtual void    CalculateLayout() = 0;
    virtual void    RenderScene() = 0;

protected:
    HRESULT CreateGraphicsResources(HWND hwnd)
    {
        HRESULT hr = S_OK;
        if (m_pRenderTarget == NULL)
        {
            RECT rc;
            GetClientRect(hwnd, &rc);

            D2D1_SIZE_U size = D2D1::SizeU(rc.right, rc.bottom);

            hr = m_pFactory->CreateHwndRenderTarget(
                D2D1::RenderTargetProperties(),
                D2D1::HwndRenderTargetProperties(hwnd, size),
                &m_pRenderTarget
                );

            if (SUCCEEDED(hr))
            {
                hr = CreateDeviceDependentResources();
            }
            if (SUCCEEDED(hr))
            {
                CalculateLayout();
            }
        }
        return hr;
    }

    template <class T>
    T PixelToDipX(T pixels) const
    {
        return static_cast<T>(pixels / m_fScaleX);
    }

    template <class T>
    T PixelToDipY(T pixels) const
    {
        return static_cast<T>(pixels / m_fScaleY);
    }

public:
    GraphicsScene() : m_fScaleX(1.0f), m_fScaleY(1.0f) {}
    virtual ~GraphicsScene() {}

    HRESULT Initialize()
    {
        HRESULT hr = DWriteCreateFactory(
            DWRITE_FACTORY_TYPE_SHARED,
            __uuidof(IDWriteFactory),
            reinterpret_cast<IUnknown**>(&m_pDWriteFactory)
        ); 

        if (SUCCEEDED(hr))
        {
            hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &m_pFactory);
            // 注意:这里应该检查 hr,但原代码没有,我们保留原逻辑
        }

        if (SUCCEEDED(hr))
        {
            hr = CreateDeviceIndependentResources();
        }
        return hr;
    }

    void Render(HWND hwnd)
    {
        HRESULT hr = CreateGraphicsResources(hwnd);
        if (FAILED(hr))
        {
            return;
        }

        assert(m_pRenderTarget != NULL);

        m_pRenderTarget->BeginDraw();
        RenderScene(); // 调用你写的绘制逻辑
       
        hr = m_pRenderTarget->EndDraw();
        if (hr == D2DERR_RECREATE_TARGET)
        {
            // 设备丢失,清理资源,下次重绘时重建
            DiscardDeviceDependentResources();
            m_pRenderTarget.Release();
        }
        else if (FAILED(hr))
        {
            // 其他错误
            OutputDebugString(L"EndDraw failed\n");
        }
    }

    virtual HRESULT Resize(UINT width, UINT height)
    {
        HRESULT hr = S_OK;
        if (m_pRenderTarget)
        {
            hr = m_pRenderTarget->Resize(D2D1::SizeU(width, height));
            if (SUCCEEDED(hr))
            {
                CalculateLayout();
            }
        }
        return hr;
    }

    void CleanUp()
    {
        DiscardDeviceDependentResources();
        DiscardDeviceIndependentResources();
    }
};

naozhong.cpp

#define _USE_MATH_DEFINES
#include <cmath>
#include <D2d1.h>
#include <dwrite.h>
#include <assert.h>
#include <atlbase.h>
#include <wrl/client.h> // Microsoft::WRL::ComPtr
#include <atlcomcli.h> // CAdapt


#pragma comment(lib, "d2d1")
#pragma comment(lib, "dwrite")

#include "basewin.h"
#include "scene.h"


class Scene : public GraphicsScene
{
public: 
    D2D_POINT_2F m_NumberPositions[12];  
    CComPtr<ID2D1SolidColorBrush> m_pFill;
    CComPtr<ID2D1SolidColorBrush> m_pStroke;
    CComPtr<IDWriteTextFormat> m_pTextFormat;
    CComPtr<IDWriteTextLayout> m_pTextLayouts[12];
    D2D1_ELLIPSE m_ellipse;
    D2D_POINT_2F m_Ticks[24];


    Scene() 
    {
        // 构造函数可以留空或进行一些初始化
    }

    HRESULT CreateDeviceIndependentResources() override;
    void    DiscardDeviceIndependentResources() override; 
    HRESULT CreateDeviceDependentResources() override;
    void    DiscardDeviceDependentResources() override;
    void    RenderScene() override;
    void    CalculateLayout() override;

    void    DrawClockHand(float fHandLength, float fAngle, float fStrokeWidth);

    HRESULT Resize(UINT width, UINT height) override
    {
        return GraphicsScene::Resize(width, height);
        // CalculateLayout() 已在基类 Resize 中调用,无需再次调用
    } 
};

HRESULT Scene::CreateDeviceIndependentResources()
{
    // m_pDWriteFactory 已在 GraphicsScene::Initialize 中创建
    // 此处可以创建其他设备无关资源,如文本格式
    HRESULT hr = m_pDWriteFactory->CreateTextFormat(
        L"Segoe UI",          
        nullptr,                 
        DWRITE_FONT_WEIGHT_NORMAL,
        DWRITE_FONT_STYLE_NORMAL,
        DWRITE_FONT_STRETCH_NORMAL,
        16.0f,                
        L"",                  
        &m_pTextFormat
        );
    return hr;
}
void Scene::DiscardDeviceIndependentResources()
{
    m_pTextFormat.Release();
}


HRESULT Scene::CreateDeviceDependentResources()
{
    HRESULT hr = m_pRenderTarget->CreateSolidColorBrush(
        D2D1::ColorF(1.0f, 1.0f, 0),
        &m_pFill
        );

    if (SUCCEEDED(hr))
    {
        hr = m_pRenderTarget->CreateSolidColorBrush(
            D2D1::ColorF(0, 0, 0),
            &m_pStroke
            );
    }
    return hr;
}
void Scene::DrawClockHand(float fHandLength, float fAngle, float fStrokeWidth)
{
    D2D1_MATRIX_3X2_F oldTransform;
    m_pRenderTarget->GetTransform(&oldTransform);   
    m_pRenderTarget->SetTransform(
        D2D1::Matrix3x2F::Rotation(fAngle, m_ellipse.point)
            );

    // endPoint defines one end of the hand.
    D2D_POINT_2F endPoint = D2D1::Point2F(
        m_ellipse.point.x,
        m_ellipse.point.y - (m_ellipse.radiusY * fHandLength)
        );

    // Draw a line from the center of the ellipse to endPoint.
    m_pRenderTarget->DrawLine(
        m_ellipse.point, endPoint, m_pStroke, fStrokeWidth);
    m_pRenderTarget->SetTransform(oldTransform); // 恢复
   
}


void Scene::RenderScene()
{  
    m_pRenderTarget->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
   
    m_pRenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::SkyBlue));

    m_pRenderTarget->FillEllipse(m_ellipse, m_pFill);
    m_pRenderTarget->DrawEllipse(m_ellipse, m_pStroke);

    // Draw tick marks
    for (DWORD i = 0; i < 12; i++)
    {
        m_pRenderTarget->DrawLine(m_Ticks[i*2], m_Ticks[i*2+1], m_pStroke, 2.0f);
    }
    //
    for (int i = 0; i < 12; i++)
    {
        DWRITE_TEXT_METRICS metrics = {};
        if (SUCCEEDED(m_pTextLayouts[i]->GetMetrics(&metrics)))
        {
            D2D_POINT_2F origin = D2D1::Point2F(
                m_NumberPositions[i].x - metrics.width / 2,
                m_NumberPositions[i].y - metrics.height / 2
            );
            m_pRenderTarget->DrawTextLayout(origin, m_pTextLayouts[i], m_pStroke);
        }     
    }    
    
    // Draw hands
    SYSTEMTIME time;
    GetLocalTime(&time);

    // 60 minutes = 30 degrees, 1 minute = 0.5 degree
    const float fHourAngle = (360.0f / 12) * (time.wHour) + (time.wMinute * 0.5f); 
    const float fMinuteAngle =(360.0f / 60) * (time.wMinute);

    const float fSecondAngle = 
        (360.0f / 60) * (time.wSecond) + (360.0f / 60000) * (time.wMilliseconds);

    DrawClockHand(0.6f,  fHourAngle,   6);
    DrawClockHand(0.85f, fMinuteAngle, 4);
    DrawClockHand(0.85f, fSecondAngle, 1);

    // Restore the identity transformation.
    m_pRenderTarget->SetTransform( D2D1::Matrix3x2F::Identity() );
}

void Scene::CalculateLayout()
{
    if (!m_pRenderTarget) return;
    D2D1_SIZE_F fSize = m_pRenderTarget->GetSize();

    const float x = fSize.width / 2.0f;
    const float y = fSize.height / 2.0f;
    const float radius = min(x, y);

    m_ellipse = D2D1::Ellipse( D2D1::Point2F(x, y), radius, radius);

    // Calculate tick marks.

    D2D_POINT_2F pt1 = D2D1::Point2F(
        m_ellipse.point.x,
        m_ellipse.point.y - (m_ellipse.radiusY * 0.9f)
        );

    D2D_POINT_2F pt2 = D2D1::Point2F(
        m_ellipse.point.x,
        m_ellipse.point.y - m_ellipse.radiusY
        );

    for (DWORD i = 0; i < 12; i++)
    {
        D2D1::Matrix3x2F mat = D2D1::Matrix3x2F::Rotation(
            (360.0f / 12) * i, m_ellipse.point);

        m_Ticks[i*2] = mat.TransformPoint(pt1);
        m_Ticks[i*2 + 1] = mat.TransformPoint(pt2);
    }
    for (int i = 1; i <= 12; i++)
    {
        wchar_t num[3] = {0};
        int len = swprintf_s(num, L"%d", i);
        m_pTextLayouts[i-1].Release();
        HRESULT hr = m_pDWriteFactory->CreateTextLayout(
            num, len, m_pTextFormat, 100.0f, 100.0f, &m_pTextLayouts[i-1]
        );     


        float angle = (360.0f / 12) * (i - 3); // -3 使 12 点位于顶部
         
        float rad = angle * static_cast<float>(M_PI) / 180.0f;
    
        float r = m_ellipse.radiusX * 0.7f;
        m_NumberPositions[i-1] = D2D1::Point2F(
            m_ellipse.point.x + cos(rad) * r,
            m_ellipse.point.y + sin(rad) * r
        );
    
    }         


}


void Scene::DiscardDeviceDependentResources()
{
    m_pFill.Release();
    m_pStroke.Release();
     
    for (auto& layout : m_pTextLayouts)
    {
        layout.Release();
    }      
}


class MainWindow : public BaseWindow<MainWindow>
{
    HANDLE  m_hTimer;
    Scene m_scene;

    BOOL    InitializeTimer();

public:

    MainWindow() : m_hTimer(NULL)
    {
    }

    void    WaitTimer();

    PCWSTR  ClassName() const { return L"Clock Window Class"; }
    LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
};

// Constants 
const WCHAR WINDOW_NAME[] = L"Analog Clock";


INT WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR, INT nCmdShow)
{
    if (FAILED(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE)))
    {
        return 0;
    }
    
    MainWindow win;

    if (!win.Create(WINDOW_NAME, WS_OVERLAPPEDWINDOW))
    {
        return 0;
    }

    ShowWindow(win.Window(), nCmdShow);

    // Run the message loop.

    MSG msg = { };
    while (msg.message != WM_QUIT)
    {
        if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
            continue;
        }
        win.WaitTimer();
    }

    CoUninitialize();
    return 0;
}

LRESULT MainWindow::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    HWND hwnd = m_hwnd;

    switch (uMsg)
    {
    case WM_CREATE:
        if (FAILED(m_scene.Initialize()) || !InitializeTimer())
        {
            return -1;
        }
        return 0;

    case WM_DESTROY:
        CloseHandle(m_hTimer);
        m_scene.CleanUp();
        PostQuitMessage(0);
        return 0;

    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(m_hwnd, &ps); // 必须调用
            m_scene.Render(m_hwnd); // 关键:触发渲染

    // 在此处添加绘图代码(可选)
    // TextOut(hdc, 50, 50, L"Hello!", 6);
            EndPaint(m_hwnd, &ps); // 必须调用
            return 0;
        }
    case WM_DISPLAYCHANGE:
        {
            PAINTSTRUCT ps;
            BeginPaint(m_hwnd, &ps);
            m_scene.Render(m_hwnd);
            EndPaint(m_hwnd, &ps);
        }
        return 0;

    case WM_SIZE:
        {
            UINT x = LOWORD(lParam);
            UINT y = HIWORD(lParam);
            m_scene.Resize(x, y);
        // CalculateLayout() 已在 Resize 中调用
            InvalidateRect(m_hwnd, NULL, FALSE);
        }
        return 0;

    case WM_ERASEBKGND:
        return 1;

    default:
        return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
}


BOOL MainWindow::InitializeTimer()
{
    m_hTimer = CreateWaitableTimer(NULL, FALSE, NULL);
    if (m_hTimer == NULL)
    {
        return FALSE;
    }

    LARGE_INTEGER li = {0};

    if (!SetWaitableTimer(m_hTimer, &li, (1000/60), NULL, NULL,FALSE))
    {
        CloseHandle(m_hTimer);
        m_hTimer = NULL;
        return FALSE;


    }

    return TRUE;
}

void MainWindow::WaitTimer()
{
    // Wait until the timer expires or any message is posted.
    if (MsgWaitForMultipleObjects(1, &m_hTimer, FALSE, INFINITE, QS_ALLINPUT) 
            == WAIT_OBJECT_0)
    {
        InvalidateRect(m_hwnd, NULL, FALSE);
    }
}

谢谢观看,再见~

ctfmon.exe(也称为 Microsoft 文字服务框架监视器)是 Windows 操作系统中用于管理输入法编辑器(IME)和语音识别服务的一个系统进程。在 Windows 10 中,ctfmon.exe 仍然承担着类似的功能,确保用户可以在不同的语言和输入法之间切换,并保持输入法状态的同步[^1]。 ### 功能概述 - **输入法管理**:负责启动和管理输入法编辑器(IME),尤其在多语言环境下,如中、日、韩等需要复杂输入法的系统中。 - **语言栏支持**:提供语言栏的显示和功能支持,用户可以通过任务栏的语言图标切换输入法。 - **语音识别**:支持 Windows 的语音识别功能,使用户能够通过语音命令与系统交互。 ### 管理与问题解决 如果在 Windows 10 中遇到与 ctfmon.exe 相关的问题,例如频繁出错、占用过高 CPU 资源或启动项异常等,可以采取以下措施进行管理和解决: #### 1. 检查启动项 ctfmon.exe 应该作为系统默认启动项之一运行。如果发现有多个实例或异常启动项,可以通过 `msconfig` 或 `任务管理器` 的“启动”标签页进行管理。确保只有一个 ctfmon.exe 启动项,并且状态为“已启用”。 #### 2. 注册表修复 如果发现 ctfmon.exe 无法正常启动,可以尝试通过注册表编辑器修复。打开注册表编辑器(`regedit`),导航至以下路径: ``` HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Run ``` 确保存在名为 `ctfmon.exe` 的字符串值,其值数据应为 `"C:\Windows\System32\ctfmon.exe"`。如果没有,可以手动创建。 #### 3. 输入法设置重置 如果输入法无法正常切换或显示问题,可以尝试重置输入法设置。打开“控制面板” > “时钟和区域” > “区域” > “管理” > “更改系统区域设置”,确保“当前系统区域设置”正确。此外,可以在“语言”设置中添加或删除输入法,以重置相关配置。 #### 4. 安全性检查 虽然 ctfmon.exe 是一个合法的系统进程,但恶意软件有时会伪装成该进程。可以通过任务管理器查看其文件位置是否为 `C:\Windows\System32\ctfmon.exe`,如果不是,应进行病毒扫描。 #### 5. 禁用不必要的输入法 如果不需要使用多语言输入法,可以禁用 ctfmon.exe 以减少资源占用。进入“设置” > “时间和语言” > “语言” > 选择当前语言 > “选项” > 删除不必要的输入法,或直接关闭“使用桌面语言栏(如果可用)”选项。 --- ### 示例代码:检查 ctfmon.exe 启动项 以下是一个 PowerShell 脚本示例,用于检查并确保 ctfmon.exe 在启动项中正确配置: ```powershell $KeyPath = "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" $ValueName = "ctfmon.exe" $ExpectedValue = "$env:SystemRoot\System32\ctfmon.exe" # 检查注册表项是否存在 if (-not (Test-Path $KeyPath)) { Write-Host "注册表路径不存在: $KeyPath" exit } # 获取当前值 $CurrentValue = (Get-ItemProperty -Path $KeyPath -Name $ValueName -ErrorAction SilentlyContinue).$ValueName if ($CurrentValue -eq $ExpectedValue) { Write-Host "ctfmon.exe 启动项已正确配置。" } else { Write-Host "ctfmon.exe 启动项未配置或配置错误,正在设置..." Set-ItemProperty -Path $KeyPath -Name $ValueName -Value $ExpectedValue Write-Host "ctfmon.exe 启动项已更新为: $ExpectedValue" } ``` --- ### 相关问题 1. 如何在 Windows 10 中完全禁用输入法? 2. ctfmon.exe 是否可以安全地被终止? 3. 如何判断 ctfmon.exe 是否被恶意软件伪装? 4. Windows 10 中如何更改默认输入法? 5. 为什么 ctfmon.exe 会占用大量 CPU 资源?
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

adfass

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值