I'm trying to make my program change the layout scale at runtime. I developed this code based on an existing powershell script. But it compiles but when running, it returns an error.
Error:
The procedure entry point SystemParametersInfo could not be located in the dynamic link library.
var
Form1: TForm1;
function SystemParametersInfo(uiAction, uiParam: UINT; pvParam: Pointer; fWinIni: UINT): BOOL; stdcall; external 'user32.dll';
implementation
{$R *.dfm}
function SetScaling: Boolean;
begin
Result := SystemParametersInfo($000F, 125, nil, 1);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
SetScaling;
end;
As it is said in the mentioned error there is no function named SystemParametersInfo in user32.dll.
There are two similarly named functions SystemParametersInfoA which is to be used by applications that work with ANSI strings and SystemParametersInfoW which is to be used by applications supporting wide strings like modern Delphi.
NOTE: SystemParametersInfo can only be found on Windows CE 5.0 and it resides in Coredll.lib library.
The WinAPI.Windows unit defines the SystemParametersInfo function. No reason to define it locally.
Your call to SystemParametersInfo with a first parameter of $000F is a request to set the screen saver timeout to 125 seconds ($000F=SPI_SETSCREENSAVETIMEOUT).
Setting DPI Scaling is a bit more complex than what you are attempting in the code that you posted. You can read the "how-to" details here.
Related
I am modulating my application to work with separate modules (plugin).
I have already successfully made my EXE application read and load the plugins, including the forms.
Now I need to do the inverse, export functions from the executable to DLL.
Example:
Inside my executable, it has a TMemo component. I want to create a function like this
function GetMemo(): widestring;
In my idea, whoever wrote the DLL plugin, when calling the function GetMemo(), would already take the contents of the TMemo in DLL.
It is possible?
The simplest way to handle this is to define a record of function pointers, and then have the EXE pass an instance of that record to each plugin while initializing it. The EXE can then implement the functions as needed and pass them to the plugins, without actually exporting them from its PE exports table like a DLL would.
For example:
type
PPluginExeFunctions = ^PluginExeFunctions;
PluginExeFunctions = record
GetMemo: function: WideString; stdcall;
...
end;
function MyGetMemoFunc: WideString; stdcall;
begin
Result := Form1.Memo1.Text;
end;
...
var
ExeFuncs: PluginExeFunctions;
hPlugin: THandle;
InitFunc: procedure(ExeFuncs: PPluginExeFunctions); stdcall;
begin
ExeFuncs.GetMemo := #MyGetMemoFunc;
...
hPlugin := LoadLibrary('plugin.dll');
#InitFunc := GetProcAddress(hPlugin, 'InitializePlugin');
InitFunc(#ExeFuncs);
...
end;
var
ExeFuncs: PluginExeFunctions;
procedure InitializePlugin(pExeFuncs: PPluginExeFunctions); stdcall;
begin
ExeFuncs := pExeFuncs^;
end;
procedure DoSomething;
var
S: WideString;
begin
S := ExeFuncs.GetMemo();
...
end;
unit uDoingExport;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
type
TForm1 = class(TForm)
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
procedure testproc; stdcall;
implementation
{$R *.dfm}
procedure testproc;
begin
ShowMessage('testproc');
End;
exports
testproc;
end.
I simply added the method I want to publish from within my EXE in the unit's Interface, and on the Implementation I added exports (method name). I am using stdcall not cdecl.
In my child, I can loadlibrary the exe file... or you can go a little crazy like Apache does, and in the previous code, add a loadlibrary, which loads a DLL, which intern can loadlibrary the caller.
My point was to show, your EXE is simply like a DLL (just a different binary header) and vise versa. Just slap EXPORTS. Proof it works, I ran tdump against the EXE:
Exports from ProjDoingExport.exe
1 exported name(s), 1 export addresse(s). Ordinal base is 1.
Sorted by Name:
RVA Ord. Hint Name
-------- ---- ---- ----
0005294C 1 0000 testproc
I know, a late answer, but, a great question!
I'm creating a new component in Delphi, which instantiates a DLL
Unit UMyComponent
interface
type
TMyComponent = class(TComponent)
...
procedure MyDllCall;
end;
procedure Register;
implementation
function MyDll: Longint; stdcall; external 'MyDllName.dll' name 'MyFunction'
procedure TMyComponent.MyDllCall;
var
res: LongInt;
begin
res:= MyDll;
end;
...
procedure Register;
begin
RegisterComponents('My Tab', [TMyComponent]);
end;
end.
I have 2 questions:
When I install the component on the IDE it searches for the physical DLL and gives an error if it's not found in path. I'd like the component to look for it when it's going to be effectively used at runtime.
Is it possible to have the dll library file name to be set at runtime? i.e: 'MyDllName.dll' could change to '10029.dll' or 'ajjdwawd.dll'
Note that I put DLL declaration in the implementation in order not to expose the function call to callers.
Thanks for answering.
Your current code uses what is known as load-time linking. The dependency must be resolved when the module is loaded, otherwise it will fail to load. You need to use the alternative method, run-time linking.
In Delphi there are two ways to do that:
Use the Win32 directly by calling LoadLibrary, GetProcAddress and FreeLibrary.
Get the Delphi RTL to do that for you using the delayed keyword.
Both approaches are covered in more detail in the documentation:
http://docwiki.embarcadero.com/RADStudio/en/Libraries_and_Packages
http://docwiki.embarcadero.com/CodeExamples/en/DelayedLoading_(Delphi)
I need your help, please. I'm trying to call function from DLL written in Delphi 10 Seattle from Inno Setup (ANSI). But I do not understand what is the problem. If I make application in Delphi and call this function from DLL, it works perfectly! See code listing:
Delphi DLL:
function Process(Pb: TProgressBar): Integer; stdcall;
var
I: integer;
begin
for I := 0 to 1000 do
begin
Pb.Position := I;
Pb.Update;
Sleep(10);
end;
end;
Exports
Process;
Inno Setup (ANSI):
function Count(Progr: TNewProgressBar): integer; external 'Process#files:CallC.dll stdcall delayload';
procedure NewButton1Click(Sender: TObject);
begin
Count(NewProgressBar1);
end;
After call I get Access Violation. But, comment in dpr file i read, ShareMem write first line, but zero effect.
Show me how to correctly update progress bar in Inno Setup from Delphi DLL, please.
You cannot call object methods this way. You may be lucky to get this working, if you use exactly the same version of Delphi as the one Inno Setup is built with, as your tests with Delphi application shows. But it is still wrong and unreliable, do not do it. As you use a different Delphi version, the layout of the progress bar class in memory is different, hence the "Access violation".
For this particular task, you can easily do with just a handle to the progress bar:
function Process(Handle: THandle): Integer;
var
I: Integer;
begin
SendMessage(Handle, PBM_SETRANGE, 0, 1000 shl 16);
for I := 0 to 1000 do
begin
SendMessage(Handle, PBM_SETPOS, I, 0);
UpdateWindow(Handle);
Sleep(10);
end;
end;
In Inno Setup, call the function like:
function Count(Handle: THandle): integer;
external 'Process#files:CallC.dll stdcall delayload';
procedure NewButton1Click(Sender: TObject);
begin
Count(NewProgressBar1.Handle);
end;
For more advanced tasks, you need to use a callback.
See
Using callback to display filenames from external decompression dll (Inno Setup)
Call C# DLL from Inno Setup with callback
I posted this online: Show form from DLL in TScrollBox
What i am trying to do is call and show a form in a Delphi TScrollBox.
Not as Show or ShowModal
Example but not with any DLL:
Form1.Parent:= ScrollBox;
Form1.Show;
How do i use this example from a DLL with a form inside
Can anyone provide an example?
Regards,
You cannot pass a Delphi object between a DLL and a host executable. That's because objects can only be operated on in the module in which they are created. Now, if you were using runtime packages, you'd be able to escape that limitation.
You could export a function from your DLL that created and showed the form. The function might look like this:
function ShowMyForm(ParentWindow: HWND): Pointer; stdcall;
Note that you cannot pass the parent as a Delphi object for exactly the same reasons as I describe above.
You also cannot specify that the parent of the form be a control in your executable. So you have to pass the parent's window handle.
The implementation would be like so:
function ShowMyForm(ParentWindow: HWND): Pointer; stdcall;
var
Form: TMyForm;
begin
Form := TMyForm.CreateParented(ParentWindow);
Form.Show;
Result := Pointer(Form);
end;
You would call it like this:
Form := ShowMyForm(ScrollBox.Handle);
You'd also need to supply a function to destroy the form when you are done:
procedure DestroyMyForm(Form: Pointer); stdcall;
begin
TMyForm(Form).Free;
end;
And you need to watch out for window re-creation. If the host window is re-created then you need to manually re-create the child form.
In short, what you are attempting is rather brittle. If I were you I would look for a different approach.
problem solved and here is the code:
//This is the DLL
library Project1dll;
uses
SysUtils,
Windows,
Classes,
DllForm in 'DllForm.pas' {frmDllForm}; // this is the other form
procedure Create_Form(ph: HWND);
begin
frmDllForm:= TfrmDllForm.CreateParented(Ph);
frmDllForm.Show;
end;
Exports
Create_Form;
begin
end.
//---------------------END--------------------------------------
//This is the project
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
ScrollBox: TScrollBox;
procedure Button1Click(Sender: TObject);
private
end;
procedure Create_Form(ph: HWND) ; external 'Project1dll.dll' name 'Create_Form';
var
Form1: TForm1;
implementation
{$R *.DFM}
function ScrollBoxDll(ph: HWND): Pointer; stdcall;
begin
Create_Form(ph);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
ScrollBoxDll(ScrollBox.Handle);
end;
end.
firstly the idea of and constructing the code was my idea, it was intended to be used as a simple way of showing forms stored in a dll file.
The main idea was to put what ever you wanted in the dll, call and show it in a TscrollBox, this can be actually a fully working database or some other path of a program that you would not really want to pass parameters to and from after it was started or closed.
I posted the question online and many did not really understand what I was trying to explain or wanted to do, they seem to think I wanted to created a from in a dll using a scrollbox, but my form or forms were already created and saved in the dll file, the scroll box was external to the dll in my main project.
All I wanted to do was call the forms and show in the scrolbox as parent to it.
I am not claiming any path of this code because many presented their ideas and I thank them all.
The code presented was already constructed long before I posted the question online but did not work as I intended because the form was only showing outside the scrollbox.
I then posted the entire project online with Board4All when a friend pointed out that I should alter a line of code.
He only has a nickname and said he was not sure if it would work depending on what version of delphi I used.
I then adjusted the line of code and it work so he the one who deserves all credits, I then decided to post the code so others would be able to use the code in their projects.
DLL registration with regsvr32.exe freezes when unit HtmlHelpViewer is used in DLL sources in Delphi XE or Delphi XE2 Update 3. Just add the unit to interface uses list. The main project (that uses DLL) freezes on exit too.
How to fix the issue?
Thanks for the help!
STEPS TO REPRODUCE THE ISSUE AND ISSUE IN SUGGESTED FIX:
1). Please create the following DLL:
library Test;
uses
ComServ,
HtmlHelpFixer,
HtmlHelpViewer;
exports
DllGetClassObject,
DllCanUnloadNow,
DllRegisterServer,
DllUnregisterServer;
begin
end.
2). Also create the following BPL linked to this DLL (by -LUTestBpl dcc32 parameter for example):
package TestBpl;
requires
Vcl;
end.
3). Then just execute: regsvr32.exe /s Test.dll. OS Windows 7 32-bit.
Update
According to the latest comments on the QC report submitted by Altaveron, this problem will be resolved in the next Delphi update, update 4. And indeed, Altaveron now confirms that update 4 does resolve the issue.
This is a known problem with the MS HTML help control, hhctrl.ocx. The best description of it that I am aware of is at the HelpWare FAR HTML FAQ. There are many QC reports describing the issue: 48983, 67463, 78998, 89616.
According to the latest QC report, this is fixed in XE2 but you report otherwise and I'd be inclined to believe you. Especially as a comparison of the source for the HtmlHelpViewer unit from XE and XE2 reveals no changes that appear related to this issue.
It's quite hard to work around the issue since the code that needs to be modified is buried deep inside the HtmlHelpViewer unit. I've had to resort to patching the HtmlHelp API call. Like this:
unit HtmlHelpFixer;
interface
implementation
uses
Windows;
function HtmlHelp(hWndCaller: HWND; pszFile: PWideChar; uCommand: UINT; dwData: DWORD): HWND;
begin
if uCommand=HH_CLOSE_ALL then begin
//don't call HtmlHelpW because it can result in a hang due to a bug in hhctrl.ocx
Result := 0;
end else begin
Result := HtmlHelpW(hWndCaller, pszFile, uCommand, dwData);
end;
end;
procedure PatchCode(Address: Pointer; const NewCode; Size: Integer);
var
OldProtect: DWORD;
begin
if VirtualProtect(Address, Size, PAGE_EXECUTE_READWRITE, OldProtect) then begin
Move(NewCode, Address^, Size);
FlushInstructionCache(GetCurrentProcess, Address, Size);
VirtualProtect(Address, Size, OldProtect, #OldProtect);
end;
end;
type
PInstruction = ^TInstruction;
TInstruction = packed record
Opcode: Byte;
Offset: Integer;
end;
procedure RedirectProcedure(OldAddress, NewAddress: Pointer);
var
NewCode: TInstruction;
begin
NewCode.Opcode := $E9;//jump relative
NewCode.Offset := NativeInt(NewAddress)-NativeInt(OldAddress)-SizeOf(NewCode);
PatchCode(OldAddress, NewCode, SizeOf(NewCode));
end;
procedure RedirectHtmlHelp;
var
HtmlHelp: function(hWndCaller: HWND; pszFile: PWideChar; uCommand: UINT; dwData: DWORD_PTR): HWND;
begin
HtmlHelp := Windows.HtmlHelp;
RedirectProcedure(#HtmlHelp, #HtmlHelpFixer.HtmlHelp);
end;
initialization
RedirectHtmlHelp;
end.
Include this unit early in your .dpr uses list, before any unit that does anything with HTML help.
The version of the code that I use does a little more and takes steps to ensure that any open help windows are closed when the DLL unloads. This no longer happens because we have stopped sending HH_CLOSE_ALL.
You will want to make sure that any help windows are shut down then keep track of the window handles returned by HtmlHelp calls, which you can now intercept. Then at shutdown send a WM_CLOSE message to those windows which replaces the missing HH_CLOSE_ALL call to HtmlHelp.
However, I believe that the code above should get you over your immediate hurdle with regsvr32 which won't be showing help windows.
Feel free to do some experimentation! At the very least, the code above gives you entry points with which you can modify the behaviour of the HtmlHelpViewer unit.
Embarcadero have fixed this issue on Delphi XE2 Update 4. But now context help doesn't work on IDE while you're using BPL with HtmlHelpViewer unit on uses clause.