Problems with Aero Glass in Delphi 7 applications - delphi

I'm trying to remake some of my older projects to support Aero Glass. Although it's kinda easy to enable glass frame, I've encountered some major problems. I used this code:
var
xVer: TOSVersionInfo;
hDWM: THandle;
DwmIsCompositionEnabled: function(pbEnabled: BOOL): HRESULT; stdcall;
DwmExtendFrameIntoClientArea: function(hWnd: HWND; const pxMarInset: PRect): HRESULT; stdcall;
bEnabled: BOOL;
xFrame: TRect;
// ...
xVer.dwOSVersionInfoSize := SizeOf(TOSVersionInfo);
GetVersionEx(xVer);
if xVer.dwMajorVersion >= 6 then
begin
hDWM := LoadLibrary('dwmapi.dll');
#DwmIsCompositionEnabled := GetProcAddress(hDWM, 'DwmIsCompositionEnabled');
#DwmExtendFrameIntoClientArea := GetProcAddress(hDWM, 'DwmExtendFrameIntoClientArea');
if (#DwmIsCompositionEnabled <> nil) and
(#DwmExtendFrameIntoClientArea <> nil) then
begin
DwmIsCompositionEnabled(#bEnabled);
if bEnabled then
begin
xRect := Rect(-1, -1, -1, -1);
DwmExtendFrameIntoClientArea(FrmMain.Handle, #xRect);
end;
end;
FreeLibrary(hDWM);
end;
So I got the pretty glass window now. Due to black being transparent color now (kinda stupid choice, why couldn't it be pink) anything that is clBlack becomes transparent, too. It means all labels, edits, button texts... even if I set text to some other color at design time, DWM still makes them that color AND transparent.
Well, my question would be - whether it's possible to solve this somehow?

Delphi 7 and all the versions till D2006 has also other problems with Windows Vista and newer.
Delphi 2007 is the fist certified version for Vista. My advice is to upgrade to Delphi 2010. Your effort to patch Delphi 7 is imho too big for the outcome. Ok, perhaps you will need to convert your application to Unicode (a far less painful process than it sounds - especially if you use Embarcadero's forums and/or this site) but it's worth the effort. And this not only for Vista compatibility but also for all the goodies which are packed with newer versions of Delphi, especially with Delphi 2010.
HTH

Related

Load TGPBitmap from MemoryStream

I have been asked to correct an issue (not related to this question) in a legacy Delphi program. After fixing some issues with missing components, I am now stuck with some GDI Plus functionality, which stops me from compiling the program. One of the functions where this is used is:
function TDownLoadItem.LoadRawBitmapFromStream(var bm: TBitmap): Boolean;
var
image: TGPBitmap;
begin
Result := False;
if Content.Size = 0 then
exit;
// NOTE: Content is a TMemoryStream, declared globally.
image := GDIPlusHelper.LoadBitmapFromStream(Content); // <== This is where the problem is....
try
bm.Width := image.GetWidth;
bm.Height := image.GetHeight;
with TGPGraphics.Create(bm.Canvas.Handle) do
try
DrawImage(image, 0, 0, image.GetWidth, image.GetHeight);
Result := True;
finally
Free;
end;
finally
image.Free;
end;
end;
I think (not sure) the last Delphi version used was 2006, I am on Delphi Rio 10.3.
Online I have managed to find GDI+ 1.2, but this does not solve the problem. The procedure LoadBitmapFromStream does not exit in these libraries. GDIPlusHelper was apparently renamed to GDIPlusHelpers and most code has changed from classes to interfaces. I suspect an older edition of the GDI Plus libraries were used, but I cannot find these.
Reworking the code would be too complex as it would require Content to be an IStream instead of a TMemoryStream. Also, simply using a TBitmap is not feasible either as other code (not shown) uses functionality specific to TGPBitmap (e.g. RotateFlip).
Any suggestions on how to fix/work around this? Thanks in advance!

How to work with 0-based strings in a backwards compatible way since Delphi XE5?

I'm trying to convert my current Delphi 7 Win32 code to Delphi XE5 Android with minimal changes, so that my project can be cross-compiled to Win32 from a range of Delphi versions and Android from XE5.
Starting from XE5 there are breaking changes in language aimed at future. One of such changes is zero-based strings.
In older versions with 1-based strings the following code was correct:
function StripColor(aText: string): string;
begin
for I := 1 to Length(aText) do
but now this is obviously not right. Suggested solution is to use:
for I := Low(aText) to High(aText) do
This way XE5 Win32 handles 1-based strings and XE5 Android handles 0-based strings right. However there's a problem - previous Delphi versions (e.g. XE2) output an error on such code:
E2198 Low cannot be applied to a long string
E2198 High cannot be applied to a long string
I have quite a lot of string manipulation code. My question is - how to modify and keep above code to be compileable in Delphi 7 Win32 and Delphi XE5 Android?
P.S. I know I can still disable ZEROBASEDSTRINGS define in XE5, but that is undesired solution since in XE6 this define will probably be gone and all strings will be forced to be 0-based.
If you want to support versions that use one based strings then don't define ZEROBASEDSTRINGS. That's the purpose of that conditional.
There's no indication that I am aware of that the conditional will be removed any time soon. It was introduced in XE3 and has survived two subsequent releases. If Embarcadero remove it, none of their Win32 customers will not upgrade and they will go bust. Embarcadero have a track record of maintaining compatibility. You can still use TP objects and short strings. Expect this conditional to live as long as the desktop compiler does.
In fact, all the evidence points towards the mobile compilers retaining support for one based string indexing. All the utility string functions like Pos use one based indices, and will continue to do so. If Embarcadero really are going to remove support for one based string indexing, they'll be removing Pos too. I don't believe that is likely any time soon.
Taking your question at face value though it is trivial to write functions that return the low and high indices of a string. You just use an IFDEF on the compiler version.
function StrLow(const S: string): Integer; inline;
begin
Result := {$IFDEF XE3UP}low(S){$ELSE}1{$ENDIF}
end;
function StrHigh(const S: string): Integer; inline;
begin
Result := {$IFDEF XE3UP}high(S){$ELSE}Length(S){$ENDIF}
end;
Update
As Remy points out, the above code is no good. That's because ZEROBASEDSTRINGS is local and what counts is its state at the place where such functions would be used. In fact it's just not possible to implement these functions in a meaningful way.
So, I believe that for code that needs to be compiled using legacy compilers, as well as the mobile compilers, you have little choice but to disable. ZEROBASEDSTRINGS.
All of the RTL's pre-existing functions (Pos(), Copy(), etc) are still (and will remain) 1-based for backwards compatibility. 0-based functionality is exposed via the new TStringHelper record helper that was introduced in XE3, which older code will not be using so nothing breaks.
The only real gotchas you have to watch out for are things like hard-coded indexes, such as your loop example. Unfortunately, without access to Low/High(String) in older Delphi versions, the only way to write such code in a portable way is to use IFDEFs, eg:
{$IFDEF CONDITIONALEXPRESSIONS}
{$IF CompilerVersion >= 24}
{$DEFINE XE3_OR_ABOVE}
{$IFEND}
{$ENDIF}
function StripColor(aText: string): string;
begin
for I := {$IFDEF XE3_OR_ABOVE}Low(aText){$ELSE}1{$ENDIF} to {$IFDEF XE3_OR_ABOVE}High(AText){$ELSE}Length(aText){$ENDIF} do
DoSomething(aText, I);
end;
Or:
{$IFDEF CONDITIONALEXPRESSIONS}
{$IF CompilerVersion >= 24}
{$DEFINE XE3_OR_ABOVE}
{$IFEND}
{$ENDIF}
function StripColor(aText: string): string;
begin
for I := 1 to Length(aText) do
begin
DoSomething(aText, I{$IFDEF XE3_OR_ABOVE}-(1-Low(AText)){$ENDIF});
end;
end;
Conditional Expressions were introduced in Delphi 6, so if you don't need to support version earlier than Delphi 7, and don't need to support other compilers like FreePascal, then you can omit the {$IFDEF CONDITIONALEXPRESSIONS} check.
This is rather a sum up of the two answers:
As pointed out by Remy Lebeau, ZEROBASEDSTRINGS is a per-block conditional. That means that the following code will not work as expected:
const
s: string = 'test';
function StringLow(const aString: string): Integer; inline; // <-- inline does not help
begin
{$IF CompilerVersion >= 24}
Result := Low(aString); // Delphi XE3 and up can use Low(s)
{$ELSE}
Result := 1; // Delphi XE2 and below can't use Low(s), but don't have ZEROBASEDSTRINGS either
{$ENDIF}
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
{$ZEROBASEDSTRINGS OFF}
Memo1.Lines.Add(Low(s).ToString); // 1
Memo1.Lines.Add(StringLow(s).ToString); // 1
{$ZEROBASEDSTRINGS ON}
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
{$ZEROBASEDSTRINGS ON}
Memo1.Lines.Add(Low(s).ToString); // 0
Memo1.Lines.Add(StringLow(s).ToString); // 1 <-- Expected to be 0
{$ZEROBASEDSTRINGS OFF}
end;
There are 2 possible solutions:
A. Every time there's string items access or iteration place an IFDEF around it, which is indeed a lot of clutter for the code, but will work properly irregardless of ZEROBASEDSTRINGS setting around it:
for I := {$IFDEF XE3UP}Low(aText){$ELSE}1{$ENDIF} to {$IFDEF XE3UP}High(aText){$ELSE}Length(aText){$ENDIF} do
B. Since the ZEROBASEDSTRINGS conditional is per-block it never gets spoiled by 3rd party code and if you don't change it in your code you are fine (above StringLow will work fine as long as the caller code has the same ZEROBASEDSTRINGS setting). Note that if target is mobile, you should not apply ZEROBASEDSTRINGS OFF globally in your code since RTL functions (e.g. TStringHelper) will return 0-based results because mobile RTL is compiled with ZEROBASEDSTRINGS ON.
On a side note - One might suggest to write an overloaded versions of Low/High for older versions of Delphi, but then Low(other type) (where type is array of something) stops working. It looks like since Low/High are not usual functions then can not be overloaded that simply.
TL;DR - Use custom StringLow and don't change ZEROBASEDSTRINGS in your code.
How about defining this as an inc file? Put additional ifdefs depending on what Delphi versions you want to support. Since this code is only for versions before the ZBS to make it possible to use Low and High on strings it will not run into the problem with the ZEROBASEDSTRINGS define only being local.
You can include this code locally (as nested routines) then which reduces the risk of colliding with System.Low and System.High.
{$IF CompilerVersion < 24}
function Low(const s: string): Integer; inline;
begin
Result := 1;
end;
function High(const s: string): Integer; inline;
begin
Result := Length(s);
end;
{$IFEND}
As LU RD told above Low and High functions for string were only introduced in XE3. So how can you use functions in earlier Delphi verions, that are missed? Just the same way as always do - if the function is missed - go and write it!
You should only activate those compatibility additions for Delphi beyond XE3 version, using conditional compilation. One way is described in other answers, using >= comparison. Another usual way would be reusing jedi.inc definitions file.
Then for earlier Delphi versions you would add your own implementations of those, like
function Low(const S: AnsiString): integer; overload;
Pay attention to the overload specifier - it is what would make the trick possible, don't forget it!
You would have to write 4 functions for Delphi 7 till 2007, covering combinations of Low/High fn name and AnsiString/WideString data type.
For Delphi 2009 till XE2 you would have to add two more functions for UnicodeString datatype.
And also mark those function inline for those Delphi versions, that support it (this is where jedi.inc comes handy again.
Hopefully you don't need supprot for UTF8String, but if you do - you know what to do about it now (if compiler would manage to tell it from AnsiString when overloading...)

How to use Delphi standard confirmation dialog but with checkbox "Don't ask me again"?

In many confirmation dialogs it is usefull to have such option (quick wayt to disable confirmation).
But i can't find how to do that. I don't want to design it myself because i need this dialog to be standard-like and don't wont to redesign with every update of Delphi. Is there simple way to use Delphi standard confirmation dialog with such checkbox ?
UPDATE2. Suggested SynTaskDialog library from Synopse project does great job (all i need and even more), i will use it in my projects. Thanks!
UPDATE. So, thank you guys for ideas. System function MessageBoxCheck is nice solution but seem to be not so stable as it should be. In general i agree that it is good idea to use latest API functions to provide users with best UI experience of modern os and use old-fashioned design for older systems. At moment i stay on simple solution (code is following), but if someone share the code with support of UI for modern OS, it will be nice.
function MsgDlgWithCB(const Msg,Title,CBMsg: string; DlgType: TMsgDlgType;
Buttons: TMsgDlgButtons; DefaultButton: TMsgDlgBtn;
var cbDontAskAnymore: TCheckBox): TForm;
var
i: integer;
b: TButton;
y: integer;
begin
Result := CreateMessageDialog(Msg, DlgType, Buttons, DefaultButton) ;
Result.Position := poScreenCenter;
cbDontAskAnymore := TCheckBox.Create(Result);
cbDontAskAnymore.Caption := CBMsg;
cbDontAskAnymore.Width := 130;
y := -1;
for i := 0 to result.ComponentCount-1 do
if result.Components[i] is TButton then
begin
b := TButton(result.Components[i]);
b.Left := b.Left + cbDontAskAnymore.Width + 16;
Result.ClientWidth := Max(Result.ClientWidth, b.Left+b.Width+16);
y := b.Top+b.Height-cbDontAskAnymore.Height;
end;
if y<0 then
y := Result.ClientHeight - cbDontAskAnymore.height - 16;
Result.Caption := Title;
cbDontAskAnymore.Parent := Result;
cbDontAskAnymore.Top := y;
cbDontAskAnymore.Left := 8;
end;
function MessageDlgCheckbox(const Msg: string; DlgType: TMsgDlgType;
Buttons: TMsgDlgButtons; DefaultButton: TMsgDlgBtn;
var cbDontAskAnymore: Boolean;
const Title: string ='Confirmation';
const CBMsg: string = 'Don''t ask anymore'): integer;
var
f: TForm;
c: TCheckbox;
begin
f := MsgDlgWithCB(Msg,Title,CBMsg,DlgType,Buttons,DefaultButton,c);
try
result := f.ShowModal;
cbDontAskAnymore := c.Checked;
finally
f.free;
end;
end;
You can use our Open Source SynTaskDialog unit.
Windows provides a generic task dialog available since Vista/Seven. But there is none available with previous versions of Windows, i.e. Windows XP or 2K.
This unit (licensed under a MPL/GPL/LGPL tri-license) will use the new TaskDialog API under Vista/Seven, and emulate it with pure Delphi code and standard themed VCL components under XP or 2K. It supports Delphi 6 up to XE4, and is Win32/Win64 Unicode ready.
Here is the result under a Windows Seven 64 bit computer:
And here is the same dialog created from our emulated pure Delphi code:
Since this screenshot was made on a Win 7 machine, the styling is native for that OS. When the emulated version of the dialog runs on XP it displays in a style native to that OS.
You have your "Do not ask for this setting next time" checkbox... and potentially much more!
The system native functionality that offers such facilities is the task dialog API introduced in Vista. This provides means for you to show much more capable dialogs than the older MessageBox API.
Should you need to support XP then you will have to create your own dialog. For example by deriving from TForm and calling ShowModal. If you do this, make the form capable of building itself dynamically. Don't make one form per message that you show!
In my codebase, I have my own wrapper of the task dialog API. This detects at runtime versions of Windows that do not support task dialog and falls back on a custom built Delphi dialog.
Regarding SHMessageBoxCheck I'd be a little wary of taking a dependency on that. According to its documentation it's not supported beyond XP, and you have to import it by ordinal. I'd personally be worried that it might be dropped from a future version of Windows. That said, MS has a strong track record of doing whatever it takes to keep legacy apps working with new OS releases.

How to get screen resolution in Firemonkey XE3?

How can I get the screen resolution in Firemonkey 2 (Delphi XE3)?
It's all changed in XE3. The platform support has been completely overhauled. See Pawel Glowacki's article for more.
var
ScreenSvc: IFMXScreenService;
Size: TPointF;
begin
if TPlatformServices.Current.SupportsPlatformService(IFMXScreenService, IInterface(ScreenSvc)) then
begin
Size := ScreenSvc.GetScreenSize;
....
end;
end;
In spite of the overhaul, this is still not much use if you have multiple monitors. Perhaps there is some multimon support in FMX2, but it's clearly not available in IFMXScreenService.

What is the recommended support for mouse-wheel capability in Delphi XE2?

I am trying to tidy up my code now that Delphi XE2 is available - my code dates from Borland Pascal 7 so there are lots of 'old' (but working!) Win32 techniques and natually I have platform independence in mind too. Support for the mouse wheel has come up before here with several prior questions 1 2 and 3. As with some of these answers, my own solution is a simple mouse message intercept using a TApplicationEvents component:
procedure TForm6.ApplicationEvents1Message(var Msg: tagMSG;
var Handled: Boolean);
procedure ProcessMouseWheelMessage;
begin
Msg.message := WM_KEYDOWN;
Msg.lParam := 0;
If Integer(Msg.wParam) > 0 then
Msg.wParam := VK_UP
else
Msg.wParam := VK_DOWN;
Handled := False;
end;
begin
Case Msg.message of
WM_MOUSEWHEEL :
ProcessMouseWheelMessage;
end;
end;
I revisited this code today because 'Msg.wParam' is now NativeInt, breaking use of negative Msg.wParam values in the above code unless you use Integer(Msg.wParam). It made me notice that I had not seen any really definitive use of the mouse wheel for Delphi code - terrible when all mice now have wheels and Delphi is at the 'cutting edge' again! I would have expected a property, a component or some other more 'exposed' solution, and what about Fire Monkey wheel support?
Do I carry on with my solution or is there a better way?
In XE2 (and indeed all the recent releases) you don't need to do anything. The standard controls support mouse wheel scrolling out of the box. Just get rid of this old code.
Delphi components that have a Windows handle (descendents of TWinControl) have the OnMouseWheel, OnMouseWheelUp and OnMouseWheelDown events.
If you want to add a mousewheel event to a control that does not descent from TWinControl, see this article: http://delphi.about.com/od/delphitips2010/qt/timage-handling-mouse-wheel-messages.htm

Resources