Winapi 从控制台CtrlHandler()调用ChangeClipboardChain()时失败

Winapi 从控制台CtrlHandler()调用ChangeClipboardChain()时失败,winapi,clipboard,Winapi,Clipboard,我知道,这种方法已经过时了,只是好奇而已。 当我从atexit处理程序调用ChangeClipboardChain()时,它成功了。当precc Ctrl-C时,它失败。我知道这一点,因为其他剪贴板查看器不接收WM_CHANGECBCHAIN 将某些内容复制到剪贴板4次时,此示例程序退出: #ifndef _WIN32_WINNT // Allow use of features specific to Windows XP or later.

我知道,这种方法已经过时了,只是好奇而已。 当我从atexit处理程序调用ChangeClipboardChain()时,它成功了。当precc Ctrl-C时,它失败。我知道这一点,因为其他剪贴板查看器不接收WM_CHANGECBCHAIN

将某些内容复制到剪贴板4次时,此示例程序退出:

#ifndef _WIN32_WINNT        // Allow use of features specific to Windows XP or later.                   
#define _WIN32_WINNT 0x0501 // Change this to the appropriate value to target other versions of Windows.
#endif                      

#include <stdio.h>
#include <tchar.h>
#include <windows.h>

static void pWin32Error(const char* fmt, ...);

static int counter=0;

static HWND nextWnd = NULL;

static
LRESULT CALLBACK WindowProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
    switch (uMsg) {
        case WM_CHANGECBCHAIN:
            {
                HWND wndRemove = (HWND)wParam;
                HWND wndNextNext = (HWND)lParam;
                printf("WM_CHANGECBCHAIN %p %p\n", (void*)wndRemove, (void*)wndNextNext);
                if (nextWnd == wndRemove) {
                    printf("saving next window %p\n", (void*)wndNextNext);
                    nextWnd = wndNextNext;
                } else if (nextWnd) {
                    printf("notifying next window %p\n", (void*)nextWnd);
                    return SendMessage(nextWnd,uMsg,wParam,lParam);
                } else {
                    printf("not notifying next window %p\n", (void*)nextWnd);
                }
            }
            break;
        case WM_DRAWCLIPBOARD:
            counter++;
            if (counter > 4) exit(0);
            printf("WM_DRAWCLIPBOARD\n");
            if (nextWnd) {
                printf("notifying next window %p\n", (void*)nextWnd);
                return SendMessage(nextWnd,uMsg,wParam,lParam);
            } else {
                printf("not notifying next window %p\n", (void*)nextWnd);
            }
            break;
        default:
            return DefWindowProc( hwnd,uMsg,wParam,lParam);
    }
    return 0;
}

static volatile HWND global_hwnd = NULL;

WNDCLASS wndclass = {
    0,/*    UINT        style;*/
    WindowProc,/*    WNDPROC     lpfnWndProc;*/
    0,/*    int         cbClsExtra;*/
    0,/*    int         cbWndExtra;*/
    NULL,/*    HINSTANCE   hInstance;*/
    NULL,/*    HICON       hIcon;*/
    NULL,/*    HCURSOR     hCursor;*/
    NULL,/*    HBRUSH      hbrBackground;*/
    NULL,/*    LPCWSTR     lpszMenuName;*/
    _T("myclipmonitor")/*    LPCWSTR     lpszClassName;*/
};

static void unreg() {
    HWND hwnd;
    hwnd = (HWND)InterlockedExchangePointer(&global_hwnd, NULL); /* ignore the "cast to greater size" warning */
    if (hwnd) {
        printf("Removing self from chain: %p <- %p\n", (void*)hwnd, (void*)nextWnd);
        if (!ChangeClipboardChain(hwnd, nextWnd) && GetLastError() != 0) {
            pWin32Error("ChangeClipboardChain() failed");
        }
    }
}

static void exitproc() {
    fprintf(stderr, "exitproc()\n");
    unreg();
}

static
BOOL WINAPI CtrlHandler( DWORD fdwCtrlType )
{
    fprintf(stderr, "CtrlHandler()\n");
    unreg();

    switch ( fdwCtrlType ) {
        case CTRL_C_EVENT:
            fprintf(stderr, "\n\n Stopping due to Ctrl-C.\n" );
            break;

        case CTRL_CLOSE_EVENT:
            fprintf(stderr, "\n\n Stopping due to closing the window.\n" );
            break;

        case CTRL_BREAK_EVENT:
            fprintf(stderr, "\n\n Stopping due to Ctrl-Break.\n" );
            break;

        // Pass other signals to the next handler - ret = FALSE

        case CTRL_LOGOFF_EVENT:
            fprintf(stderr, "\n\n Stopping due to logoff.\n" );
            break;

        case CTRL_SHUTDOWN_EVENT:
            fprintf(stderr, "\n\n Stopping due to system shutdown.\n" );
            break;

        default:
            fprintf(stderr, "\n\n Stopping due to unknown event.\n" );
    }

    return FALSE;
}

int _tmain(int argc, _TCHAR* argv[])
{
    ATOM classatom;
    HINSTANCE hinst = GetModuleHandle(NULL);
    MSG msg;

    wndclass.hInstance = hinst;
    classatom = RegisterClass(&wndclass);
    if (classatom == 0) {
        pWin32Error("RegisterClass() failed");
        return -1;
    }
    global_hwnd = CreateWindowEx(0, (LPCTSTR)classatom, NULL, 0, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinst, NULL);
    if (!global_hwnd) {
        pWin32Error("CreateWindowEx() failed");
        return -1;
    }

    printf("hwnd = %p\n", (void*)global_hwnd);
    nextWnd = SetClipboardViewer(global_hwnd);
    if (!nextWnd && GetLastError() != 0) {
        pWin32Error("SetClipboardViewer() failed");
        return -1;
    }
    printf("nextWnd = %p\n", (void*)nextWnd);

    atexit(exitproc);

    if (0 == SetConsoleCtrlHandler( CtrlHandler, TRUE )) {
        pWin32Error("SetConsoleCtrlHandler() failed");
        return -1;
    }

    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return 0;
}


static char *cleanstr(char *s)
{
    while(*s) {
        switch((int)*s){
            case 13:
            case 10:
            *s=' ';
            break;
        }
        s++;
    }
    return s;
}

static void __pWin32Error(int level, DWORD eNum, const char* fmt, va_list args)
{
    char emsg[1024];
    char *pend = emsg + sizeof(emsg);
    size_t count = sizeof(emsg);
    unsigned u;

    do {
        u = (unsigned)_vsnprintf(pend - count, count, fmt, args);
        if (u >= count) break;
        count -= u;

        u = (unsigned)_snprintf(pend - count, count, ": ");
        if (u >= count) break;
        count -= u;

        u = FormatMessageA( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                                                            NULL, eNum,
                                                            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
                                                            pend - count, (DWORD)count, NULL );
        if (u == 0) {
            u = (unsigned)_snprintf(pend - count, count, "0x%08x (%d)", eNum, eNum);
        }
    } while(0);

    emsg[sizeof(emsg)-1] = '\0';
    pend = cleanstr(emsg);

    if (pend < emsg + sizeof(emsg)-1) {
        pend++;
        *pend = '\0';
    }
    pend[-1] = '\n';
    puts(emsg);
}

static void pWin32Error(const char* fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    __pWin32Error(0, GetLastError(), fmt, args);
    va_end(args);
}
\ifndef\u WIN32\u WINNT//允许使用特定于Windows XP或更高版本的功能。
#定义_WIN32\u WINNT 0x0501//将其更改为适当的值,以针对其他版本的Windows。
#恩迪夫
#包括
#包括
#包括
静态无效pwin32错误(常量字符*fmt,…);
静态整数计数器=0;
静态HWND nextWnd=NULL;
静止的
LRESULT回调WindowProc(HWND HWND、UINT uMsg、WPARAM WPARAM、LPARAM LPARAM)
{
开关(uMsg){
案例WM_CHANGECBCHAIN:
{
HWND wndRemove=(HWND)wParam;
HWND wndNextNext=(HWND)lParam;
printf(“WM_changecbcchain%p%p\n”,(void*)wndRemove,(void*)wndNextNext);
如果(nextWnd==wndRemove){
printf(“保存下一个窗口%p\n”,(void*)wndNextNext);
nextWnd=wndNextNext;
}否则如果(下一次){
printf(“通知下一个窗口%p\n”,(void*)nextWnd);
返回SendMessage(nextWnd、uMsg、wParam、lParam);
}否则{
printf(“不通知下一个窗口%p\n”,(void*)nextWnd);
}
}
打破
案例WM_Draw剪贴板:
计数器++;
如果(计数器>4)退出(0);
printf(“WM_DRAWCLIPBOARD\n”);
如果(下一次){
printf(“通知下一个窗口%p\n”,(void*)nextWnd);
返回SendMessage(nextWnd、uMsg、wParam、lParam);
}否则{
printf(“不通知下一个窗口%p\n”,(void*)nextWnd);
}
打破
违约:
返回DefWindowProc(hwnd、uMsg、wParam、lParam);
}
返回0;
}
静态易失性HWND全局_HWND=NULL;
WNDCLASS WNDCLASS={
0,/*UINT样式*/
WindowProc,/*WNDPROC lpfnWndProc*/
0,/*int cbClsExtra*/
0,/*int-cbWndExtra*/
NULL,/*HINSTANCE HINSTANCE*/
空,/*HICON-HICON*/
空,/*HCURSOR或HCURSOR*/
空,/*HBRUSH hbrBackground*/
NULL,/*LPCWSTR lpszMenuName*/
_T(“myclipmonitor”)/*LPCWSTR lpszClassName*/
};
静态void unr(){
HWND-HWND;
hwnd=(hwnd)InterlockedExchangePointer(&global_hwnd,NULL);/*忽略“转换为更大大小”警告*/
如果(hwnd){

printf(“从链中删除self:%p线程安全性肯定是这里的问题。您的CtrlHandler是在Windows启动的工作线程上调用的。user32中的线程安全性总是模糊的,但严重偏向于“无”。