I'm receiving an error when building the IdMessageHelper.pas unit in the IndyProtocols90 package. All instances of LoadFromStream and LoadFromFile are claiming there is an issue with the signature:
[Error] IdMessageHelper.pas(78): E2250 There is no overloaded version of 'LoadFromStream' that can be called with these arguments
procedure Internal_TIdMessageHelper_LoadFromStream(AMsg: TIdMessage; AStream: TStream;
const AHeadersOnly: Boolean; const AUsesDotTransparency: Boolean);
var
LMsgClient: TIdMessageClient;
begin
if AUsesDotTransparency then begin
AMsg.LoadFromStream(AStream, AHeadersOnly);
end else
begin
// clear message properties, headers before loading
AMsg.Clear;
LMsgClient := TIdMessageClient.Create;
try
Internal_TIdMessageClientHelper_ProcessMessage(LMsgClient, AMsg, AStream, AHeadersOnly, False);
finally
LMsgClient.Free;
end;
end;
end;
I see that IdMessageHelper is new to this version, but the method that's being called (IdMessage.LoadFromStream for example), the arguments for it haven't changed from the last few versions - at least not for the ones that I have the source.
procedure TIdMessage.LoadFromStream(AStream: TStream; const AHeadersOnly: Boolean = False);
var
LMsgClient: TIdMessageClient;
begin
// clear message properties, headers before loading
Clear;
LMsgClient := TIdMessageClient.Create;
try
LMsgClient.ProcessMessage(Self, AStream, AHeadersOnly);
finally
LMsgClient.Free;
end;
end;
I'm pretty sure I removed all previous versions and packages since this was a clean install of D2005.
The IdMessageHelper unit introduces new LoadFrom...() and SaveTo...() methods for the TIdMessage component, to add an AUsesDotTransparency parameter when loading/saving emails.
In Delphi 2005 and later, it does this by defining a class helper (which is a feature introduced in Delphi 2005) to add new methods to the TIdMessage component without having to modify the IdMessage.pas unit itself. This allows Indy to let people use familiar IdMessage1.LoadFrom...() and IdMessage1.SaveTo...() syntax when using the new functionality 1.
Things were done this way so as not to cause interface breaking changes in the IdMessage unit itself. I blogged about this new addition at the time the IdMessageHelper.pas unit was first added to Indy:
New TIdMessage helper
In your case, the error message is complaining about Line 78:
AMsg.LoadFromStream(AStream, AHeadersOnly);
That line is the new 3-parameter TIdMessageHelper.LoadFromStream() method attempting to call the pre-existing 2-parameter TIdMessage.LoadFromStream() method when AUsesDotTransparency is True:
procedure TIdMessage.LoadFromStream(AStream: TStream; const AHeadersOnly: Boolean = False);
I have tested this new class helper in later Delphi versions and it works fine for me. You should not be getting a compiler error, as there should not be any ambiguity.
However, I have not tested the class helper in Delphi 2005 specifically (as I do not have that version installed), so it is possible that the compiler error could be indicating that class helpers (being a new language feature at the time) were still a little buggy, and were fixed later on.
If you can't find the cause of the ambiguity, you can work around the issue by modifying IdMessageHelper.pas to undefine HAS_CLASS_HELPER for Delphi 2005 1, and then recompile Indy again.
1: In older versions of Delphi where class helpers are not available, IdMessageHelper.pas also defines several standalone TIdMessageHelper_LoadFrom...() and TIdMessageHelper_SaveTo...() functions, so people can still utilize the new AUsesDotTransparency functionality, just with a less-desirable calling syntax.
EDIT: it turns out that class helpers were very buggy in Delphi 2005, and were not officially supported until Delphi 2006:
Class helpers have now been formally introduced in the Win32 compiler [in Delphi 2006]. In Delphi 2005 class helpers were not formally available, and although you could use them they were actually quite buggy. It was quite easy to get internal compiler errors while using them, nothing you could complain to with Borland about as this feature was not officially supported.
So, I have now disabled the TIdMessageHelper helper class in Delphi 2005, and have updated the above mentioned blog article accordingly.
Related
I still use Delphi XE4 (newest compiler I use of multiple Delphi compilers) and need a specific workaround for the fact they completely hid FClients in TBasicAction in this version. I connect/disconnect clients runtime while setting enabled/disabled (to avoid flicker with ~100+ actions and ui elements) thus this workaround for XE4:
Here's my naive attempt and simply returning the field.
TmscBasicActionCrack = class(TBasicAction)
end;
{$IFDEF mymsDELPHIXE4}
TmscBasicActionHelper = class helper for TBasicAction
public
function Helper_Get_Private_FClients: TList<System.Classes.TBasicActionLink>;
end;
{$ENDIF}
{$IFDEF mymsDELPHIXE4}
//------------------------------------------------------------------------------
function TmscBasicActionHelper.Helper_Get_Private_FClients: TList<System.Classes.TBasicActionLink>;
begin
Result := Self.FClients;
end;
{$ENDIF}
However, I get error
E2003 Undeclared identifier: TList<>
I must admit I never go around to using generics with Delphi since I initially heard of stability problems + I need to maintain compability with Lazarus/FreePascal.
I am aware the most recent versions Delphi has altered class helpers again, but I am for now mostly interested in getting this to work with Delphi XE4
The error is indicating that the TList<T> type is unknown to the compiler. To use it you must include System.Generics.Collections in your uses clause.
Facts:
Successfull independent efforts to bring Rtti.TVirtualInterface introduced in Delphi XE2 to prior Delphi versions were made respectively by
Vincent Parrett in Delphi.Mocks.VirtualInterface unit (Delphi Mocks)
Stefan Glienke in DSharp.Core.VirtualInterface.pas unit (DSharp)
Findings:
TRttiIndexedProperty is derived from TRttiMember.
TRttiType and TRttiInstanceType depend on TRttiIndexedProperty.
Rtti.pas depends on TypInfo.pas where some breaking changes where also introduced.
Question:
Is there a hope that one day someone will make it possible to bring TRttiIndexedProperty on Delphi XE ?
TRttiIndexedProperty can't be back-ported to older Delphi versions because it depends on the compiler writing out RTTI data for indexed properties, what only Delphi XE2's compiler does. You can't read something that isn't there.
The only possibility you have would be to write this data by hand. So you have to write a parser that runs over all your code and generates the necessary type information for all indexed properties. And because your parser isn't the compiler you would also have to write little helper functions that write and read the indexed-property.
The output could be something like this:
TMyClass = class
private
...
public
property MyArray[Index: Integer]: TMyObject read GetMyArray write SetMyArray;
// autogenerated code
class procedure RegisterIndexedPropertyInfos(Registry: TMyRttiIndexedPropertyRegistry); static;
end;
// autogenerated code
class procedure TMyClass.RegisterIndexedPropertyInfos(Registry: TMyRttiIndexedPropertyRegistry): TMyRttiIndexedProperty;
begin
Registry.Register('MyArray', [TMyRttiIndex.Create('Index', TypeInfo(Integer))], TypeInfo(TMyObject), #TMyClass.GetMyArray, #TMyClass.SetMyArray);
end;
// When using RichRTTI you can omit this line and use the the RttiContext to find RegisterIndexedPropertyInfos
RegisterIndexedPropertyClass(TMyClass, #TMyClass.RegisterIndexedPropertyInfos);
What's the reason this won't compile?
type
IInterfaceA = interface ['{44F93616-0161-4912-9D63-3E8AA140CA0D}']
procedure DoA;
end;
IInterfaceB = interface(IInterfaceA) ['{80CB6D35-E12F-462A-AAA9-E7C0F6FE0982}']
procedure DoB;
end;
TImplementsAB = class(TSingletonImplementation, IInterfaceB)
procedure DoA;
procedure DoB;
end;
var
ImplementsAB: TImplementsAB;
InterfaceA: IInterfaceA;
InterfaceB: IInterfaceB;
begin
ImplementsAB := TImplementsAB.Create;
InterfaceA := ImplementsAB; >> incompatible types
...
end
In contrast this is how I make it work:
InterfaceA := ImplementsAB as InterfaceB;
or
InterfaceA := InterfaceB;
I mean, if IInterfaceB inherits from IInterfaceA and TImplementsAB implements IInterfaceB, it wouldn't be logical to also implement IInterfaceA and be type compatible?
This so because early OLE/COM had a bug and Borland decided to be compatible with it. This is mentioned in this article: New Delphi language feature: Multiple inheritance for interfaces in Delphi for .NET. The solution is to list all ancestor interfaces explicitly in the class as Mikael wrote.
Some quotes from the linked article:
The problem was in COM itself. To load a module, COM would load the DLL, GetProcAddress on a well-known entry point that was supposed to be exported from the DLL, call the DLL function to obtain an IUnknown interface, and then QueryInterface for IClassFactory. The problem was, when Microsoft added support for IClassFactory2, they added the QueryInterface for IClassFactory2 after the existing code that queried for IClassFactory. IClassFactory2 would only be requested if the query for IClassFactory failed.
Thus, COM would never request IClassFactory2 on any COM server that implemented both IClassFactory2 and IClassFactory.
This bug existed in COM for a long time. Microsoft said that they couldn't fix the COM loader with an OS service pack because both Word and Excel (at the time) relied on the buggy behavior. Regardless of whether it's fixed in the latest releases of COM or not, Borland has to provide some way to preserve this behavior in Win32 Delphi for the forseeable future. Suddenly adding all ancestors into an implementing class that weren't there before is very likely to break existing code that unintentionally falls into the same pattern as the COM loader.
Another way to make it work is to include both interfaces in the class declaration.
TImplementsAB = class(TSingletonImplementation, IInterfaceA, IInterfaceB)
procedure DoA;
procedure DoB;
end;
I guess this is what is required for the compiler to realize that TImplementsAB implements both IInterfaceA and IInterfaceB.
In Delphi XE UxTheme unit there is the following declaration
function DrawThemeTextEx(hTheme: HTHEME; hdc: HDC; iPartId: Integer; iStateId: Integer;
pszText: LPCWSTR; cchText: Integer; dwTextFlags: DWORD; pRect: PRect;
var pOptions: TDTTOpts): HResult; stdcall;
external themelib name 'DrawThemeTextEx' delayed;
My Windows XP Professional with SP3 does not have such function in the uxtheme.dll (checked with dllexp.exe from http://www.nirsoft.net/utils/dll_export_viewer.html)
It seems like my application does delayed loading of the procedure and fails runtime with the following error:
Project mtgstudio.exe raised exception class EExternalException
with message 'External exception C0FB007F'.
The issue was encountered in JVCL as well but resolved there as per http://andy.jgknet.de/blog/2009/09/once-upon-a-delayed-jvcl-time/
I need UxTheme fixed because they are used by the DevExpress components as well.
Is there a way to patch/fix the UxTheme.pas DrawThemeTextEx declaration to be non-delayed?
The fundamental problem is that this API was introduced in Vista and is not available in XP.
The whole point of the delayed keyword is to allow functions like this to be made available easily to developers who want to take advantage of newer API functions. But to do so the developer typically must also provide fallback implementations for older OS versions, which DevExpress appear to have failed to do.
The bug is therefore not in UxTheme.pas, but in the DevExpress component that calls an API that is not implemented on the platform. The fix is to re-work the DevExpress code to avoid calling this API on XP.
I suggest you contact DevExpress who quite probably already have a fix in their latest versions.
I have to use an existing dll, but got a problem with it.
Here's how I try to use it:
unit u_main;
...
implementation
procedure getUserData(var User, Pass: string); stdcall; external 'Common5.dll';
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
u, p: string;
begin
getUserData(u,p);
end;
...
end.
When I press the button the get the userData, I get an EInvalidPointer exception.
The dll is registerd and in some other projects it's in use and work. any ideas?
EDIT:
The DLL was created in Delphi7 and is now used in a Delphi 2009 project.
Maybe there's a problem with unicode strings or something like that?
You need to rebuild the Delphi 7 DLL, make it follow the WinApi standard of getting PChar and BufferLen parameters. You've got multiple problems with the current implementation:
string is platform-specific, it's implementation may change between delphi versions (and did change). You're not supposed to use string outside the platform!
You're passing the parameters as "var", suggesting the DLL might change the value of user and/or pass. String is an special, managed type, changing it requires allocating memory for the new string. This in turns requires you to share the memory manager between the DLL and the EXE (using sharemem.pas and BorlandMM.dll - or variants). The trouble is, sharing the memory manager between different versions of Delphi is an unsupported configuration! (gotton from embarcadero forums)
The Delphi 7 is hoping to receive an simple AnsiString (1 byte chars), while the Delphi 2009 exe is sending Unicode strings (2 bytes per char).
Along with using PChar, be sure to pre-allocate the space before you call GetUserData. i.e. if you assign 'foo' into a pchar that's empty, you'll blow up. So either use static length PChar/PAnsiChar arrays, or use this technique:
var
s : AnsiString;
begin
setlength(s,256);
MyDLLProc(PAnsiChar(s));
end;