How Do I Properly Call GetLongPathName Using Delphi 2009 and Unicode Strings? - delphi

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;

Related

Access the true buffer of a record in Unicode version of Delphi - ADO

Prior to upgrading to Delphi 2010 we were able to extract data stored in a access database string field which actually contains an array of bytes.
This was achieved with something like:
GetMem(buff, 66);
try
if Table.FieldByName('BytesInStrField').GetData(buff, True) then //True false ignored anyway
begin
Move(Buff^, X, 65);
end;
finally
//
end;
Since we have upgraded even the Buffer seems to stop reading at the first instance of #0#0 (String terminator)
The problem is that this data is no longer accessible to us. I would like to mention it was not myself who decided to put an array of Bytes in a Microsoft Access String Field.
Does anyone have any idea's how I can read the entire filed without truncation, I am trying hard to avoid writing my own Direct Binary Read of the entire DB.
As this is Delphi Accessing Microsoft Access I am using the TADO components.
Thanks for reading.
The GetFieldData methods of TCustomADODataSet are what you need. There are three:
function GetFieldData(Field: TField; Buffer: Pointer): Boolean; override;
function GetFieldData(Field: TField; Buffer: Pointer; NativeFormat: Boolean): Boolean; override;
function GetFieldData(FieldNo: Integer; Buffer: Pointer): Boolean; overload; override;
The hard work is done in the second one, which is also the one used from the TField.GetData method.
You will need to derive your own descendant of TADODataSet, override the second version of the GetFieldData method with your own version. Call inherited for all other fields, but for your specific BytesInString field, read the buffer yourself and avoid the variant conversion that is done in the TCustomADODataSet.GetFieldData method.
If you want to avoid having to insert your own descendant everywhere, declare an interceptor class in a, for example, ADOInterceptor unit:
TADODataSet = class(ADODB.TADODataSet)
public
function GetFieldData(Field: TField; Buffer: Pointer; NativeFormat: Boolean): Boolean; override;
;
And make sure that this unit is used everywhere that ADODB is used and it appears AFTER the ADODB unit in the uses clause.
Did you try Table.FieldByName('BytesInStrField').AsBytes
Have you tried Table.GetBlobFieldData()?

Utf8ToString and older Delphi versions

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}

Sending Delphi string as a parameter to DLL

I want to call a DLL function in Delphi 2010. This function takes a string and writes it to a printer with an USB interface. I do not know in which language is the DLL developed. According to the documentation, the syntax of the function is:
int WriteUSB(PBYTE pBuffer, DWORD nNumberOfBytesToWrite);
How can I declare and use my function in Delphi?
I declare the function like this:
var
function WriteUSB(myP:pByte;n:DWORD): integer ; external 'my.dll';
Should I use stdcall or cdecl in the declaration?
I call the DLL function like this:
procedure myProc;
var
str : string:
begin
str := 'AAAAAAAAAAAAAAAAAAAAA';
WriteUSB(str,DWORD(length(tmp)));
end;
But this code give me exception all the time. I know that the problem is that String is Unicode and each character > 1 byte. I tried to convert to different string types ( AnsiChar and ShortString) but I failed.
What is the correct way to do this?
A couple things. First off, if this is a C interface, which it looks like it is, then you need to declare the import like this:
function WriteUSB(myP:pAnsiChar; n:DWORD): integer; cdecl; external 'my.dll';
Then to call the function, you need to use an Ansi string, and convert it to a PAnsiChar, like so:
procedure myProc;
var
str : AnsiString;
begin
str := 'AAAAAAAAAAAAAAAAAAAAA';
WriteUSB(PAnsiChar(str), length(str));
end;
(The cast to DWORD is unnecessary.) If you do it like this, it should work without giving you any trouble.
You could convert the string to AnsiString (as already mentioned) if you're only going to use Ansi characters but if you want to use unicode strings AND the DLL/printer will accept them you could try something along the lines of (untested but I think it's generally corrext):
procedure myProc;
var
str: string;
buff: TBytes;
begin
str := 'blahblahblah'; // plus additional unicode stuff
buff := TEncoding.Default.GetBytes(str); // of TEncoding.UTF8 or... etc
WriteUSB(#buff[0], Length(buff));
end;
Don't know whether this will work with this particular DLL but it is a more general way of coping with the shift to unicode strings rather than having to assume (and cast to) AnsiString everywhere.
Thanks a lot for all the feedbacks. I make it work by combining your feedbacks. The solution is:
Declaration (I add cdecl):
function WriteUSB( pc:pByte;n:DWORD): integer ; cdecl; external 'my.dll';
And the call:
Procedure myProc;
Var
str : string;
buff : TBytes;
begin
str := 'My string";
buff := TEncoding.Default.GetBytes(str); // of TEncoding.UTF8 or... etc
WriteUSB(pByte(#buff[0]), Length(buff))
...
End;
I do have some problems with Swedish characters but I will solve it. Now I know that the DLL call is correct.
Thanks again for all feedback. This is a great forum.
BR
Delphi User
Try casting it with pchar in your call:
WriteUSB(pchar(str),DWORD(length(tmp)));

Delphi 2010 variant to unicode problem

I am working on a DLL in Delphi 2010. It exports a procedure that receives an array of variants. I want to be able to take one of these variants, and convert it into a string, but I keep getting ?????
I cannot change the input variable - it HAS to be an array of variants.
The host app that calls the DLL cannot be changed. It is written in Delphi 2006.
Sample DLL code:
Procedure TestArr(ArrUID : array of variant); stdcall;
var
i: integer;
s: string;
begin
s:= string(String(Arruid[0]));
showmessage(s);
end;
Using D2006 my DLL works fine. I have tried using VartoStr - no luck. When I check the VarType I am getting a varString. Any suggestions how to fix this?
You host application is sending an AnsiString and you dll is expecting a UnicodeString.
Unicode strings was introduced in Delphi 2009, it does not exist in Delphi 2006. How to fix it? Try [untested]:
Procedure TestArr(ArrUID : array of variant); stdcall;
var
i: integer;
s: AnsiString;
begin
s:= Ansistring(VarToStr(Arruid[0]));
showmessage(s);
end;
or maybe [also untested]:
Procedure TestArr(ArrUID : array of variant); stdcall;
var
i: integer;
s: AnsiString;
begin
s:= Ansistring(AnsiString(Arruid[0]));
showmessage(s);
end;
You can also check if theres is a function Like VarToStr that accepts AnsiStrings (maybe in the AnsiStrings unit?).
1/ How have you call the VarToStr() function ? VarToString(Arruid[0]) ?
2/ Does your Delphi2006 Application send AnsiString or WideString to the DLL ?
If so, and if (1) is not working, try to cast to AnsiString instead of string.

Spellcheck components for Delphi

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.

Resources