GlobalFree - Incompatible types: 'NativeUInt' and 'PWideChar' - delphi

Documentation of WinHttpGetIEProxyConfigForCurrentUser says:
The caller must free the lpszProxy, lpszProxyBypass and
lpszAutoConfigUrl strings in the WINHTTP_CURRENT_USER_IE_PROXY_CONFIG
structure if they are non-NULL. Use GlobalFree to free the strings.
I wrote the following code (Delphi 10.3.2):
var
VConfig: TWinHttpCurrentUserIEProxyConfig;
begin
FillChar(VConfig, SizeOf(VConfig), 0);
if not WinHttpGetIEProxyConfigForCurrentUser(VConfig) then begin
RaiseLastOSError;
end;
...
if VConfig.lpszAutoConfigUrl <> nil then begin
GlobalFree(VConfig.lpszAutoConfigUrl); // <-- Error
end;
and got an error:
[dcc32 Error] E2010 Incompatible types: 'NativeUInt' and 'PWideChar'
Questions:
should I type-cast PWideChar to NativeUInt?
can I use GlobafFreePtr instead of GlobafFree (it accepts PWideChar and works fine in my tests)?

When MSDN tells you to free with a specific function then doing just that is your best bet.
Parts of the Windows API is written in C and (some parts even without STRICT defined?) and other languages with better type checking will require casts in some places.
In the case of HGLOBALs you have the GlobalFlags function that can help you out. In your case the low byte of flags is zero indicating that there are no locks. If the strings had been allocated as movable the documentation would have to tell you to lock before accessing the memory and it does not.
The final nail in the coffin is to debug the function and if you do that you will see that it calls GlobalAlloc with flags set to 0x40 (GPTR) and should therefore be passed to GlobalFree without unlocking. If your compiler complains then you must cast to the appropriate type:
GlobalFree(HGLOBAL(VConfig.lpszAutoConfigUrl));

Related

Delphi 2007->10.1 Berlin Port: Solving E2251 Ambiguious overloaded call to StrLen

Background: Porting my code to Delphi 10.1 Berlin and working through the third party libraries. Some are no longer available so I will try to fix the code...
The following code (passing params from one instance of a program to another) raises E2251 Ambiguious overloaded call to StrLen. I understand why, I just don't know the best way to resolve it.
type
PInstInfo = ^TInstInfo;
TInstInfo = packed record
FirstInstanceWnd:HWND;
ParamCount:Integer;
Params:Array[0..MAX_PARAMS-1, 0..MAX_PARAM_SIZE] of Char;
end;
// Memory is filled with:
lpInfo^.ParamCount:=ParamCount;
if lpInfo^.ParamCount>MAX_PARAMS then
lpInfo^.ParamCount:=MAX_PARAMS;
for i:=0 to lpInfo^.ParamCount-1 do
begin
tempStr:=ParamStr(i+1);
if length(tempStr)>MAX_PARAM_SIZE then
setLength(tempStr,MAX_PARAM_SIZE);
StrCopy(#(lpInfo^.Params[i,0]),PChar(tempStr));
end;
// and notify the first instance
PostMessage(lpInfo^.FirstInstanceWnd, MSG_2ND_INSTANCE, 0, 0);
// And read using:
if lpInfo <> nil then
try
// get Parameters
params:=TStringList.Create;
try
for i:=0 to lpInfo^.ParamCount-1 do
begin
SetString(tempStr,
PChar(#(lpInfo^.Params[i,0])),
StrLen(#(lpInfo^.Params[i,0]))); <--- E2251 Ambiguious overloaded call to StrLen
params.Add(tempStr);
end;
InstanceStarted(params);
finally
params.Free;
end;
Thanks
By default, the # address operator produces an untyped pointer. There are two overloaded versions of StrLen(), one that takes a PAnsiChar and one that takes a PWideChar. An untyped pointer can be passed to both overloads, thus the ambiguity. The PWideChar overload did not exist in Delphi 2007, which is why the code compiled before.
To fix the code, you will have to either:
use the {$TYPEDADDRESS ON} or {$T+} compiler directive to enable Type-checked pointers so taking the address of a Char variable using the # operator will produce a typed PChar pointer instead of an untyped pointer.
{$TYPEDADDRESS ON}
SetString(tempStr,
#(lpInfo^.Params[i,0]),
StrLen(#(lpInfo^.Params[i,0])));
use the same type-cast you use in the 2nd parameter of SetString():
SetString(tempStr,
PChar(#(lpInfo^.Params[i,0])),
StrLen(PChar(#(lpInfo^.Params[i,0]))));
Get rid of the calls to SetString() and StrLen(), since a null-terminated character pointer can be assigned directly to a String variable:
tempStr := PChar(#(lpInfo^.Params[i,0]));
{$TYPEDADDRESS ON}
tempStr := #(lpInfo^.Params[i,0]);
With that said, do be aware that the Char data type changed from Ansi to Unicode in D2009, so this code will only work when sending the parameters to Unicode versions of your app, not to Ansi versions. If you need to continue supporting older versions of your app, you should define a different window message for passing Unicode parameters, and then have your Unicode app support both messages for receiving, and analyze the target HWND to decide which message to use for sending.

Delphi: Invalid typecast with Move

I am installing an old component pack TSCap32 and get error Invalid typecast in multiple location with Move command.
var
pCopiedDib: PChar;
...
Move(pOrigDibBmi^, TByteArray(pCopiedDib^)[0], BmiSize);
The unit itself declare TByteArray like bellow:
type
TByteArray = array[0..0] of Byte;
How to resolve this error?
The cast that you are using requires that the TByteArray and pCopiedDib^ types have the same size. They do not: TByteArray has size 1, and pCopiedDib^ has size 2. Thus the compiler rejects your code because it is not valid.
The code was presumably originally written for pre-Unicode Delphi where PChar is an alias for PAnsiChar. In that scenario the code compiles. Now, you use a Unicode Delphi and PChar is an alias for PWideChar, and hence the types have different size.
Exactly how to fix your code is unclear. If you define pCopiedDib as PAnsiChar then your code will compile, but whether or not it will work is a different matter altogther. I suggest that you study this code further now that you know the cause of the error.

Using SmartPointer as result data type in function requires invoke call explicitly

I am using the SmartPointer in http://members.adug.org.au/2011/12/05/smart-pointers/
I defined a IStringList:
type
IStringList = ISmartPtr<TStringList>;
I may then use as follow without memory leak prompt:
var S: IStringList;
begin
S := TSmartPtr<TStringList>.Create();
S.Add('abc');
end;
If I use the IStringList as result data type in a function:
function GetList: IStringList;
begin
Result := TSmartPtr<TStringList>.Create();
Result.Add('abc'); // E2010
end;
I get a compiler error:
[dcc32 Error] Unit2.pas(31): E2010 Incompatible types: 'SmartPointer.ISmartPtr<System.Classes.TStringList>' and 'Procedure of object'
A workaround solution would be:
Result.Invoke.Add('abc');
But that defeat the purpose of syntax cleanliness of using SmartPointer. Is there a solution?
It's quite an interesting one. For whatever reason, the compiler treats Result differently from other variables. I see no good reason for that, so this feels like a compiler bug.
I see a few workarounds:
Declare a local variable, use that, and finish the function by assigning that local variable to Result.
Using parens to call the function: Result().Add('abc').
Using a better smart pointer. For instance, a smart pointer that is not based on method invocation would be a good start. A generic record with an implicit conversion operator would presumably work.
Find a version of the compiler without the bug, if indeed it is a compiler bug.
Give up on smart pointers and write traditional style code. Every time I contemplate using smart pointers in Delphi I conclude that I'd rather see the explicit try/finally blocks and understand when my objects are created and destroyed.
FWIW, you can greatly simplify the issue by stripping out the smart pointer code, and removing the generic types. Here is the cleanest SSCCE that I can concoct:
{$APPTYPE CONSOLE}
type
TFunc = reference to function: TObject;
procedure Foo;
var
F: TFunc;
begin
F.ClassName; // compiles
end;
function Bar: TFunc;
begin
Result().ClassName; // compiles
Result.ClassName; // [dcc32 Error] E2003 Undeclared identifier: 'ClassName'
end;
begin
end.
I'm now convinced that this is a compiler bug.
It is presumably related to the rather unusual language feature of Delphi that means the function call parentheses can be omitted for a function that has no parameters. This convenience sometimes brings with it ambiguity. However, in this case there is no ambiguity. The . operator has no meaning when applied to a TFunc<TObject> and so the only way to interpret these statements is that the function is called, and the . operator applied to the returned value.
Bug report: QC123218.
{ I'm not allowed to comment... -.-' Feel free making it one. }
I'm afraid there is no way without defeating the purpose of smart pointers. I tried to solve your problem as part of trying to find a way to assign an anonymous method to an interface variable or parameter (SO question from July 2013). Coming back to it every now and then or asking around didn't help in finding a solution.

Calling GetStringTypeW from non-unicode Delphi (7)

I'm attempting to call GetStringTypeW from a non-unicode delphi application and, no matter what I do, I get ERROR_INVALID_FLAGS back. I couldn't find any code sample of that function in use either.
I also had to redifine the function header because the ones provided in windows.pas incorrectly identifies the 3rd parameter as a boolean (it's an INT)
here is my definition for the function:
function GetStringTypeW(dwInfoType: DWORD; lpSrcStr: PWideChar; cchSrc: Integer; lpCharType: Pointer): BOOL;
(For some reason, that function isn't defined as stdcall. Trying it to define it as stdcall will result in an access violation.)
And my call:
var
aCharType: Array of WORD;
APassword: WideString
begin
{..}
SetLength(aCharType, Length(APassword));
if not GetStringTypeW(CT_CTYPE1, PWideChar(APassword[1]), Length(APassword), #aCharType[0]) then
RaiseLastOSError;
{..}
The error I get is
System Error. Code: 1004.
Invalid flags.
I've verified that CT_CTYPE1 is equal to 1.
Does anyone know what could be wrong or have a code sample for using this function ?
The declaration in Windows.pas is indeed wrong, but your correction is wrong, too. You've fixed the parameter types, but you need to fix the calling convention. It should be stdcall:
function GetStringTypeW(
dwInfoType: DWORD;
const lpSrcStr: PWideChar;
cchSrc: Integer;
lpCharType: PWordArray
): BOOL; stdacll;
Without the calling-convention fix, Delphi puts most of the parameters in registers, but the OS expects to find them on the stack. The stack doesn't contain the right values at the right places, to it fails. A bad calling convention can also lead to access violations because the stack is left in an inconsistent state, but since you immediately throw your own exception anyway, that might conceal the stack problems.
When you use stdcall, you get an access violation because you're passing a character and claiming it's a pointer. The OS attempts to dereference the "pointer," but since the character value doesn't represent a valid address, it fails. Type-cast the whole string, not just one character, when you call the function:
GetStringTypeW(CT_CTYPE1, PWideChar(APassword), Length(APassword), PWordArray(aCharType))
Your second parameter is wrong. You are type-casting a single WideChar value to a PWideChar, instead of obtaining the memory address of that WideChar. In other words, change this:
PWideChar(APassword[1])
To this:
PWideChar(APassword)
Or this (only if the length of APassword is never 0):
#APassword[1]

What does delphi compiler error E2134 mean?

In some code I am fixing up, which makes heavy use of generics and interfaced types, I am getting error
E2134, Type '<void>' has no type info.
I believe it is because I am in the middle of a refactor where some deeply nested set of units that all use generics are out of sync, but the error is not happening in a place where I can make use of the error message to fix the code, because there is nothing wrong with the code, at the location where the error is appearing.
Here is the context, mocked up, because I can not post the code, there is too much:
unit GenericThing;
...
interface
...
type
...
IThingListOf<ThingT> = interface( IThingContainer )
function getEnumerator: TEnumerator<ThingT>;
function getCount: Integer;
function getThing( Index: integer ): ThingT;
function getFirst: ThingT;
function IndexOf( value: ThingT): integer;
function addItem( const Thing: ThingT ): ThingT;
function removeItem( const Thing: ThingT ): Integer;
procedure clear;
procedure Sort; overload;
procedure Sort(const AComparer: IComparer<ThingT>); overload;
property Count: integer read getCount;
property First: ThingT read getFirst;
property Items[Index: integer]: ThingT read getThing; default;
end;
// error appears on whatever line number comes after the declaration of IThingListOf<ThingT>...end;
function AnythingYouLikeHere:Integer; // there is nothign wrong with this line, but you get the E2134 here.
It appears that the problem is in IThingContainer itself:
IThingContainer = interface ...
...
procedure DoSomething(const Param);
end;
THe above "const Param" has no type information. This is a weird (armpit) of Pascal/Delphi in my opinion, where you completely violate Wirth's idea of strong typing. It is about as weakly typed as a "void *" pointer in C, or the "Pointer" type in Delphi, but it is rarely used, except for in places like the standard pre-object-pascal RTL functions like Move, and so on. In my opinion, untyped parameters in interfaces, used in generics, should either be allowed, or disallowed, but not allowed sometimes, and disallowed other times.
This is a case of a Pascal feature from 1978 mixing badly with an ObjectPascal feature from 2009.
The error message means there's no type info available for the given type.
Here's a minimal program which produces the message:
type
{$M+}
IThing = interface
procedure P(const X);
end;
{$M-}
begin
end.
The problem, it would appear, is that IThingListOf<>, or one of its ancestors, was compiled with {$M+} active. The compiler presumes from this that you really want full type info for the interface; originally it was used by SOAP etc. support to produce stubs etc. The interface RTTI doesn't support untyped parameters (logically enough, they can't be marshalled by SOAP etc.) - and they show up as being of void type, and you end up with this error message.
The solution is to either not use {$M+} - though presumably the RTTI is being used, otherwise it wouldn't be enabled - or use e.g. Pointer instead, and pass the address explicitly.
It's kinda hard to say from this, especially without the definition of IThingContainer available. If you comment the interface definition out, will it compile past that point? Obviously it will break when you try to create a class that implements the interface, but does commenting it out fix this problem?
If so, then the compiler's choking on something in the interface definition. Try commenting out parts of it to figure out where the problem is. If not, then you'll have to look somewhere else.

Resources