AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / coding / Perguntas / 79576268
Accepted
sad
sad
Asked: 2025-04-16 09:40:24 +0800 CST2025-04-16 09:40:24 +0800 CST 2025-04-16 09:40:24 +0800 CST

Como fazer um botão na barra de tarefas exibir uma barra de progresso em C?

  • 772

A única maneira (pelo que eu sei) de criar uma barra de progresso em um botão da barra de tarefas é usar a interface ITaskbarList3 , mas isso só pode ser usado em C++, não em C. Existe alguma maneira de fazer isso em C, interagindo apenas com funções winapi?

c
  • 2 2 respostas
  • 95 Views

2 respostas

  • Voted
  1. Wisblade
    2025-04-16T19:00:16+08:002025-04-16T19:00:16+08:00

    Embora as interfaces COM possam se parecer muito com C++, com chamadas como pTaskbar->lpVtbl->Release(pTaskbar), elas não são. São estruturas com ponteiros de função C, e você passa constantemente como argumento o "objeto" principal — diferentemente do que seria feito em C++.

    O código a seguir deve mostrar como fazer isso:

    #define UNICODE
    
    #include <windows.h>
    #include <shobjidl.h>
    #include <objbase.h>
    
    #pragma comment(lib, "ole32.lib")
    #pragma comment(lib, "shell32.lib")
    #pragma comment(lib, "user32.lib")
    #pragma comment(lib, "gdi32.lib")
    
    #define ID_BUTTON 1001
    #define ID_TIMER  1002
    
    // This is the main COM interface for ITaskbarList3.
    static ITaskbarList3* pTaskbar = NULL;
    static HWND hwndMain = NULL;
    static int progress = 0;
    
    LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
    {
        switch (msg) {
            case WM_CREATE: {
                CreateWindowW(L"BUTTON", L"Show progress bar", WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON, 10, 10, 265, 40, hwnd, (HMENU)ID_BUTTON, NULL, NULL);
                break;
            }
            case WM_COMMAND:
                if (LOWORD(wParam) == ID_BUTTON) {
                    progress = 0;
                    // Start the progress bar.
                    pTaskbar->lpVtbl->SetProgressState(pTaskbar, hwnd, TBPF_NORMAL);
                    SetTimer(hwnd, ID_TIMER, 50, NULL);
                }
                break;
            case WM_TIMER:
                if (wParam == ID_TIMER) {
                    progress += 2;
                    if (progress <= 100)
                        // Advance the progress bar.
                        pTaskbar->lpVtbl->SetProgressValue(pTaskbar, hwnd, progress, 100);
                    else {
                        KillTimer(hwnd, ID_TIMER);
                        // Hide the progress bar.
                        pTaskbar->lpVtbl->SetProgressState(pTaskbar, hwnd, TBPF_NOPROGRESS);
                    }
                }
                break;
            case WM_DESTROY:
                PostQuitMessage(0);
                break;
            default:
                return DefWindowProc(hwnd, msg, wParam, lParam);
        }
        return 0;
    }
    
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
    {
        // Never ever forget this!
        CoInitialize(NULL);
        // Create the COM interface.
        CoCreateInstance(&CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, &IID_ITaskbarList3, (void**)&pTaskbar);
        WNDCLASSW wc = { 0 };
        wc.lpfnWndProc = WndProc;
        wc.hInstance = hInstance;
        wc.lpszClassName = L"TaskbarDemo";
        wc.hCursor = LoadCursor(NULL, IDC_ARROW);
        RegisterClassW(&wc);
        hwndMain = CreateWindowW(L"TaskbarDemo", L"Taskbar Progress Demo",
            WS_OVERLAPPEDWINDOW ^ WS_THICKFRAME ^ WS_MAXIMIZEBOX,
            CW_USEDEFAULT, CW_USEDEFAULT, 300, 100,
            NULL, NULL, hInstance, NULL);
        ShowWindow(hwndMain, nCmdShow);
        UpdateWindow(hwndMain);
        MSG msg;
        while (GetMessage(&msg, NULL, 0, 0)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        // Destroy the COM interface.
        if (pTaskbar)
            pTaskbar->lpVtbl->Release(pTaskbar);
        // Never ever forget this!
        CoUninitialize();
        return 0;
    }
    

    Testado no Windows 10, MSVC 2019, 64 bits, C17. Não deve ser muito diferente com outros compiladores ou padrões C, exceto, é claro, pelas diretivas #pragma comment(lib, ...), que só serão reconhecidas pelo MSVC — vincule essas 4 bibliotecas manualmente, se necessário.

    Você pode compilá-lo com a seguinte linha de comando, a partir de um prompt do ambiente VS:

    cl /std:c17 main.c /link
    

    Não verifiquei com o MinGW ou outras versões do VS - como eu disse, este é um código C e Windows padrão, e seria compilado até mesmo em Windows muito antigos, se ITaskbarList3estivesse disponível. COM é algo muito antigo na API do Windows.

    Você compila, executa e clica no botão para exibir uma barra de progresso simulada. Os pontos-chave são destacados no código com comentários; todo o resto está sem comentários, pois está lá apenas para fornecer um aplicativo Windows mínimo – não há necessidade de entrar em detalhes sobre o gerador de mensagens ou o temporizador. Eu sei, é um pouco confuso, mas o objetivo não era mostrar as "melhores práticas" para construir um aplicativo Windows em C. Ele compila sem avisos, então é o suficiente para um exemplo rápido.

    Para obter detalhes sobre cada função, consulte a documentação da Microsoft, especialmente se você não estiver familiarizado com interfaces COM (tenha cuidado para inicializar/desinicializar corretamente a API COM no início/fim do seu programa).

    • 5
  2. Best Answer
    IInspectable
    2025-04-16T20:13:05+08:002025-04-16T20:13:05+08:00

    O COM foi projetado para ser independente de linguagem. A introdução ao Modelo de Objeto Componente explica isso:

    Para entender COM [...], é crucial entender que não é uma linguagem orientada a objetos, mas um padrão. [...]. [Objetos COM] podem ser escritos em diferentes linguagens e podem ser estruturalmente bem diferentes, e é por isso que COM é chamado de padrão binário ; um padrão que se aplica depois que um programa foi traduzido para código de máquina binário.

    O único requisito de linguagem para COM é que o código seja gerado em uma linguagem que possa criar estruturas de ponteiros e, explícita ou implicitamente, chamar funções por meio de ponteiros. Linguagens orientadas a objetos, como C++ e Smalltalk, fornecem mecanismos de programação que simplificam a implementação de objetos COM, mas linguagens como C, Java e VBScript podem ser usadas para criar e utilizar objetos COM.

    C é capaz de criar estruturas (ou arrays) de ponteiros de função e chamar funções por meio deles. Isso significa que C pode ser usado para implementar e utilizar interfaces COM. A única razão pela qual você não encontrará muita abordagem para o uso de COM em C é que ele é muito trabalhoso.

    No nível binário, um ponteiro de interface COM é um ponteiro para uma estrutura com uma (ou mais) tabelas de ponteiros de função. O compilador MIDL padrão gera arquivos de cabeçalho que chamam o ponteiro da tabela de funções lpVtbl. Em C++, isso é tratado de forma transparente pelo compilador, com os métodos sendo exibidos como se fossem funções-membro da classe. Em C, por outro lado, você precisa digitar manualmente as indireções de ponteiro e passar o thisponteiro (implícito em C++).

    Supondo que pTaskbarList3seja um ponteiro para uma ITaskbarList3interface, os seguintes trechos mostram como chamar o ITaskbarList::HrInitmétodo.

    C:

    hr = pTaskbarList3->lpVtbl->HrInit(pTaskbarList3);
    // or, if COBJMACROS is defined
    hr = ITaskbarList3_HrInit(pTaskbarList3);
    

    C++:

    hr = pTaskbarList3->HrInit();
    

    O código gerado é idêntico em ambos os casos. O compilador C++ apenas nos permite ser muito menos prolixos.

    Os arquivos de cabeçalho fornecidos com o SDK do Windows geralmente fornecem uma interface C++ e uma interface C, que estão disponíveis condicionalmente com base na __cplusplusdefinição de "se" ou não. Ao usar um compilador C, você pode, opcionalmente, habilitar macros de conveniência por meio do COBJMACROSsímbolo do pré-processador. Essas macros nos permitem nomear o ponteiro da interface apenas uma vez:

    hr = pInterface->lpVtbl->Method(pInterface, ...);
    // vs.
    hr = IInterface_Method(pInterface, ...);
    

    Com os conceitos básicos abordados, veja como você criaria e usaria a ITaskbarList3interface em C.

    static ITaskbarList3* pTaskbarList3 = NULL;
    
    // ...
    
    // Create a TaskbarList object and request its ITaskbarList3 interface
    HRESULT hr = CoCreateInstance(&CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER,
                                  &IID_ITaskbarList3, &pTaskbarList3);
    
    // Initialize the TaskbarList object
    if (SUCCEEDED(hr))
    {
        hr = ITaskbarList3_HrInit(pTaskbarList3);
    }
    
    // Set the progress state to indeterminate
    if (SUCCEEDED(hr))
    {
        hr = ITaskbarList3_SetProgressState(pTaskbarList3, hwnd, TBPF_INDETERMINATE);
    }
    

    Abaixo está um programa de exemplo completo que demonstra como usar a ITaskbarList3interface de C:

    #define UNICODE
    #define COBJMACROS
    
    #include <Shobjidl.h>
    #include <objbase.h>
    #include <winerror.h>
    
    #include <Windows.h>
    
    
    static UINT REGM_TASKBAR_BUTTON_CREATED = 0;
    static ITaskbarList3* pTaskbarList3 = NULL;
    
    static LRESULT WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
    
    
    int main()
    {
        // Initialize COM library for the current thread
        HRESULT hr = CoInitialize(NULL);
        if (FAILED(hr))
            return hr;
    
        // Register the taskbar button created message; this needs to be done before the
        // application window is created
        REGM_TASKBAR_BUTTON_CREATED = RegisterWindowMessageW(L"TaskbarButtonCreated");
        if (REGM_TASKBAR_BUTTON_CREATED == 0)
            return HRESULT_FROM_WIN32(GetLastError());
    
        // Register window class
        WNDCLASSW wc = { .style = CS_HREDRAW | CS_VREDRAW,
                         .lpfnWndProc = WndProc,
                         .hInstance = GetModuleHandleW(NULL),
                         .hCursor = LoadCursorW(NULL, IDC_ARROW),
                         .hbrBackground = (HBRUSH)(COLOR_WINDOW + 1),
                         .lpszClassName = L"MainWndCls" };
        ATOM cls_atom = RegisterClassW(&wc);
        if (cls_atom == 0)
            return HRESULT_FROM_WIN32(GetLastError());
    
        // Create window
        HWND hwnd
            = CreateWindowW(wc.lpszClassName, L"Taskbar Button Progressbar Example", WS_OVERLAPPEDWINDOW | WS_VISIBLE,
                            CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, wc.hInstance, NULL);
        if (hwnd == NULL)
            return HRESULT_FROM_WIN32(GetLastError());
    
        // Run a message loop
        MSG msg = { 0 };
        while (GetMessageW(&msg, NULL, 0, 0))
        {
            DispatchMessageW(&msg);
        }
    }
    
    static LRESULT WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
    {
        if (msg == REGM_TASKBAR_BUTTON_CREATED)
        {
            if (pTaskbarList3 == NULL)
            {
                // Create a TaskbarList object and request its ITaskbarList3 interface
                HRESULT hr
                    = CoCreateInstance(&CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, &IID_ITaskbarList3, &pTaskbarList3);
    
                // Initialize the TaskbarList object
                if (SUCCEEDED(hr))
                {
                    hr = ITaskbarList3_HrInit(pTaskbarList3);
                }
    
                // Set the progress state to indeterminate
                if (SUCCEEDED(hr))
                {
                    hr = ITaskbarList3_SetProgressState(pTaskbarList3, hwnd, TBPF_INDETERMINATE);
                }
            }
        }
    
        switch (msg)
        {
        case WM_DESTROY:
            // Clean up
            if (pTaskbarList3)
            {
                ITaskbarList3_Release(pTaskbarList3);
                pTaskbarList3 = NULL;
            }
            // Request breaking out of the message loop
            PostQuitMessage(0);
            return 0;
    
        default:
            break;
        }
    
        return DefWindowProcW(hwnd, msg, wparam, lparam);
    }
    

    Você pode compilá-lo a partir de um prompt de comando do Visual Studio usando a seguinte linha de comando:

    cl main.c /std:c17 /W4 /O2 /link Ole32.lib User32.lib /SUBSYSTEM:CONSOLE
    

    Alguns pontos que vale a pena destacar:

    • O programa registra a L"TaskbarButtonCreated"mensagem para ser notificado quando o botão da barra de tarefas estiver pronto para ser usado.
    • O programa evita , decididamente, a desinicialização explícita da biblioteca COM (via CoUninitialize). Temos décadas de evidências de que permitir que a biblioteca COM seja limpa como parte do encerramento de threads é a opção mais segura. Ignore a documentação neste caso.
    • 4

relate perguntas

  • Multiplicação mais rápida que *

  • Usando uma macro para comprimento de string no especificador de formato scanf () em C

  • Como você pode definir o tipo de dados de #define para long double?

  • Ponteiros const incompatíveis

  • Mudança de cor não gradual no OpenGL

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Reformatar números, inserindo separadores em posições fixas

    • 6 respostas
  • Marko Smith

    Por que os conceitos do C++20 causam erros de restrição cíclica, enquanto o SFINAE antigo não?

    • 2 respostas
  • Marko Smith

    Problema com extensão desinstalada automaticamente do VScode (tema Material)

    • 2 respostas
  • Marko Smith

    Vue 3: Erro na criação "Identificador esperado, mas encontrado 'import'" [duplicado]

    • 1 respostas
  • Marko Smith

    Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores?

    • 1 respostas
  • Marko Smith

    Como faço para corrigir um erro MODULE_NOT_FOUND para um módulo que não importei manualmente?

    • 6 respostas
  • Marko Smith

    `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso?

    • 3 respostas
  • Marko Smith

    Um programa vazio que não faz nada em C++ precisa de um heap de 204 KB, mas não em C

    • 1 respostas
  • Marko Smith

    PowerBI atualmente quebrado com BigQuery: problema de driver Simba com atualização do Windows

    • 2 respostas
  • Marko Smith

    AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos

    • 1 respostas
  • Martin Hope
    Fantastic Mr Fox Somente o tipo copiável não é aceito na implementação std::vector do MSVC 2025-04-23 06:40:49 +0800 CST
  • Martin Hope
    Howard Hinnant Encontre o próximo dia da semana usando o cronógrafo 2025-04-21 08:30:25 +0800 CST
  • Martin Hope
    Fedor O inicializador de membro do construtor pode incluir a inicialização de outro membro? 2025-04-15 01:01:44 +0800 CST
  • Martin Hope
    Petr Filipský Por que os conceitos do C++20 causam erros de restrição cíclica, enquanto o SFINAE antigo não? 2025-03-23 21:39:40 +0800 CST
  • Martin Hope
    Catskul O C++20 mudou para permitir a conversão de `type(&)[N]` de matriz de limites conhecidos para `type(&)[]` de matriz de limites desconhecidos? 2025-03-04 06:57:53 +0800 CST
  • Martin Hope
    Stefan Pochmann Como/por que {2,3,10} e {x,3,10} com x=2 são ordenados de forma diferente? 2025-01-13 23:24:07 +0800 CST
  • Martin Hope
    Chad Feller O ponto e vírgula agora é opcional em condicionais bash com [[ .. ]] na versão 5.2? 2024-10-21 05:50:33 +0800 CST
  • Martin Hope
    Wrench Por que um traço duplo (--) faz com que esta cláusula MariaDB seja avaliada como verdadeira? 2024-05-05 13:37:20 +0800 CST
  • Martin Hope
    Waket Zheng Por que `dict(id=1, **{'id': 2})` às vezes gera `KeyError: 'id'` em vez de um TypeError? 2024-05-04 14:19:19 +0800 CST
  • Martin Hope
    user924 AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos 2024-03-20 03:12:31 +0800 CST

Hot tag

python javascript c++ c# java typescript sql reactjs html

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve