Archive: Removing empty dir after reboot


Removing empty dir after reboot
Hi,

Is it possible to remove an empty dir after a reboot ?

I need to reboot to remove a DLL.
I used the code :

Delete /REBOOTOK "$INSTDIR\mydll.dll"

After the reboot, my dll is deleted but I still have an empty directory : "c:\program files\myapp"

Is it possible to remove this directory (that's now empty and can then be removed successfully) ?

Thanks for your help

Chag


AFAIK this isn't possible easily - hence all those empty folders in my Program Files folder :(


Couldn't you just do this?

  Delete /REBOOTOK $INSTDIR\MyDLL.dll
Delete /REBOOTOK $INSTDIR

Adding an entry to the WININIT.INI file may work. I'm not sure how it works or if you can do it on NT systems, but something like:

  WriteINIStr $WINDIR\WININIT.INI Rename Nul $INSTDIR
or:
  WriteINIStr $WINDIR\WININIT.INI Rename Nul $INSTDIR\NUL

Hi, i tried


Delete /REBOOTOK $INSTDIR
but it doesn't work

and i'd liked to avoid modifying ini files.

Try this:

Delete /REBOOTOK "$INSTDIR\mydll.dll"
RMDir /r "$INSTDIR"


RMDir will delete the folder/directory. :)
and the "/r" switch will delete it recursively.

Originally posted by SQwerl
Try this:
Delete /REBOOTOK "$INSTDIR\mydll.dll"
RMDir /r "$INSTDIR"


RMDir will delete the folder/directory. :)
and the "/r" switch will delete it recursively.
The problem is if it has to reboot, RMDir /r "$INSTDIR" will never have a chance to run. Perhaps an /REBOOTOK option for RMDir is needed?

Robert

Old topic, but I have the same problem. Is there a solution now? A /REBOOTOK option for RMDir would be nice...


Oops :) My mistake, I thought that this would be a problem, but the uninstaller runs from a different location or something, so Uninstall.exe can always be deleted.


Yea, it generates a couple of files in the TEMP folder in the WINDOWS Dir. That way, the Uninstaller can "self-delete" itself.

-Duane


Originally posted by chag
i'd liked to avoid modifying ini files.
This is how the /REBOOTOK option works.

All of the installers I know use the WININIT.INI file to delete files on reboot. I just don't know how these things work on NT systems. I'd go to the Win98 computer but it's dead.

I'll do some research.

Originally posted by petersa
This is how the /REBOOTOK option works.

All of the installers I know use the WININIT.INI file to delete files on reboot. I just don't know how these things work on NT systems. I'd go to the Win98 computer but it's dead.

I'll do some research.
There is a registry location for it on WinNT systems.

I just tried a quick check using Registry Monitor, but I couldn't find anything...

Justin? How exactly does the /REBOOTOK option work? What registry or file changes does it involve?


Here is the function in the source code that is called if the /REBOOTOK flag appears.

BOOL MoveFileOnReboot(LPCTSTR pszExisting, LPCTSTR pszNew)
{
BOOL fOk = 0;
HMODULE hLib=LoadLibrary("kernel32.dll");
if (hLib)
{
typedef BOOL (WINAPI *mfea_t)(LPCSTR lpExistingFileName,LPCSTR lpNewFileName,DWORD dwFlags);
mfea_t mfea;
mfea=(mfea_t) GetProcAddress(hLib,"MoveFileExA");
if (mfea)
{
fOk=mfea(pszExisting, pszNew, MOVEFILE_DELAY_UNTIL_REBOOT|MOVEFILE_REPLACE_EXISTING);
}
FreeLibrary(hLib);
}

if (!fOk)
{
static char szRenameLine[1024];
static char wininit[1024];
static char tmpbuf[1024];
int cchRenameLine;
char *szRenameSec = "[Rename]\r\n";
HANDLE hfile, hfilemap;
DWORD dwFileSize, dwRenameLinePos;
static const char nulint[4]="NUL";

if (pszNew) GetShortPathName(pszNew,tmpbuf,1024);
else *((int *)tmpbuf) = *((int *)nulint);
// wininit is used as a temporary here
GetShortPathName(pszExisting,wininit,1024);
cchRenameLine = wsprintf(szRenameLine,"%s=%s\r\n",tmpbuf,wininit);

GetWindowsDirectory(wininit, 1024-16);
lstrcat(wininit, "\\wininit.ini");
hfile = CreateFile(wininit,
GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);

if (hfile != INVALID_HANDLE_VALUE)
{
dwFileSize = GetFileSize(hfile, NULL);
hfilemap = CreateFileMapping(hfile, NULL, PAGE_READWRITE, 0, dwFileSize + cchRenameLine + 10, NULL);

if (hfilemap != NULL)
{
LPSTR pszWinInit = (LPSTR) MapViewOfFile(hfilemap, FILE_MAP_WRITE, 0, 0, 0);

if (pszWinInit != NULL)
{
int do_write=0;
LPSTR pszRenameSecInFile = findinmem(pszWinInit, szRenameSec,-1);
if (pszRenameSecInFile == NULL)
{
lstrcpy(pszWinInit+dwFileSize, szRenameSec);
dwFileSize += 10;
dwRenameLinePos = dwFileSize;
do_write++;
}
else
{
char *pszFirstRenameLine = findinmem(pszRenameSecInFile, "\n",-1)+1;
int l=pszWinInit + dwFileSize-pszFirstRenameLine;
if (!findinmem(pszFirstRenameLine,szRenameLine,l))
{
void* data=(void*)GlobalAlloc(GMEM_FIXED,l);
mini_memcpy(data, pszFirstRenameLine, l);
mini_memcpy(pszFirstRenameLine + cchRenameLine, data, l);
GlobalFree((HGLOBAL)data);

dwRenameLinePos = pszFirstRenameLine - pszWinInit;
do_write++;
}
}

if (do_write)
{
mini_memcpy(&pszWinInit[dwRenameLinePos], szRenameLine,cchRenameLine);
dwFileSize += cchRenameLine;
}

UnmapViewOfFile(pszWinInit);

fOk++;
}
CloseHandle(hfilemap);
}
SetFilePointer(hfile, dwFileSize, NULL, FILE_BEGIN);
SetEndOfFile(hfile);
CloseHandle(hfile);
}
}
return fOk;
}


As you can see it writes to the WININIT.INI weather it's 9x or NT.

KiCHiK

Thanks, KiCHiK, we're a step closer but I'm no programmer so this doesn't mean much to me. Any translators out there?

I'm guessing it adds a NUL=[file to delete] line to the [Rename] section of the $WINDIR\WININIT.INI file. How close am I?


Almost :) It only write NUL=... if it got pszNew as a null string. That means that can also rename to pszNew.


So when does it write pszNew?


It writes it when you use Rename /REBOOTOK. If you want to delete use NUL.

KiCHiK


Okay, thanks KiCHiK! Now we all know how /REBOOTOK works, I reckon we could probably get rid of the functionality and put it into FUNCTIONS.HTM, or something?

  ClearErrors
Delete C:\WINDOWS\FileIn.Use
IfErrors 0 +2 ; if it was deleted then skip the next line
WriteINIStr $WINDIR\WININIT.INI Rename NUL C:\WINDOWS\FileIn.Use
One thing I don't understand, is why +2 is necessary instead of +1, i.e. [skip over the next 1 line].

Originally posted by petersa
One thing I don't understand, is why +2 is necessary instead of +1, i.e. [skip over the next 1 line].
I belive it is because it means Goto 2 lines from here.. So if you did 1 it would goto the first line after it..

Originally posted by petersa
Okay, thanks KiCHiK! Now we all know how /REBOOTOK works, I reckon we could probably get rid of the functionality and put it into FUNCTIONS.HTM, or something?
  ClearErrors
Delete C:\WINDOWS\FileIn.Use
IfErrors 0 +2 ; if it was deleted then skip the next line
WriteINIStr $WINDIR\WININIT.INI Rename NUL C:\WINDOWS\FileIn.Use
One thing I don't understand, is why +2 is necessary instead of +1, i.e. [skip over the next 1 line].
a) The code in NSIS tries to do the remove-on-reboot the NT way and if that fails it drops back to the win9x way.

b)The WriteINIStr way won't work, because WriteINIStr will update
any entry that already begins with NUL=.

c) +2 is necessary because +1 jumps to the next instruction (+0 does the instruction again, +1 goes to next, +2 skips the next, etc).

-Justin

Oops... Sorry I mixed you up. I accidentally ignored the first block of code :( :hang:


Right, so 0 and +0 are different...that makes sense then.

Okay, so how does the NT way work?


The NT way is MoveFileExA.
Read at MSDN on MoveFileEx and you will understand it all.