Inno Setup failing to import DLL - delphi

I'm not having any luck importing a Delphi DLL into Inno Setup (Unicode). The DLL has a simple procedure..
procedure Foo(); stdcall;
begin
end;
exports
Foo;
The DLL is included in the installer source, and added to the files list:
[Files]
Source: "MyDLL.dll"; Flags: dontcopy
Then, I extract this DLL in the initialization:
function InitializeSetup(): Boolean;
begin
ExtractTemporaryFile('MyDLL.dll');
end;
And finally, declared this procedure in the script:
function DoFoo(): Bool;
external 'Foo#MyDLL.dll stdcall';
However, when I run the setup, I get an error:
Cannot Import dll: <utf8>MyDLL.dll.
What am I doing wrong?

Since you haven't used delayed loading in your function import, Inno Setup loader failed to run because it didn't found your library. That's because checks whether function exports are available are performed before the InitializeSetup event is fired and so your library was not yet extracted from the archive.
In your case is adding delayload import option the right way. But you can omit manual extracting and tell the installer to extract the library for you if you add files: prefix before the library file name. This prefix is documented as:
During Setup, a special 'files:' prefix may also be used to instruct
Setup to automatically extract one or more DLLs from the [Files]
section before loading the first DLL.
The whole import in your case can be then shortened to:
[Files]
Source: "MyDLL.dll"; Flags: dontcopy
[Code]
procedure Foo;
external 'Foo#files:MyDLL.dll stdcall delayload';

I found the solution right after posting this question by using delayload on the import...
function DoFoo(): Bool;
external 'Foo#MyDLL.dll stdcall delayload';

Related

app crash on call .dll in delphi

I have a DLL it crated with Delphi xe10.2 and it contain a function
function calc(b : integer;a:integer) : Integer;
begin
Result := a+b;
end;
and i will call it on other program like this
function calc(b : integer;a:integer): Integer; stdcall; external 'my.dll';
i copied the DLL in System32 folder and application .exe folder
procedure TForm1.Button1Click(Sender: TObject);
begin
showmessage(inttostr(calc(2,3)));
end;
but when i run from delphi IDE nothing happened.it dont show any error and also dont show application main form...
how can i fix this ?!
System32 is the 64-bit system folder. Your application is a 32-bit application, and hence does not search for DLLs in System32. It will search in SysWOW64 instead, which is the 32-bit system folder. If you ran the program without debugging, you would see an error message telling you that the DLL could not be located.
Now, you should never modify the contents of system folders. Remove the DLL from System32 and instead place it in the same directory as your executable file.
The other problem, given the code shown, is that the exported DLL function uses the register calling convention, but you import it using the stdcall calling convention. You must ensure that the calling conventions used by DLL and EXE match each other.

Point to a DLL using a dynamic path

My problem is not exporting a function, but importing it. I know for sure that both the function and DLL both work because I have used a hard-coded path to point to the DLL.
This is what is currently working:
function RoamingAppDataPath: String; external 'C:\Users\Peter\AppData\Roaming\ss\Application\ss.dll';
However I need to point to the DLL with a dynamic value so what I tried to do is
Declare a global variable (DLLPath: String)
Assign DLLPath the value - RoamingAppDataPath+'\ss\Application\ss.dll'
Note: RoamingAppDataPath is a function that outputs the path to the roaming app data folder.
The code I am trying to run is:
function RoamingAppDataPath: String; external DLLPath;
When I compile the code, Delphi is telling me that it is expecting a constant expression:
E2026 Constant expression expected
What is the work around for this?
You have to bind at runtime and that means you need to use LoadLibrary and GetProcAddress:
var
lib: HMODULE;
RoamingAppDataPath: function: string;
lib := LoadLibrary(dllfilename);
if lib=0 then
RaiseLastOSError;
Pointer(RoamingAppDataPath) := GetProcAddress(lib, 'RoamingAppDataPath');
And then you can call it:
radp := RoamingAppDataPath;
Some comments:
I don't know why you write this function when it exists in standard system libraries.
Using string across DLL boundaries is liable to fail. You need to be using ShareMem and make sure that all code is built with the same Delphi version. Better to allocate the buffer in the calling code.
Even if you would be able to use a Variable, you would nowhere be able to set a value to DLLPATH, since already initalization would not be used if a static DLL can not be used.
You will have to use dynamic loadingif you want to define the path for the DLL.
procedure Test;external 'Notexists.DLL';
var
Form2: TForm2;
implementation
{$R *.dfm}
procedure TForm2.Button1Click(Sender: TObject);
begin
test;
end;
initialization
Showmessage('Hallo'); // will never be seen if test is used.

Work around with Delphi DLL

I have two applications in Delphi for which I don't have any code source:
I use an interface from application A to call an DLL file from application B. Example, I usually pass a service number, 200011, from interface A to call DLL file B for a value return. But, recently the application A have changed the variable. I have to add P00200011 to call DLL file B.
I have tried to create an DLL C#, but the DLL in B is created with the fastcall convention and I cannot change this DLL file.
What are others ways I can do it? I'm out of ideas.
You need to write a wrapper DLL. You build your DLL with the functions you want to intercept, and in your code you simply load and call the original DLL. Then you place your wrapper in the same directory of your application. All calls from the application will go to your wrapper DLL and from there to the original DLL.
Here is a simple example
supose you have this library (B.DLL)
library B;
function B_FUNCTION(value:integer): integer; export;
begin
result:=value+1;
end;
exports B_FUNCTION;
end.
And this program that uses it
program A;
{$apptype console}
function B_FUNCTION(value:integer): integer; external 'b.dll';
var i:integer;
begin
i:=B_FUNCTION(2010);
writeln(i);
end.
Compile both programs and run them. The result printed is 2011.
Now, code your wrapper DLL
library w;
uses windows;
function B_FUNCTION(value:integer): integer; export;
var
adll: Thandle;
afunc: function(v:integer):integer;
begin
adll:=LoadLibrary('TRUE_B.DLL');
afunc:= GetProcAddress(adll,'B_FUNCTION');
result:=afunc(value+1);
FreeLibrary(adll);
end;
exports B_FUNCTION;
end.
Build it, you'll have A.EXE, B.DLL and W.DLL. Replace them
REN B.DLL TRUE_B.DLL
REN W.DLL B.DLL
Execute A, now it will spit 2012.
It's not entirely obvious to me which parts are yours and what calls what, but you should be able to create your own intermediate DLL in Delphi with an interface that uses fastcall and which forwards the call to the real DLL using another calling convention.

Free Pascal can't find entry point for dll

I am complete new to pascal.
I want to call my function in .dll file in free pascal and I get following error when I run the project:
The procedure entry point GetProcAddress could not be located in the dynamic link library HNLib.dll.
here is the code:
Program Test;
function GetProcAddress : Integer; cdecl; external 'HNLib.dll';
function GetProcAddress : Single; cdecl; external 'HNLib.dll';
procedure GetProcAddress( X : Single); cdecl; external 'HNLib.dll';
procedure GetProcAddress; cdecl; external 'HNLib.dll';
begin
GetProcAddress( 5.5 );
readln;
end.
.pas file and dll are in one directory.
Please Help ME!
GetProcAddress is not what you seem to think it is; it's purpose is to locate named procedures or functions in a DLL and return the address of that function so it can be called from your code. You have to first use LoadLibrary to load the dynamic link library (DLL) into memory, and then pass a handle to that DLL as the first parameter of GetProcAddress and the name of the function whose address you want as the second parameter. If the function can be found in the DLL, it's address is returned, and you can use that address to call the function.
(In addition, GetProcAddress is pretty Windows-specific, and the majority of functions in the WinAPI are stdcall and not cdecl. Unless you have documentation saying that the functions are using the cdecl calling convention, you should probably use stdcall.)
You would also need at least the Windows unit in your uses clause, since that's where GetProcAddress and LoadLibrary are declared.
See the WinAPI documentation on LoadLibrary and GetProcAddress for more information.
For a beginning programmer, you'll probably find it easier to use static linking of the functions instead of dynamic (which you get with GetProcAddress). An example of static linking would be (untested !!!- just a quick code example, since I don't have 'HNLib.DLL' to link against):
// Your Dll import unit
unit MyDllProcs;
interface
function GetIntCalcResult(const IntVal: Integer);
implementation
function GetIntCalcResult(const IntVal: Integer); stdcall; external 'HNLib.dll';
end.
// Your own app's code
program Test;
interface
uses MyDllProcs;
implementation
function DoSomethingWithDll(const ValueToCalc: Integer): Integer;
begin
Result := GetIntCalcResult(ValueToCalc);
end;
begin
WriteLn('DoSomethingWithDll returned ', DoSomethingWithDll(10));
ReadLn;
end.
Note that when statically linking DLL functions like this, your DLL must be available when your app starts, and the function must be contained in that DLL; if not, your application won't load.
Also, note that you can't typically have multiple functions with the same name in the DLL, as there's no information available to use to figure which one to load when the load is being done. Each should have a separate, distinct name or the loading will probably fail.

INNO Setup: How to implement file update based on different versions of the application

I have an application written in Delphi that has several versions that contain binaries and database (MDB) with catalog data.
During the product life cycle fixes/enhancements are either in database file or in some binary files.
Version are preserved in Registry.
Users might have different versions of the program when new patch is available.
Now users have different versions how to implement following scenario in Inno Setup:
If user have version A prevent installation.
If user have version B copy db over and file1, file2, file3.
If user have version C just update file1.
What is the correct way to implement this in Inno setup?
I am not sure if it is the correct way to do it, but you can use the [code] section and the BeforeInstall Flags
like so
[Files]
Source: "MYPROG.EXE"; DestDir: "{app}"; BeforeInstall: MyBeforeInstall('{app}')
Source: "MYFILE.EXE"; DestDir: "{app}"; BeforeInstall: MyBeforeInstall('{app}')
Source: "MYDB.MDB"; DestDir: "{app}"; BeforeInstall: MyBeforeInstall('{app}')
[Code]
function MyBeforeInstall(InstallPath): Boolean;
begin
Result:= FALSE;
//Check if this file is ok to install
MsgBox(CurrentFileName , mbInformation, MB_OK);
end;
Then use CurrentFileName to determine if the file can be installed, I am not sure if it will just quit the installer if the result is false, or skip the individual file.
You can also use the [Types]/[Components] section to determine what files will be installed, but I don't know if there is a way to auto select that.
Inno will look at file version information by default. So if your patch just needs to only update a file when the version in the patch is newer, do nothing; Inno already behaves that way.
If, on the other hand, your patch needs to replace a file with the same version (or there is no version information in the file), use the replacesameversion flag. This causes Inno to compare the contents of the file, and replace it if it is different. See the help for Files for more information on this flag.
You can create functions for checking the version.
See this website for more details
(http://agiletracksoftware.com/blog.html?id=4)
[Code]
; Each data file contains a single value and can be loaded after extracted.
; The filename and DestDir from the [Files] section must match the names
; and locations used here
function GetAppMajorVersion(param: String): String;
var
AppVersion: String;
begin
ExtractTemporaryFile('major.dat');
LoadStringFromFile(ExpandConstant('{tmp}\major.dat'), AppVersion);
Result := AppVersion;
end;
function GetAppMinorVersion(param: String): String;
var
AppMinorVersion: String;
begin
ExtractTemporaryFile('minor.dat');
LoadStringFromFile(ExpandConstant('{tmp}\minor.dat'), AppMinorVersion);
Result := AppMinorVersion;
end;
function GetAppCurrentVersion(param: String): String;
var
BuildVersion: String;
begin
ExtractTemporaryFile('build.dat');
LoadStringFromFile(ExpandConstant('{tmp}\build.dat'), BuildVersion);
Result := BuildVersion;
end;
Code extract from AgileTrack Blog:
Using Inno Setup to Create a Versioned Installer

Resources