TIP: How to stop the annoying ns???.tmp firewall messages...

Submitted by haustin on April 25, 2008 - 5:13am.

You know all those annoying "ns???.tmp" firewall messages? If not, see here. Most (if not all) of them are caused by nsExec::Exec and variants.

ExecDos::exec does not have this problem: any firewall complaints name "Foo Portable" rather than an ever-changing ns???.tmp executable file. Not only can your firewall remember a static name, ExecDos also saves about 300 bytes compressed, 1K uncompressed. Smiling

Most of the existing nsExec::ExecToStack usage can be replaced with ExecDos::exec without the /TOSTACK option, since the calling scripts never bother to Pop the output off the stack anyway (requires additional Pop after the exit code).

Finally, the call to `$COMSPEC /C move /Y "${FOODIR}\*.*" "${BARDIR}"` can be pulled into the NSIS script using Rename in a FindFirst/FindNext loop, eliminating the "suspicious" attempts to access cmd.exe (or command.com) for this simple task.

New Portable Apps users can breathe a little easier.  -hea

<EDIT>
Modified subject, linked to related post and made minor grammar change.
Linked to ExecDos page at SourceForge.
</EDIT>

( categories: )

IMPROVED! Replacing `$COMSPEC /C MOVE` with Find/Rename

I recommended doing this in my opening post, so I figured I would post some working sample code for replacing

nsExec::Exec `$COMSPEC /C move /Y "C:\OLD\DIR\*.*" "C:\NEW\DIR"`

with Rename in a FindFirst/FindNext loop.

I created a simple macro to do the work, and included it in a sample script so you can play with it.

Page instfiles               ; show the log screen
ShowInstDetails show         ; show details on the log screen
OutFile "testmove.exe"

;↓↓↓ COPY everything between these two lines ↓↓↓
;usage: ${MoveFiles} "filespec" "source-directory" "destination-directory" $tmp1 $tmp2
; NOTE: $tmp1 and $tmp2 are temporary variables -- they get clobbered.
!macro MoveFiles filespec sourcedir destdir tmp1 tmp2
    FindFirst ${tmp1} ${tmp2} "${sourcedir}\${filespec}"
    ; ${tmp1} = FindFirst/FindNext search handle
    ; ${tmp2} = first filename that matches ${filespec} in ${sourcedir}
; loop:                    
    StrCmp ${tmp2} "" +4 ; "done" (${tmp2} == "", if no more matching files)
    Rename "${sourcedir}\${tmp2}" "${destdir}\${tmp2}"
    FindNext ${tmp1} ${tmp2} ; ${tmp2} = next file matching ${filespec} in ${sourcedir}
    Goto -3 ; "loop"
; done:
    FindClose ${tmp1}        ; finished with this search; close handle
!macroend
!define MoveFiles "!insertmacro MoveFiles"
;↑↑↑ COPY everything between these two lines ↑↑↑

Section Main
    DetailPrint "Moving files..."
    ${MoveFiles} "*.*" "C:\OLD\DIR" "C:\NEW\DIR" $0 $1
    DetailPrint `"Processing"...`
    Sleep 2000
    DetailPrint "Moving them back..."
    ${MoveFiles} "*.*" "C:\NEW\DIR" "C:\OLD\DIR" $0 $1
SectionEnd

So, if you include the macro in your script, you can replace the unnecessary nsExec DLL call with the following:

${MoveFiles} "*.*" "C:\OLD\DIR" "C:\NEW\DIR" $0 $1
; NOTE: $0 and $1 are temporary variables -- they get clobbered.

Not only is this cleaner (by staying in the native NSIS executable), it will result in one less ever-changing ns???.tmp warning from user firewalls. Smiling

Enjoy. -hea

<EDIT>
I got rid of the arbitrary register usage... Now the user simply specifies which two variables should be used as temporary variables. Since ${MoveFiles} would rarely be used more than a couple of times per script, the additional complexity of a function didn't seem to make sense.
</EDIT>

This is a really useful

This is a really useful macro. Few things for your code reviewer Eye-wink

1. It's worth mentioning that using a filespec of "*" or "*.*" will also yield directory search results, so depending on whether the user wishes to move subdirectories as well, this is useful info.

2. When using "*" or "*.*" as filespec, the first two matches returned are "." and "..". These should obviously be skipped in the Rename loop to avoid disaster Laughing out loud

Yes and No

  1. Yes, unlike the Command Shell's built-in MOVE command, neither Rename nor my macro restrict filespec-matching to regular files. Whether this is a dangerous thing or more user-friendly depends on your perspective. Smiling   MOVE simply won't allow you to filespec directories at all -- you have to name them one-by-one.   ⇒  Users should be aware of this difference in behavior, however.  ⇐  
  2. No; although FindFirst/FindNext does return dot and dot-dot whenever they match a filespec, Rename sensibly ignores them.

The key thing my code reviewer missed is that Rename doesn't have the equivalent of the /Y option... I would edit the macro to add a Delete prior to the Rename, but someone replied to that post. Sticking out tongue   Note that both Rename and MOVE/Y will ignore attempts to move a directory if one of the same name already exists in the destination (and the Delete I'd like to add would simply ignore the directory, too). If you need to move such a directory, you have to RMDir /R the destination copy first. (Or, you could use RecFind.nsh to recursively move the source files and RMDir /R the source directory afterwards.) Someone could add this to the macro if they needed it, but I don't think it would be a good idea to have in the general-purpose version.

EDIT: It wouldn't be difficult to add an option to ignore directory moves, or even an option to force it using either of the aforementioned methods.

Thoughts? -hea

Curious, did you find out #2

Curious, did you find out #2 above in documentation somewhere, or by testing?

Testing and source...

Curious, did you find out #2 above in documentation somewhere, or by testing?

The internal NSIS commands aren't what I would call fully documented, so I tend to RTFS (ahem, the source Smiling) or test for specific cases. All NSIS commands are actually implemented in Source\exehead\exec.c, with some calling out to util.c (note: the top-level Source directory is for the compiler).

In this case, Rename simply calls MoveFile(). Since MSDN doesn't specify for the latter, I tested it. Eye-wink   It's a shame they didn't use MoveFileEx(), as it has built-in support for the optional MOVE/Y-type move: MOVEFILE_REPLACE_EXISTING).

-hea

Self Ping

I'm pinging this topic to say I'm now aware of this. More info here:
http://portableapps.com/node/13203#comment-80330

Let's keep discussion confined to this thread so we stay on track. Maybe do a test release this week using the macro.

Live with purpose.

Cool.

I am going to be in Rome for the next week, but I doubt you'd need any help from me anyway. Sticking out tongue

If you want to change the macro to behave more like MOVE/Y, just insert a Delete before the Rename and adjust the relative jump offsets. Note that the macro will still move directories that match "filespec", unlike the MOVE command.

I'll post a general-purpose version of the macro shortly.

Thanks. -hea

Sure thing

I'm thinking we should BSD license it. Add some stack push exch so it doesn't clobber any temps and then give it to NSIS's code library. This would certainly be handy for folks beyond us.

Live with purpose.

NEW and IMPROVED! MoveFiles.nsh

@John
I was about to post a revised macro when I saw your post about Push/Exch and BSD licensing... Sticking out tongue   So, I restructured it as a Function and put it in an include file along with the BSD license header. Please check out the results on drop.io.

@wraithdu
Notice I introduced the nasty [ RMDir /r ], so I had to add handling for dot and dot-dot after all. Smiling

I made a copy of the FirefoxPortable\App\firefox directory in my C:\temp and tested MoveFiles.nsh with the following script:

Page instfiles               ; show the log screen
ShowInstDetails show         ; show details on the log screen
OutFile "testmove.exe"

!include MoveFiles.nsh

Section Main
    StrCpy $0 0
    StrCpy $1 1
    StrCpy $2 2
    StrCpy $3 3
    StrCpy $4 4
    StrCpy $5 5
    CreateDirectory "C:\temp\firefox-NEW\firefox.exe"
    DetailPrint "Moving firefox.* with mode=DOS (like MOVE)..."
    ${MoveFiles} DOS "firefox.*" "C:\temp\firefox" "C:\temp\firefox-NEW"
    DetailPrint "Moving firefox.* with mode=FORCE (like MOVE/Y)..."
    ${MoveFiles} FORCE "firefox.*" "C:\temp\firefox" "C:\temp\firefox-NEW"
    DetailPrint "Moving all files and directories (mode=DIR+FORCE)..."
    ${MoveFiles} DIR+FORCE "*" "C:\temp\firefox" "C:\temp\firefox-NEW"
    DetailPrint " "
    DetailPrint `"Processing"...`
    Sleep 2000
    DetailPrint " "
    DetailPrint "Moving only the files back (mode=DOS)..."
    ${MoveFiles} DOS "*" "C:\temp\firefox-NEW" "C:\temp\firefox"
    Sleep 500
    DetailPrint " "
    DetailPrint "Moving the directories back (MODE=DIR)..."
    ${MoveFiles} DIR "*" "C:\temp\firefox-NEW" "C:\temp\firefox"
    DetailPrint "$0 $1 $2 $3 $4 $5" ; check stack handling of registers
    RMDir "C:\temp\firefox-NEW"
SectionEnd

and got this output (which is correct):

Create folder: C:\temp\firefox-NEW\firefox.exe
Moving firefox.* with mode=DOS (like MOVE)...
Moving firefox.* with mode=FORCE (like MOVE/Y)...
Moving all files and directories (mode=DIR+FORCE)...
Rename: C:\temp\firefox\AccessibleMarshal.dll->C:\temp\firefox-NEW\AccessibleMarshal.dll
Rename: C:\temp\firefox\browserconfig.properties->C:\temp\firefox-NEW\browserconfig.properties
Rename: C:\temp\firefox\chrome->C:\temp\firefox-NEW\chrome
Rename: C:\temp\firefox\components->C:\temp\firefox-NEW\components
Rename: C:\temp\firefox\defaults->C:\temp\firefox-NEW\defaults
Rename: C:\temp\firefox\dictionaries->C:\temp\firefox-NEW\dictionaries
Rename: C:\temp\firefox\extensions->C:\temp\firefox-NEW\extensions
Remove folder: C:\temp\firefox-NEW\firefox.exe\
Rename: C:\temp\firefox\firefox.exe->C:\temp\firefox-NEW\firefox.exe
Rename: C:\temp\firefox\freebl3.dll->C:\temp\firefox-NEW\freebl3.dll
Rename: C:\temp\firefox\greprefs->C:\temp\firefox-NEW\greprefs
Rename: C:\temp\firefox\install.log->C:\temp\firefox-NEW\install.log
Rename: C:\temp\firefox\js3250.dll->C:\temp\firefox-NEW\js3250.dll
Rename: C:\temp\firefox\LICENSE->C:\temp\firefox-NEW\LICENSE
Rename: C:\temp\firefox\nspr4.dll->C:\temp\firefox-NEW\nspr4.dll
Rename: C:\temp\firefox\nss3.dll->C:\temp\firefox-NEW\nss3.dll
Rename: C:\temp\firefox\nssckbi.dll->C:\temp\firefox-NEW\nssckbi.dll
Rename: C:\temp\firefox\old-homepage-default.properties->C:\temp\firefox-NEW\old-homepage-default.properties
Rename: C:\temp\firefox\plc4.dll->C:\temp\firefox-NEW\plc4.dll
Rename: C:\temp\firefox\plds4.dll->C:\temp\firefox-NEW\plds4.dll
Rename: C:\temp\firefox\plugins->C:\temp\firefox-NEW\plugins
Rename: C:\temp\firefox\README.txt->C:\temp\firefox-NEW\README.txt
Rename: C:\temp\firefox\res->C:\temp\firefox-NEW\res
Rename: C:\temp\firefox\searchplugins->C:\temp\firefox-NEW\searchplugins
Rename: C:\temp\firefox\smime3.dll->C:\temp\firefox-NEW\smime3.dll
Rename: C:\temp\firefox\softokn3.dll->C:\temp\firefox-NEW\softokn3.dll
Rename: C:\temp\firefox\ssl3.dll->C:\temp\firefox-NEW\ssl3.dll
Rename: C:\temp\firefox\uninstall->C:\temp\firefox-NEW\uninstall
Rename: C:\temp\firefox\updater.exe->C:\temp\firefox-NEW\updater.exe
Rename: C:\temp\firefox\updater.ini->C:\temp\firefox-NEW\updater.ini
Rename: C:\temp\firefox\xpcom.dll->C:\temp\firefox-NEW\xpcom.dll
Rename: C:\temp\firefox\xpcom_compat.dll->C:\temp\firefox-NEW\xpcom_compat.dll
Rename: C:\temp\firefox\xpcom_core.dll->C:\temp\firefox-NEW\xpcom_core.dll
Rename: C:\temp\firefox\xpicleanup.exe->C:\temp\firefox-NEW\xpicleanup.exe
Rename: C:\temp\firefox\xpistub.dll->C:\temp\firefox-NEW\xpistub.dll
 
"Processing"...
 
Moving only the files back (mode=DOS)...
Rename: C:\temp\firefox-NEW\AccessibleMarshal.dll->C:\temp\firefox\AccessibleMarshal.dll
Rename: C:\temp\firefox-NEW\browserconfig.properties->C:\temp\firefox\browserconfig.properties
Rename: C:\temp\firefox-NEW\firefox.exe->C:\temp\firefox\firefox.exe
Rename: C:\temp\firefox-NEW\freebl3.dll->C:\temp\firefox\freebl3.dll
Rename: C:\temp\firefox-NEW\install.log->C:\temp\firefox\install.log
Rename: C:\temp\firefox-NEW\js3250.dll->C:\temp\firefox\js3250.dll
Rename: C:\temp\firefox-NEW\LICENSE->C:\temp\firefox\LICENSE
Rename: C:\temp\firefox-NEW\nspr4.dll->C:\temp\firefox\nspr4.dll
Rename: C:\temp\firefox-NEW\nss3.dll->C:\temp\firefox\nss3.dll
Rename: C:\temp\firefox-NEW\nssckbi.dll->C:\temp\firefox\nssckbi.dll
Rename: C:\temp\firefox-NEW\old-homepage-default.properties->C:\temp\firefox\old-homepage-default.properties
Rename: C:\temp\firefox-NEW\plc4.dll->C:\temp\firefox\plc4.dll
Rename: C:\temp\firefox-NEW\plds4.dll->C:\temp\firefox\plds4.dll
Rename: C:\temp\firefox-NEW\README.txt->C:\temp\firefox\README.txt
Rename: C:\temp\firefox-NEW\smime3.dll->C:\temp\firefox\smime3.dll
Rename: C:\temp\firefox-NEW\softokn3.dll->C:\temp\firefox\softokn3.dll
Rename: C:\temp\firefox-NEW\ssl3.dll->C:\temp\firefox\ssl3.dll
Rename: C:\temp\firefox-NEW\updater.exe->C:\temp\firefox\updater.exe
Rename: C:\temp\firefox-NEW\updater.ini->C:\temp\firefox\updater.ini
Rename: C:\temp\firefox-NEW\xpcom.dll->C:\temp\firefox\xpcom.dll
Rename: C:\temp\firefox-NEW\xpcom_compat.dll->C:\temp\firefox\xpcom_compat.dll
Rename: C:\temp\firefox-NEW\xpcom_core.dll->C:\temp\firefox\xpcom_core.dll
Rename: C:\temp\firefox-NEW\xpicleanup.exe->C:\temp\firefox\xpicleanup.exe
Rename: C:\temp\firefox-NEW\xpistub.dll->C:\temp\firefox\xpistub.dll
 
Moving the directories back (MODE=DIR)...
Rename: C:\temp\firefox-NEW\chrome->C:\temp\firefox\chrome
Rename: C:\temp\firefox-NEW\components->C:\temp\firefox\components
Rename: C:\temp\firefox-NEW\defaults->C:\temp\firefox\defaults
Rename: C:\temp\firefox-NEW\dictionaries->C:\temp\firefox\dictionaries
Rename: C:\temp\firefox-NEW\extensions->C:\temp\firefox\extensions
Rename: C:\temp\firefox-NEW\greprefs->C:\temp\firefox\greprefs
Rename: C:\temp\firefox-NEW\plugins->C:\temp\firefox\plugins
Rename: C:\temp\firefox-NEW\res->C:\temp\firefox\res
Rename: C:\temp\firefox-NEW\searchplugins->C:\temp\firefox\searchplugins
Rename: C:\temp\firefox-NEW\uninstall->C:\temp\firefox\uninstall
0 1 2 3 4 5
Remove folder: C:\temp\firefox-NEW\
Completed

Let me know what you think. -hea

P.S. Now what about my other "TIP" posts. Laughing out loud

Other

The only other post that seems to apply mentioned the couple typo bugs in the Mozilla launchers (StrCpy instead of StrCmp and profile instead of plugin) and these are both fixed in the current Firefox, Thunderbird and Sunbird launchers. The KompoZer fix will happen with the next release (it's not big enough for a revision).

As for the other comments, we won't be doing a global INI in the portableapps folder for anything. Settings like those in the INI are on a per-app basis just as they are with local apps. And I'll just update the comments in the code and readme to say that all INI entries are required... we've only had maybe 5 comments about that out of around 5 million Firefox Portable downloads... so we really don't need to add support for optional INI entries.

Live with purpose.

Re:

The only other post that seems to apply mentioned the couple typo bugs in the Mozilla launchers

Yeah, I thought there were more that I'd renamed, but apparently some were just comments in random other threads. Smiling   I considered the typo issue resolved when you acknowledged that thread.

As for the other comments, we won't be doing a global INI in the portableapps folder for anything. Settings like those in the INI are on a per-app basis just as they are with local apps.

Actually, things that PortableApps generally have in common (such as the additional splash screens and the ability to run in LIVE mode) are not directly comparable to native app settings. While desirable to adjust on a per-app basis, those PortableApps-"system"-wide settings are more similar to XP's themes (you can set a standard appearance, but individual apps can do whatever they want) and Recycle Bin (you can override system-wide individual apps' ability to use it). The PortableApps equivalent to a system-wide setting would be a global INI. Other possibilities for global preferences would be WaitForProgram and DisableCleanupSplash (for apps that take a long time to shut down after the user initiates a close and/or have complex cleanup; it should be grayscale even for released apps).

And I'll just update the comments in the code and readme to say that all INI entries are required... we've only had maybe 5 comments about that out of around 5 million Firefox Portable downloads... so we really don't need to add support for optional INI entries.

Technically, it's removing support for required INI entries... Laughing out loud   My guess would be that only a tiny fraction of confused or annoyed end users ever create accounts to complain. I was thinking of the more common case of end users asking in the forums how to disable splash screens (or any other single-line change to the INI).

In the other thread I said that:

there really aren't any required INI entries (since no INI file is required at all). So, it probably makes more sense to make all entries optional. Then you can tell someone in the forums, Copy the following to a file called FirefoxPortable.ini in the same directory as the FirefoxPortable.exe program:

[FirefoxPortable]
DisableSplashScreen=true

This is much more user-friendly than telling a neophyte to drill down into Other\Source and copy the FirefoxPortable.ini file up to the FirefoxPortable folder, then edit it and change only the DisableSplashScreen line from false to true (ignore the other lines, they're magic).

From a logical standpoint, the only reason for requiring certain never-modified lines to be present in an optional INI file would be to act as some form of sanity check that the INI file is valid. In reality, the app-specific [SectionName] is sufficient for this purpose without requiring that neophytes be exposed to configuration entries that will more than likely break their application if modified. I generally consider reduced opportunity for failure to be an improvement. Smiling

Thanks for listening to my nonsense again. -hea

Nicely done

Very excellent macro. I just used it in my uTorrent script, makes life much easier Smiling

...

Isn't uTorrent already fully portable? Just stick a blank settings.dat in the same directory as utorrent.exe...

Bascially, yes. You have to

Basically, yes. You have to manually add relative paths for a few things (which I did for the default settings.dat), and move settings files around to comply with PAF specs.

...

Do relative paths actually work? I remember trying a relative path once for my download folder and it only worked sometimes; other times it just errored out (current-directory issues, I bet).

I tested it with a torrent

I tested it with a torrent and it works, both the .torrent storage and the actual download (different folders). I also spied into the resume.dat and the paths are stored as ..\..\path\blah.torrent and ..\..\blah\files.

Hmm.

Must have gotten lucky. Either that or they fixed the bug since the last time I tried it. When I tried it, it worked initially but a few hours later (after adding another torrent, I think) I got an error like "couldn't open file" or somesuch.

Lots of programs choke on relative paths because in their purest form, they're relative to the current directory, which can change over the course of an app's execution. In order for them to work the way most people expect (relative to app dir), the app either has to explicitly change the current directory to the app dir before every file open (which is bad form), or else generate a hybrid path, e.g. M:\PortableApps\AppNamePortable\..\..\Documents\Downloads.