E2251 Ambiguous overloaded call to - delphi

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.

Related

Pre-unicode compiled DLL gives access violations when calling from Delphi XE5

I have a DLL that was provided by a 3rd party company and when called from Delphi 2007, it worked perfectly fine. The following code is a sample of how the DLL was used in Delphi 2007:
Procedure XC_eXpressLink(hHandle: Hwnd; Parameters: pChar; Result: pChar); stdcall; external 'XCClient.dll';
Here is how the procedure was called:
procedure TForm1.Button1Click(Sender: TObject);
var Result: array[0..2000] of char;
sParams: String;
begin
sParams := RemoveCRLF(memoParameters.Text); //Remove TMemo CR/LF
XC_eXpressLink(Handle, pChar(sParams), Result);
memoResults.Text := String(Result);
end;
I'm not sure what the DLL was compiled in, but I'm assuming it is expecting ansi and not unicode. After converting the code to ansi in Delphi XE5, the code is now as follows:
Procedure XC_eXpressLink(hHandle: Hwnd; Parameters: pAnsiChar; Result: pAnsiChar); stdcall; external 'XCClient.dll';
and
procedure TForm1.Button1Click(Sender: TObject);
var Result: array[0..2000] of Ansichar;
sParams: AnsiString;
begin
sParams := RemoveCRLF(memoParameters.Text); //Remove TMemo CR/LF
XC_eXpressLink(Handle, pAnsiChar(sParams), Result);
memoResults.Text := AnsiString(Result);
end;
memoParameters is a TMemo on the form which provides the parameters for the dll procedure. The RemoveCRLF is a function that removes any carriage returns and line feeds from memoParameters. MemoResults is another TMemo on the form that provides the return results of the dll procedure.
I'm getting access violations when the changed code is run in Delphi XE5. Since I changed all the parameters to use ansi, shouldn't the dll be getting the same parameter format as before? Am I doing something wrong? Will I be able to get this older compiled DLL to work in Delphi XE5?
I contacted the company, OpenEdge, which supplies the dll for X-Charge (for credit card integration). To resolve the problem, the Handle must have a value of 0 and you have to add /IGNOREHANDLEPARAMETER to the parameters list that is sent to the dll. Note, this parameter will only work with the full version XC8.1.1.6.exe installation or later.
procedure TForm1.Button1Click(Sender: TObject);
var Result: array[0..2000] of Ansichar;
sParams: AnsiString;
begin
sParams := RemoveCRLF(memoParameters.Text); //Remove TMemo CR/LF
XC_eXpressLink(0, pAnsiChar(sParams), Result);
memoResults.Text := AnsiString(Result);
end;

Using LockBox3 with Delphi XE4 without installation

I'm porting program from Delphi 2009 to XE4 and got problem with LockBox encryption. Encrypt/decrypt unit is using just one component:
interface
function Encrypt(aStr: String): String;
function Decrypt(aStr: String): String;
function NeedEncrypt(): Boolean;
implementation
uses
windows,
strUtils,
LbClass;
var
LbRijndael: TLbRijndael;
localNeedEncrypt: Boolean;
function NeedEncrypt(): Boolean;
begin
Result := localNeedEncrypt;
localNeedEncrypt := False;
end;
function Encrypt(aStr: AnsiString): AnsiString;
begin
Result := aStr;
if RightStr(aStr, 2) = '==' then
Exit;
Result := LbRijndael.EncryptString(aStr);
end;
function Decrypt(aStr: AnsiString): AnsiString;
begin
Result := aStr;
if RightStr(aStr, 2) = '==' then
Result := LbRijndael.DecryptString(aStr)
else
localNeedEncrypt := True;
end;
initialization
LbRijndael := TLbRijndael.Create(nil);
LbRijndael.GenerateKey('KEYABC');
LbRijndael.CipherMode := cmECB;
LbRijndael.KeySize := ks128;
end.
As I understood there is no LockBox2 for Delphi XE4.
Can I use LockBox3 for this purpose? If yes, can I use just needed units without installation into Delphi (this was done with LockBox2)?
Whilst the LB2 and LB3 APIs are very different, you should be able to port this code across without too much difficulty. As you are creating the components dynamically at runtime, you shouldn't need to install the packages into your IDE, providing your library path is set to include the LB3 source.

Strange Format result in Delphi XE2 when using Currency data types

In Delphi XE2, I bumped against a strange formatting difference when formatting Currency. Using Double works as expected.
It looks that when using %F or %N (floating point or numeric) you always get 3 decimal digits, even if you request fewer.
With format '%.1f' a Double value of 3.1415 will become '3.1', but a Currency value of 3.1415 will become '3.142' (assuming en-US locale).
With format '%4.0n' a Double value of 3.1415 will become ' 3', but a Currency value of 3.1415 will become '3.142' (assuming en-US locale).
I wrote the below quick DUnit test case, and will investigate further tomorrow.
This particular project cannot be changed to anything other than Delphi XE2 (big corporates are not flexible in what tools they use), so I'm looking for a solution that solves this in Delphi XE2.
In the mean time: what are your thoughts?
unit TestSysUtilsFormatUnit;
interface
uses
TestFramework, System.SysUtils;
type
TestSysUtilsFormat = class(TTestCase)
strict private
DoublePi: Double;
CurrencyPi: Currency;
FloatFormat: string;
NumericFormat: string;
Expected_Format_F: string;
Expected_Format_N: string;
public
procedure SetUp; override;
procedure TearDown; override;
published
procedure Test_Format_F_Double;
procedure Test_Format_F_Currency;
procedure Test_Format_N_Double;
procedure Test_Format_N_Currency;
end;
implementation
procedure TestSysUtilsFormat.Test_Format_F_Double;
var
ReturnValue: string;
begin
ReturnValue := System.SysUtils.Format(FloatFormat, [DoublePi]);
Self.CheckEqualsString(Expected_Format_F, ReturnValue); // actual '3.1'
end;
procedure TestSysUtilsFormat.Test_Format_F_Currency;
var
ReturnValue: string;
begin
ReturnValue := System.SysUtils.Format(FloatFormat, [CurrencyPi]);
Self.CheckEqualsString(Expected_Format_F, ReturnValue); // actual '3.142'
end;
procedure TestSysUtilsFormat.Test_Format_N_Double;
var
ReturnValue: string;
begin
ReturnValue := System.SysUtils.Format(NumericFormat, [DoublePi]);
Self.CheckEqualsString(Expected_Format_N, ReturnValue); // actual ' 3'
end;
procedure TestSysUtilsFormat.Test_Format_N_Currency;
var
ReturnValue: string;
begin
ReturnValue := System.SysUtils.Format(NumericFormat, [CurrencyPi]);
Self.CheckEqualsString(Expected_Format_N, ReturnValue); // actual '3.142'
end;
procedure TestSysUtilsFormat.SetUp;
begin
DoublePi := 3.1415;
CurrencyPi := 3.1415;
FloatFormat := '%.1f';
Expected_Format_F := '3.1';
NumericFormat := '%4.0n';
Expected_Format_N := ' 3';
end;
procedure TestSysUtilsFormat.TearDown;
begin
end;
initialization
RegisterTest(TestSysUtilsFormat.Suite);
end.
Posting this as an answer on the request of the asker in the comments to the question above.)
I can't reproduce the issue on either XE2 or XE3, with a plain console application. (It was just quicker to set up for me.)
Here's the code I used in it's entirely (on both XE2/XE3):
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
SysUtils;
const
DoublePi: Double = 3.1415;
CurrencyPi: Currency = 3.1415;
FloatFormat = '%.1f';
NumericFormat = '%4.0n';
begin
WriteLn(Format('Double (.1f) : '#9 + FloatFormat, [DoublePi]));
WriteLn(Format('Currency (.1f) : '#9 + FloatFormat, [CurrencyPi]));
WriteLn(Format('Currency (4.0n): '#9 + NumericFormat, [CurrencyPi]));
ReadLn;
end.
Here's the output from the XE2 run (Delphi® XE2 Version 16.0.4429.46931):
:
This was a bug in early Delphi XE 2 versions in these methods:
function WideFormatBuf(var Buffer; BufLen: Cardinal; const Format;
FmtLen: Cardinal; const Args: array of const;
const AFormatSettings: TFormatSettings): Cardinal;
function FormatBuf(var Buffer; BufLen: Cardinal; const Format;
FmtLen: Cardinal; const Args: array of const;
const AFormatSettings: TFormatSettings): Cardinal;
Fails:
Embarcadero® RAD Studio XE2 Version 16.0.4256.43595 (Update 2)
(The odd thing is: that version indicates "no updates available" with starting the "check for updates")
I did not have time to check intermediate versions.
Works:
Embarcadero® RAD Studio XE2 Version 16.0.4429.46931 (Update 4))
Embarcadero® Delphi® XE2 Version 16.0.4504.48759 (Update 4 hotfix 1)
One of the things that XE2 Update 4 (with or without the hotfix) breaks is the creation of a standard (non-IntraWeb) unit test project.
This menu entry is missing: File -> New -> Other -> Unit Test -> Test Project.
As a reminder to myself, this is the skeleton code to quickly get started with the missing Test Project entry:
program UnitTest1;
{
Delphi DUnit Test Project
-------------------------
This project contains the DUnit test framework and the GUI/Console test runners.
Add "CONSOLE_TESTRUNNER" to the conditional defines entry in the project options
to use the console test runner. Otherwise the GUI test runner will be used by
default.
}
{$IFDEF CONSOLE_TESTRUNNER}
{$APPTYPE CONSOLE}
{$ENDIF}
uses
Forms,
TestFramework,
GUITestRunner,
TextTestRunner;
{$R *.RES}
begin
Application.Initialize;
if IsConsole then
with TextTestRunner.RunRegisteredTests do
Free
else
GUITestRunner.RunRegisteredTests;
end.

What's the equivalent in Delphi 3 of Supports for Interfaces?

I support an application written in Delphi 3 and I would like to put in some improvements to the source code while waiting for the opportunity to upgrade it to a newer version of Delphi. One of the things I would like to use is Interfaces. I know Delphi 3 already has the concept of Interfaces but I am having trouble finding out how to do the equivalent of
if Supports(ObjectInstance, IMyInterface) then
Write your own implementation of "Supports" function. In Delphi 2009 you can use
function MySupports(const Instance: TObject; const IID: TGUID): Boolean;
var
Temp: IInterface;
LUnknown: IUnknown;
begin
Result:= (Instance <> nil) and
((Instance.GetInterface(IUnknown, LUnknown)
and (LUnknown.QueryInterface(IID, Temp) = 0)) or
Instance.GetInterface(IID, Temp));
end;
Test:
procedure TForm4.Button3Click(Sender: TObject);
var
Obj: TInterfacedObject;
begin
Obj:= TInterfacedObject.Create;
if MySupports(Obj, IUnknown) then
ShowMessage('!!');
end;
Hope it will work in Delphi 3

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.

Resources