WM_SETTEXT使用ES_RIGHT样式的多行编辑框花费的时间过长

时间:2018-12-06 06:41:19

标签: c++ winapi

我使用WM_SETTEXT将大约1 MB的Unicode文本发送到多行TEXTBOX。需要30秒或更长时间才能完成。但是从剪贴板粘贴相同的1MB速度非常快。问题是什么?任何想法或链接表示赞赏。 我为所有代码使用VS2017社区。

编辑:我删除了以前的编辑以澄清问题。 这是最小,完整,可验证的代码

#include "stdafx.h"
#include <windows.h>

struct VIEWTEMPLATE : public DLGTEMPLATE {
    unsigned __int16 nMenu;
    unsigned __int16 nClass;
    unsigned __int16 nTitle;
    unsigned __int16 nPointSize;
    wchar_t wszFaceName[10];
};

WNDPROC g_lpfnOriginalWndProc;
HINSTANCE g_hInstance;

static void uPasteFromClipBoard(HWND hwndControl)
{
    HWND wndParent = GetParent(hwndControl);
    if (!IsClipboardFormatAvailable(CF_UNICODETEXT)) return;
    if (!OpenClipboard(hwndControl)) return;
    const wchar_t * lpwszText = nullptr;
    HGLOBAL hgClipboardData = GetClipboardData(CF_UNICODETEXT);
    if (hgClipboardData)
    {
        lpwszText = (const wchar_t *)GlobalLock(hgClipboardData);
    }
    if (!lpwszText)
    {
        CloseClipboard();
        return;
    }
    SendMessageW(hwndControl, WM_SETTEXT, 0, (LPARAM)lpwszText);
    GlobalUnlock(hgClipboardData);
    CloseClipboard();
}

static LRESULT uCtrlProc(HWND hwndControl, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_PASTE: uPasteFromClipBoard(hwndControl); return 1;
    default: break;
    }
    return CallWindowProc(g_lpfnOriginalWndProc, hwndControl, uMsg, wParam, lParam);
}

static int uInitDialog(HWND hwndDlg)
{
    //create a multiline edit control
    int iStyle1 = ES_RIGHT | ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL;
    //I discovered that removing ES_RIGHT solves the problem
    int iStyle2 = WS_CHILD | WS_BORDER | WS_VSCROLL;
    int cpLeft = 10;
    int cpTop = 10;
    int cpWidth = 500;
    int cpHeight = 200;
    HWND hwndEdit = CreateWindowExW(
        0, L"EDIT", L"Paste here", iStyle1 | iStyle2,
        cpLeft, cpTop, cpWidth, cpHeight,
        hwndDlg, nullptr, g_hInstance, nullptr
    );
    if (!hwndEdit)
        return false;
    ShowWindow(hwndEdit, SW_SHOW);
    g_lpfnOriginalWndProc = (WNDPROC)GetWindowLongPtrW(hwndEdit, GWLP_WNDPROC);
    SetWindowLongPtrW(hwndEdit, GWLP_WNDPROC, (ULONG_PTR)uCtrlProc);
    return true;
}

static INT_PTR uDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_INITDIALOG: return uInitDialog(hwndDlg);
    default: return 0L;
    }
};

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
    _In_opt_ HINSTANCE hPrevInstance,
    _In_ LPWSTR    lpCmdLine,
    _In_ int       nCmdShow)
{
    //create a dialog template. resource dialogs have no problem
    VIEWTEMPLATE *dt;
    dt = (VIEWTEMPLATE *)malloc(sizeof(VIEWTEMPLATE));
    if (!dt) return 0;
    memset(dt, 0, sizeof(*dt));
    dt->style =
        DS_SETFONT | WS_POPUP | WS_VISIBLE |
        DS_MODALFRAME | DS_3DLOOK | WS_CAPTION | WS_SYSMENU |
        WS_BORDER | WS_MINIMIZEBOX;
    dt->dwExtendedStyle = WS_EX_OVERLAPPEDWINDOW;
    dt->cdit = 0;
    dt->x = 0;
    dt->y = 0;
    dt->cx = 520;
    dt->cy = 220;
    dt->nPointSize = 10;
    LRESULT iResult = DialogBoxIndirectParamW(
        nullptr,
        dt,
        nullptr,
        uDlgProc,
        0
    );
    free(dt);
    return 0;
}

1 个答案:

答案 0 :(得分:0)

对话框模板具有非常特定的格式,并且需要DWORD对齐。您的实现不正确,可能会导致未定义的行为。

一个更简单的解决方案是使用资源编辑器创建一个对话框,然后简单地调用DialogBox(hinstance, MAKEINTRESOURCE(IDD_DIALOG1), 0, DialogProc)。这种方法更容易,更通用,更安全。

默认情况下,编辑控件的限制为32K。在上面的示例中,此限制没有更改,因此不清楚如何插入1 MB。

编辑控件将自动处理WM_PASTE消息,只要它具有焦点即可。如果编辑控件没有焦点,则将WM_PASTE发送到对话框,您可以简单地将WM_PASTE传递给编辑控件。

SetWindowLongPtr是子类化控件的过时方法。请改用SetWindowSubclass

请尝试以下示例。我再次使用了对话框模板方法,但强烈建议使用资源编辑器。此代码应以Unicode编译。

//#define UNICODE
#include <Windows.h>
#include <stdio.h>
#include <CommCtrl.h>

//adding library (Visual Studio specific)
#pragma comment(lib, "comctl32.lib")

#define ID_EDIT 200

LRESULT CALLBACK EditProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp,
    UINT_PTR, DWORD_PTR)
{
    switch(msg) 
    {
    case WM_PASTE: 
    {
        if(!IsClipboardFormatAvailable(CF_UNICODETEXT)) break;
        if(!OpenClipboard(hwnd)) break;
        HANDLE hdata = GetClipboardData(CF_UNICODETEXT);
        const wchar_t *lpwszText = (const wchar_t*)GlobalLock(hdata);
        SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)lpwszText);
        GlobalUnlock(hdata);
        CloseClipboard();
        return FALSE;
    }
    case WM_NCDESTROY:
        RemoveWindowSubclass(hwnd, EditProc, 0);
        return DefSubclassProc(hwnd, msg, wp, lp);
    }
    return DefSubclassProc(hwnd, msg, wp, lp);
}

INT_PTR CALLBACK DlgProc(HWND hwnd, UINT uMsg, WPARAM wparam, LPARAM)
{
    switch(uMsg)
    {
    case WM_INITDIALOG: 
    {
        HWND hedit = GetDlgItem(hwnd, ID_EDIT);

        //increase the text limit
        SendMessage(hedit, EM_LIMITTEXT, 0x7FFF'FFFF, 0);

        //subclass edit control if necessary
        SetWindowSubclass(hedit, EditProc, 0, 0);
        return TRUE;
    }

    case WM_COMMAND:
        switch(LOWORD(wparam))
        {
        case IDOK: EndDialog(hwnd, IDOK);
        case IDCANCEL: EndDialog(hwnd, IDCANCEL);
        }
        break;
    }
    return FALSE;
};

int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE, LPWSTR, int)
{
    HGLOBAL hgbl = GlobalAlloc(GMEM_ZEROINIT, 2048);
    if(!hgbl)
        return -1;

    const wchar_t *buf;
    int buflen;
    DLGITEMTEMPLATE *item;

    LPWORD ptr = (LPWORD)GlobalLock(hgbl);

    DLGTEMPLATE *dlgtemplate = (DLGTEMPLATE*)ptr;
    dlgtemplate->style = WS_POPUP | WS_BORDER | WS_SYSMENU | DS_MODALFRAME | WS_CAPTION;
    dlgtemplate->cdit = 1; /* Number of controls */
    dlgtemplate->x = 10;
    dlgtemplate->y = 10;
    dlgtemplate->cx = 200;
    dlgtemplate->cy = 200;
    ptr = (LPWORD)(dlgtemplate + 1);

    //no menu
    *ptr++ = 0;

    //predefined dialog box class (by default)
    *ptr++ = 0;

    //Title
    buf = L"Dialog title";
    buflen = wcslen(buf);
    memcpy(ptr, buf, buflen * sizeof(wchar_t));
    ptr += buflen + (buflen % 2);

    ptr++;

    //-----------------------
    // Define a edit control.
    //-----------------------
    item = (DLGITEMTEMPLATE*)ptr;
    item->x = 10;
    item->y = 10;
    item->cx = 180;
    item->cy = 180;
    item->id = ID_EDIT;
    item->style = WS_BORDER | WS_CHILD | WS_VISIBLE |
        ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN | WS_VSCROLL;
    ptr = (LPWORD)(item + 1);

    //edit class
    *ptr++ = 0xFFFF;
    *ptr++ = 0x0081;

    //edit window text not set...

    ptr++;

    GlobalUnlock(hgbl);
    LRESULT ret = DialogBoxIndirect(hInstance, dlgtemplate, NULL, DlgProc);
    GlobalFree(hgbl);
    return ret; 
}