Archive: Finding DLLs on the dev machine


Finding DLLs on the dev machine
Ok, I have a program that uses a bunch of DLLs on my dev machine. I want to package all those DLLs up along with my app into the installer. I know the names of all the DLLs, I just don't know where they're all located, I just know they're all on my %PATH% somewhere. The easiest thing for me would be if there were a "SearchPath" function that got invoked at compile time. (That's all I need since I know all the DLLs I'm using are on my %PATH% somewhere. I guess it would be a different situation if some of them were only listed in the registry.)

Anyway, I looked at the Locate plug-in but it doesn't look like it's really made for this sort of search.

The SearchPath command would be great, but apparently it only gets invoked at install time, not compile time.

Exec and ShellExec commands also only get called on the target machine.

Am I missing something simple?

This was a problem I had with InnoSetup too. I eventually just ended up writing my own InnoSetup pre-processor in perl to find the things on my path. I can always do that again with NSIS, but I'd rather just be able to use work with NSIS rather than having to go around it.


I find it odd that this sort of functionality isn't more in demand. If I test my program on machine A and it works, I'd like to build the installer on machine A, and I'd like to make sure I package up the same DLLs that I tested with, and not a different version that also just happens to be installed on my machine somewhere.


I think this is no good idea. There are several issues with searching on compile time:

- You can easily mess up some installation by using
files from c:\windows\system32.
These versions are sometimes newer than they should be
for older Windows versions. If you e.g. use Win XP and
create an installer using the system32 files you will
mess up Win 9x installations. The newer files seem to
be non-generic.

- It could happen that you get the wrong file (if someone/
some software creates a new file with that name in your
PATH).

- You should know about each dll whether you are allowed
(copyright) to include it or whether this is necessary
(may be included by Windows / Office / Software xyz).

I suggest you make a sub directory with those files you really you really need, look carefully for versions and copyright.

This way you can be sure what really gets installed.

I think you can find the policy to distribute those DLLs on your development system's installation cd etc. At least Microsoft generally makes statements about this.

Here some example information for MS Visual C++
http://msdn.microsoft.com/library/en...distribvc6.asp

Of course you should use the dll versions from the latest service pack.


Wow, fast reply!

stb
I think this is no good idea. There are several issues with searching on compile time:

- You can easily mess up some installation by using
files from c:\windows\system32.
These versions are sometimes newer than they should be
for older Windows versions. If you e.g. use Win XP and
create an installer using the system32 files you will
mess up Win 9x installations. The newer files seem to
be non-generic.
I'm not just trying to indiscriminately include every single dll my program is linked against. Like I said, I have the list of DLL names I want to include with my app. The only thing I'm pulling out of C:\WINDOWS currently is the redistributable Visual C++ runtime libraries, and I don't see how that's going to mess up an installation.

stb
- It could happen that you get the wrong file (if someone/
some software creates a new file with that name in your
PATH).
Are you talking about at installer compile-time for me, or run-time for the user? If the former, my whole point is that I just finished tested my app on a particular machine. It passed testing, so whatever DLLs I was just using are the ones I want to include. If someone has recently created a new file on my path with the same name, and my app passed testing successfully, then that's the DLL I want to ship.

If you meant the latter, something happening on the user's machine, then that's not a problem either. As I said, I'm installing all the DLLs to the app's exe directory so Windows will link to them regardless of whatever else is installed on the system. Probably I should use a different approach for MSVCR71.dll, since users might have that installed already, but the others are very unlikely to be present prior to installing my app.

stb
- You should know about each dll whether you are allowed
(copyright) to include it or whether this is necessary
(may be included by Windows / Office / Software xyz).
That's got nothing to do with my problem. As I said I have the list of DLLs I want to include. I know what each one of them is for and why I need to distribute it with my app. And I know what the license terms are, too (most are LGPL libs.)

stb
I suggest you make a sub directory with those files you really you really need, look carefully for versions and copyright.

This way you can be sure what really gets installed.
Yeh, that's annoying though, because most of these DLLs are are external LGPL open source projects. I compile them from source and they get updated from time to time, and I use the resulting DLLs in various projects. I don't want to have to copy DLLs all over the place every time I recompile OpenSourceProjectXYZ. As it is, they're installed to my path and so all projects that use them automatically pick up the changes. You could argue that's a bad idea. If it were more than just me coding, I would probably agree.

stb
I think you can find the policy to distribute those DLLs on your development system's installation cd etc. At least Microsoft generally makes statements about this.

Here some example information for MS Visual C++
http://msdn.microsoft.com/library/en...distribvc6.asp

Of course you should use the dll versions from the latest service pack. [/B]
Licensing issues are not the problem.
Thanks for taking the time to reply, though.

The only thing I'm pulling out of C:\WINDOWS currently is the redistributable Visual C++ runtime libraries, and I don't see how that's going to mess up an installation.
This is bad behaviour. If Microsoft includes some special DLL for a specific operating system (think about XP/Vista) these DLLs should not be replaced. Microsoft will use a higher version number so if you compare your version (generic, older) to the installed one you (your installer) will decide not to update (would be a downgrade).

The other way: If you have installed XP/Vista on your development machine and create an installer using the system32 files, your installer will contain those XP/Vista-specific releases which are newer than the generic ones. Your installer will install them on older Windows (9x) versions because they have a higher version number. This may break those older installations.

You should always install the latest _generic_ versions of the DLLs. In case of Microsoft Visual Studio 6 you should download the latest service pack and use the versions supplied in there.

Are you talking about at installer compile-time for me, or run-time for the user?
We're both talking about compile-time.

If someone has recently created a new file on my path with the same name, and my app passed testing successfully, then that's the DLL I want to ship.
This is bad, see above. If you ship just the files that make the application work on YOUR machine the software may only work on machines like YOURS.

Ok, if you install files only to your app's exe directory, you might not break the target machine (as long as your app's exe directory is not in the PATH and is no current dir when executing other applications...)

But the installed files might have dependencies which could be OS-specific (e.g. XP and higher) etc.

Probably I should use a different approach for MSVCR71.dll, since users might have that installed already, but the others are very unlikely to be present prior to installing my app.
MSVCR71.dll will not be present on all Windows systems. You should include it.

Yeh, that's annoying though, because most of these DLLs are are external LGPL open source projects. I compile them from source and they get updated from time to time, and I use the resulting DLLs in various projects. I don't want to have to copy DLLs all over the place every time I recompile OpenSourceProjectXYZ.
I understand that. You should create a common directory "DLLs for distribution" which can be used by all installers.

As it is, they're installed to my path and so all projects that use them automatically pick up the changes.
Do you mean "when running the application"? This is run-time behaviour (by design). If you mean compile time, we're back to your feature request: I think this would be a "dangerous" feature.

If it were more than just me coding, I would probably agree.
Hey, you would curse if some other developer installs incompatible DLLs to your system. Think about your users.

Anyway, it's a good idea to not install those DLLs shared (i.e. in system32), you're not joining the DLL hell this way. And in case of problems your users can easily zero out your app's exe dir...

CompileTime.nsi

Name "CompileTime"
OutFile "CompileTime.exe"

!include "FileFunc.nsh"
!insertmacro GetParameters

Function .onInit
${GetParameters} $0
StrCmp $0 '' end

FileOpen $1 $0 w
IfErrors end

SearchPath $2 riched20.dll
IfErrors +3
FileWrite $1 'SetOutPath "$EXEDIR"$\r$\n'
FileWrite $1 'File "$2"$\r$\n'

SearchPath $2 myfile2.dll
IfErrors +2
FileWrite $1 'File "$2"$\r$\n'

SearchPath $2 myfile3.dll
IfErrors +2
FileWrite $1 'File "$2"$\r$\n'

FileClose $1

end:
Quit
FunctionEnd

Section
SectionEnd

RunTime.nsi
Name "RunTime"
OutFile "RunTime.exe"

Section
!execute 'CompileTime.exe files.lst'
!include 'files.lst'
SectionEnd

You can use System plug-in for LoadLibrary(), GetModuleFileName(), FreeLibrary() calls chain. Search order includes PATH.
Edited: oups.. API/NSIS SearchPath looks better :)


Originally posted by Instructor
CompileTime.nsi
Name "CompileTime"
OutFile "CompileTime.exe"
...

...
RunTime.nsi
Name "RunTime"
OutFile "RunTime.exe"

Section
!execute 'CompileTime.exe files.lst'
!include 'files.lst'
SectionEnd
Heh heh. That's cute. So you make one "installer" whose job is to be run when making the actual installer.

Anyway, I didn't realize there was a compile time !execute directive, so that means I can just implement the DLL finder in whatever language I want, be it your NSIS script or python or whatever. Great. Thanks Instructor!

And @stb, thanks for your efforts to get me to mend my wicked ways. I'll consider changing the way I do things.

--bb

@ Instructor
a couple of issues on the above post:
!execute or could be !system should not included in section as they are "Compiler Utility Commands".
The line "...FileWrite $1 'SetOutPath "$EXEDIR"..." should not be used.
RunTime:

Name "RunTime"
OutFile "RunTime.exe"
InstallDir '$PROGRAMFILES\MyTest'

!system 'CompileTime.exe files.nsh'
; !execute 'CompileTime.exe files.nsh'

Section

SetOutPath '$INSTDIR'
!include 'files.nsh'

SectionEnd

CompileTime:
Name "CompileTime"
OutFile "CompileTime.exe"

!include "FileFunc.nsh"
!insertmacro GetParameters

Function .onInit
${GetParameters} $0
StrCmp $0 '' end

FileOpen $1 $0 w
IfErrors end

SearchPath $2 'Notepad.exe'
IfErrors +2
; FileWrite $1 'SetOutPath "$EXEDIR"$\r$\n'
FileWrite $1 'File "$2"$\r$\n'

SearchPath $2 'regedit.exe'
IfErrors +2
FileWrite $1 'File "$2"$\r$\n'

SearchPath $2 'winhlp32.exe'
IfErrors +2
FileWrite $1 'File "$2"$\r$\n'

FileClose $1

end:
Quit
FunctionEnd

Section
SectionEnd

They worked just fine for me. Thus, I think "Locate" could be more reliable and flexible instead of "SearchPath".

!execute or could be !system should not included in section as they are "Compiler Utility Commands".
Why not?
The line "...FileWrite $1 'SetOutPath "$EXEDIR"..." should not be used.
How you set different OutPath for the files included on compile time?
Thus, I think "Locate" could be more reliable and flexible instead of "SearchPath".
The easiest thing for me would be if there were a "SearchPath" function that got invoked at compile time. (That's all I need since I know all the DLLs I'm using are on my %PATH% somewhere...)

@ Instructor:
Sorry if I miss something here but when I use !execute in section the compiler returns this error:

"Processing script file: "D:\NSISProjects\Compile Time\run_time.nsi"
Name: "RunTime"
OutFile: "RunTime.exe"
InstallDir: "$PROGRAMFILES\MyTest"
Section: ""
SetOutPath: "$INSTDIR"
!execute: "CompileTime.exe files.nsh"
!include: could not find: "files.nsh"
Error in script "D:\NSISProjects\Compile Time\run_time.nsi" on line 12 -- aborting creation process"

And when I use "...FileWrite $1 'SetOutPath "$EXEDIR"..." I have this:
(SetOutPath "D:\NSISProjects\Compile Time"
File "C:\WINDOWS\system32\winhlp32.exe"
File "C:\WINDOWS\system32\xvidcore.dll"
File "C:\WINDOWS\vmmreg32.dll")

Otherwise I have:
(File "C:\WINDOWS\system32\winhlp32.exe"
File "C:\WINDOWS\system32\xvidcore.dll"
File "C:\WINDOWS\vmmreg32.dll")

I can't see why I should point outputpath to where I am compiling. This needed only if I want to copy those files in my compiling dir. If I just want to include them in my installation I don't need that line.


I think you probably need SetOutPath $EXEDIR before FileOpen in the CompileTime.nsi script to ensure that it writes the output file in the same directory.

-Stu


Special for Red Wine :)

CompileTime.nsi

Name "CompileTime"
OutFile "CompileTime.exe"

!include "FileFunc.nsh"
!insertmacro GetParameters

Function .onInit
${GetParameters} $0
StrCmp $0 '' end

FileOpen $1 $0 w
IfErrors end

FileWrite $1 'SetOutPath "$$INSTDIR"$\r$\n'

SearchPath $2 riched20.dll
IfErrors +2
FileWrite $1 'File "$2"$\r$\n'

SearchPath $2 twain.dll
IfErrors +2
FileWrite $1 'File "$2"$\r$\n'

SearchPath $2 diskcopy.dll
IfErrors +3
FileWrite $1 'SetOutPath "$$INSTDIR\folder1"$\r$\n'
FileWrite $1 'File "$2"$\r$\n'

SearchPath $2 wtsapi32.dll
IfErrors +3
FileWrite $1 'SetOutPath "$$INSTDIR\folder2"$\r$\n'
FileWrite $1 'File "$2"$\r$\n'

FileClose $1

end:
Quit
FunctionEnd

Section
SectionEnd

RunTime.nsi
Name "RunTime"
OutFile "RunTime.exe"

InstallDir '$PROGRAMFILES\MyTest'
Page directory
Page instfiles

Section
!execute 'CompileTime.exe files.lst'
!include 'files.lst'
SectionEnd

I got things working to my liking using an external python script I whipped up in a jiff. The script generates both a list of files to install and a list of files to uninstall.


!system "c:\python24\python finddlls.py dab_dlls.nsi dab_undlls.nsi" = 0


The nice thing about using !system instead of !execute is that you can check the return code (the "= 0" part on the end). And if it's not what's expected the compile will abort.

I'm kind of surprised there's not an easier way to handle uninstallation with NSIS. Automatic removal of everything installed is default behavior in InnoSetup and it makes a lot of sense to me. The installer knows where its putting files, why can't it just remove them too? If I were going to be using NSIS a lot for a lot of different projects I would be very tempted to write an expanded version of the above Python script which takes a list of files and spits out one .nsi with all the File commands and one with all the Delete commands.

NSIS is very flexible, I like the possibility to do things in uninstall unrelated to install. Having only a single file list is possible using macros and/or a file list stored in INSTDIR or registry (which could be read and used by uninstall). Some standard way (included with NSIS) would be fine anyway (especially doing the opposite of commands like RegDll and File in reverse order cannot be solved by a simple macro alone).


@ Instructor:
Your last post above works just fine for me. Congrats!
Though it's an excellent job, it is quite different from your previous job. That one never worked for me. Perhaps I missed something. Thanks, regards!