Been trying to use the following code in order to check if Windows Aero is enabled:
function AeroEnabled: boolean;
var
enabled: bool;
begin
// Function from the JwaDwmapi unit (JEDI Windows Api Library)
DwmIsCompositionEnabled(enabled);
Result := enabled;
end;
...
if (CheckWin32Version(5,4)) and (AeroEnabled) then
CampaignTabs.ColorBackground := clBlack
else begin
GlassFrame.Enabled := False;
CampaignTabs.ColorBackground := clWhite;
end;
However, doing so on a pre-vista machine causes the app to crash because the DWMApi.dll is missing. I've also tried this code however it produces 2 AV's in a row. How can I do this ? I am using Delphi 2010. :)
You've got your versions wrong. Vista/2008 server are version 6.0. Your test should be:
CheckWin32Version(6,0)
I believe that you are using Delphi 2010 or later in which case you should simply call the DwmCompositionEnabled function from the built-in Dwmapi unit. This organises the version check and the delayed binding for you. No need for JEDI.
Edit: Text below was written before the question was edited.
Probably the easiest approach is to check the Windows version. You need Win32MajorVersion>=6 (i.e. Vista or 2008 server) in order to call DwmIsCompositionEnabled.
If you were binding yourself then you would call LoadLibrary with DWMApi.dll and if that succeeded you would then call GetProcAddress to bind. If that succeeded you are good. But, as I said, since you aren't handling the binding yourself then a version check is probably the simplest.
So the function would be:
function AeroEnabled: boolean;
var
enabled: bool;
begin
if Win32MajorVersion>=6 then begin
DwmIsCompositionEnabled(enabled);
Result := enabled;
end else begin
Result := False;
end;
end;
Note, I'm assuming that your library is doing late binding, i.e. explicit linking. If not then you'll need LoadLibrary/GetProcAddress, exactly as is done in #RRUZ's code to which you link.
Related
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!
I'm currently developing some simple auto-updater app. The most important feature is the possibility of self updating. That's why I plan to put most logic in external DLL. After my DLL grown a little I started to get problems with FreeLibrary call in main app. During dll debugging I've found function responsible for that bug:
function TpmDSServerUpdateDownloader.DownloadUpdates: Boolean;
var
LSQLConnection: TSQLConnection;
LSQLServerMethod: TSqlServerMethod;
LUpdatePackageLink: string;
begin
try
{$IFDEF DEBUG}
Sleep(10000);
{$ENDIF}
// Getting update package link
FUpdateServerIP := '127.0.0.1';
FUpdateServerPort := 8080;
LSQLConnection := TSQLConnection.Create(nil);
LSQLServerMethod:= TSQLServerMethod.Create(nil);
LSQLConnection.DriverName :='DataSnap';
LSQLConnection.LoginPrompt := False;
LSQLConnection.Params.Add('CommunicationProtocol=HTTP');
LSQLConnection.Params.Add('Hostname=' + FUpdateServerIP);
LSQLConnection.Params.Add('Port=' + IntToStr(FUpdateServerPort));
LSQLConnection.Params.Add('ConnectTimeout=' + IntToStr(10000));
LSQLConnection.Connected := True;
LSQLServerMethod.SQLConnection:= LSQLConnection;
LSQLServerMethod.ServerMethodName:= 'TServerMethods1.GetUpdatePackageLink';
LSQLServerMethod.Params[0].AsInteger := 1;
LSQLServerMethod.ExecuteMethod;
LUpdatePackageLink := LSQLServerMethod.Params[1].AsString;
// Downloading update package with LUpdatePackage link
finally
LSQLConnection.Connected := False;
LSQLServerMethod.Free;
FreeAndNil(LSQLConnection);
end;
end;
The problem appears when I'm using dbExpress components from that function. I'm wondering if freeing the TSQLConnection/TSQLServerMethod leaves some working dbExpress threads/objects like it was with SQLMonitor in IBObjects. Maybe you have some ideas how to solve that? I would be very grateful for help.
Greetings
Michal
This is a Delphi BUG.
However there is a solution:
following Microsoft closing thread cannot be called from DLL unload, so FreeLibrary freez on WaitForMultipleObject inifinite loop in the TThread.WaitFor proc of the TDBXScheduler which is created in the initialization section of the Data.DBXCommon and going to be auto close in the finialization section of that unit. This makes the error. So we must close that thread earlier.
The solution is you need to export new procedure and call it before FreeLibrary:
uses
Data.DBXCommon
.....
procedure FinishDLLWork; stdcall; export;
begin
TDBXScheduler.Instance.Free;
end;
and call it just before FreeLibrary;
TDBXScheduler on freeining TDBXScheduler.Instance will auto set it to Nil so this call is fine(check TDBXScheduler.Destroy;)
Unfortunately it cannot be called in the DLL_THREAD_DETACH or DLL_PROCESS_DETACH - it is too late.
This is probably similar / continuation on the previous question below:
Why Delphi XE3 gives "E2382 Cannot call constructors using instance variables"?
Now I'm trying Delphi XE4 with the same code (with 'constructor' changed to 'procedure' as per the solution of the above question).
Now I have also these things in a generics list, i.e. I have
TCoordRect = object
public
function Something: Boolean;
end;
and then a list of these in a function parameter, which I loop through and try to access the items directly:
function DoSomething(AList: TList<TCoordRect>): Boolean;
var
i: Integer;
begin
Result := False;
for i := 0 to AList.Count - 1 do
begin
Result := Result or AList[i].Something; // <-- Here comes the compiler error!
end;
end;
This gives the compiler error "E2036 Variable required". However, if I don't access it directly, i.e put instead a local variable and use that first, then it works:
function DoSomething(AList: TList<TCoordRect>): Boolean;
var
i: Integer;
ListItem: TCoordRect;
begin
Result := False;
for i := 0 to AList.Count - 1 do
begin
ListItem := AList[i];
Result := Result or ListItem.Something; // <-- Now this compiles!
end;
end;
And another "workaround" is to remove all these 'object' types and change them to 'class', but I'm curious as to why this does not work like it used to? Is it again just something with "the compiler moving towards mobile development" or is there some more specific reason, or is this even a bug? BTW I also reported this as a QC issue, so will see if something comes from there.
It's a compiler bug, and it's present in all earlier versions of the compiler. The fault is not limited to XE4. Submitting a QC report is the correct response.
I would not be surprised if Embarcadero never attempt to fix it. That's because you are using deprecated object. Switch to using record and the code compiles.
The issue you have uncovered in this question is unrelated to the SO question you refer to at the top of your question.
Incidentally, this really is a case of old meets new. Legacy Turbo Pascal objects, and modern day generic containers. You are mixing oil and water!
I'm writing an app I'd like to be backwardly compatible to some extent on XP, or at the very least windows vista.
EDIT FOR CLARITY: I need to be able to do what the first code snippet below does, but in XP. "Does anybody know the best approach to take under XP, given the functions aren't available in USER32.DLL.?"
My initial prototype code on windows 7 just called CreateProcess to start up displayswitch.exe, which is deployed with windows 7.
if you are not familiar with it, it's a handy little utility that is what gets invoked when you press the windows key and the letter P. you can read more about it here.
while this was adequate, i subsequently needed to sense the current state (eg internal vs external or extend vs clone), so i have now coded up a winapi solution that works well on windows 7 (and i presume 8). it involves making calls to SetDisplayConfig and QueryDisplayConfig in User32.DLL
The pertinent section of it is here (minus the many, many structures i had to hand craft in pascal code from the original klingon).
function getTopology : DISPLAYCONFIG_TOPOLOGY_ID ;
var NumPathArrayElements,
NumModeInfoArrayElements : UINT32;
var PathArrayElements_Size,
ModeInfoArrayElements_Size : UINT32;
error : Longint;
paths : PDISPLAYCONFIG_PATH_INFO_array;
info : PDISPLAYCONFIG_MODE_INFO_array;
begin
NumModeInfoArrayElements := 0;
Result := DISPLAYCONFIG_TOPOLOGY_EXTERNAL;
inc(result);
error := GetDisplayConfigBufferSizes(QDC_DATABASE_CURRENT,NumPathArrayElements,NumModeInfoArrayElements);
case error of
ERROR_SUCCESS :
begin
PathArrayElements_Size := sizeof(DISPLAYCONFIG_PATH_INFO) * NumPathArrayElements ;
ModeInfoArrayElements_Size := sizeof(DISPLAYCONFIG_MODE_INFO) * NumModeInfoArrayElements;
GetMem(paths,PathArrayElements_Size);
try
GetMem(info,ModeInfoArrayElements_Size );
try
error := QueryDisplayConfig(QDC_DATABASE_CURRENT,NumPathArrayElements, paths,NumModeInfoArrayElements, info,result);
case error of
ERROR_SUCCESS :;
else
Result := DISPLAYCONFIG_TOPOLOGY_EXTERNAL;
inc(result);
end;
finally
FreeMem(info,ModeInfoArrayElements_Size );
end;
finally
FreeMem(paths,PathArrayElements_Size);
end;
end;
end;
end;
function setTopology ( top : DISPLAYCONFIG_TOPOLOGY_ID) : boolean;
var flags : dword;
begin
result := false;
flags := DecodeDISPLAYCONFIG_TOPOLOGY_ID_SDC(top);
if flags <> 0 then
begin
result := SetDisplayConfig(0,nil,0,nil,SDC_APPLY or flags) = ERROR_SUCCESS;
end;
end;
Since these functions don't exist in XP (as far as I know), I am looking for a stable way of achieving a similar thing in XP. whilst i am coding in Delphi, it's not necessary that the solution be presented as such. i am quite happy to just look at how it's done, or read a description of the appropriate steps, and implement it myself.
(removed full listing as it was confusing the issue as it did not appear like a question)
I inherited a GUI implemented in Delphi RadStudio2007 targeted for Windows XP embedded. I am seeing a lot of code that looks like this:
procedure TStatusForm.Status_refresh;
begin
if DataModel.CommStatus = COMM_OK then begin
if CommStatusOKImage.Visible<>True then CommStatusOKImage.Visible:=True;
if CommStatusErrorImage.Visible<>False then CommStatusErrorImage.Visible:=False;
end else begin
if CommStatusOKImage.Visible<>False then CommStatusOKImage.Visible:=False;
if CommStatusErrorImage.Visible<>True then CommStatusErrorImage.Visible:=True;
end;
end
I did find this code sample on the Embarcadero site:
procedure TForm1.ShowPaletteButtonClick(Sender: TObject);
begin
if Form2.Visible = False then Form2.Visible := True;
Form2.BringToFront;
end;
That shows a check of Visible before changing it, but there is no explanation of what is served by checking it first.
I am trying to understand why the original developer felt that it was necessary to only set the Visible flag if it was to be changed, and did not choose to code it this way instead:
procedure TStatusForm.Status_refresh;
begin
CommStatusOKImage.Visible := DataModel.CommStatus = COMM_OK;
CommStatusErrorImage.Visible := not CommStatusOKImage.Visible;
end
Are there performance issues or cosmetic issues (such as screen flicker) that I need to be aware of?
As Remy Lebeau said, Visible setter already checks if new value differs.
For example, in XE, for TImage, assignment to Visible actually invokes inherited code:
procedure TControl.SetVisible(Value: Boolean);
begin
if FVisible <> Value then
begin
VisibleChanging;
FVisible := Value;
Perform(CM_VISIBLECHANGED, Ord(Value), 0);
RequestAlign;
end;
end;
So, there is no benefits of checking it. However, might there in your code are used some third-party or rare components - for them all may be different, though, I doubt it.
You can investigate it yourself, using "Find Declaration" context menu item in editor (or simply Ctrl+click), and/or stepping into VCL code with "Use debug dcus" compiler option turned on.
Like many properties, the Visible property setter checks if the new value is different than the current value before doing anything. There is no need to check the current property value manually.
Well, I doubt it will, but maybe there could be issues specifically for forms in recent Delphi versions. The Visible property is redeclared in TCustomForm to assure the execution of the OnCreate event prior to setting the visibility. It is technically not overriden since TControl.SetVisible is not virtual, but it has the same effect:
procedure TCustomForm.SetVisible(Value: Boolean);
begin
if fsCreating in FFormState then
if Value then
Include(FFormState, fsVisible) else
Exclude(FFormState, fsVisible)
else
begin
if Value and (Visible <> Value) then SetWindowToMonitor;
inherited Visible := Value;
end;
end;
This implementation in Delphi 7 still does not require checking the visibility manually, but check this yourself for more recent versions.
Also, I agree with Larry Lustig's comment because the code you provided does not testify of accepted syntax. It could have better been written as:
procedure TForm1.ShowPaletteButtonClick(Sender: TObject);
begin
if not Form2.Visible then Form2.Visible := True;
Form2.BringToFront;
end;