Google Search

Google Search Results

Tuesday, November 18, 2008

Authenticode my way

After looking at the various available methods for code signing, I decided to do things a little different which seems to better meet the needs of my particular project. In my case I need to verify that an EXE was created (signed) by me, and only by me. Most of the examples on MSDN and other places where to validate a signature in order to know that it was signed by the name on the signature, and that some CA (Verisign, etc) says you are who you say you are.

Anyway, as always, here is the code that works for me. TFileStream and TMemoryStream are part of Borland's VCL library, but it should be obvious what they are doing, just reading/writing a file, so you can replace with .NET or whatever you like. I only use assert() for error checking to keep the sample small, replace with something else of course!

// First you run this code, just once, to create private/public key pairs and
// save them to files. PublicKey.dat you would include with your program,
// and you would not give out PrivateKey.dat and keep in a safe place.
// I plan on compiling the PublicKey.dat file in the EXE I ship.
HCRYPTPROV cp;
assert(CryptAcquireContext(&cp,NULL,NULL,PROV_RSA_AES,CRYPT_VERIFYCONTEXT));
HCRYPTKEY ck;
assert(CryptGenKey(cp,AT_SIGNATURE,RSA1024BIT_KEYCRYPT_EXPORTABLE,&ck));
BYTE pkblob[8192];
DWORD pkblob_size = sizeof(pkblob);
assert(CryptExportKey(ck,NULL,PRIVATEKEYBLOB,0,pkblob,&pkblob_size));
TFileStream *fs = new TFileStream("c:\\PrivateKey.dat",fmCreate);
fs->WriteBuffer(pkblob,pkblob_size);
delete fs;
pkblob_size = sizeof(pkblob);
assert(CryptExportKey(ck,NULL,PUBLICKEYBLOB,0,pkblob,&pkblob_size));
fs = new TFileStream("c:\\PublicKey.dat",fmCreate);
fs->WriteBuffer(pkblob,pkblob_size);
delete fs;
CryptDestroyKey(ck);
CryptReleaseContext(cp,0);

// Then you would run this code to sign an EXE (or any other file for that matter).
// I plan on making it so this runs after I link the EXE in the Builder IDE, so
// anytime I rebuild the EXE it gets signed. This just appends the signature
// to the end of the EXE, but you could save to a seperate file or whatever you
// like. The signature doesn't need to be in the same file.

HCRYPTPROV cp;
assert(CryptAcquireContext(&cp,NULL,NULL,PROV_RSA_AES,CRYPT_VERIFYCONTEXT));
TFileStream *fs = new TFileStream("c:\\PrivateKey.dat",fmOpenRead);
TMemoryStream *ms = new TMemoryStream();
ms->CopyFrom(fs,0);
delete fs;
HCRYPTKEY ck;
assert(CryptImportKey(cp,(BYTE*)ms->Memory,ms->Size,NULL,0,&ck));
CryptDestroyKey(ck);
delete ms;
HCRYPTHASH ch;
assert(CryptCreateHash(cp,CALG_MD5,0,0,&ch));
fs = new TFileStream("c:\\test.exe",fmOpenReadWrite);
ms = new TMemoryStream();
ms->CopyFrom(fs,0);
assert(CryptHashData(ch,(BYTE*)ms->Memory,ms->Size,0));
delete ms;
BYTE sig[8192];
DWORD sig_size = sizeof(sig);
assert(CryptSignHash(ch,AT_SIGNATURE,NULL,0,sig,&sig_size));
fs->WriteBuffer(sig,sig_size);
delete fs;
CryptDestroyHash(ch);
CryptReleaseContext(cp,0);

// This code is used to verify a signed file. This is the code I'll ship in
// my EXE that also will contain the PublicKey.dat file as part of the EXE.

HCRYPTPROV cp;
assert(CryptAcquireContext(&cp,NULL,NULL,PROV_RSA_AES,CRYPT_VERIFYCONTEXT));
TFileStream *fs = new TFileStream("c:\\PublicKey.dat",fmOpenRead);
TMemoryStream *ms = new TMemoryStream();
ms->CopyFrom(fs,0);
delete fs;
HCRYPTKEY ck;
assert(CryptImportKey(cp,(BYTE*)ms->Memory,ms->Size,NULL,0,&ck));
delete ms;
HCRYPTHASH ch;
assert(CryptCreateHash(cp,CALG_MD5,0,0,&ch));
fs = new TFileStream("c:\\test.exe",fmOpenRead);
ms = new TMemoryStream();
ms->CopyFrom(fs,0);
delete fs;
assert(CryptHashData(ch,(BYTE*)ms->Memory,ms->Size - 128,0));
if(!CryptVerifySignature(ch,(BYTE*)ms->Memory + ((int)ms->Size - 128),128,ck,NULL,0))
ShowMessage("Invalid!");
else
ShowMessage("Valid!");
delete ms;
CryptDestroyKey(ck);
CryptDestroyHash(ch);
CryptReleaseContext(cp,0);

I think this is similar to what WinVerifyTrust() does, as far as the hash and signature part, but it also includes "certificate" type information which is more than I needed for this project.

Try edited your signed file, in this case c:\test.exe, you should get "Invalid!" when you try to verify it after editing it.

Tuesday, September 9, 2008

Need help on VCL, just ask

I've got tons of source code I can share, but instead of posting everything in the first day, I'm going to task requests. If anyone has some code they would like to see, or would like me to help fix your code or answer your problem... feel free to post it as a comment to this post.

You won't find many VCL 6.0 experts out there willing to help in this way, most have moved on to other things, newer versions, and .NET... but for those of you like me who are stuck with 6.0, I'm here to help.

My code/experience includes:

GUI
Sockets/TCP-IP
HTTP
FTP
POP3
SMTP
WebDav
MAPI
TAPI
Networking
ISAM Database Code
File i/o
Encryption
Compression - GZIP
Multi-threading
IOCP
Named-pipes
IAT Hooking
Screen capturing and keyboard logging
Services
COM
Outlook Object Model (OOM) and other OLE and Office Automation

O-ya, and I've done a lot with MS Access and VBA too.

Windows NT Services, Install and Uninstall

Here is some generic code that I use in all of my applications that run as a service. Just call InstallService() to install, and UninstallService() to uninstall. This code doesn't really need VCL or any other libraries (just replace the one part that uses AnsiString with sprintf() or whatever you want), it is really just Win32 API calls, so should be easy to take this code and use in Pascal, VB, or whatever you want.

This is just a function I use instead of MessageBox, in the code below. AppDisplayName is whatever your application is named, I use "const char*" for this variable.

static void ShowPrompt(char *text)
{
MessageBox(NULL,text,AppDisplayName,0);
}

static void InstallService(void)
{
// Load SCM.
SC_HANDLE scm = OpenSCManager(NULL,NULL,SC_MANAGER_CREATE_SERVICE);
if(!scm)
{
ShowPrompt("Error opening SCM, unable to install service.");
return;
}

char filename[MAXPATH];
GetModuleFileName(GetModuleHandle(NULL),filename,sizeof(filename));

// Create our service and give it the path to our EXE.
// We create an auto-load service which will load at system startup.
SC_HANDLE sh = CreateService(scm,AppDisplayName,AppDisplayName,
SERVICE_ALL_ACCESS,SERVICE_WIN32_OWN_PROCESS,
SERVICE_AUTO_START,SERVICE_ERROR_NORMAL,
filename,NULL,NULL,NULL,NULL,NULL);
if(!sh)
{
ShowPrompt("Error installing service.");
CloseServiceHandle(scm);
return;
}

// Start our service.
if(!StartService(sh,0,NULL))
{
ShowPrompt("Error starting service.");
CloseServiceHandle(sh);
CloseServiceHandle(scm);
return;
}

// Close handles.
CloseServiceHandle(sh);
CloseServiceHandle(scm);

AnsiString aStr;
aStr.sprintf("%s has been installed and is now running as a service.",AppDisplayName);
ShowPrompt(aStr.c_str());
}

static void UninstallService(void)
{
// Load SCM.
SC_HANDLE scm = OpenSCManager(NULL,NULL,SC_MANAGER_CREATE_SERVICE);
if(!scm)
{
ShowPrompt("Error opening SCM, unable to uninstall service.");
return;
}

SC_HANDLE sh = OpenService(scm,AppDisplayName,SERVICE_ALL_ACCESS);
if(!sh)
{
CloseServiceHandle(scm);
ShowPrompt("Unable to open service, it may not be installed.");
return;
}

// Tell service to stop, but we don't care if this fails (it usually
// just means the service isn't running, but not matter what we want
// to try to uninstall anyway).
SERVICE_STATUS ss;
ControlService(sh,SERVICE_CONTROL_STOP,&ss);

// Uninstall service.
if(!DeleteService(sh))
{
if(GetLastError() != ERROR_SERVICE_MARKED_FOR_DELETE)
{
CloseServiceHandle(sh);
CloseServiceHandle(scm);
ShowPrompt("Unable to uninstall service.");
return;
}
}

// Close handles.
CloseServiceHandle(sh);
CloseServiceHandle(scm);

ShowPrompt("Service uninstalled.");
}

vclutils.h

In some of my other blogs I reference this include file. It isn't much, just a way to avoid compiler warnings when using dateutils.hpp, here you go... include this file instead of dateutils.hpp directly:

----------- vclutils.h ----------

#ifndef VCLUTILS_H
#define VCLUTILS_H 1

// Use this header instead of directly to avoid compiler warnings (see below)

#include

// Because "dateutils.hpp" includes a bunch of static const variables
// (also from math.hpp) we have to do this to get rid of warnings.
namespace DoNotUse
{
static const Extended eliminateWarnings = NaN + Infinity + NegInfinity +
OneHour + OneMinute + OneSecond + OneMillisecond + MinSingle + MaxSingle +
MinDouble + MaxDouble + MinExtended + MaxExtended + MinComp + MaxComp;
}

#endif // don't nest header

TEdit control for money/currency values

Here is some code for a TEdit derived control that auto-formats for currency/money. I know a couple of the includes you will not have, like psparts and vclutils, but once you try to compile it should be obvious what you need to add to get it to compile (if anything). Actually I'll go ahead and include vclutils.h in another blog, but I can't include psparts.h because it contains a lot of things I don't want to (or cannot) share, it probably isn't even needed. This one does derive from another class I call TPSEdit, but just change to TEdit and you should be fine (TPSEdit doesn't do much)... the main thing to learn from this anyway are the little tricks, which you can then use for other stuff, like the Paint() event:

----- PSMoneyEdit.cpp --------

#include
#include
#include "vclutils.h"
#include "psparts.h"
#include "PSMoneyEdit.h"
#pragma package(smart_init)

static const double min = 0.00;
static const double max = 10000000.00;

static double TextToDoubleFieldValue(char *text,int decimal_count)
{
AnsiString t = AnsiString(text).Trim();
int l = t.Length();
BOOL neg = l && (t[1] == '-' t[1] == '(');
AnsiString w;
AnsiString d;
BOOL dec = FALSE;
for(int x=1;x<=l;x++)
{
if(t[x] == '.')
dec = TRUE;

if(isdigit(t[x]))
{
if(dec)
d = d + t[x];
else
w = w + t[x];
}
}

l = w.Length();
if(l > 8)
{
if(neg)
return(min);
else
return(max);
}

if(neg)
w = "-" + w;

if(decimal_count && !d.IsEmpty())
w = w + '.' + d.SubString(1,decimal_count);

double ret = atof(w.c_str());
if(ret < min)
ret = min;
else if(ret > max)
ret = max;

return(ret);
}

__fastcall TPSMoneyEditEdit::TPSMoneyEditEdit(TComponent* Owner)
: inherited(Owner)
{
}

void __fastcall TPSMoneyEditEdit::WndProc(TMessage &Message)
{
if(Message.Msg == CN_KEYDOWN)
{
if(Message.WParam == VK_RETURN)
{
if(FTabOnReturn)
Message.WParam = VK_TAB;
else
Message.WParam = 0;
}
}

inherited::WndProc(Message);
}

void __fastcall TPSMoneyEditEdit::CreateWnd(void)
{
inherited::CreateWnd();
}

void __fastcall TPSMoneyEditEdit::DoEnter(void)
{
inherited::DoEnter();
SelectAll();
}

void __fastcall TPSMoneyEditEdit::DoExit(void)
{
double v = Owner->Value;
Owner->Value = v;
inherited::DoExit();
}

void __fastcall TPSMoneyEditEdit::ChangeScale(int M, int D)
{
M;
D;
}

__fastcall TPSMoneyEdit::TPSMoneyEdit(TComponent* Owner)
: inherited(Owner)
{
EditCtrl = new TPSMoneyEditEdit(this);
EditCtrl->AutoSize = FALSE;
EditCtrl->BorderStyle = bsNone;
Height = 21;
Width = 100;
Text = "0.00";
FBorder = TRUE;
Alignment = taRightJustify;
FDecimalCount = 2;
MaxLength = 10 + (FDecimalCount ? FDecimalCount + 1 : 0);
}

void __fastcall TPSMoneyEdit::SetValue(double val)
{
if(val < min)
val = min;
else if(val > max)
val = max;
Text = FloatToStrF(val,ffNumber,15,FDecimalCount);
}

double __fastcall TPSMoneyEdit::GetValue(void)
{
return(TextToDoubleFieldValue(Text.c_str(),FDecimalCount));
}

void TPSMoneyEdit::SetRects(void)
{
if(!EditCtrl !Parent)
return;

TRect rc = BoundsRect;

InflateRect(&rc,-2,-2);

Canvas->Font = EditCtrl->Font;
Canvas->Font->PixelsPerInch = EditCtrl->Font->PixelsPerInch;

int h = Canvas->TextHeight("A");
rc.top += Floor((float)(rc.Height() - h) / 2);
rc.bottom = rc.top + h;

rc.left += Canvas->TextWidth(FPrefix);

EditCtrl->BoundsRect = rc;
}

void __fastcall TPSMoneyEdit::SetPrefix(AnsiString val)
{
if(val == FPrefix)
return;

FPrefix = val;

SetRects();

Invalidate();
}

void __fastcall TPSMoneyEdit::SetDecimalCount(int val)
{
if(val == FDecimalCount)
return;

FDecimalCount = val;

Value = Value;

MaxLength = 10 + (FDecimalCount ? FDecimalCount + 1 : 0);
}

void __fastcall TPSMoneyEdit::SetParent(TWinControl* AParent)
{
inherited::SetParent(AParent);
EditCtrl->Parent = AParent;
SetRects();
}

void __fastcall TPSMoneyEdit::Paint(void)
{
TCanvas *c = Canvas;
TRect rc = ClientRect;
c->Brush->Color = EditCtrl->Color;
c->FillRect(rc);
if(FBorder)
DrawEdge(c->Handle,&rc,EDGE_SUNKEN,BF_RECT);

rc.left += 2;
if(!rc.Height() % 2)
rc.bottom -= 1;

c->Font = EditCtrl->Font;
c->Font->PixelsPerInch = EditCtrl->Font->PixelsPerInch;

DrawText(c->Handle,FPrefix.c_str(),-1,&rc,DT_NOPREFIXDT_LEFTDT_VCENTERDT_SINGLELINE);

inherited::Paint();
}

void __fastcall TPSMoneyEdit::Resize(void)
{
inherited::Resize();
SetRects();
}

void __fastcall TPSMoneyEdit::MouseDown(TMouseButton Button,TShiftState Shift,int X,int Y)
{
if(ComponentState.Contains(csDesigning))
inherited::MouseDown(Button,Shift,X,Y);
}

void __fastcall TPSMoneyEdit::MouseUp(TMouseButton Button,TShiftState Shift,int X,int Y)
{
if(ComponentState.Contains(csDesigning))
inherited::MouseUp(Button,Shift,X,Y);
}

void __fastcall TPSMoneyEdit::SetBounds(int ALeft, int ATop, int AWidth, int AHeight)
{
inherited::SetBounds(ALeft,ATop,AWidth,AHeight);
if(ComponentState.Contains(csLoading))
SetRects();
}

void __fastcall TPSMoneyEdit::SetBorder(bool val)
{
if(FBorder == val)
return;

FBorder = val;

SetRects();
}

void __fastcall TPSMoneyEdit::CMVisibleChanged(TMessage &msg)
{
EditCtrl->Visible = Visible;
#pragma warn -parm
}
#pragma warn .parm

void __fastcall TPSMoneyEdit::CMEnabledChanged(TMessage &msg)
{
EditCtrl->Enabled = Enabled;
#pragma warn -parm
}
#pragma warn .parm

void __fastcall TPSMoneyEdit::WMContextMenu(TWMContextMenu &msg)
{
if(!EditCtrl !EditCtrl->Focused())
msg.Result = 1;
else
inherited::Dispatch((void*)&msg);
}

namespace Psmoneyedit
{
void __fastcall PACKAGE Register()
{
TComponentClass classes[1] = {__classid(TPSMoneyEdit)};
RegisterComponents("ERIC", classes, 0);
}
}

static inline void ValidCtrCheck(TPSMoneyEdit *)
{
new TPSMoneyEdit(NULL);
}


--------- PSMoneyEdit.h -------------

#ifndef PSMoneyEditH
#define PSMoneyEditH

#include
#include
#include
#include
#include "PSEdit.h"

class PACKAGE TPSMoneyEdit;

class PACKAGE TPSMoneyEditEdit : public TPSEdit
{
typedef TPSEdit inherited;
protected:
void __fastcall WndProc(TMessage &Message);
void __fastcall CreateWnd(void);
DYNAMIC void __fastcall DoEnter(void);
DYNAMIC void __fastcall DoExit(void);
DYNAMIC void __fastcall ChangeScale(int M, int D);
TPSMoneyEdit* __fastcall GetMoneyOwner(void){return((TPSMoneyEdit*)inherited::Owner);}
__property TPSMoneyEdit* Owner = {read=GetMoneyOwner};
public:
__fastcall TPSMoneyEditEdit(TComponent* Owner);
__published:
};

class PACKAGE TPSMoneyEdit : public TPaintBox
{
friend TPSMoneyEditEdit;
typedef TPaintBox inherited;
protected:
int FDecimalCount;
AnsiString FPrefix;
bool FBorder;
void __fastcall SetValue(double val);
double __fastcall GetValue(void);
void __fastcall SetPrefix(AnsiString val);
void __fastcall SetDecimalCount(int val);
void __fastcall SetBorder(bool val);
void __fastcall SetParent(TWinControl* AParent);

TAlignment __fastcall GetAlignment(void){return(EditCtrl->Alignment);}
void __fastcall SetAlignment(TAlignment val){EditCtrl->Alignment = val;}
bool __fastcall GetReadOnly(void){return(EditCtrl->ReadOnly);}
void __fastcall SetReadOnly(bool val){EditCtrl->ReadOnly = val;}
bool __fastcall GetParentColor(void){return(EditCtrl->ParentColor);}
void __fastcall SetParentColor(bool val){EditCtrl->ParentColor = val;}
int __fastcall GetMaxLength(void){return(EditCtrl->MaxLength);}
void __fastcall SetMaxLength(int val){EditCtrl->MaxLength = val;}
AnsiString __fastcall GetText(void){return(EditCtrl->Text);}
void __fastcall SetText(AnsiString val){EditCtrl->Text = val;}
bool __fastcall GetTabStop(void){return(EditCtrl->TabStop);}
void __fastcall SetTabStop(bool val){EditCtrl->TabStop = val;}
TTabOrder __fastcall GetTabOrder(void){return(EditCtrl->TabOrder);}
void __fastcall SetTabOrder(TTabOrder val){EditCtrl->TabOrder = val;}
TNotifyEvent __fastcall GetOnEnter(void){return(EditCtrl->OnEnter);}
void __fastcall SetOnEnter(TNotifyEvent val){EditCtrl->OnEnter = val;}
TNotifyEvent __fastcall GetOnExit(void){return(EditCtrl->OnExit);}
void __fastcall SetOnExit(TNotifyEvent val){EditCtrl->OnExit = val;}
TKeyEvent __fastcall GetOnKeyDown(void){return(EditCtrl->OnKeyDown);}
void __fastcall SetOnKeyDown(TKeyEvent val){EditCtrl->OnKeyDown = val;}
bool __fastcall GetTabOnReturn(void){return(EditCtrl->TabOnReturn);}
void __fastcall SetTabOnReturn(bool val){EditCtrl->TabOnReturn = val;}
int __fastcall GetSelStart(void){return(EditCtrl->SelStart);}
void __fastcall SetSelStart(int val){EditCtrl->SelStart = val;}
TPopupMenu* __fastcall _GetPopupMenu(void){return(EditCtrl->PopupMenu);}
void __fastcall SetPopupMenu(TPopupMenu* val){EditCtrl->PopupMenu = val;}

__property int MaxLength = {read=GetMaxLength,write=SetMaxLength};

void __fastcall Paint(void);
DYNAMIC void __fastcall Resize(void);
DYNAMIC void __fastcall MouseDown(TMouseButton Button,TShiftState Shift,int X,int Y);
DYNAMIC void __fastcall MouseUp(TMouseButton Button,TShiftState Shift,int X,int Y);
void __fastcall SetBounds(int ALeft, int ATop, int AWidth, int AHeight);
void __fastcall CMVisibleChanged(TMessage &msg);
void __fastcall CMEnabledChanged(TMessage &msg);
void __fastcall WMContextMenu(TWMContextMenu &msg);
BEGIN_MESSAGE_MAP
VCL_MESSAGE_HANDLER(CM_VISIBLECHANGED,TMessage,CMVisibleChanged);
VCL_MESSAGE_HANDLER(CM_ENABLEDCHANGED,TMessage,CMEnabledChanged);
VCL_MESSAGE_HANDLER(WM_CONTEXTMENU,TWMContextMenu,WMContextMenu);
END_MESSAGE_MAP(inherited);
public:
TPSMoneyEditEdit *EditCtrl;
__fastcall TPSMoneyEdit(TComponent* Owner);
__property double Value = {read=GetValue,write=SetValue,stored=false};
__property AnsiString Text = {read=GetText,write=SetText};
__property int SelStart = {read=GetSelStart,write=SetSelStart,nodefault};

BOOL Focused(void){return(EditCtrl->Focused());}
BOOL CanFocus(void){return(EditCtrl->CanFocus());}
void SetFocus(void){return(EditCtrl->SetFocus());}
void SetRects(void);
__published:
__property AnsiString Prefix = {read=FPrefix,write=SetPrefix};
__property int DecimalCount = {read=FDecimalCount,write=SetDecimalCount,default=2};
__property bool Border = {read=FBorder,write=SetBorder,default=TRUE};
__property TAlignment Alignment = {read=GetAlignment,write=SetAlignment,default=taRightJustify};
__property bool ReadOnly = {read=GetReadOnly,write=SetReadOnly,default=FALSE};
__property bool ParentColor = {read=GetParentColor,write=SetParentColor,default=1};
__property bool TabStop = {read=GetTabStop,write=SetTabStop,default=TRUE};
__property TTabOrder TabOrder = {read=GetTabOrder,write=SetTabOrder,default=-1};
__property TNotifyEvent OnEnter = {read=GetOnEnter,write=SetOnEnter};
__property TNotifyEvent OnExit = {read=GetOnExit,write=SetOnExit};
__property TKeyEvent OnKeyDown = {read=GetOnKeyDown,write=SetOnKeyDown};
__property bool TabOnReturn = {read=GetTabOnReturn,write=SetTabOnReturn,default=TRUE};
__property TPopupMenu* PopupMenu = {read=_GetPopupMenu,write=SetPopupMenu};
};

#endif

Clipboard Change Notification

This is my first post to my new blog. This blog will be a place for me to post programming information, a way for me to share my knowledge, most of which I learned on-line, so it feels right to give some back.

I've been working fulltime for the same company for the past 8 years, developing software mostly for Windows PCs. I am the only developer at the small company I work for, so I do everything (we do have a testing department of 4 to 6 people, so I only debug my code, then send it off to testing for the major testing). Most of my code is C++, and most uses the Borland VCL library, and I compile most of it with Borland C++ Builder version 6 (BCB6). I don't like .NET because you can't write one set of code that works across Windows 98 through Vista, so I haven't done anything professional using .NET other than web-based stuff (ASP.NET).

I don't like talking about myself, so let's get right to some free source code, do whatever the heck you want with it:

This is a VCL component that notifys you anytime the Windows clipboard changes, just drop on your form and set the OnClipboardChanged event. Then you can use VCL's Clipboard() object to work with the clipboard if you want.

---------- MyClipboard.cpp -----------

#include
#include
#include "MyClipboard.h"
#pragma package(smart_init)

__fastcall TMYClipboard::TMYClipboard(TComponent* Owner)
: inherited(Owner)
{
FWindowHandle = AllocateHWnd(WndProc);
NextViewerWindow = SetClipboardViewer(FWindowHandle);
}

__fastcall TMYClipboard::~TMYClipboard()
{
ChangeClipboardChain(FWindowHandle,NextViewerWindow);
DeallocateHWnd(FWindowHandle);
}

void __fastcall TMYClipboard::WndProc(TMessage &Msg)
{
if(Msg.Msg == WM_DRAWCLIPBOARD)
{
if(FOnClipboardChanged)
FOnClipboardChanged(this);
if(NextViewerWindow)
SendMessage(NextViewerWindow,Msg.Msg,Msg.WParam,Msg.LParam);
}
else if(Msg.Msg == WM_CHANGECBCHAIN)
{
if(NextViewerWindow == (HWND) Msg.WParam)
NextViewerWindow = (HWND) Msg.LParam;
else if(NextViewerWindow)
SendMessage(NextViewerWindow,Msg.Msg,Msg.WParam,Msg.LParam);
Msg.Result = 0;
}
else
Msg.Result = DefWindowProc(FWindowHandle,Msg.Msg,Msg.WParam,Msg.LParam);
}


namespace Myclipboard
{
void __fastcall PACKAGE Register()
{
TComponentClass classes[1] = {__classid(TMYClipboard)};
RegisterComponents("MYPAGE", classes, 0);
}
}

static inline void ValidCtrCheck(TMYClipboard *)
{
new TMYClipboard(NULL);
}

--------------- MyClipboard.h ----------------

#ifndef MYClipboardH
#define MYClipboardH

#include
#include

class PACKAGE TMYClipboard : public TComponent
{
typedef TComponent inherited;
private:
protected:
HWND FWindowHandle;
HWND NextViewerWindow;
void __fastcall WndProc(TMessage &Msg);
void __fastcall (__closure *FOnClipboardChanged)(TObject *Sender);
public:
__fastcall TMYClipboard(TComponent* Owner);
__fastcall ~TMYClipboard();
__published:
__property void __fastcall (__closure *OnClipboardChanged)(TObject *Sender)={read=FOnClipboardChanged,write=FOnClipboardChanged};
};

#endif