Al mejor estilo messenger. Este truco es muy interesante debido a los pocos ejemplos que hay para Delphi disponibles en internet. Sé también que a más de uno le va a resultar muy interesante. Vamos insertar imagenes en un componente TRrichEdit al mejor estilo Messenger o ICQ. Para ello vamos a utilizar la Api de OLE. El ejemplo propuesto esta hecho en Delphi 7 con Windows XP. Lamentablemente no he tenido tiempo para hacerlo funcionar en 98 asi que le dejo esa tarea al lector Primero vamos a diseñar un formulario similar al siquiente con un TRichEdit, un TPanel y 7 TImage donde vamos a mostrar los emoticons que el usuario puede elegir. A fin de cuentas nos tiene que quedar algo de la siquiente manera: Para mayor claridad se muestra el dfm del formulario anterior. object Form1: TForm1 Left = 475 Top = 230 Width = 245 Height = 183 Caption = 'RichEdit con Imagenes' Color = clBtnFace Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -11 Font.Name = 'MS Sans Serif' Font.Style = [] OldCreateOrder = False PixelsPerInch = 96 TextHeight = 13 object Image1: TImage Escrito por Hechicero (Esteban Cesar Calabria) Fecha Ultima Modificacion: 24 de Julio del 2004 1 Left = 8 Top = 118 Width = 24 Height = 24 Picture.Data = { 0A544A504547496D616765D7030000FFD8FFE000104A46494600010101006000 [Datos Omitidos] E0A2B746CEC0A6A251BDB929676CFBEE1EA5CB80B8F2DDD2B65F39C03FD2022C 1468738CFFD9} OnClick = Button4Click end object Image2: TImage Left = 40 Top = 117 Width = 24 Height = 24 Picture.Data = { 0A544A504547496D616765D4030000FFD8FFE000104A46494600010101006000 [Datos Omitidos] 50E5F1E6A44F6634625631EFF26ACA282701CEDDC4C9C0F5C292B4374B3DD6D7 CFFFD9} OnClick = Button4Click end object Image3: TImage Left = 72 Top = 117 Width = 24 Height = 24 Picture.Data = { 0A544A504547496D616765C7030000FFD8FFE000104A46494600010101006000 [Datos Omitidos] CB4CD0720D093D257BE59145A08D0007010F033FFFD9} OnClick = Button4Click end object Image4: TImage Left = 104 Top = 117 Width = 24 Height = 24 Picture.Data = { 0A544A504547496D616765E0030000FFD8FFE000104A46494600010101006000 [Datos Omitidos] 8CB2706D0EF21028DE80720ACFFFD9} OnClick = Button4Click end object Image5: TImage Left = 136 Top = 117 Width = 24 Height = 24 Picture.Data = { 0A544A504547496D616765DB030000FFD8FFE000104A46494600010101006000 [Datos Omitidos] 45140A68C2F187BFFFD9} OnClick = Button4Click end object Image6: TImage Left = 168 Top = 117 Width = 24 Height = 24 Picture.Data = { 0A544A504547496D616765F9030000FFD8FFE000104A46494600010101006000 [Datos Omitidos] 03B8AFD6D73FFFD9} OnClick = Button4Click end object Image7: TImage Left = 200 Top = 117 Width = 24 Height = 24 Picture.Data = { 0A544A504547496D6167650F040000FFD8FFE000104A46494600010101006000 Escrito por Hechicero (Esteban Cesar Calabria) Fecha Ultima Modificacion: 24 de Julio del 2004 2 [Datos Omitidos] 3FD075DA4CA3CAFC4A476E2C7661EA9873C36C449A09DD965DF555CFFFD9} OnClick = Button4Click end object Panel1: TPanel Left = 0 Top = 89 Width = 237 Height = 24 Align = alTop Caption = 'Elija la imagen para insertar un Emoticon....' TabOrder = 0 end object RichEdit1: TRichEdit Left = 0 Top = 0 Width = 237 Height = 89 Align = alTop TabOrder = 1 end end La VCL no nos a las herramientas suficientes para poder insertar imagenes en un RichEdit de forma directa. Debemos usar la Api de Ole. Los RichEdit implementan la interfaz IRichEditOle que tiene el guid ['{00020D00-0000-0000C000-000000000046}']. Lamentablemente no existe en ninguna unit que viene con delphi esa interfaz declarada. Pero a no desesperar, este truco muestra a continuación como debiera ser la declaración de esa interfaz. Tan sólo hay que crear una unit con el nombre RichOle y copiar el código : //---------------------------------------------------------------// // Converted from richole.h // OLE Extensions to the Rich Text Editor // Copyright (c) 1985-1996, Microsoft Corporation // //---------------------------------------------------------------unit RichOle; interface uses Windows, ActiveX, Richedit; //--- Types -----------------------------------------------------// Structure passed to GetObject and InsertObject type TREObject = packed record cbStruct : DWORD; // Size of structure cp : longint; // Character position of object clsid : TCLSID; // Class ID of object oleobj : IOleObject; // OLE object interface stg : IStorage; // Associated storage interface olesite : IOLEClientSite; // Associated client site interface sizel : TSize; // Size of object (may be 0,0) dvaspect : DWORD; // Display aspect to use dwFlags : DWORD; // Object status flags dwUser : DWORD // Dword for user's use end; //--- Constants -------------------------------------------------const // Flags to specify which interfaces should be returned in the structure above REO_GETOBJ_NO_INTERFACES = $00000000; Escrito por Hechicero (Esteban Cesar Calabria) Fecha Ultima Modificacion: 24 de Julio del 2004 3 REO_GETOBJ_POLEOBJ REO_GETOBJ_PSTG REO_GETOBJ_POLESITE REO_GETOBJ_ALL_INTERFACES = = = = $00000001; $00000002; $00000004; $00000007; // Place object at selection REO_CP_SELECTION = $FFFFFFFF; // -1 // Use character position to specify object instead of index REO_IOB_SELECTION = $FFFFFFFF; // -1 REO_IOB_USE_CP = $FFFFFFFE; // -2 // Object flags REO_NULL REO_READWRITEMASK REO_DONTNEEDPALETTE REO_BLANK REO_DYNAMICSIZE REO_INVERTEDSELECT REO_BELOWBASELINE REO_RESIZABLE REO_LINK REO_STATIC REO_SELECTED REO_OPEN REO_INPLACEACTIVE REO_HILITED REO_LINKAVAILABLE REO_GETMETAFILE = = = = = = = = = = = = = = = = $00000000; $0000003F; $00000020; $00000010; $00000008; $00000004; $00000002; $00000001; $80000000; $40000000; $08000000; $04000000; $02000000; $01000000; $00800000; $00400000; // // // // // // // // // // // // // // // // // flags for IRichEditOle::GetClipboardData () // and IRichEditOleCallback.QueryAcceptData RECO_PASTE = $00000000; // RECO_DROP = $00000001; // RECO_COPY = $00000002; // RECO_CUT = $00000003; // RECO_DRAG = $00000004; // No flags Mask out RO bits Object doesn't need palette Object is blank Object defines size always Object drawn all inverted if sel Object sits below the baseline Object may be resized Object is a link (RO) Object is static (RO) Object selected (RO) Object open in its server (RO) Object in place active (RO) Object is to be hilited (RO) Link believed available (RO) Object requires metafile (RO) (), IRichEditOleCallback.GetClipboardData () paste from clipboard drop copy to the clipboard cut to the clipboard drag //--- IRichEditOle ----------------------------------------------// Interface used by the client of RichEdit to perform OLE-related operations. type IRichEditOle = interface ['{00020D00-0000-0000-C000-000000000046}'] function GetClientSite (out lplpolesite : IOLECLIENTSITE) : HResult; stdcall; function GetObjectCount : longint; stdcall; function GetLinkCount : longint; stdcall; function GetObject (iob : longint; out reobject : TREObject; dwFlags : DWORD) : HRESULT; stdcall; function InsertObject (const reobject : TREObject) : HResult; stdcall; function ConvertObject (iob : longint; const clsidNew : TCLSID; lpStrUserTypeNew : LPCSTR) : HRESULT; stdcall; function ActivateAs (const clsid, clsidAs : TCLSID) : HRESULT; stdcall; function SetHostNames (lpstrContainerApp, lpstrContainerObj : LPCSTR) : HRESULT; stdcall; function SetLinkAvailable (iob : longint; fAvailable : BOOL) : HRESULT; stdcall; function SetDvaspect (iob : longint; dvaspect : DWORD) : HRESULT; stdcall; function HandsOffStorage (iob : longint) : HRESULT; stdcall; function SaveCompleted (iob : longint; stg : IStorage) : HRESULT; stdcall; function InPlaceDeactivate : HRESULT; stdcall; function ContextSensitiveHelp (fEnterMode : BOOL) : HRESULT; stdcall; function GetClipboardData (const chrg : TCharRange; reco : DWORD; out dataobj : IDataObject) : HRESULT; stdcall; function ImportDataObject (dataobj : IDataObject; cf : TClipFormat; hMetaPict : HGLOBAL) : HRESULT; stdcall; end; //--- IRichEditOleCallback ----------------------------------------// Interface used by the RichEdit to get OLE-related stuff from the application // using RichEdit. IRichEditOleCallback = interface ['{00020D03-0000-0000-C000-000000000046}'] function GetNewStorage (out stg : IStorage) : HRESULT; stdcall; function GetInPlaceContext (out Frame : IOleInPlaceFrame; out Doc : Escrito por Hechicero (Esteban Cesar Calabria) Fecha Ultima Modificacion: 24 de Julio del 2004 4 IOleInPlaceUIWindow; var FrameInfo : TOleInPlaceFrameInfo) : HRESULT; stdcall; function ShowContainerUI (fShow : BOOL) : HRESULT; stdcall; function QueryInsertObject (const clsid : TCLSID; stg : IStorage; cp : longint) : HRESULT; stdcall; function DeleteObject (oleobj : IOLEObject) : HRESULT; stdcall; function QueryAcceptData (dataobj : IDataObject; var cfFormat : TClipFormat; reco : DWORD; fReally : BOOL; hMetaPict : HGLOBAL) : HRESULT; stdcall; function ContextSensitiveHelp (fEnterMode : BOOL) : HRESULT; stdcall; function GetClipboardData (const chrg : TCharRange; reco : DWORD; out dataobj : IDataObject) : HRESULT; stdcall; function GetDragDropEffect (fDrag : BOOL; grfKeyState : DWORD; var dwEffect : DWORD) : HRESULT; stdcall; function GetContextMenu (seltype : Word; oleobj : IOleObject; const chrg : TCharRange; var menu : HMENU) : HRESULT; stdcall; end; implementation end. Para obtener la interfaz anterior de nuestro componente TRichEdit de delphi debemos utilizar la funcion de la api de windows SendMessage para enviarle el mensaje EM_GETOLEINTERFACE a nuestro editor de texto enriquecido. No obstante, para complicarnos la existencia, en ninguna unit de Delphi esta declarado ese mensaje. Por lo tanto vamos a tener que escribirlo a mano en la sección const de nuestra unit. En un acto de originalidad la llamaremos “Unit1”. El encabezado propuesto es: unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls, StdCtrls, ComCtrls, Mask, Activex, ComObj, Shlobj, RichOle, jpeg; const EM_GETOLEINTERFACE = WM_USER + 60; CLSID_NULL: TGUID = ( D1:$00000000;D2:$0000;D3:$0000;D4:($00,$00,$00,$00,$00,$00,$00,$00)); Acto seguido vamos a programar una clase que implemente la interfaz IDataObject (esta ultima si ya viene declarada en la unit Activex) para poder crear un objeto Ole que tenga una referencia al bitmap que queremos insertar en el RichEdit. La declaración de esa clase tiene la siguiente forma: type TImageDataObject = class(TInterfacedObject,IDataObject) private FRecMediul:STGMEDIUM; FRecFormat: TFormatEtc; procedure setBitmap(const Value: HBitmap); published property Bitmap:HBITMAP write setBitmap; property RecFormat:TFormatEtc read FRecFormat; protected Escrito por Hechicero (Esteban Cesar Calabria) Fecha Ultima Modificacion: 24 de Julio del 2004 5 function GetData(const formatetcIn: TFormatEtc; out medium: TStgMedium): HResult; stdcall; function GetDataHere(const formatetc: TFormatEtc; out medium: TStgMedium): HResult; stdcall; function QueryGetData(const formatetc: TFormatEtc): HResult; stdcall; function GetCanonicalFormatEtc(const formatetc: TFormatEtc; out formatetcOut: TFormatEtc): HResult; stdcall; function SetData(const formatetc: TFormatEtc; var medium: TStgMedium; fRelease: BOOL): HResult; stdcall; function EnumFormatEtc(dwDirection: Longint; out enumFormatEtc: IEnumFormatEtc): HResult; stdcall; function DAdvise(const formatetc: TFormatEtc; advf: Longint; const advSink: IAdviseSink; out dwConnection: Longint): HResult; stdcall; function DUnadvise(dwConnection: Longint): HResult; stdcall; function EnumDAdvise(out enumAdvise: IEnumStatData): HResult; stdcall; end; Lo unico que nos queda es la clase de nuestro formulario y la implementación. Dada la característica pragmatica de este articulo no nos vamos a detener en la explicación de esta última. Para mayor detalle de lo que hacen las funciones de la API de Ole recomiendo hecharle un vistazo a la MSDN Library de Microsoft. A Continuación, la cereza del postre: TForm1 = class(TForm) Panel1: TPanel; Image1: TImage; RichEdit1: TRichEdit; Image2: TImage; Image3: TImage; Image4: TImage; Image5: TImage; Image6: TImage; Image7: TImage; procedure Button4Click(Sender: TObject); private FPoints:TList; { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.Button4Click(Sender: TObject); var RE: IRichEditOle; RCS: IOleClientSite; JI: IOleObject; ILB: ILockBytes; SI: IStorage; REO: TReObject; ds:IDataObject; b : TBitmap; MyData : TImageDataObject; Escrito por Hechicero (Esteban Cesar Calabria) Fecha Ultima Modificacion: 24 de Julio del 2004 6 p : PFormatEtc; begin if (Sender is TImage) then Begin RE:=nil; //mio MyData := TImageDataObject.Create; b := TBitmap.Create; b.Assign((Sender as TImage).Picture.Graphic); MyData.Bitmap := b.Handle; //mio SendMessage(RichEdit1.Handle,EM_GETOLEINTERFACE,0,integer(@RE)); OleCheck(RE.GetClientSite(RCS)); OleCheck(CreateILockBytesOnHGlobal(0,true,ILB)); OleCheck(StgCreateDocFileOnILockBytes(ILB,STGM_SHARE_EXCLUSIVE+STGM_CREATE +STGM_READWRITE,0,SI)); //Si quisieramos leerla directamente de disco // OleCreateFromFile(CLSID_NULL,'F:\Archivos de programa\Borland\Delphi7\Projects\Proyectos mios\Imagenes en un RichEdit\dibujo.bmp',IOleObject,OLERENDER_DRAW,nil,RCS,SI,JI); OleCheck(OleCreateStaticFromData(MyData,IOleObject,OLERENDER_FORMAT, @MyData.RecFormat,RCS,SI,JI)); OleSetContainedObject(JI,true); FillChar(REO,sizeof(REO),0); REO.cbStruct:=sizeof(REO); REO.cp:=REO_CP_SELECTION; REO.dvAspect:=DVASPECT_CONTENT; REO.oleobj:=JI; REO.olesite:=RCS; REO.stg:=SI; OleCheck(RE.InsertObject(REO)); end; end; { TImageDataObject } function TImageDataObject.DAdvise(const formatetc: TFormatEtc; advf: Integer; const advSink: IAdviseSink; out dwConnection: Integer): HResult; begin result := E_NOTIMPL; end; function TImageDataObject.DUnadvise(dwConnection: Integer): HResult; begin result := E_NOTIMPL; end; function TImageDataObject.EnumDAdvise( out enumAdvise: IEnumStatData): HResult; begin result := E_NOTIMPL; end; function TImageDataObject.EnumFormatEtc(dwDirection: Integer; out enumFormatEtc: IEnumFormatEtc): HResult; begin result := E_NOTIMPL; end; function TImageDataObject.GetCanonicalFormatEtc( const formatetc: TFormatEtc; out formatetcOut: TFormatEtc): HResult; begin result := E_NOTIMPL; end; function TImageDataObject.GetData(const formatetcIn: TFormatEtc; out medium: TStgMedium): HResult; var hDest : HBITMAP; begin Escrito por Hechicero (Esteban Cesar Calabria) Fecha Ultima Modificacion: 24 de Julio del 2004 7 medium.tymed := TYMED_GDI; medium.hBitmap := Self.FRecMediul.hBitmap; medium.unkForRelease := nil; result:= S_OK; end; function TImageDataObject.GetDataHere(const formatetc: TFormatEtc; out medium: TStgMedium): HResult; begin result := E_NOTIMPL; end; function TImageDataObject.QueryGetData(const formatetc: TFormatEtc): HResult; begin result := E_NOTIMPL; end; procedure TImageDataObject.setBitmap(const Value: HBITMAP); var Medium : STGMEDIUM; Format :TFormatEtc; begin Medium.tymed := TYMED_GDI; Medium.hBitmap := Value; Medium.unkForRelease := nil; Format.cfFormat := CF_BITMAP; Format.ptd := nil; Format.dwAspect := DVASPECT_CONTENT; Format.lindex := -1; format.tymed := Tymed_Gdi; Self.SetData(Format,Medium,true); end; function TImageDataObject.SetData(const formatetc: TFormatEtc; var medium: TStgMedium; fRelease: BOOL): HResult; begin Self.FRecMediul := medium; Self.FRecFormat := formatetc; result := S_OK; end; end. Así podemos insertar emoticons dentro de un Richedit. Ahora no tenemos que envidiarle al MSN y al ICQ o a cualquier mensajero. Escrito por Hechicero (Esteban Cesar Calabria) Fecha Ultima Modificacion: 24 de Julio del 2004 8