Topic 4.2

RegDLL & RegSrv32

RegDLL & RegSrv32.exe—DLL Handling

Dynamic Libraries

In short, a dllAn acronym for Dynamic Link Library is a collection of small executable code, which can be called upon when needed by a running program. The DLL lets the executable communicate with a specific device such as a printer or may contain code to do any number of particular functions. The main advantage that I'm aware of is when and/or if the DLL is called, then it is loaded which means less resource intensive load on memory—better known as ramAn acronym for Random Access Memory. These files can be in differing file extensions, i.e.  .ocx or even .cpl. Here's a bit more detail on the more common extensions:

  • ActiveX Control Files (.ocx)–Can perform a variety of reusable functions and are comparable to Java applets in their functionality. First introduced by Microsoft in 1996 as part of their Component Object Model (COM) and Object Linking and Embedding (OLE) technologies.
  • Control Panel Files (.cpl)–There is a control panel file for every item located or listed in the Windows Control Panel. Each is a specialized file that may reference zero or more additional DLLs.
  • Device Driver Files (.drv)–There are one or many device driver files for each hardware component installed on a Windows PC. One of the more prevalent are those installed for printers or network adapters; i.e. when you plug in a flash drive in a PC for the first time, that system takes a moment to install the required drivers.
With the PA.c Launcher that I'm currently developing I have eliminated the need to use the custom.nsh file to register DLLs. You can visit the GitHub project page here which will take you to the features section of the read me file for more information.
Registering Files

If you need to register/unregister DLL files you can do so a couple of ways. The first of which we'll be discussing is how to do so using the Regsvr32 command line. Windows PCs with Internet Explorer 3.0 or later have Regsvr32.exe. So trust me when I say there's a good chance your PC came stock with this. If you are running on a x64 computer, there are two variants you can consider. They can be found in either $WINDIR\system32 or in $WINDIR\SysWow32.

The parameters you can use with RegSrv32 are /u /s /i /n. The /u command switch will unregister the file. The /i switch can be used with /u to call for DLL uninstallation. The /n parameter will not call DllRegisterServer; it's used with /i which is the install switch. If you use /s, which means silent, no message boxes will be displayed on Windows XP or later.

When using Regsvr32.exe from the command line you'll get message boxes after calling it. The DLLSelfRegister function will be invoked unless using the aforementioned switch of course; if successful an alert box will be shown denoting it's success—as the same for failure which throws an error message.

It's been my experience that the x64 RegSvr32.exe registers x86 DLL's properly on Windows Vista?Won't work on Windows XP. You can find out more by visiting this article. and above so I use it when installing on x64 systems even when registering a x86 file for PAFs that can't run on WinXP and older anyway.

That's just my opinion but you may use what works for you.

CLI Example
;= Regardless of architecture we're using just the following
!define REGSVR `$SYSDIR\regsvr32.exe` ;= define where RegSrv32 is
!define DLL `$AppDirectory\App\MyLegalProgram\myLegit.dll` ;= define the file to register
;=#
;= Command line usage is the same for both variants of RegSrv32 as follows
;= regsvr32 [/u] [/s] [/n] [/i[:cmdline]] DLL
;=#
;= So in our custom.nsh file it would be similar too the following
Exec `"${REGSVR}" /s "${DLL}"`	;= notice the lack of the /i switch
;= That's because we aren't actually installing it
;= Additionally you may also use the following
ExecWait `"${REGSVR}" /s "${DLL}"` $0 ;= The $0 will contain the error code if any
;= The above will wait for exe to quit it's process before continuing
GUIDs/CLSIDs

A Class ID (CLSID) is a 128 bit (large) number that represents a unique ID for a program or application component. Think of a CLSID as a social security number but for an application or its component. CLSIDs are used by Windows to identify software components without having to know their name. They can also be used by applications to identify a computer, file or other similar information. These IDs are generated by using the current time, network adapter address (if present), and other items on a computer so that no two numbers will ever be the same.

The registry entries for any registered DLL are usually located in the HKEY_CLASSES_ROOT\CLSID section of the registry. Each entry under CLSID is a subtree whose name is a bracketed GUID?GUIDs are used to identify components and interfaces in the form of class IDs (CLSID) and interface IDs (IID). You can find out more by visiting this article. (i.e. HKCR\CLSID\{00000000-0000-0000-0000-000000000000}). If a DLL has been properly registered, it will have a subtree underneath this called InProcServer32. The default value of the InProcServer32 subtree is the full path to the DLL which corresponds to that GUID. This value may be one of two types; REG_SZ or REG_EXPAND_SZ. If its REG_EXPAND_SZ, the path will typically contain an environment variable such as %SystemRoot% in the path name.

A developer has full control over what happens when you call RegSvr32. Usually they will register COM controls in HKCR, but they don't have to. They can do whatever they feel like doing and there's no absolute way for you to tell whether they've done it or not.
Usage
RegDLLView
To find out all the information that you will need from a DLL file for registering, I use a little program called RegDLLView by NirSoft. This makes finding the GUID, ProgID, and CLSID ridiculously easy.

If you're PAFing with my build of PAL then all you will need is the ProgID and the file path of the DLL(s) that need registering. If you haven't already done so, grab both variants of RegDLLView (x32 & x64—if you do not have a 64-bit machine do not worry about it but you're obviously limited to only building 32-bit compliant PAFs). Once you've got RegDLLView, go on and launch the 64-bit version (don't worry about trash or clean up.. RegDLLView is completely portable by itself) and wait for it to finish loading (refer to the bottom, left corner of the window; you should see something like, "Loading..."). Once it's all loaded, look for the column Last Registered On in the top window pane and click on it until the list is sorted to the most recent date.

Now you can look for your applications DLL files. Scan for your application's company name. If there isn't anything for your application then close out RegDLLView and open the 32-bit version and repeat the previous steps. If you do find something (there can be more than one) that relates to your application, refer to the file path column and locate that DLL. If the DLL's location is inside the application folder then no furthur action is required on your part because the DLL will be in the %PAL:AppDir%\AppName folder anyway. However, if the DLL is not located in the application folder and it is somewhere like $SYSDIR\DynamicLibrary.dll than you're going to need to copy the DLL into your PAF's folder (i.e. ..\App\DefaultData so you can access them from ..\%PAL:DataDir% as well as have a back up if needed later).

After you've copied the DLL you can now select the DLL reference in the top window pane of RegDLLView. Notice in the bottom window pane there is now one or more lines of data in regards to this DLL file. You'll only need the first line as this is where you can find the ProgID and that is what we want! Now in the Launcher.ini you just need the ProgID and the file path to the DLL(s) you want to register before launching the actual application. So you can now add the section [RegesterDLL1] and [RegesterDLL2] and so on depending on how many you might need. Your Launcher.ini file should have something similiar to this:

[RegisterDLL1]
ProgID=MyAppControlPanel
File=%PAL:AppDir%\controller.cpl

[RegisterDLL2]
ProgID=DynamicLibrary
File=%PAL:DataDir%\dynlib.dll
Macros

The following are the macros that I've written for use in the RegDLLs.nsh segment—in both the official builds of PAL and in my own variant of PAL.

DLL::GetGUID

Discription
The following macro is one I wrote which uses the System plugin. What this does is returns a DLLs GUID by means of its ProgID. Use RegDLLView to find a DLLs ProgID If it doesn't find a GUID an error flag is set to which you may check for.
;=# 
; ${DLL::GetGUID} $0 "PROGID"
;
;	$0     = Will return the GUID
;	PROGID = The DLLs ProgID
;
!define DLL::GetGUID "!insertmacro _DLL::GetGUID"
!macro _DLL::GetGUID _RETURN _ProgID
	System::Call `ole32::CLSIDFromProgID(w,&g16)i("${_ProgID}",.r0).r2`
	StrCmp $2 "-2147221005" 0 +4
	SetErrors
	StrCpy ${_RETURN} ""
	Goto +2
	StrCpy ${_RETURN} $0
!macroend

DLL::Backup

The following macro tries to use what we just discussed above. I do this because the GUID allows you to go straight to the registry branch (usually in HKCR\CLSID but can be found in HKLM\Software\Classes\CLSID as well) to find a DLLs location instead of having to search the registry recursively through routines that are time consuming and a waste of space.

Discription
This macro will locate the path to a locally registered DLL using the GUID of that DLL. If the location is found it will then write the value of its location to the PortableApps.comLauncherRuntimeData.ini file for later use and unregisters the DLL.
;=#
; ${DLL::Backup} "GUID" /DISABLEFSR "SECTION" "KEY" $0 $1
;
;    ::Backup     = Makes a backup of a local DLL file. 
;    GUID         = GUID | i.e. {00000000-0000-0000-0000-000000000000}
;    /DISABLEFSR  = Disables redirection if x64. Use "" to skip.
;    SECTION      = The section in the INI file.
;    KEY          = The key to write the value to in the INI file.
;    $0           = Return after call
;    $1           =   ''    ''    ''
;
!define DLL::Backup "!insertmacro _DLL::Backup"
!macro _DLL::Backup _GUID _FSR _SECTION _KEY _ERR1 _ERR2
	ClearErrors
	ReadRegStr $0 HKLM `SOFTWARE\Classes\CLSID\${_GUID}\InprocServer32` ""
	${IfNot} ${Errors}
		StrCmpS $Bit 64 0 +6
		StrCmp ${_FSR} /DISABLEFSR 0 +5
		${WriteRuntimeData} ${_SECTION} ${_KEY} `$0`
		ExecDos::Exec /TOSTACK /DISABLEFSR `"${REGSVR}" /s /u "$0"`
		Goto +2
		ExecDos::Exec /TOSTACK `"${REGSVR}" /s /u "$0"`
		Pop ${_ERR1}
		Pop ${_ERR2}
	${EndIf}
!macroend

DLL::Restore

Discription
This macro will read a value from PortableApps.comLauncherRuntimeData.ini to see if a locally installed DLL was found in ${SegmentPre} and if so will then proceed to register that DLL file again.
;=#
; ${DLL::Restore} /DISABLEFSR "SECTION" "KEY" $0 $1
;
;    ::Restore     = Restores a local DLL file from a backup. 
;    /DISABLEFSR   = Disables redirection if x64. Use "" to skip.
;    SECTION       = The section in the INI file.
;    KEY           = The key to find the value from in the INI file.
;    $0            = Return after call
;    $1            =   ''    ''    ''
;
!define DLL::Restore "!insertmacro _DLL::Restore"
!macro _DLL::Restore _FSR _SECTION _KEY _ERR1 _ERR2
	ClearErrors
	${ReadRuntimeData} $0 ${_SECTION} ${_KEY}
	${IfNot} ${Errors}
		StrCmpS $Bit 64 0 +4
		StrCmp ${_FSR} /DISABLEFSR 0 +3
		ExecDos::Exec /TOSTACK /DISABLEFSR `"${REGSVR}" /s "$0"`
		Goto +2
		ExecDos::Exec /TOSTACK `"${REGSVR}" /s "$0"`
		Pop ${_ERR1}
		Pop ${_ERR2}
	${EndIf}
!macroend

DLL::Register

Discription
This macro will check the archutechture of the host PC and registers a DLL file accordingly.
;=#
; ${DLL::Register} "DLL" /DISABLEFSR $0 $1
;
;    ::Register		= Registers a specified DLL file.
;    DLL			= Path to DLL | i.e. "$AppDirectory\Program\file.dll"
;    /DISABLEFSR	= Disables redirection if x64. Use "" to skip.
;    $0				= Return after call
;    $1				=   ''    ''    ''
;
!define DLL::Register `!insertmacro _DLL::Register`
!macro _DLL::Register _DLL _FSR _ERR1 _ERR2
	StrCmpS $Bit 64 0 +4
	StrCmp ${_FSR} /DISABLEFSR 0 +3
	ExecDos::Exec /TOSTACK /DISABLEFSR `"${REGSVR}" /s "${_DLL}"`
	Goto +2
	ExecDos::Exec /TOSTACK `"${REGSVR}" /s "${_DLL}"`
	Pop ${_ERR1}
	Pop ${_ERR2}
!macroend

DLL::UnRegister

Discription
This macro will check the archutechture of the host PC and unregisters a DLL file accordingly.
;=#
; ${DLL::UnRegister} "DLL" /DISABLEFSR $0 $1
;
;    ::UnRegister	= Unregisters a specified DLL file.
;    DLL			= Path to DLL | i.e. "$AppDirectory\Program\file.dll"
;    /DISABLEFSR	= Disables redirection if x64. Use "" to skip.
;    $0				= Return after call
;    $1				=   ''    ''    ''
;
!define DLL::UnRegister `!insertmacro _DLL::UnRegister`
!macro _DLL::UnRegister _DLL _FSR _ERR1 _ERR2
	StrCmpS $Bit 64 0 +4
	StrCmp ${_FSR} /DISABLEFSR 0 +3
	ExecDos::Exec /TOSTACK /DISABLEFSR `"${REGSVR}" /s /u "${_DLL}"`
	Goto +2
	ExecDos::Exec /TOSTACK `"${REGSVR}" /s /u "${_DLL}"`
	Pop ${_ERR1}
	Pop ${_ERR2}
!macroend

Example Usage

Discription
Below is an example custom.nsh file with the above four macros which will show how to use these in action.

;= DEFINES
;= ################
!define DLL32	`$AppDirectory\file32.dll`
!define DLL32ID	`{CDC95B92-E27C-4745-A8C5-64A52A78855D}`
!define DLL64	`$AppDirectory\file64.dll`
!define DLL64ID	`{CDC95B92-E27C-4745-A8C5-64A52A78855D}`
!define REGSVR	`$SYSDIR\regsvr32.exe`

;= MACROS
;= ################
!define DLL::Backup "!insertmacro _DLL::Backup"
!macro _DLL::Backup _GUID _FSR _SECTION _KEY _ERR1 _ERR2
	ClearErrors
	ReadRegStr $0 HKLM `SOFTWARE\Classes\CLSID\${_GUID}\InprocServer32` ""
	${IfNot} ${Errors}
		StrCmpS $Bit 64 0 +6
		StrCmp ${_FSR} /DISABLEFSR 0 +5
		${WriteRuntimeData} ${_SECTION} ${_KEY} `$0`
		ExecDos::Exec /TOSTACK /DISABLEFSR `"${REGSVR}" /s /u "$0"`
		Goto +2
		ExecDos::Exec /TOSTACK `"${REGSVR}" /s /u "$0"`
		Pop ${_ERR1}
		Pop ${_ERR2}
	${EndIf}
!macroend
!define DLL::Restore "!insertmacro _DLL::Restore"
!macro _DLL::Restore _FSR _SECTION _KEY _ERR1 _ERR2
	ClearErrors
	${ReadRuntimeData} $0 ${_SECTION} ${_KEY}
	${IfNot} ${Errors}
		StrCmpS $Bit 64 0 +4
		StrCmp ${_FSR} /DISABLEFSR 0 +3
		ExecDos::Exec /TOSTACK /DISABLEFSR `"${REGSVR}" /s "$0"`
		Goto +2
		ExecDos::Exec /TOSTACK `"${REGSVR}" /s "$0"`
		Pop ${_ERR1}
		Pop ${_ERR2}
	${EndIf}
!macroend
!define DLL::Register `!insertmacro _DLL::Register`
!macro _DLL::Register _DLL _FSR _ERR1 _ERR2
	StrCmpS $Bit 64 0 +4
	StrCmp ${_FSR} /DISABLEFSR 0 +3
	ExecDos::Exec /TOSTACK /DISABLEFSR `"${REGSVR}" /s "${_DLL}"`
	Goto +2
	ExecDos::Exec /TOSTACK `"${REGSVR}" /s "${_DLL}"`
	Pop ${_ERR1}
	Pop ${_ERR2}
!macroend
!define DLL::UnRegister `!insertmacro _DLL::UnRegister`
!macro _DLL::UnRegister _DLL _FSR _ERR1 _ERR2
	StrCmpS $Bit 64 0 +4
	StrCmp ${_FSR} /DISABLEFSR 0 +3
	ExecDos::Exec /TOSTACK /DISABLEFSR `"${REGSVR}" /s /u "${_DLL}"`
	Goto +2
	ExecDos::Exec /TOSTACK `"${REGSVR}" /s /u "${_DLL}"`
	Pop ${_ERR1}
	Pop ${_ERR2}
!macroend

;= CUSTOM 
;= ################
${SegmentFile}
${SegmentPre} ;= Unregister Local DLLs
	${DLL::Backup} `${DLL32ID}` "" RegisterDLL DLL32 $0 $1
	${If} $Bit == 64
		${DLL::Backup} `${DLL64ID}` /DISABLEFSR RegisterDLL DLL64 $0 $1
	${EndIf}
!macroend
${SegmentPrePrimary} ;= Register Portable DLLs
	${DLL::Register} `${DLL32}` "" $0 $1
	${If} $Bit == 64
		${DLL::Register} `${DLL64}` /DISABLEFSR $0 $1
	${EndIf}
!macroend
${SegmentPostPrimary} ;= Unregister Portable DLLs
	${DLL::UnRegister} `${DLL32}` "" $0 $1
	${If} $Bit == 64
		${DLL::UnRegister} `${DLL64}` /DISABLEFSR $0 $1
	${EndIf}
!macroend
${SegmentUnload} ;= Re-register Local DLLs
	${DLL::Restore} "" RegisterDLL DLL32 $0 $1
	${If} $Bit == 64
		${DLL::Restore} /DISABLEFSR RegisterDLL DLL64 $0 $1
	${EndIf}
!macroend
NOTE
The above example is just to show how to utilize these macros and is not meant to work for every situation. Some programs will have multiple registered DLLs for you to manage.
Other Macros
Discription
The following macros are alternatives which you may use for registering and unregistering DLL files but be sure to include the x64 plugin as these macros makes use of it's function.

RegisterDLL

;=# 
;= ${RegisterDLL} "SomeLibrary.dll"
;
!define RegisterDLL "!insertmacro _RegisterDLL"
!macro _RegisterDLL _DLL
	${If} ${RunningX64}
		${DisableX64FSRedirection}
		ExecWait '"$SYSDIR\regsvr32.exe" /s "${_DLL}"'
		${EnableX64FSRedirection}
	${Else}
		RegDLL "${_DLL}"
	${EndIf}
!macroend

UnregisterDLL

;=# 
;= ${UnregisterDLL} "SomeLibrary.dll"
;
!define UnregisterDLL "!insertmacro _UnregisterDLL"
!macro _UnregisterDLL _DLL
	${If} ${RunningX64}
		${DisableX64FSRedirection}
		ExecWait '"$SYSDIR\regsvr32.exe" /s /u "${_DLL}"'
		${EnableX64FSRedirection}
	${Else}
		UnRegDLL "${_DLL}"
	${EndIf}
!macroend