Конференция "WinAPI" » Ctrl+C - запрет ОС на копирование в буфер обмена [D7, WinXP]
 
  • Игорь Шевченко © (21.05.10 23:16) [20]
    копирование произоводится корректно, как при Ctrl+C, Ctrl+Ins там и при вызове из локального меню
  • allrussia (22.05.10 00:08) [21]
    Игорь Шевченко ©   (21.05.10 23:16) [20]

    копирование произоводится корректно, как при Ctrl+C, Ctrl+Ins там и при вызове из локального меню



    спасибо за пример - он безусловно пока самый рабочий. В принципе у меня он был немного в другом виде, но метод запомнить раскладку, сменить на русский и затем обратно я уже использовал.

    Я как раз ищу рабочий пример без смены раскладки.

    Во-вторых, вопрос к вам как к программисту: как поведет себя код при отсутствии русской раскладки?
    Я пробовал удалять раскладки и код работает, но все же, какие могут быть курьезы?

    И в-третьих, как же быть со вставкой? Основная проблема здесь:
    Открываем блокнот Windows. переключаем раскладку на русский. пишем текст. переключаем обратно на англ. копируем в буфер. вставляем в мемо в нашу программу (форма и мемо). результат: "?" и абракадабра...

    Здесь уже не моможет включение русской раскладки перед вставкой, т.к. русский текст был скопирован с некорректной кодовой страницей в Clipboard.
  • allrussia (22.05.10 00:25) [22]
    да, и еще вопрос, как это размножить на 100 мемо программно

    procedure TForm1.FormCreate(Sender: TObject);
    begin
    FOldMemoWndProc := Memo1.WindowProc;
    Memo1.WindowProc := MemoWndProc;
    FOldEditWndProc := Edit1.WindowProc;
    Edit1.WindowProc := EditWndProc;
    Memo1.Lines.Text := 'Это русский текст';
    end;

    procedure TForm1.MemoWndProc(var Message: TMessage);
    var
    LKL: array [0..1023] of char;
    begin
    if Message.Msg = WM_COPY then
    begin
      GetKeyboardLayoutName(LKL);
      LoadKeyboardLayout('00000419',KLF_ACTIVATE);
    end;
    FOldMemoWndProc(Message);
    if Message.Msg = WM_COPY then
      LoadKeyboardLayout(LKL,KLF_ACTIVATE);
    end;

  • Игорь Шевченко © (22.05.10 00:28) [23]
    allrussia   (22.05.10 00:08) [21]

    Мне, если честно, лень искать материалы по работе с Clipboard. Разумеется, переключение языка раскладки - это грубый (и не всегда полезный) метод. То, что при этом Clipboard устанавливает нужный формат - это побочный эффект от переключения, я уверен, что этого же эффекта можно добиться прямым путем.

    вот тут http://www.codeguru.com/cpp/w-p/clipboard/article.php/c3009
    есть неплохая программка, которая позволяет крутить данные в Clipboard под разным углом.

    Вот тут (http://msdn.microsoft.com/en-us/library/ms649013(VS.85).aspx
    ) есть описание, почему действует переключение клавиатуры:

    "The data is a handle to the locale identifier associated with text in the clipboard. When you close the clipboard, if it contains CF_TEXT data but no CF_LOCALE data, the system automatically sets the CF_LOCALE format to the current input language. You can use the CF_LOCALE format to associate a different locale with the clipboard text.
    An application that pastes text from the clipboard can retrieve this format to determine which character set was used to generate the text."

    Твори, выдумывай, пробуй.
  • Игорь Шевченко © (22.05.10 00:29) [24]
    allrussia   (22.05.10 00:25) [22]


    > да, и еще вопрос, как это размножить на 100 мемо программно


    Написать наследник от TMemo и использовать наследник. Заменять можно и в runtime
  • Германн © (22.05.10 01:07) [25]

    > Заменять можно и в runtime

    <offtop>
    Эх, если бы были механизмы/утилиты сделать подобное в designtime.
    </offtop>
  • allrussia (22.05.10 02:02) [26]
    спасибо, но со вставкой все равно непонятно в этом случае как быть

    Открываем блокнот Windows. переключаем раскладку на русский. пишем текст. переключаем обратно на англ. копируем в буфер. вставляем в мемо в нашу программу (форма и мемо). результат: "?" и абракадабра...

    здеть уже SetClipBoardData(CF_Locale,0419) не помогает..... ведь копирование было средствами ОС из чужой программы
  • allrussia (22.05.10 02:07) [27]
    вот тут http://www.codeguru.com/cpp/w-p/clipboard/article.php/c3009
    есть неплохая программка, которая позволяет крутить данные в Clipboard под разным углом.


    да а смысл? я сам могу "крутить" данные с помощью кнопок и мыши (вы сами мне давали функцию BufferToClipBoard)... Мне-то нужно перехватывать системную вставку-копирование..

    тем более прога под последними ОС не фурычит...
  • GrayFace © (22.05.10 22:20) [28]
    Вот как я это делел, без завязки на русский:
    procedure SendKeyboardLayout(Wnd, Msg:int; lParam:int=0; wParam:int=0);
    var a:HKL; s:string;
    begin
     SetLength(s,8);
     Win32Check(GetKeyboardLayoutName(ptr(s)));
     if s='00000409' then
     begin
       a:=ActivateKeyboardLayout(
            LoadKeyBoardLayout(ptr(intToHex(GetUserDefaultLangID,8)),0),0);
       if a=0 then RaiseLastOSError;
       SendMessage(Wnd, Msg, lParam, wParam);
       if ActivateKeyboardLayout(a,0)=0 then RaiseLastOSError;
     end else
       SendMessage(Wnd, Msg, lParam, wParam);
    end;

    procedure TForm1.Copy1Click(Sender: TObject);
    begin
     SendKeyboardLayout(GetFocus, WM_COPY);
    end;

    procedure TForm1.Cut1Click(Sender: TObject);
    begin
     SendKeyboardLayout(GetFocus, WM_CUT);
    end;

    procedure TForm1.Paste1Click(Sender: TObject);
    begin
     SendMessage(GetFocus, WM_PASTE, 0, 0);
    end;



    Это код для пунктов меню. Если кодировка английская, он устанавливает кодировку для языка пользователя.

    Перехват оконной процедуры у тебя нормальный, только не понятно, откуда ты нашел Handle у TComponent - он только у TWinControl.

    Игорь Шевченко ©   (21.05.10 19:58) [12]
    Но гораздо проще надписать компоненты и в них заменять оконную процедуру на нужную (как это неоднократно демонстрируется в stdctrls.pas)

    Не-не-не. Только лишняя морока. А этот метод еще можно продолжыть, отлавливая создание дочерних контролов.

    Игорь Шевченко ©   (22.05.10 0:28) [23]
    Спасибо, это интересно.
  • Игорь Шевченко © (23.05.10 00:39) [29]
    GrayFace ©   (22.05.10 22:20) [28]


    > Не-не-не. Только лишняя морока. А этот метод еще можно продолжыть,
    >  отлавливая создание дочерних контролов.


    Сказки не рассказывай, ладно ?


    > procedure SendKeyboardLayout(Wnd, Msg:int; lParam:int=0;
    >  wParam:int=0);


    Срочно читать Фаулера. Наизусть.

    Почему помогает смена языка клавиатуры - потому что ясным английским языком написано, что если при закрытии Clipboard locale не установлен, то он становится равным текущему языку ввода. Умные люди советуют передавать CF_UNICODETEXT и, соответственно, строку в Unicode (UCS-2), которая при вставке в Ansi-контрол должна быть преобразована в соотвествии с locale потока, а не языка ввода. А он по умолчанию равен языку локализации системы :)
  • allrussia (23.05.10 00:48) [30]
    GrayFace

    > Это код для пунктов меню.


    тема совсем не о пунктах меню.
    с пунктами меню проблем нет, причем приведены более удобные методы
    BufferToClipBoard

    кстати, вот это


    procedure TForm1.Paste1Click(Sender: TObject);
    begin
    SendMessage(GetFocus, WM_PASTE, 0, 0);
    end;



    у вас даже не сработает, если

    Открываем блокнот Windows. переключаем раскладку на русский. пишем текст. переключаем обратно на англ. копируем в буфер. вставляем в мемо в нашу программу (форма и мемо). результат: "?" и абракадабра...

    будет та же абракадабра... :))

    увы, ваш вариант не принимается :(

    просто с помощью меню никто не копирует это долго и нудно
    основная задача заменить операции стандартных комбинации клавиш на свои
  • Eraser © (23.05.10 00:51) [31]
    > allrussia

    не совсем вкурсе вопроса насчет раскладок и кодовых страниц клипбоарда, сам не сталкивался с этой проблемой в плане разработки, только слышал.

    но что если просто отслеживать буфер обмена (благо есть такие функции) и, в случае появления кривого, текста вручную подменять его, к примеру, на юникодовский вариант?
  • allrussia (23.05.10 01:23) [32]

    > и, в случае появления кривого, текста вручную подменять
    > его, к примеру, на юникодовский вариант?


    так для  этого и приведены ссылки в [14] И. Ш. и
    здесь А.P.: http://pda.delphimaster.net/?id=1271161706&n=0
  • Игорь Шевченко © (23.05.10 01:31) [33]
    Я сильно извиняюсь, а что, пользователей нельзя попросить вручную переключиться на русский язык перед копированием ? Мои пользователи и я научились.

    К тому же в последних версиях Delphi этой проблемы нет, так как юникод.

    Я к чему - конечно чистое искусство, это хорошо и красиво, но стоит ли овчинка выделки ? Если бы подобный механизм был бы действительно необходим и востребован, MS бы давно придумал нужный API для изменения поведения Clipboard в желаемую сторону.
  • Германн © (23.05.10 02:42) [34]

    > Я к чему - конечно чистое искусство, это хорошо и красиво,
    >  но стоит ли овчинка выделки ?

    +1
  • allrussia (23.05.10 18:10) [35]

    > Я сильно извиняюсь, а что, пользователей нельзя попросить
    > вручную переключиться на русский язык перед копированием
    > ? Мои пользователи и я научились.
    >


    Можно, просто у меня другой подход к созданию собственных программ. Просто я - админ, и мое кредо: для пользователей должно быть все прозрачно.
    Для меня - "кривые руки программиста" - это не "кривой" (пусть даже дебильный) для вас, программистов с большой буквы, код, а "кривая" работа программы, в данном случае, непродуманность в отношении буфера обмена Windows; в общих случаях, ка правило, это неюзабельный интерфейс, отсутствие горячих клавиш, многозадачности и многопоточности и пр.
    Меня красота кода не волнует. Поэтому и считаю, что хороший и талантливый программист, как правило, хреновый дизайнер и наоборот. Я из последних.
  • Дмитрий Белькевич (24.05.10 11:05) [36]

    > Просто я - админ, и мое кредо: для пользователей должно
    > быть все прозрачно.


    Переходи на Delphi 2009 и выше. На неюникодных версиях красивого решения, боюсь, нет.
  • QAZ (24.05.10 13:11) [37]

    >  непродуманность в отношении буфера обмена Windows


    > хороший и талантливый программист


    на самом деле все в буфере продумано,а вы видимо не такой талантливый, раз в этом сомневаетесь
    для начала стоит изучить первоисточники,типа msdn а не "самоучитель дельфи глава 7 TClipboard"

    так вот ,о чем это я
    1)чтобы "перехватить" Ctrl+с и тд нужно просто перерегистрировать данный хоткей в своей проге (невероятно ,правда?)
    2)буфер может хранить текст сразу в 3х кодировках дос,анси и уникод. Но! все почемуто используют тока одну (вот и зависимость от текущей клавы)
    что из этого следует...
    1)ловим нажатие хоткея в очереди главного окна
    2)смотрим какая кодировка в буфере и конвертируем в нужную или винда сделает это сама если в буфере установить нужный локаль(иначе возьмет текущую раскладку клавы)
    3)когда копируем из себя то,либо забиваем в буфер сразу в 3х кодировках, либо одну и в буфере установить нужный локаль(иначе возьмет текущую раскладку клавы)
    4)и естествено мы забиваем на всякие TClipboard ,а используем АПИ

    вот и вся теория...

    ps есть ищо варианты
  • QAZ (24.05.10 13:18) [38]

    > нужно просто перерегистрировать данный хоткей в своей проге

    правда придется еще чучуть доработать штоб работало во всей системе после етого ;)
  • allrussia (25.05.10 12:56) [39]
    Окончательный рабочий вариант


    var cl: string;
    ...

    procedure ClipboardCopy(const Text: String);
    var
    Len, wLen: Integer;
    hClip: THandle;
    pwStr: PWideChar;
    begin
    with Clipboard do
    begin
      Open;
      try
        if (Win32Platform = VER_PLATFORM_WIN32_NT) then
        begin
          Len := Length(Text) + 1;
          wLen := Len shl 1;
          hClip := GlobalAlloc(GMEM_MOVEABLE, wLen);
          try
            pwStr := PWideChar(GlobalLock(hClip));
            MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, PChar(Text), Len, pwStr, wLen);
            GlobalUnlock(hClip);
            SetAsHandle(CF_UNICODETEXT, hClip);
          except
            GlobalFree(hClip);
            raise;
          end;
        end else
          SetTextBuf(PChar(Text));
      finally
        Close;
      end;
    end;
    end;

    procedure ClipboardPaste(var Text: String);
    var
    Len, wLen: Integer;
    hClip: THandle;
    pwStr: PWideChar;
    begin
    Text := '';
    with Clipboard do
    try
      Open;
      if HasFormat(CF_TEXT) or HasFormat(CF_UNICODETEXT) then
      begin
        if (Win32Platform = VER_PLATFORM_WIN32_NT) then
        begin
          hClip := GetAsHandle(CF_UNICODETEXT);
          wlen := GlobalSize(hClip);
          pwStr := GlobalLock(hClip);
          try
            Len := (wLen div 2) - 1;
            SetLength(Text, Len);
            WideCharToMultiByte(CP_ACP, 0, pwStr, wlen, PChar(Text), Len, nil, nil);
          finally
            GlobalUnlock(hClip);
          end;
        end else
        begin
          hClip := GetAsHandle(CF_TEXT);
          Len := GlobalSize(hClip);
          SetLength(Text, Len);
          SetLength(Text, GetTextBuf(PChar(Text), Len));
        end;
      end;
    finally
      Close;
    end;
    end;

    function NewMemoProc(wnd:HWND; uMsg:UINT;
                        wParam:WPARAM; lParam:LPARAM):integer; stdcall;
    begin
     case uMsg of
     WM_COPY:
       begin
         uMsg:=0;
         ClipboardCopy(cl);
       end;
     WM_CUT:
       begin
         SendMessage(wnd,EM_REPLACESEL,1,cardinal(pchar('')));
         uMsg:=0;
         ClipboardCopy(cl);
       end;
     WM_PASTE:
       begin
         uMsg:=0;
         ClipboardPaste(cl);
       end;
     end;
     Result:= CallWindowProc(Pointer(GetWindowLong(wnd,GWL_USERDATA)),
                                              wnd,uMsg,wParam,lParam);
    end;

    form1.create
    procedure CreateOwnClipboard;
    var i: integer;
       h: hwnd;
       c: TComboBoxInfo;
    begin
    with form1 do
    for i :=0 to componentCount - 1 do
    begin
      if Components[i] is TEdit then
      begin
        h:= TEdit(Components[i]).Handle;
        SetWindowLong(h,GWL_USERDATA,SetWindowLong(h, GWL_WNDPROC, LPARAM(@NewMemoProc)))
      end;
      if Components[i] is TMemo then
      begin
        h:= TMemo(Components[i]).Handle;
        SetWindowLong(h,GWL_USERDATA,SetWindowLong(h, GWL_WNDPROC, LPARAM(@NewMemoProc)))
      end;
      if Components[i] is TComboBox then
      begin
        c.cbSize:= SizeOf(TCOMBOBOXINFO);
        GetComboBoxInfo(TComboBox(Components[i]).Handle,c);
        h:= c.hwndItem;
        SetWindowLong(h,GWL_USERDATA,SetWindowLong(h, GWL_WNDPROC, LPARAM(@NewMemoProc)))
      end;
    end;
    end;

    MemoKeyDown
    begin
     with TFlat(Sender) do
     begin
       if not ReadOnly then
       if ((Key = ord('V'))  and (ssCtrl  in Shift)) or
          ((Key = VK_INSERT) and (ssShift in Shift)) then
       begin
         SelText:=cl;
       end;
       if ((Key = ord('C'))   and (ssCtrl  in Shift)) or
          ((Key = VK_INSERT)  and (ssCtrl  in Shift)) or
          ((Key = ord('X'))   and (ssCtrl  in Shift)) or
          ((Key = VK_DELETE)  and (ssShift in Shift)) then
          cl:= SelText;
     end;
    end;

    так же по аналогии с EditOnKeyDown и ComboboxOnKeyDown

 
Конференция "WinAPI" » Ctrl+C - запрет ОС на копирование в буфер обмена [D7, WinXP]
Есть новые Нет новых   [118638   +31][b:0][p:0.004]