Delphi: Invalid typecast with Move - delphi

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.

Related

Delphi SHGetFolderPath and null terminated string

I am using this function to get user folder:
function LocalAppDataPath : string;
const
SHGFP_TYPE_CURRENT = 0;
var
path: array [0..MaxChar] of char;
begin
SHGetFolderPath(0,CSIDL_LOCAL_APPDATA,0,SHGFP_TYPE_CURRENT,#path[0]);
Result := StrPas(path);
end;
It is working great.
But after I add this lib to my project: OpenJpeg
The returned values from this function is: C. It seems like for some reason each char in the "path" array has #0 after it and for this reason the returned string it truncated right at the start.
When I remove: OpenJpeg from my uses it all works good.
Any idea why it happens?
At the very top of the OpenJpeg unit is found this code:
type
Char = AnsiChar;
This unit is therefore toxic when used in a Unicode Delphi. When you use Char in your code you are expecting a WideChar but this evil type alias confounds you.
My advice is to modify the library to remove that type alias and replace every use of Char with AnsiChar.
If you don't want to do that you can fully qualify Char as System.Char, or use WideChar explicitly in your code. However, this unit is, as I said, toxic, and it really needs to be remedied.
If you enable the Typed-Checked Pointers compiler option, the compiler will stop you from passing a PAnsiChar to a function expecting a PWideChar, and vice versa, when using the # address operator. Better to find your mistakes at compile time if possible.
You can simplify the code by passing path in place of #path[0].
Please don't use StrPas. It has long been deprecated. You can simply write
Result := path;
The array is one character longer than necessary. That is, assuming MaxChar is what I think it is. Anyway, the array should be
path: array [0..MAX_PATH-1] of char;
You also fail to perform any error checking on the API call. Please check for errors as described in the documentation.

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.

Why can't I use a resourcestring as a constant?

I've downloaded embtvstools (Embarcadero TVirtualShellTools) from: http://embtvstools.svn.sourceforge.net/
However when I create a new package, drop the .pas files (and a missing compilers.inc from VirtualTreeView in) and compile the lot, I get an error E2026, why is this and how do I avoid/workaround this?
resourcestring
sAssociationChanged = 'Association Changed';
sItemCreate = 'Item Create';
sItemDelete = 'Item Delete';
....
const
// Literal translations of TShellNotifyEvent type. Useful when using the
// OnShellNotify event to print out what event occurred. VirtualShellUtilities.pas
// has a helper function ShellNotifyEventToStr that uses these.
VET_NOTIFY_EVENTS: array[0..19] of WideString = (
sAssociationChanged,
sAttributes,
sItemCreate,
.....
[Pascal Error] IDEVirtualResources.pas(155): E2026 Constant expression expected
[Pascal Error] IDEVirtualResources.pas(156): E2026 Constant expression expected
[Pascal Error] IDEVirtualResources.pas(157): E2026 Constant expression expected
Update
Changing the widestring to a string stops the compiler complaining, (I suspect it will create some issue elsewhere because widestring <> string) I would like to keep the constant of type widestring.
As Uwe points out in the comments, resourcestring in Unicode versions of Delphi is of type WideString. But you are using pre-Unicode Delphi and so resourcestring is simply AnsiString. This explains the compilation error.
How to proceed depends on what you are attempting to do. If you intend to translate these strings into different languages then you may be in a bind. If you are intending to do that then you would obviously be far better off with a Unicode version of Delphi.
So, since you are sticking with a pre-Unicode Delphi I guess you don't actually need to translate the strings. In which case just change the declaration of the const array from WideString to string. As it happens, this array is declared by this code but never once referred to.

Discrepancy of types

Delphi.
Why
type
myInt = Integer;
myNewInt = -2147483648..2147483647;
var
a: myint;
b: myNewInt;
begin
a := b;
end;
It is compiled normally though types formally different - one is declared here, another undertakes from other module. And if
uses
windows;
type
_FILETIMEA = record // Copy from windows.pas
dwLowDateTime: DWORD;
dwHighDateTime: DWORD;
end;
var
x: _FILETIMEA;
y: windows._FILETIME;
begin
x := y;
end;
Will cause a compilation error (in line x:=y; [DCC Error] ... E2010 Incompatible types: 'windows._FILETIME' and '_FILETIMEA'), though type _FILETIMEA = Windows._FILETIME ?
Delphi doesn't support duck typing. You have 2 different records. They just look a like, but they are 2 different types for the compiler. If you want to assign the two variables you have to type cast them what is possible because they have the same size.
x := _FILETIMEA(y);
Because you're not doing the same thing. :) Delphi is strongly typed (there aren't many exceptions - almost everything has a specific type).
type
myInt = Integer;
myNewInt = -2147483648..2147483647;
There is nothing incompatible about these types. They're both the same type (integer), and support the same range of values. There's no conflict about allowing the assignment of one to the other.
// Your unit name
unit GuTypes;
Type
_FILETIMEA = record // Copy from windows.pas
dwLowDateTime: DWORD;
dwHighDateTime: DWORD;
end;
This is a new type, defined by your code (even though it's identical to the one in Windows.pas, it's not the same). It's GuTypes._FileTimeA, which is not the same as Windows._FileTimeA.
This is actually a good thing, because it allows different units to use the same typenames without conflict; you can prefix with the unit name as a "namespace" to clarify which one you mean to use. For instance, if you have yours defined in GuTypes, and Windows has one declared there, in a third unit MyAppCode, you can do this safely:
var
GFileTime: GuTypes._FILETIMEA;
WFileTime: Windows._FILETIMEA;
Even if one of the types changes, your code is safe from side effects, because the two types can't accidentally be interchanged.
You can typecast to force the assignment to work, but that's usually a bad idea; it defeats the whole purpose of using a strongly typed language. Typecasting tells the compiler "I'm smarter than you, and I know this is wrong but I want you to ignore it and do it anyway."
A better solution would be either to declare your variable as you did the y in your example (Windows._FILETYPEA), or to declare a compatible type (type TMyFileTimeA = Windows._FILTETIMEA;).
See the XE2 Wiki Pages on Type Compatibility and Identity. Look specifically at the Assignment Compatibility section.

Porting mozilla's NSModule to Delphi

In extension to this question, I guess I'll best show what I've got so far.
What I'm trying to do is create a Firefox extension with Delphi, that'll work with the Firefox versions of the future that will use an exported NSModule structure, and no longer an NSGetModule function.
Main questions I'm struggling with for the moment is:
Is the code below correct? I may be wrong with how the pointers and arrays of records work.
How to debug this? If I build it and it runs then I'm kind of sure it'll work, but in debugging my library I can only check if my init code does its job. (and for now, Firefox 3.6 doesn't seem to pick up my #mozilla.org/network/protocol;1?name=xxm contract)
The code I'm trying to port is here:
http://mxr.mozilla.org/mozilla-central/source/xpcom/components/Module.h
type
TConstructorProcPtr=function(aOuter:nsISupports;const aIID:TGUID;var aResult:pointer):nsresult;
TLoadFuncPrt=function:nsresult;
TUnloadFuncPrt=procedure;
TCIDEntry=record
cid:TGUID;
service:boolean;
getFactoryProc:pointer;//TGetFactoryProcPtr;
constructorProc:TConstructorProcPtr;
end;
TContractIDEntry=record
contractid:PChar;
cid:TGUID;//PGUID?
end;
TCategoryEntry=record
category,entry,value:PChar;
end;
TXPCOMModule=packed record
kVersion:integer;//=1;
mVersion:cardinal;//kModuleVersion
mCIDs:^TCIDEntry;//pointer to first in array, last should be nil
mContractIDs:^TContractIDEntry;//pointer to first in array, last should be nil
mCategoryEntries:^TCategoryEntry;//pointer to first in array, last should be nil
getFactoryProcPtr:pointer;//TGetFactoryProcPtr;
loadProc:TLoadFuncPrt;
unloadProd:TUnloadFuncPrt;
end;
You almost certainly need the cdecl calling convention on all your procedure- and function-pointer declarations:
TConstructorProcPtr = function(aOuter: nsISupports; const aIID: TGUID; var aResult: Pointer): nsresult; cdecl;
TLoadFuncPrt = function: nsresult; cdecl;
TUnloadFuncPrt = procedure; cdecl;
I assume you've declared nsISupports as a Delphi interface. Otherwise, you need to make sure the aOuter parameter above is a pointer as it is in the C++ code.
For TContractIDEntry, and all the other places where you use PChar, I advise you to use PAnsiChar instead. The size of Delphi's Char type changed a couple of years ago, but the C++ char is and always will be one byte, so use Delphi's one-byte character type explicitly. Also, your comment wondering whether to declare the cid field as a PGUID was correct; asterisk means pointer.
TContractIDEntry = record
contractid: PAnsiChar;
cid: PGUID;
end;
The kVersion field should not be a member of the record you declare. In C++, it's a static member, which means it occupies no space in the structure itself; it's shared by all instances of that type. It's equivalent to a class field in a Delphi class, but I don't think records offer that feature. Make it a unit-level variable instead of a field.

Resources