Club Delphi  
    FTP   CCD     Buscar   Trucos   Trabajo   Foros

Retroceder   Foros Club Delphi > Principal > API de Windows
Registrarse FAQ Miembros Calendario Guía de estilo Buscar Temas de Hoy Marcar Foros Como Leídos

Respuesta
 
Herramientas Buscar en Tema Desplegado
  #1  
Antiguo 26-08-2006
Avatar de Faust
Faust Faust is offline
Miembro
 
Registrado: abr 2006
Ubicación: México D.F.
Posts: 930
Poder: 18
Faust Va por buen camino
Cool Error al copiar texto

Saludos compas del Club Delphi

Hace tiempo estaba desarrollando una aplicación que monitoreaba el portapapeles en busca de contenido apto para mi aplicación, pero al encontrar un error que jamás pude erradicar, me dí por vencido, ahora he retomado esta aplicación de nuevo, y e identifico más o menos por donde va el error, usé el truco 214 de Trucomania y un artículo que leí en la revista Síntesis no. 17 de Grupo Albor, lo que hago es que al crear el form registrarme para ver los mensajes del portapapeles;

Código Delphi [-]
Self.SiguienteHandle := SetClipboardViewer(Self.Handle)

al destruir el form, informar que salgo de la cadena de mensajes del portapapeles:

Código Delphi [-]
ChangeClipboardChain(Self.Handle, Self.SiguienteHandle);

al detectar un cambio en la cadena del portapapeles trato el mensaje:

Código Delphi [-]
procedure TForm1.WMChangeCBCHain(var ChangeCBCHainMessage: TMessage);
begin
  if (ChangeCBCHainMessage.WParam = Self.SiguienteHandle) then
  begin
    SiguienteHandle := ChangeCBCHainMessage.LParam;
    ChangeCBCHainMessage.Result:= 0
  end
    else
      if (SiguienteHandle <> 0) then
        ChangeCBCHainMessage.Result:= SendMessage(SiguienteHandle, WM_CHANGECBCHAIN, ChangeCBCHainMessage.WParam, ChangeCBCHainMessage.LParam);
end;

y al recibir el mensaje de que ha cambiado el portapapeles ejecuto lo siguiente

Código Delphi [-]
procedure TForm1.WMDrawClipboard(var DrawClipboardMessage: TMessage);
begin
  DrawClipBoardMessage.Result:= SendMessage(SiguienteHandle, WM_DRAWCLIPBOARD, 0, 0);
  if ClipBoard.HasFormat(CF_TEXT) then
    begin
      Memo1.Clear;
      Memo1.Text:= ClipBoard.AsText  //Aquí sucede el error
    end
end;
Ahora algo importante: el error sucede aquí:
Código Delphi [-]
Memo1.Text:= ClipBoard.AsText  //Aquí sucede el error
Solo cuando estoy ejecutando algún programa de Office, por ejemplo Excel y solamente cuando efectúo una operación de arrastre y lo raro es que algunas veces aparece el mensaje de error en Office y otras en mi aplicación, el mensaje de error de Office es: "No se puede vaciar el portapapeles"y el que aparece en mi aplicación es: "Cannot open clipboard", lo que me hace pensar que al efectuar una operación de arrastre del contenido de algún documento de Office sucede lo siguiente:
  1. Office almacena el contenido previo del portapapeles a una variable temporal.
  2. Office ocupa el portapapeles para realizar su operación de arrastre, esto desencadena el mensaje WM_DRAWCLIPBOARD, pero antes de hacer esto bloquea el acceso al portapapeles.
  3. Al terminar la operación de arrastre Office restaura el contenido del portapapeles previo a la operación de arrastre y libera el portapapeles.
Y es así como mi aplicación capta el mensaje WM_DRAWCLIPBOARD y trata de obtener acceso al portapapeles, pero al estar bloqueado por Office cae en el error "Cannot open clipboard", y cuando mi programa llega a obtener acceso al portapapeles, en Office ocurre el error "No se puede vaciar el contenido del portapapeles", creo que la solución a mi problema es saber cuando está bloqueado el acceso al portapapeles, si estoy en lo correcto, como se hace eso, si no por favor que alguien me ayude y tengo otra pequeña duda, en dónde, cómo y que debo asignar como valor al campo Result de los mensajes WM_CHANGECBCHAIN y WM_DRAWCLIPBOARD, según la ayuda de Windows se debe devolver cero despues de una operación exitosa con el mensaje.

El código de aquí es solo de prueba, una vez que funcione exitosamente lo implementaré en mi aplicación.

Bueno, después de alargarme un poco con la explicación de mi problema me despido, enviando un saludo y un abrazo amistoso a todos los delphimaniacos de Club Delphi y agradeciendo de una vez a todos aquellos que me puedan ayudar.

Gracias
__________________
Herr Heins Faust
Responder Con Cita
  #2  
Antiguo 26-08-2006
Avatar de Lepe
[Lepe] Lepe is offline
Miembro Premium
 
Registrado: may 2003
Posts: 7.424
Poder: 28
Lepe Va por buen camino
La filosofía es la siguiente:
Cualquier programa puede registrarse como Visor del portapapeles. Cuando el usuario copia algo en el portapapeles, windows mira quien es el primer programa "visor del portapapeles" (obviamente el programa de windows) y le pasa un mensaje indicando que el contenido del portapapeles ha cambiado. Ese programa, debe continuar la cadena, es decir, seguir informando al resto de programas que son visores del portapapeles del cambio surgido.

Por tanto tenemos que:

- Registrar nuestro programa para que capture cosas del Portapapeles automaticamente, y guardar quien es el siguiente programa "visor del portapapeles". Además debemos quitarnos de esa lista al cerrar nuestro programa:
Código Delphi [-]
var NextClipboard:Thandle; // variable global 
procedure RegistrarVisor;
begin
      NextClipboard := SetClipboardViewer(frmppal.Handle);
end;

procedure EliminarVisor;
begin
      ChangeClipboardChain(FrmPpal.Handle, NextClipboard)
// aquí se elimina nuestro programa en la cadena de "visores del portapapeles" y se añade el que existía antes.
end;

- Obviamente necesitamos responder cuando cambie el portapapeles:
Código Delphi [-]
Tform1 = class(TForm)
  private
      procedure CambioPortapapeles(var Msg: TWMDrawClipboard); message wm_drawclipboard; 
// cada vez que se dibuje algo en el portapapeles, que nos avise
end;
implementation

procedure TFrmPpal.CambioPortapapeles(var Msg: TWMDrawClipboard);
var
  i:     Integer;
  found: Boolean;
  str:   String;

begin
  found := False;
  if (Clipboard.HasFormat(CF_TEXT)) then
  begin
    Str := clipboard.AsText; // ya tenemos el contenido nuevo

   // ahora enviamo el cambio que ha habido en el portapapeles al 
  // siguiente programa visor del portapapeles.
    SendMessage(NextClipboard, Msg.Msg, Msg.Msg, Msg.Msg);
  end
end;

El error que yo veo, es que la linea
Código Delphi [-]
 DrawClipBoardMessage.Result:= SendMessage(SiguienteHandle, WM_DRAWCLIPBOARD, 0, 0);
tienes que ponerlo al final de la rutina ¿por qué?, porque un programa "visor del portapapeles" puede cambiar el contenido del mismo, y si lo hace, tu línea
Código Delphi [-]
Memo1.Text:= ClipBoard.AsText
está desfasada con el contenido real del portapapeles.

Es más yo lo modificaba y solo ponía esto:
Código Delphi [-]
    SendMessage(NextClipboard, Msg.Msg, Msg.Msg, Msg.Msg);

Tu procedimiento WMChangeCBCHain creo que es inconsistente, simplemente elimínalo.

Saludos
__________________
Si usted entendió mi comentario, contácteme y gustosamente,
se lo volveré a explicar hasta que no lo entienda, Gracias.
Responder Con Cita
  #3  
Antiguo 26-08-2006
Avatar de Faust
Faust Faust is offline
Miembro
 
Registrado: abr 2006
Ubicación: México D.F.
Posts: 930
Poder: 18
Faust Va por buen camino
Exclamation ¿y luego cómo sé cuál es el siguiente visor?

Gracias por tu respuesta... pero...

Cita:
Empezado por Lepe
Tu procedimiento WMChangeCBChain creo que es inconsistente, simplemente elimínalo.
Pero si elimino el procedimiento WMChangeCBChain cómo sé si el handle al siguiente visor sigue siendo válido
__________________
Herr Heins Faust
Responder Con Cita
  #4  
Antiguo 26-08-2006
Avatar de dec
dec dec is offline
Moderador
 
Registrado: dic 2004
Ubicación: Alcobendas, Madrid, España
Posts: 13.107
Poder: 34
dec Tiene un aura espectaculardec Tiene un aura espectacular
Hola,

Cita:
Empezado por Faust
Pero si elimino el procedimiento WMChangeCBChain cómo sé si el handle al siguiente visor sigue siendo válido
Según la ayuda del SDK de Win32 sobre la función "SetClipboardViewer":

Cita:
If the function succeeds, the return value identifies the next window in the clipboard viewer chain. If an error occurs or there are no other windows in the clipboard viewer chain, the return value is NULL. To get extended error information, call GetLastError.
Es decir, por eso Lepe recoge el resultado de dicha función en la variable "global" "NextClipboard", y es esta misma variable la que se utiliza después en la función "ChangeClipboardChain".
__________________
David Esperalta
www.decsoftutils.com
Responder Con Cita
  #5  
Antiguo 28-08-2006
Avatar de Faust
Faust Faust is offline
Miembro
 
Registrado: abr 2006
Ubicación: México D.F.
Posts: 930
Poder: 18
Faust Va por buen camino
Red face Continuo con el error en office

Gracias por sus respuestas camaradas, aunque he usado la solución de Lepe han continuado los errores en Office, por lo que yo creo que la solución es en saber si algún otro programa ha bloqueado temporalmente el portapapeles, pues así antes de extraer el contenido del portapapeles puedo preguntar si está disponible, y evitar el error de Office.

De nuevo gracias por su ayuda y les mando un afectuoso saludo.
__________________
Herr Heins Faust
Responder Con Cita
  #6  
Antiguo 28-08-2006
Avatar de seoane
[seoane] seoane is offline
Miembro Premium
 
Registrado: feb 2004
Ubicación: A Coruña, España
Posts: 3.717
Poder: 24
seoane Va por buen camino
Te propongo una solución, no utilizar la unit clipbrd y copiar el contenido del portapapeles usando solo funciones de la API. Para copiar el texto podemos usar una función como esta:

Código Delphi [-]
function LeerTexto: string;
var
  hText: THandle;
  pText: PChar;
begin
  Result:= EmptyStr;
  if IsClipboardFormatAvailable(CF_TEXT) then
    if OpenClipboard(0) then
    try
      hText:= GetClipboardData(CF_TEXT);
      if hText <> 0 then
      begin
        pText:= GlobalLock(hText);
        if pText <> nil then
        begin
          Result:= String(PChar(pText));
          GlobalUnlock(hText);
        end;
      end;
    finally
      CloseClipboard;
    end;
end;

La funcion anterior intentara copiar el texto del portapapeles, si no lo consigue devolvera una cadena vacia, pero no mostrara ningun error. Asi que podriamos utilizarla de la siguiente manera:

Código Delphi [-]
procedure TForm1.WMDrawClipboard(var Msg: TMessage);
var
  Str:   String;
begin
  Str:= LeerTexto;
  if Str <> EmptyStr then
    memo1.Lines.Add(Str);
  Msg.Result:= SendMessage(NextClipboard, Msg.Msg, Msg.wParam, Msg.LParam);
end;

¿Que te parece? por lo menos a mi ya no me sale ningún error al arrastrar en excel.
Responder Con Cita
  #7  
Antiguo 28-08-2006
Avatar de Lepe
[Lepe] Lepe is offline
Miembro Premium
 
Registrado: may 2003
Posts: 7.424
Poder: 28
Lepe Va por buen camino
Tengo un programa como he dicho, y me lee todo el contenido cada vez que se copia algo. Uso office 2002.

En casos de arrastrar y soltar, no me lee el portapapeles, ya no se "copia nada en esos momentos", incluso arrastrando desde excel a word y viceversa. Puede que un office de versión superior esté "haciendo virguerías".

Saludos
__________________
Si usted entendió mi comentario, contácteme y gustosamente,
se lo volveré a explicar hasta que no lo entienda, Gracias.
Responder Con Cita
Respuesta


Herramientas Buscar en Tema
Buscar en Tema:

Búsqueda Avanzada
Desplegado

Normas de Publicación
no Puedes crear nuevos temas
no Puedes responder a temas
no Puedes adjuntar archivos
no Puedes editar tus mensajes

El código vB está habilitado
Las caritas están habilitado
Código [IMG] está habilitado
Código HTML está deshabilitado
Saltar a Foro

Temas Similares
Tema Autor Foro Respuestas Último mensaje
copiar texto sin formato en un TRichEdit!!!! aranel OOP 1 20-12-2005 20:24:17
Copiar texto en RichEdit darkerbyte Varios 4 16-11-2005 02:37:17
Copiar el texto de un RichEdit soul6301 Varios 5 11-11-2005 04:31:24
Copiar las columnas en un archivo de texto Paulina DelphiPACK 0 08-10-2004 19:54:07
Copiar texto de un RichEdit a otro SCH Varios 2 11-08-2003 09:16:26


La franja horaria es GMT +2. Ahora son las 14:30:12.


Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi
Copyright 1996-2007 Club Delphi