I want to use some units in both older and newer versions of Delphi. Since a recent Delphi version, Utf8Decode throws a deprecated warning which advises to switch to Utf8ToString. Problem is older versions of Delphi don't declare this function, so which {$IFDEF}label should I use to define a wrapper around Utf8Decode named Utf8String (or perhaps Utf8ToWideString)?
Or in other words: which version was Utf8ToString introduced?
I think I would implement it with an $IF so that calling code can use either the new RTL function, or fall back on the older deprecated version. Since the new UTF8ToString returns a UnicodeString I think it is safe to assume that it was introduced in Delphi 2009.
{$IF not Declared(UTF8ToString)}
function UTF8ToString(const s: UTF8String): WideString;
begin
Result := UTF8Decode(s);
end;
{$IFEND}
As far as I remember:
UTF8String and associated UTF8Encode / UTF8Decode were introduced in Delphi 6;
UTF8ToWideString and UTF8ToString were introduced in Delphi 2009 (i.e. Unicode version), as such:
function UTF8Decode(const S: RawByteString): WideString;
deprecated 'Use UTF8ToWideString or UTF8ToString';
In order to get rid of this compatibility issue, you can either define your own UTF8ToString function (as David propose), or either use your own implementation.
I rewrote some (perhaps) faster version for our framework, which works also with Delphi 5 (I wanted to add UTF-8 support for some legacy Delphi 5 code, some 3,000,000 source code lines with third party components which stops easy upgrade - at least for the manager's decision). See all corresponding RawUTF8 type in SynCommons.pas:
{$ifdef UNICODE}
function UTF8DecodeToString(P: PUTF8Char; L: integer): string;
begin
result := UTF8DecodeToUnicodeString(P,L);
end;
{$else}
function UTF8DecodeToString(P: PUTF8Char; L: integer): string;
var Dest: RawUnicode;
begin
if GetACP=CODEPAGE_US then begin
if (P=nil) or (L=0) then
result := '' else begin
SetLength(Dest,L); // faster than Windows API / Delphi RTL
SetString(result,PAnsiChar(pointer(Dest)),UTF8ToWinPChar(pointer(Dest),P,L));
end;
exit;
end;
result := '';
if (P=nil) or (L=0) then
exit;
SetLength(Dest,L*2);
L := UTF8ToWideChar(pointer(Dest),P,L) shr 1;
SetLength(result,WideCharToMultiByte(GetACP,0,pointer(Dest),L,nil,0,nil,nil));
WideCharToMultiByte(GetACP,0,pointer(Dest),L,pointer(result),length(result),nil,nil);
end;
{$endif}
Related
Having this:
procedure Foo;
begin
end;
function Bar: TProcedure;
begin
Result := Foo;
end;
The following compiles:
var
tmp: TProcedure;
begin
tmp := Bar();
tmp();
...but the following doesn't compile in Delphi:
Bar()();
Is there a reason for this "limitation"? Does Bar()(); syntax compile in some other "flavour" of Pascal? Would Bar()(); syntax compile in some other context?
Simply call as
TProcedure(Bar());
I don't think there is a limitation in the grammar of the language. The statement Bar()() should be valid. So, I believe that this is a compiler bug in older versions of Delphi. This program compiles in Delphi 2010 and later:
{$APPTYPE CONSOLE}
uses
SysUtils;
procedure Foo;
begin
end;
function Bar: TProcedure;
begin
Result := Foo;
end;
begin
Bar()();
end.
It is quite possible that the compiler bug was fixed before Delphi 2010, it's just that's the version that I have to hand. It seems that the bug is still present in Delphi 7, and fixed by Delphi 2010. So the bug appears to have been fixed somewhere in between those two versions.
Update 1
Marco reports that the Free Pascal compiler accepts Bar()().
Update 2
Rudy has done some testing and reports that the bug is still present in D2007 so the fix was either D2009 and D2010. My instincts tell me that Embarcadero would have discovered the problem themselves when adding the anonymous methods feature and that would have been the trigger for the fix to be made.
DLL registration with regsvr32.exe freezes when unit HtmlHelpViewer is used in DLL sources in Delphi XE or Delphi XE2 Update 3. Just add the unit to interface uses list. The main project (that uses DLL) freezes on exit too.
How to fix the issue?
Thanks for the help!
STEPS TO REPRODUCE THE ISSUE AND ISSUE IN SUGGESTED FIX:
1). Please create the following DLL:
library Test;
uses
ComServ,
HtmlHelpFixer,
HtmlHelpViewer;
exports
DllGetClassObject,
DllCanUnloadNow,
DllRegisterServer,
DllUnregisterServer;
begin
end.
2). Also create the following BPL linked to this DLL (by -LUTestBpl dcc32 parameter for example):
package TestBpl;
requires
Vcl;
end.
3). Then just execute: regsvr32.exe /s Test.dll. OS Windows 7 32-bit.
Update
According to the latest comments on the QC report submitted by Altaveron, this problem will be resolved in the next Delphi update, update 4. And indeed, Altaveron now confirms that update 4 does resolve the issue.
This is a known problem with the MS HTML help control, hhctrl.ocx. The best description of it that I am aware of is at the HelpWare FAR HTML FAQ. There are many QC reports describing the issue: 48983, 67463, 78998, 89616.
According to the latest QC report, this is fixed in XE2 but you report otherwise and I'd be inclined to believe you. Especially as a comparison of the source for the HtmlHelpViewer unit from XE and XE2 reveals no changes that appear related to this issue.
It's quite hard to work around the issue since the code that needs to be modified is buried deep inside the HtmlHelpViewer unit. I've had to resort to patching the HtmlHelp API call. Like this:
unit HtmlHelpFixer;
interface
implementation
uses
Windows;
function HtmlHelp(hWndCaller: HWND; pszFile: PWideChar; uCommand: UINT; dwData: DWORD): HWND;
begin
if uCommand=HH_CLOSE_ALL then begin
//don't call HtmlHelpW because it can result in a hang due to a bug in hhctrl.ocx
Result := 0;
end else begin
Result := HtmlHelpW(hWndCaller, pszFile, uCommand, dwData);
end;
end;
procedure PatchCode(Address: Pointer; const NewCode; Size: Integer);
var
OldProtect: DWORD;
begin
if VirtualProtect(Address, Size, PAGE_EXECUTE_READWRITE, OldProtect) then begin
Move(NewCode, Address^, Size);
FlushInstructionCache(GetCurrentProcess, Address, Size);
VirtualProtect(Address, Size, OldProtect, #OldProtect);
end;
end;
type
PInstruction = ^TInstruction;
TInstruction = packed record
Opcode: Byte;
Offset: Integer;
end;
procedure RedirectProcedure(OldAddress, NewAddress: Pointer);
var
NewCode: TInstruction;
begin
NewCode.Opcode := $E9;//jump relative
NewCode.Offset := NativeInt(NewAddress)-NativeInt(OldAddress)-SizeOf(NewCode);
PatchCode(OldAddress, NewCode, SizeOf(NewCode));
end;
procedure RedirectHtmlHelp;
var
HtmlHelp: function(hWndCaller: HWND; pszFile: PWideChar; uCommand: UINT; dwData: DWORD_PTR): HWND;
begin
HtmlHelp := Windows.HtmlHelp;
RedirectProcedure(#HtmlHelp, #HtmlHelpFixer.HtmlHelp);
end;
initialization
RedirectHtmlHelp;
end.
Include this unit early in your .dpr uses list, before any unit that does anything with HTML help.
The version of the code that I use does a little more and takes steps to ensure that any open help windows are closed when the DLL unloads. This no longer happens because we have stopped sending HH_CLOSE_ALL.
You will want to make sure that any help windows are shut down then keep track of the window handles returned by HtmlHelp calls, which you can now intercept. Then at shutdown send a WM_CLOSE message to those windows which replaces the missing HH_CLOSE_ALL call to HtmlHelp.
However, I believe that the code above should get you over your immediate hurdle with regsvr32 which won't be showing help windows.
Feel free to do some experimentation! At the very least, the code above gives you entry points with which you can modify the behaviour of the HtmlHelpViewer unit.
Embarcadero have fixed this issue on Delphi XE2 Update 4. But now context help doesn't work on IDE while you're using BPL with HtmlHelpViewer unit on uses clause.
I inherited some Delphi components/code that currently compiles with C++ Builder 2007. I'm simply now trying to compile the components with C++ Builder RAD XE. I don't know Delphi (object pascal).
Here are the versions of the 'Supports' functions that appear to be in conflict. Is there a compiler switch I can use to make RAD XE backward compatible? Or is there something I can do to these function calls to correct the ambiguous nature?
The error I'm getting is:
[DCC Error] cxClasses.pas(566): E2251 Ambiguous overloaded call to 'Supports'
SysUtils.pas(19662): Related method: function Supports(const TObject; const TGUID; out): Boolean;
cxClasses.pas(467): Related method: function Supports(TObject; const TGUID; out): Boolean;
{$IFNDEF DELPHI5}
procedure FreeAndNil(var Obj);
var
Temp: TObject;
begin
Temp := TObject(Obj);
Pointer(Obj) := nil;
Temp.Free;
end;
function Supports(const Instance: IUnknown; const Intf: TGUID; out Inst): Boolean; overload;
begin
Result := (Instance <> nil) and (Instance.QueryInterface(Intf, Inst) = 0);
end;
function Supports(Instance: TObject; const Intf: TGUID; out Inst): Boolean; overload;
var
Unk: IUnknown;
begin
Result := (Instance <> nil) and Instance.GetInterface(IUnknown, Unk) and
Supports(Unk, Intf, Inst);
end;
{$ENDIF}
{$IFNDEF DELPHI6}
function Supports(const Instance: TObject; const IID: TGUID): Boolean;
var
Temp: IUnknown;
begin
Result := Supports(Instance, IID, Temp);
end;
{$ENDIF}
You are using some devexpress components and the problem is that you are using versions of the code that pre-date C++ Builder XE. The particular problem is that the conditional defines declared in cxVer.inc do not know about XE. Consequently, this cxClasses.pas file does not know which version of Delphi it is targeting.
In most circumstances you could simply add the necessary defines and the code would start working. However, your version of the devexpress code is for RAD Studio 2007 which uses ANSI strings, but you are trying to compile on XE which uses Unicode strings. This difference needs major changes to the rest of the source code.
Unfortunately, to get this code to work on XE you will need to get hold of the latest versions of all your 3rd party components. The updated versions have had the necessary changes to support Unicode text.
What's more, your code may also need some significant re-work to support Unicode but I am less sure on that point because my experience is with Delphi rather than C++ Builder.
I need to change the old Win98 short path names to long path names. I had a routine that worked fine with Delphi 4, but when I upgraded to Delphi 2009 and Unicode, it didn't work with Unicode strings.
I looked around and couldn't find a Unicode-compatible version of it.
It appears that the correct routine to use is GetLongPathName from the WinAPI. But it doesn't seem to be in the SysUtils library of Delphi 2009 and I have not been able to figure out how to declare it properly to access the WinAPI routine.
Also, it does seem that it may be tricky to call, because I've read the SO Question: Delphi TPath.GetTempPath result is cropped but that didn't help me get to first base.
Can someone please explain how to declare this function and use it properly passing a Unicode string in Delphi 2009?
Sure. You do need not a separate unit and can declare GetLongPathName anywhere:
function GetLongPathName(ShortPathName: PChar; LongPathName: PChar;
cchBuffer: Integer): Integer; stdcall; external kernel32 name 'GetLongPathNameW';
function ExtractLongPathName(const ShortName: string): string;
begin
SetLength(Result, GetLongPathName(PChar(ShortName), nil, 0));
SetLength(Result, GetLongPathName(PChar(ShortName), PChar(Result), length(Result)));
end;
procedure Test;
var
ShortPath, LongPath: string;
begin
ShortPath:= ExtractShortPathName('C:\Program Files');
ShowMessage(ShortPath);
LongPath:= ExtractLongPathName(ShortPath);
ShowMessage(LongPath);
end;
One of the requirements for the twitter client we are developing for the community is a spellcheck component. What are some of the spellcheck components/systems you have used in applications and what was your experience using it?
Addict Component Suite is the most complete one for Delphi, but it's not free.
But I think you are looking for freeware for your twitter utility, I have used LS Speller for free project and worked fine with me, it's based on ISpell, so you can update it with newer dictories.
But there's no D2009 update yet, and seems it's not actively developed.
Another option to use the MS Word built in dictionary.
Windows comes with a spell checker API (Windows 8).
TWindow8SpellChecker = class(TCustomSpellChecker)
private
FSpellChecker: ISpellChecker;
public
constructor Create(LanguageTag: UnicodeString='en-US');
procedure Check(const text: UnicodeString; const Errors: TList); override; //gives a list of TSpellingError objects
function Suggest(const word: UnicodeString; const Suggestions: TStrings): Boolean; override;
end;
With implementation:
constructor TWindow8SpellChecker.Create(LanguageTag: UnicodeString='en-US');
var
factory: ISpellCheckerFactory;
begin
inherited Create;
factory := CoSpellCheckerFactory.Create;
OleCheck(factory.CreateSpellChecker(LanguageTag, {out}FSpellChecker));
end;
procedure TWindow8SpellChecker.Check(const text: UnicodeString; const Errors: TList);
var
enumErrors: IEnumSpellingError;
error: ISpellingError;
spellingError: TSpellingError;
begin
if text = '' then
Exit;
OleCheck(FSpellChecker.Check(text, {out}enumErrors));
while (enumErrors.Next({out}error) = S_OK) do
begin
spellingError := TSpellingError.Create(
error.StartIndex,
error.Length,
error.CorrectiveAction,
error.Replacement);
Errors.Add(spellingError);
end;
end;
function TWindow8SpellChecker.Suggest(const word: UnicodeString; const Suggestions: TStrings): Boolean;
var
hr: HRESULT;
enumSuggestions: IEnumString;
ws: PWideChar;
fetched: LongInt;
begin
if (word = '') then
begin
Result := False;
Exit;
end;
hr := FSpellChecker.Suggest(word, {out}enumSuggestions);
OleCheck(hr);
Result := (hr = S_OK); //returns S_FALSE if the word is spelled correctly
ws := '';
while enumSuggestions.Next(1, {out}ws, {out}#fetched) = S_OK do
begin
if fetched < 1 then
Continue;
Suggestions.Add(ws);
CoTaskMemFree(ws);
end;
end;
The TSpellingError object is a trivial wrapper around four values:
TSpellingError = class(TObject)
protected
FStartIndex: ULONG;
FLength: ULONG;
FCorrectiveAction: CORRECTIVE_ACTION;
FReplacement: UnicodeString;
public
constructor Create(StartIndex, Length: ULONG; CorrectiveAction: CORRECTIVE_ACTION; Replacement: UnicodeString);
property StartIndex: ULONG read FStartIndex;
property Length: ULONG read FLength;
property CorrectiveAction: CORRECTIVE_ACTION read FCorrectiveAction;
property Replacement: UnicodeString read FReplacement;
end;
In the blog comments Ken just suggested LS Spell which uses the ISpell dictionaries. It is for Delphi 5, 6 and 7, so as long as it doesn't make explicit use of other string types might work fine.
I've been using Addict and have been pretty happy with it. I've used it mainly in conjunction with WPTools for mail merge & emailing.
You can use Aspell (Win32 version: http://aspell.net/win32/).
In your Delphi project you could use the command line pipe interface: aspell pipe:
C:\Programme\Aspell\bin>aspell pipe
#(#) International Ispell Version 3.1.20 (but really Aspell 0.50.3)
hello
*
world
*
helllo
& helllo 18 0: hello, Helli, hell lo, hell-lo, hell, Heall, hallo, he'll, hullo, Heller, heller, hellos, Jello, jello, Halli, Holli, hallow, hollow
wourld
& wourld 12 0: world, would, wold, whorled, wield, weld, wild, wooled, whirled, worlds, woulds, word
I use the TRichView component as my "text editor" in my Delphi application.
It supports many spellcheckers that work with Delphi. You may want to compare the ones that it supports:
http://www.trichview.com/features/spellcheck.html
DevExpress VCL also has a spell checker, though I have only played with a bit. I also own Addict which I use in software projects.
If you can guarantee that your client always has MS Word installed, I'd suggest MS Word's built in spellchecker too with OLE automation.