Save text from the clipboard to a file - delphi

I was trying out below code that should save clipboard text to a text file in Delphi XE6. The code runs fine but generates only junk values in the output file, even when the clipboard contains a copied text fragment. How can the code be changed to work properly?
function SaveClipboardTextDataToFile(
sFileTo : string ) : boolean;
var
ps1,
ps2 : PChar;
dwLen : DWord;
tf : TextFile;
hData : THandle;
begin
Result := False;
with Clipboard do
begin
try
Open;
if( HasFormat( CF_TEXT ) ) then
begin
hData :=
GetClipboardData( CF_TEXT );
ps1 := GlobalLock( hData );
dwLen := GlobalSize( hData );
ps2 := StrAlloc( 1 + dwLen );
StrLCopy( ps2, ps1, dwLen );
GlobalUnlock( hData );
AssignFile( tf, sFileTo );
ReWrite( tf );
Write( tf, ps2 );
CloseFile( tf );
StrDispose( ps2 );
Result := True;
end;
finally
Close;
end;
end;
end;

You see junk because CF_TEXT is ANSI. You request ANSI text, the OS converts the clipboard contents to ANSI, and you put it in unicode string. Use CF_UNICODETEXT for unicode applications.
Also consider the points raised in the comments to the question.

If you have Delphi XE6 then you can use some of the already implemented features
uses
System.SysUtils,
System.IOUtils,
Vcl.Clipbrd;
function SaveClipboardTextDataToFile( const sFileTo : string ) : boolean;
var
LClipboard : TClipboard;
LContent : string;
begin
// get the clipboard content as text
LClipboard := TClipboard.Create;
try
LContent := LClipboard.AsText;
finally
LClipboard.Free;
end;
// save the text - if any - into a file
if not LContent.IsEmpty
then
begin
TFile.WriteAllText( sFileTo, LContent );
Exit( True );
end;
Result := False;
end;

Related

How run 64Bit Compiled PE in 64BitOS and to stop 32Bit Compiled PE in 64BitOS?

How run 64Bit Compiled PE in 64BitOS and to stop 32Bit Compiled PE in 64BitOS?
I am having one Delphi XE2 Project to to create some node and subnodes in Windows Registry as described below :
and my Project Compiler option as below :
I have defined the following codes :
function GetWinDir: string;
var
WindowsDirectory: array[0..MAX_PATH] of Char;
begin
GetWindowsDirectory(WindowsDirectory, MAX_PATH - 1);
SetLength(Result, StrLen(WindowsDirectory));
Result := IncludeTrailingPathDelimiter(WindowsDirectory);
end;
function GetSysDir: string;
var
SystemDirectory: array[0..MAX_PATH] of Char;
begin
GetSystemDirectory(SystemDirectory, MAX_PATH - 1);
SetLength(Result, StrLen(SystemDirectory));
Result := IncludeTrailingPathDelimiter(SystemDirectory);
end;
function GetSysNativeDir: string;
var
WindowsDirectory: array[0..MAX_PATH] of Char;
begin
GetWindowsDirectory(WindowsDirectory, MAX_PATH - 1);
SetLength(Result, StrLen(WindowsDirectory));
Result := IncludeTrailingPathDelimiter(WindowsDirectory) + 'Sysnative\';
end;
procedure TMainForm.BitBtn01Click(Sender: TObject);
var
RegistryEntry : TRegistry;
RegistryEntryValue : string;
begin
RegistryEntry := TRegistry.Create(KEY_READ or KEY_WOW64_64KEY);
RegistryEntry.RootKey := HKEY_CLASSES_ROOT;
if (not RegistryEntry.KeyExists('CLSID\{BE800AEB-A440-4B63-94CD-AA6B43647DF9}\')) then
begin
RegistryEntry.Access:= KEY_WRITE or KEY_WOW64_64KEY;
if RegistryEntry.OpenKey('CLSID\{00000000-0000-0000-0000-000000000001}\',true) then
begin
Memo01.Font.Color := 3992580;
Memo01.Lines.Add('Windows Registry Entry Has Been Found In Your System');
RegistryEntry.WriteString('', 'Delphi Application Wizard');
RegistryEntry.OpenKey('Subnode 01\',true);
RegistryEntry.WriteExpandString('', '%SystemRoot%\System32\Application Wizard 01.dll');
RegistryEntry.WriteString('Subnode String 01', '00001');
RegistryEntry.CloseKey();
RegistryEntry.OpenKey('CLSID\{00000000-0000-0000-0000-000000000001}\Subnode 02\',true);
RegistryEntry.WriteExpandString('', '%SystemRoot%\System32\Application Wizard 02.dll');
RegistryEntry.WriteString('Subnode String 02', '00002');
RegistryEntry.CloseKey();
RegistryEntry.OpenKey('CLSID\{00000000-0000-0000-0000-000000000001}\Subnode 03\',true);
RegistryEntry.WriteExpandString('', '%SystemRoot%\System32\Application Wizard 03.dll');
RegistryEntry.WriteString('Subnode String 03', '00003');
RegistryEntry.CloseKey();
RegistryEntry.OpenKey('CLSID\{00000000-0000-0000-0000-000000000001}\Subnode 04\',true);
RegistryEntry.WriteExpandString('', '%SystemRoot%\System32\Application Wizard 04.dll');
RegistryEntry.WriteString('Subnode String 04', '00004');
RegistryEntry.CloseKey();
RegistryEntry.OpenKey('CLSID\{00000000-0000-0000-0000-000000000001}\Subnode 05\',true);
RegistryEntry.WriteExpandString('', '%SystemRoot%\System32\Application Wizard 05.dll');
RegistryEntry.WriteString('Subnode String 05', '00005');
Memo01.Font.Color := 3992580;
Memo01.Lines.Add('Windows Registry Entry Has Been Created Successfully')
end
else if RegistryEntry.OpenKey('CLSID\{00000000-0000-0000-0000-000000000001}\',false) then
begin
Memo01.Font.Color := 7864575;
Memo01.Lines.Add('Windows Registry Entry Has Not Been Created Successfully')
end
end
else
begin
if (RegistryEntry.KeyExists('CLSID\{00000000-0000-0000-0000-000000000001}\')) then
begin
Memo01.Font.Color := 7864575;
Memo01.Lines.Add('Windows Registry Entry Has Been Found In Your System')
end;
end;
RegistryEntry.CloseKey();
RegistryEntry.Free;
end;
My questions is that :
Though I am trying to write the the default string for every Subnode as %SystemRoot%\System32\Application Wizard 01.dll yet %SystemRoot%\SysWow64\Application Wizard 01.dll is written. How to avoid that?
I have tried Sir Rufo's Solution. I have tried the following codes :
const
RegistryEntry = 'CLSID\{00000000-0000-0000-0000-000000000001}\Subnode 01';
RegistryEntryString = '%SystemRoot%\System32\Application Wizard 01.dll';
type
TGetInfoFunc = function : WideString; stdcall;
function ExpandEnvironmentStringsStr( const AStr : string ) : string;
begin
SetLength( Result, ExpandEnvironmentStrings( PChar( AStr ), nil, 0 ) );
ExpandEnvironmentStrings( PChar( AStr ), PChar( Result ), Length( Result ) );
end;
function GetWinDir: string;
var
WindowsDirectory: array[0..MAX_PATH] of Char;
begin
GetWindowsDirectory(WindowsDirectory, MAX_PATH - 1);
SetLength(Result, StrLen(WindowsDirectory));
Result := IncludeTrailingPathDelimiter(WindowsDirectory);
end;
function GetSysDir: string;
var
SystemDirectory: array[0..MAX_PATH] of Char;
begin
GetSystemDirectory(SystemDirectory, MAX_PATH - 1);
SetLength(Result, StrLen(SystemDirectory));
Result := IncludeTrailingPathDelimiter(SystemDirectory);
end;
function GetSysNativeDir: string;
var
WindowsDirectory: array[0..MAX_PATH] of Char;
begin
GetWindowsDirectory(WindowsDirectory, MAX_PATH - 1);
SetLength(Result, StrLen(WindowsDirectory));
Result := IncludeTrailingPathDelimiter(WindowsDirectory) + 'Sysnative\';
end;
procedure TMainForm.BitBtn01Click(Sender: TObject);
var
LReg : TRegistry;
LRegDataInfo : TRegDataInfo;
LDllFileName : string;
LLib : HMODULE;
LFunc : TGetInfoFunc;
LStr : string;
begin
LReg := TRegistry.Create;
try
LReg.RootKey := HKEY_CLASSES_ROOT;
if LReg.OpenKeyReadOnly( RegistryEntry )
then
if LReg.GetDataInfo( '', LRegDataInfo )
then
begin
case LRegDataInfo.RegData of
rdString : //Just Read The Existing String
LDllFileName := LReg.ReadString( '' );
rdExpandString : //String Needs To Be Expanded
LDllFileName := ExpandEnvironmentStringsStr( LReg.ReadString( '' ) );
end;
end;
finally
LReg.Free;
end;
Label01.Caption := LDllFileName; //Just For Information
//No Information From Registry
if LDllFileName = ''
then
raise Exception.Create( 'Not registered' );
//Load The Library
LLib := LoadLibrary( PChar( LDllFileName ) );
if LLib <> 0
then
try
#LFunc := GetProcAddress( LLib, 'GetInfo' );
LStr := LFunc;
finally
FreeLibrary( LLib );
end
else
raise Exception.CreateFmt( 'Dll-File "%s" not found!', [LDllFileName] );
//Show The Information
ShowMessage( LStr );
end;
procedure TMainForm.BitBtn02Click(Sender: TObject);
var
LReg : TRegistry;
begin
LReg := TRegistry.Create;
try
LReg.RootKey := HKEY_CLASSES_ROOT;
if LReg.OpenKey( RegistryEntry, True )
then
try
//We Write As REG_EXPAND_SZ To Flag That This Contain Environment Variables That Has To Be Expanded
LReg.WriteExpandString( '', RegistryEntryString );
finally
LReg.CloseKey;
end
else
raise Exception.CreateFmt( 'Not allowed to create the registry key HKCR\%s', [RegistryEntryString] );
finally
LReg.Free;
end;
end;
procedure TMainForm.FormCreate(Sender: TObject);
begin
Caption := Application.Title{$IFDEF WIN64} + ' Win64'{$ELSE} + ' Win32'{$ENDIF};
end;
But it is not working. Registry key is written under [HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{00000000-0000-0000-0000-000000000001}\Subnode 01] though it is not the actual problem, it can be resolved using RegistryEntry.Access:= KEY_WRITE or KEY_WOW64_64KEY, but the actual problem is that 32Bit compiler version is running under 64Bit environment and the string is written as %SystemRoot%\SysWow64\Application Wizard 01.dll but not the %SystemRoot%\System32\Application Wizard 01.dll.
I think, my problem can be resolved if I can run 64-Bit PE only in Windows 64Bit OS not allowing 32Bit PE, though my project is having both 32Bit and 64Bit Platform. I don't need to compile two different PEs based on Target Platforms
I have also tried %SystemRoot%\SysNative\Application Wizard 01.dll after detecting IsWow64Process Function.
I have also tried BasePointer's solution, it is also not working.
All the permutation and combination can be possible by a beginner, I have tried but my problem is still remaining.
Assuming that you really do need to write to both 32 and 64 bit views of the registry, the solution is as I described in an earlier question. You need to write the registry entries for the 32 bit DLL from 32 bit code, and write the registry entries for the 64 bit DLL from 64 bit code.
Your problems all stem from attempting to modify the 64 bit view of the registry from a 32 bit process. The registry redirector is getting in the way of you doing that. The information I provided in your other question is enough to show that you cannot write the information you desire to the 64 bit view from a 32 bit process.

Substitute for SHGetFileInfoW function

I'm having problem with SHGetFileInfoW function I'm using.
It's a quite slow and first read on startup (Initialization) consumes 100ms.
In MSDN stays that it should be read from thread, not the main thread because it can stuck process.
I want to use some other function, if there is any, in order to read Icons.
Another thing. How is possible to read big icons, currently I can read up to 32x32 (SHGFI_LARGEICON)
Thanks!
Actual code:
procedure TForm1.LoadIcons;
var
Info: TShFileInfo;
Icon: TIcon;
Flags: UINT;
FileName: PAnsiChar;
begin
FileName := '.txt';
Flags := SHGFI_USEFILEATTRIBUTES or SHGFI_ICON or SHGFI_LARGEICON;
Icon := TIcon.Create;
try
SHGetFileInfo(FileName, FILE_ATTRIBUTE_NORMAL, Info,
SizeOf(Info), Flags);
Icon.Handle := Info.hIcon;
Image1.Picture.Assign(Icon);
Image1.Refresh;
finally
DestroyIcon(Info.hIcon);
Icon.Free;
end;
end;
You could find the DefaultIcon for a given file extension from the Registry and use ExtractIconEx. Here is an example
I don't know if it's faster than SHGetFileInfo
EDIT:
I have extracted (from the sample) the part which gets the ICON from the Extension.
It actually works very fast. could be optimized more.
(I modified the code a bit):
// find the icon for a certain file extension in the registry
function TForm1.RegistryIconExtraction(Extension : string): integer;
var
RegKey : TRegistry;
IconPos : integer;
AssocAppInfo : string;
ExtractPath, FileName : string;
IconHandle, PLargeIcon, PSmallIcon : HICON;
AnIcon : TIcon;
begin
Result := 0; // default icon
if Extension[1] <> '.' then Extension := '.' + Extension;
RegKey := TRegistry.Create(KEY_READ);
try
// KEY_QUERY_VALUE grants permission to query subkey data.
RegKey.RootKey := HKEY_CLASSES_ROOT; // set folder for icon info lookup
if RegKey.OpenKeyReadOnly(Extension) then // extension key exists?
try
AssocAppInfo := RegKey.ReadString(''); // read app key
RegKey.CloseKey;
except
Exit;
end;
if ((AssocAppInfo <> '') and // app key and icon info exists?
(RegKey.OpenKeyReadOnly(AssocAppInfo + '\DefaultIcon'))) then
try
ExtractPath := RegKey.ReadString(''); // icon path
RegKey.CloseKey;
except
Exit;
end;
finally
RegKey.Free;
end;
// IconPos after comma in key ie: C:\Program Files\Winzip\Winzip.Exe,0
// did we get a key for icon, does IconPos exist after comma seperator?
If ((ExtractPath <> '') and (pos(',', ExtractPath) <> 0)) then
begin
// Filename in registry key is before the comma seperator
FileName := Copy(ExtractPath, 1, Pos(',', ExtractPath) - 1);
// extract the icon Index from after the comma in the ExtractPath string
try
IconPos := StrToInt(copy(ExtractPath, Pos(',', ExtractPath) + 1,
Length(ExtractPath) - Pos(',', ExtractPath) + 1));
except
Exit;
end;
IconHandle := ExtractIconEx(PChar(FileName), IconPos, PLargeIcon, PSmallIcon, 1);
If (PLargeIcon <> 0) then
begin
AnIcon := TIcon.Create;
AnIcon.Handle := PLargeIcon;
Image1.Picture.Assign(AnIcon);
Image1.Refresh;
AnIcon.Free;
end;
DestroyIcon(PLargeIcon);
DestroyIcon(PSmallIcon);
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
t1, t2: DWORD;
begin
t1 := GetTickCount;
RegistryIconExtraction('.txt');
t2 := GetTickCount;
Memo1.Lines.Add(IntToStr(t2-t1));
end;
EDIT2: The sample code is now Vista/Win7 UAC compliant.

Get FileVersion with Build

DelphiXe.
For reception of the version of a file I use function:
function FileVersion(AFileName: string): string;
var
szName: array[0..255] of Char;
P: Pointer;
Value: Pointer;
Len: UINT;
GetTranslationString: string;
FFileName: PChar;
FValid: boolean;
FSize: DWORD;
FHandle: DWORD;
FBuffer: PChar;
begin
try
FFileName := StrPCopy(StrAlloc(Length(AFileName) + 1), AFileName);
FValid := False;
FSize := GetFileVersionInfoSize(FFileName, FHandle);
if FSize > 0 then
try
GetMem(FBuffer, FSize);
FValid := GetFileVersionInfo(FFileName, FHandle, FSize, FBuffer);
except
FValid := False;
raise;
end;
Result := '';
if FValid then
VerQueryValue(FBuffer, '\VarFileInfo\Translation', p, Len)
else
p := nil;
if P <> nil then
GetTranslationString := IntToHex(MakeLong(HiWord(Longint(P^)),
LoWord(Longint(P^))), 8);
if FValid then
begin
StrPCopy(szName, '\StringFileInfo\' + GetTranslationString +
'\FileVersion');
if VerQueryValue(FBuffer, szName, Value, Len) then
Result := StrPas(PChar(Value));
end;
finally
try
if FBuffer <> nil then
FreeMem(FBuffer, FSize);
except
end;
try
StrDispose(FFileName);
except
end;
end;
end;
For the majority of executed files and libraries it returns correct value. But at some files the version is cut off and shown without Build.
Here for example file BASS.DLL (http://us.un4seen.com/files/bass24.zip)
In Windows Explorer in properties of a file I see version 2.4.7.1, function result='2.4.7' :(
I open a file through Resourcehacker.exe (http://angusj.com/resourcehacker/), I look structure VersionInfo:
1 VERSIONINFO
FILEVERSION 2,4,7,1
PRODUCTVERSION 2,4,0,0
FILEOS 0x4
FILETYPE 0x2
{
BLOCK "StringFileInfo"
{
BLOCK "000004b0"
{
VALUE "CompanyName", "Un4seen Developments"
VALUE "FileDescription", "BASS"
VALUE "FileVersion", "2.4.7"
VALUE "LegalCopyright", "Copyright © 1999-2010"
}
}
BLOCK "VarFileInfo"
{
VALUE "Translation", 0x0000 0x04B0
}
}
Question: how to receive 2.4.7.1, i.e. the full version?
If you want the file version of the root block, then forget about the language specific translation:
function FileVersion(const FileName: TFileName): String;
var
VerInfoSize: Cardinal;
VerValueSize: Cardinal;
Dummy: Cardinal;
PVerInfo: Pointer;
PVerValue: PVSFixedFileInfo;
begin
Result := '';
VerInfoSize := GetFileVersionInfoSize(PChar(FileName), Dummy);
GetMem(PVerInfo, VerInfoSize);
try
if GetFileVersionInfo(PChar(FileName), 0, VerInfoSize, PVerInfo) then
if VerQueryValue(PVerInfo, '\', Pointer(PVerValue), VerValueSize) then
with PVerValue^ do
Result := Format('v%d.%d.%d build %d', [
HiWord(dwFileVersionMS), //Major
LoWord(dwFileVersionMS), //Minor
HiWord(dwFileVersionLS), //Release
LoWord(dwFileVersionLS)]); //Build
finally
FreeMem(PVerInfo, VerInfoSize);
end;
end;
If you want to edit the BASS audio library (Which you can't). The reason why is because it is compressed with "Petite v1.4" which Ian wants so people can not edit it easily.
Also to get the version bass.dll has an export that when used you can get the absolute version of it like you want without a lot of hacking and what not.

Resetting a PChar variable

I don't know much about delphi win 32 programming, but I hope someone can answer my question.
I get duplicate l_sGetUniqueIdBuffer saved into the database which I want to avoid.
The l_sGetUniqueIdBuffer is actually different ( the value of l_sAuthorisationContent is xml, and I can see a different value generated by the call to getUniqueId) between rows. This problem is intermittant ( duplicates are rare...) There is only milliseconds difference between the update date between the rows.
Given:
( unnesseary code cut out)
var
l_sGetUniqueIdBuffer: PChar;
FOutputBufferSize : integer;
begin
FOutputBufferSize := 1024;
...
while( not dmAccomClaim.ADOQuClaimIdentification.Eof ) do
begin
// Get a unique id for the request
l_sGetUniqueIdBuffer := AllocMem (FOutputBufferSize);
l_returnCode := getUniqueId (m_APISessionId^, l_sGetUniqueIdBuffer, FOutputBufferSize);
dmAccomClaim.ADOQuAddContent.Active := False;
dmAccomClaim.ADOQuAddContent.Parameters.ParamByName('pContent').Value := (WideString(l_sAuthorisationContent));
dmAccomClaim.ADOQuAddContent.Parameters.ParamByName('pClaimId').Value := dmAccomClaim.ADOQuClaimIdentification.FieldByName('SB_CLAIM_ID').AsString;
dmAccomClaim.ADOQuAddContent.Parameters.ParamByName('pUniqueId').Value := string(l_sGetUniqueIdBuffer);
dmAccomClaim.ADOQuAddContent.ExecSQL;
FreeMem( l_sAuthorisationContent, l_iAuthoriseContentSize );
FreeMem( l_sGetUniqueIdBuffer, FOutputBufferSize );
end;
end;
I guess i need to know, is the value in l_sGetUniqueIdBuffer being reset for every row??
AllocMem is implemented as follows
function AllocMem(Size: Cardinal): Pointer;
begin
GetMem(Result, Size);
FillChar(Result^, Size, 0);
end;
so yes, the value that l_sGetUniqueBuffer is pointing to will always be reset to an empty string.
Debugging
var
l_sGetUniqueIdBuffer: PChar;
FOutputBufferSize : integer;
list: TStringList;
begin
FOutputBufferSize := 1024;
...
list := TStringList.Create;
try
list.Sorted := True;
while( not dmAccomClaim.ADOQuClaimIdentification.Eof ) do
begin
// Get a unique id for the request
l_sGetUniqueIdBuffer := AllocMem (FOutputBufferSize);
l_returnCode := getUniqueId (m_APISessionId^, l_sGetUniqueIdBuffer, FOutputBufferSize);
dmAccomClaim.ADOQuAddContent.Active := False;
dmAccomClaim.ADOQuAddContent.Parameters.ParamByName('pContent').Value := (WideString(l_sAuthorisationContent));
dmAccomClaim.ADOQuAddContent.Parameters.ParamByName('pClaimId').Value := dmAccomClaim.ADOQuClaimIdentification.FieldByName('SB_CLAIM_ID').AsString;
dmAccomClaim.ADOQuAddContent.Parameters.ParamByName('pUniqueId').Value := string(l_sGetUniqueIdBuffer);
if list.IndexOf(l_sGetUniqueIdBuffer) <> - 1 then
write; //***** Place a breakpoint here.
list.Add(l_sGetUniqueIdBuffer);
dmAccomClaim.ADOQuAddContent.ExecSQL;
FreeMem( l_sAuthorisationContent, l_iAuthoriseContentSize );
FreeMem( l_sGetUniqueIdBuffer, FOutputBufferSize );
end;
finally
list.Free;
end;
end;

How to assign a multiline string value without quoting each line?

Is there a way to assign a multiline string value in Delphi without having to quote each line?
Edit (the specific problem): I have some SQL queries which I want to test outside Delphi. When copying the queries it is a bit of overhead to add and replace quotes every time.
Here's code for an application you can add to the IDE's Tools menu that might help. It was posted a while back to one of the CodeGear newsgroups by TeamB member Peter Below:
program ClipToStringConst;
// Remove the dot from the line below for a console app,
// per Rob Kennedy's comment. It works fine without being
// a console app.
{.$APPTYPE CONSOLE}
uses
Windows,
Classes,
Sysutils,
APIClipboard;
const
cIndent = ' '; // 2 spaces
cSingleQuote = '''';
EndChar : array [Boolean] of Char = ('+',';');
procedure Process;
var
SL: TStringlist;
i, max: Integer;
begin
if ClipboardHasFormat( CF_TEXT ) then
begin
SL := TStringlist.Create;
try
SL.Text := ClipboardAsString;
max := SL.count-1;
for i:= 0 to max do
SL[i] := cIndent +
AnsiQuotedStr( TrimRight(SL[i])+#32, cSingleQuote ) +
EndChar[i = max];
StringToClipboard( SL.Text );
finally
SL.Free;
end; { Finally }
end;
end;
begin
try
Process;
except
on E: Exception do
ShowException( E, ExceptAddr );
end;
end.
Just select the text in the SQL management tool after you've tested it and copy it to the clipboard. Switch to the Delphi Code Editor, place the insertion point where you want the constant text to appear, choose 'Clipboard To Const' or whatever you called it from the Tools menu, and then Ctrl+V to paste it into the editor.
It's a pretty handy little tool. You can also modify it to work the opposite way (ConstantToClipboard) to remove the source formatting and revert back to raw SQL, although I haven't bothered to do so yet.
EDIT: Missed a unit (APIClipboard). This needs to be a separate unit, obviously. Again, thanks to Peter Below:
{== Unit APIClipboard =================================================}
{: Clipboard access routines using only API functions
#author Dr. Peter Below
#desc Version 1.0 created 5 Juli 2000<BR>
Current revision 1.0<BR>
Last modified 5 Juli 2000<P>
This unit provides simply clipboard access routines that do not rely on
the VCL Clipbrd unit. That unit drags in Dialogs and Forms and a major
part of the VCL as a consequence, not appropriate for simple console
or non-form programs. This unit uses only API routines, the only VCL
units used are Classes (for exceptions and streams) and SysUtils.
}
{=====================================================================}
unit APIClipboard;
interface
uses
Windows, Classes;
procedure StringToClipboard( const S: String );
function ClipboardAsString: String;
procedure CopyDataToClipboard( fmt: DWORD; const data; datasize: Integer;
emptyClipboardFirst: Boolean = true );
procedure CopyDataFromClipboard( fmt: DWORD; S: TStream );
function ClipboardHasFormat( fmt: DWORD ): Boolean;
implementation
uses
Sysutils;
type
{: This is an internal exception class used by the <see unit=APIClipboard> }
EClipboardError = class( Exception )
public
constructor Create( const msg: String );
end;
resourcestring
eSystemOutOfMemory =
'could not allocate memory for clipboard data.';
eLockfailed =
'could not lock global memory handle.';
eSetDataFailed =
'could not copy data block to clipboard.';
eCannotOpenClipboard =
'could not open the clipboard.';
eErrorTemplate =
'APIClipboard: %s'#13#10+
'System error code: %d'#13#10+
'System error message: %s';
{-- EClipboardError.Create --------------------------------------------}
{: Creates a new EclipboardError object
#Param msg is the string to embed into the error message
#Precondition none
#Postcondition none
#desc Composes an error message that contains the passed message and the
API error code and matching error message. The CreateFmt constructor
inherited from the basic Exception class is used to do the work.
Created 5.7.2000 by P. Below
}{---------------------------------------------------------------------}
constructor EClipboardError.Create( const msg: String );
begin { Create }
CreateFmt( eErrorTemplate,
[msg, GetLastError, SysErrorMessage(GetLastError)] );
end; { EClipboardError.Create }
{-- DataToClipboard ---------------------------------------------------}
{: Copies a block of memory to the clipboard in a given format
#Param fmt is the clipboard format to use
#Param data is an untyped const parameter that addresses the data to copy
#Param datasize is the size of the data, in bytes
#Precondition The clipboard is already open. If not an EClipboardError
will result. This precondition cannot be asserted, unfortunately.
#Postcondition Any previously exisiting data of this format will have
been replaced by the new data, unless datasize was 0 or we run into an
exception. In this case the clipboard will be unchanged.
#desc Uses API methods to allocate and lock a global memory block of
approproate size, copies the data to it and submits the block to the
clipboard. Any error on the way will raise an EClipboardError
exception.<BR>
Created 5.7.2000 by P. Below
#Raises EClipboardError
}{---------------------------------------------------------------------}
procedure DataToClipboard( fmt: DWORD; Const data; datasize: Integer );
var
hMem: THandle;
pMem: Pointer;
begin { DataToClipboard }
if datasize <= 0 then
Exit;
hMem := GlobalAlloc( GMEM_MOVEABLE or GMEM_SHARE or GMEM_ZEROINIT, datasize );
if hmem = 0 then
raise EClipboardError.Create( eSystemOutOfMemory );
pMem := GlobalLock( hMem );
if pMem = nil then
begin
GlobalFree( hMem );
raise EClipboardError.Create( eLockFailed );
end;
Move( data, pMem^, datasize );
GlobalUnlock( hMem );
if SetClipboardData( fmt, hMem ) = 0 then
raise EClipboardError( eSetDataFailed );
// Note: API docs are unclear as to whether the memory block has
// to be freed in case of failure. Since failure is unlikely here
// lets blithly ignore this issue for now.
end; { DataToClipboard }
{-- DataFromClipboard -------------------------------------------------}
{: Copies data from the clipboard into a stream
#Param fmt is the clipboard format to look for
#Param S is the stream to copy to
#precondition S <> nil
#postcondition If data was copied the streams position will have moved
#desc Tries to get a memory block for the requested clipboard format.
Nothing
further is done if this fails (because the format is not available or
the clipboard is not open, we treat neither as error here), otherwise
the memory handle is locked and the data copied into the stream. <P>
Note that we cannot determine the actual size of the data originally
copied to the clipboard, only the allocated size of the memory block!
Since GlobalAlloc works with a granularity of 32 bytes the block may be
larger than required for the data and thus the stream may contain some
spurious bytes at the end. There is no guarantee that these bytes will
be 0. <P>
If the memory handle obtained from the clipboard cannot be locked we
raise an <see class=EClipboardError> exception.
Created 5.7.2000 by P. Below
#Raises EClipboardError
}{---------------------------------------------------------------------}
procedure DataFromClipboard( fmt: DWORD; S: TStream );
var
hMem: THandle;
pMem: Pointer;
datasize: DWORD;
begin { DataFromClipboard }
Assert( Assigned( S ));
hMem := GetClipboardData( fmt );
if hMem <> 0 then
begin
datasize := GlobalSize( hMem );
if datasize > 0 then
begin
pMem := GlobalLock( hMem );
if pMem = nil then
raise EclipboardError.Create( eLockFailed );
try
S.WriteBuffer( pMem^, datasize );
finally
GlobalUnlock( hMem );
end;
end;
end;
end; { DatafromClipboard }
{-- CopyDataToClipboard -----------------------------------------------}
{: Copies a block of memory to the clipboard in a given format
#Param fmt is the clipboard format to use
#Param data is an untyped const parameter that addresses the data to copy
#Param datasize is the size of the data, in bytes
#Param emptyClipboardFirst determines if the clipboard should be emptied,
true by default
#Precondition The clipboard must not be open already
#Postcondition If emptyClipboardFirst is true all prior data will be
cleared from the clipboard, even if datasize is <= 0. The clipboard
is closed again.
#desc Tries to open the clipboard, empties it if required and then tries to
copy the passed data to the clipboard. This operation is a NOP if
datasize <= 0. If the clipboard cannot be opened a <see
class=EClipboardError>
is raised.
Created 5.7.2000 by P. Below
#Raises EClipboardError
}{---------------------------------------------------------------------}
procedure CopyDataToClipboard( fmt: DWORD; const data; datasize: Integer;
emptyClipboardFirst: Boolean = true );
begin { CopyDataToClipboard }
if OpenClipboard( 0 ) then
try
if emptyClipboardFirst then
EmptyClipboard;
DataToClipboard( fmt, data, datasize );
finally
CloseClipboard;
end
else
raise EclipboardError.Create( eCannotOpenClipboard );
end; { CopyDataToClipboard }
{-- StringToClipboard -------------------------------------------------}
{: Copies a string to clipboard in CF_TEXT clipboard format
#Param S is the string to copy, it may be empty.
#Precondition The clipboard must not be open already.
#Postcondition Any prior clipboard content will be cleared, but only
if S was not empty. The clipboard is closed again.
#desc Hands the brunt of the work off to <See routine=CopyDataToClipboard>,
but only if S was not empty. Otherwise nothing is done at all.<BR>
Created 5.7.2000 by P. Below
#Raises EClipboardError
}{---------------------------------------------------------------------}
procedure StringToClipboard( const S: String );
begin
if Length(S) > 0 Then
CopyDataToClipboard( CF_TEXT, S[1], Length(S)+1);
end; { StringToClipboard }
{-- CopyDataFromClipboard ---------------------------------------------}
{: Copies data from the clipboard into a stream
#Param fmt is the clipboard format to look for
#Param S is the stream to copy to
#Precondition S <> nil<P>
The clipboard must not be open already.
#Postcondition If data was copied the streams position will have moved.
The clipboard is closed again.
#desc Tries to open the clipboard, and then tries to
copy the data to the passed stream. This operation is a NOP if
the clipboard does not contain data in the requested format.
If the clipboard cannot be opened a <see class=EClipboardError>
is raised.
Created 5.7.2000 by P. Below
#Raises EClipboardError
}{---------------------------------------------------------------------}
procedure CopyDataFromClipboard( fmt: DWORD; S: TStream );
begin { CopyDataFromClipboard }
Assert( Assigned( S ));
if OpenClipboard( 0 ) then
try
DataFromClipboard( fmt , S );
finally
CloseClipboard;
end
else
raise EclipboardError.Create( eCannotOpenClipboard );
end; { CopyDataFromClipboard }
{-- ClipboardAsString -------------------------------------------------}
{: Returns any text contained on the clipboard
#Returns the clipboards content if it contained something in CF_TEXT
format, or an empty string.
#Precondition The clipboard must not be already open
#Postcondition The clipboard is closed.
#desc If the clipboard contains data in CF_TEXT format it is copied to a
temp memory stream, zero-terminated for good measure and copied into
the result string.
Created 5.7.2000 by P. Below
#Raises EClipboardError
}{---------------------------------------------------------------------}
function ClipboardAsString: String;
const
nullchar: Char = #0;
var
ms: TMemoryStream;
begin { ClipboardAsString }
if not IsClipboardFormatAvailable( CF_TEXT ) then
Result := EmptyStr
else
begin
ms:= TMemoryStream.Create;
try
CopyDataFromClipboard( CF_TEXT , ms );
ms.Seek( 0, soFromEnd );
ms.WriteBuffer( nullChar, Sizeof( nullchar ));
Result := PChar( ms.Memory );
finally
ms.Free;
end;
end;
end; { ClipboardAsString }
{-- ClipboardHasFormat ------------------------------------------------}
{: Checks if the clipboard contains data in the specified format
#Param fmt is the format to check for
#Returns true if the clipboard contains data in this format, false
otherwise
#Precondition none
#Postcondition none
#desc This is a simple wrapper around an API function.
Created 5.7.2000 by P. Below
}{---------------------------------------------------------------------}
function ClipboardHasFormat( fmt: DWORD ): Boolean;
begin { ClipboardHasFormat }
Result := IsClipboardFormatAvailable( fmt );
end; { ClipboardHasFormat }
end.
Sample use:
Prepare the text in your SQL editor, text editor, or whatever:
SELECT
lname,
fname,
dob
FROM
employees
Select all of the text, and copy to the clipboard using Ctrl+C.
Switch to the IDE's Code Editor, run the ClipboardToStringConst application (using the Tools menu item you added, or whatever other means you want). Place the editor's cursor (insertion point) where you want the constant text to appear, and press Ctrl+V to paste in the text.
const
MySQLText = | // The pipe indicates the insertion point.
The result:
const
MySQLText = 'SELECT '+
' lname, '+
' fname, '+
' dob '+
'FROM '+
' employees ';
You mean something like this?
myStr := 'first line'#13#10'secondline'#13#10'thirdline';
We had the same problem, and finally we created a small IDE plugin (merged with existing solutions). That creates two extra menu items (Copy and Paste extra). One of this pastes the formatted content of the clipboard to the code editor, the other does the same thing in reverse (copy the content of the selection to the clipboard and removes the extra charachters).
To use this:
Create new Package in Delphi
Add to "designide" to requires section (and remove anything else)
Create new Unit, and copy the code
Build and Install
Sample code:
unit ClipboardWizard;
interface
uses
Windows, SysUtils, Classes, ToolsAPI,
{$ifdef VER280} // XE7
VCL.Menus
{$else}
Menus
{$endif};
type
TClipboardWizard = class(TInterfacedObject, IOTAWizard)
private
FMainMenuItem, FCopyMenuItem, FPasteMenuItem: TMenuItem;
// Formatting
function GetFormattedString: string;
function RemoveUnneededChars(const Value: string): string;
// Menu events
procedure CopyToClipboard(Sender: TObject);
procedure PasteFromClipboard(Sender: TObject);
public
// TObject
constructor Create;
destructor Destroy; override;
// IOTANotifier
procedure AfterSave;
procedure BeforeSave;
procedure Destroyed;
procedure Modified;
// IOTAWizard
function GetIDString: string;
function GetName: string;
function GetState: TWizardState;
procedure Execute;
end;
procedure Register;
implementation
uses
Vcl.Clipbrd, System.StrUtils;
procedure Register;
begin
RegisterPackageWizard(TClipboardWizard.Create);
end;
// Formatting
function TClipboardWizard.RemoveUnneededChars(const Value: string): string;
var
List: TStringList;
q: integer;
s : string;
begin
if Trim(Value) <> '' then
begin
List := TStringList.Create;
try
List.Text := Value;
for q := 0 to List.Count - 1 do
begin
s := Trim(List[q]);
if Length(s) > 0 then
if s[1] = '''' then
s := Copy(s, 2, Length(s));
s := TrimLeft(ReverseString(s));
if Length(s) > 0 then
if s[1] = '+' then
s := TrimLeft(Copy(s, 2, Length(s)));
if Length(s) > 0 then
if s[1] = ';' then
s := TrimLeft(Copy(s, 2, Length(s)));
if Length(s) > 0 then
if s[1] = '''' then
s := TrimLeft(Copy(s, 2, Length(s)));
s := StringReplace(s, '''''', '''', [rfReplaceAll]);
List[q] := ReverseString(s)
end;
Result := List.Text;
finally
List.Free;
end;
end
else
Result := '';
end;
procedure TClipboardWizard.CopyToClipboard(Sender: TObject);
begin
with BorlandIDEServices as IOTAEditorServices do
if Assigned(TopView) then
Clipboard.AsText := RemoveUnneededChars(TopView.Block.Text);
end;
function TClipboardWizard.GetFormattedString: string;
const
FSingleQuote = '''';
Indent: array [boolean] of string = (' ', '');
EndChar: array [boolean] of string = (' +', ';');
var
List: TStringlist;
q: Integer;
begin
if Clipboard.HasFormat(CF_TEXT) then
begin
List := TStringlist.Create;
try
List.Text := Clipboard.AsText;
for q := 0 to List.Count - 1 do
List[q] := Indent[q <> 0] + AnsiQuotedStr(TrimRight(List[q]) + #32, FSingleQuote) +
EndChar[q = (List.Count - 1)];
Result := List.Text;
finally
List.Free;
end;
end;
end;
procedure TClipboardWizard.PasteFromClipboard(Sender: TObject);
begin
with BorlandIDEServices as IOTAEditorServices do
if Assigned(TopView) then
begin
TopView.Buffer.EditPosition.InsertText(GetFormattedString);
TopView.Paint; // invalidation
end;
end;
{ Anything else }
constructor TClipboardWizard.Create;
var
NTAServices : INTAServices;
begin
NTAServices := BorlandIDEServices as INTAServices;
// Main Menu
FMainMenuItem := TMenuItem.Create(nil);
FMainMenuItem.Caption := 'Clibrd Extra' ;
NTAServices.MainMenu.Items.Add(FMainMenuItem);
// Sub Menus
FCopyMenuItem := TMenuItem.Create(nil);
FCopyMenuItem.Caption := 'Copy to clipboard';
FCopyMenuItem.OnClick := Self.CopyToClipboard;
FMainMenuItem.Add(FCopyMenuItem);
FPasteMenuItem := TMenuItem.Create(nil);
FPasteMenuItem.Caption := 'Paste from clipboard';
FPasteMenuItem.OnClick := Self.PasteFromClipboard;
FMainMenuItem.Add(FPasteMenuItem);
end;
destructor TClipboardWizard.Destroy;
begin
if Assigned(FPasteMenuItem) then
FreeAndNil(FPasteMenuItem);
if Assigned(FCopyMenuItem) then
FreeAndNil(FCopyMenuItem);
if Assigned(FMainMenuItem) then
FreeAndNil(FMainMenuItem);
inherited;
end;
{ IOTANotifier }
procedure TClipboardWizard.AfterSave;
begin
end;
procedure TClipboardWizard.BeforeSave;
begin
end;
procedure TClipboardWizard.Destroyed;
begin
end;
procedure TClipboardWizard.Modified;
begin
end;
{ IOTAWizard }
function TClipboardWizard.GetIDString: string;
begin
Result := 'Clipboard.Wizard7';
end;
function TClipboardWizard.GetName: string;
begin
Result := 'Clipboard Wizard7';
end;
function TClipboardWizard.GetState: TWizardState;
begin
Result := [];
end;
procedure TClipboardWizard.Execute;
begin
end;
end.
I know the code is not perfect, but it works :-)
You could consider putting your SQL in TQuery components on Forms or Data Modules.
This solves the copy/paste problem, but it introduces others (such as the diffs between two versions of a query being worse).
You can't define a string on multiple lines without the quotes:
const
myString = 'this is a long string that extends' +
'to a second line';
Although, you can make a string out of control characters like:
const
myString = #83#84#82#73#78#71;
But that does not attribute to readble code.
In versions of Delphi >= 2007, if you are entering a quoted string over multiple lines it will automatically add a closing quote and + ' on the next line if you don't close the quote yourself.
It's not a solution to the problem but it does help speed up typing in long strings.
The short answer is no, it can't be done. (I know that is not what you want to hear.)
However Andreas Hausladen did develop an extension capable of just this. I googled for it but couldn't find it. I think it was in his DLangExtensions pack, of which he dropped support in late 2007 already. :(
I'm surprised no one's mentioned resources. Although a pain to implement the first time, once you've done it once you can implement retrieving long multiline strings from files without too much trouble. Random instructions I found here: http://www.delphibasics.info/home/delphibasicssnippets/usingresourcefileswithdelphi
With GExperts:
enable GExperts -> Editor Experts -> Paste Strings As
assign a shortcut
I am late to Party, but if GExperts i no Option:
Fast Solution: Use IDE Macro Recorder...
copy text (start recording SHIFT + STRG + R)
Press [Pos1] ['] [End] [' + sLineBreak +] [change line to one down]
(stop recording SHIFT + STRG + R)
(replay Key Strokes SHIFT + STRG + P) repeat until last line ... delete the + which is too much ...
Partially finished;
The Escaping of ' for Strings is not done this way ...

Resources