Translated by Alex tilles in Windows Developer Network (Issue 1) Article : "Writing your own install and uninstall Code ". This is an article with certain technical content. I believe many developers need the technology described in this article, including several important technical points:
Rundll32.exe Program ;
DLL or EXE self-deletion technology;
Embedded resource processing skills;
Lzcopy API usage example;
Compress.exe,expand.exe instructions for use;
Summary
When I write the "what to do" Program (this is an application compiled by the author, it is small and exquisite, very practical-the Translator's note), I want to write my own installation and uninstallation.CodeThe main purpose is to control the screen that the user sees during the installation/uninstallation process as desired. In this article, we will discuss how to use the self-deleted dynamic link library (DLL) to implement the self-deleted executable program, so as to install/uninstall the program. I believe many of my friends want to do this when writing a Windows program. This article will also show some very useful technologies that will surely open your eyes ......
Difficulties in implementing self-deletion and uninstallation programs
The most challenging part of writing an uninstallation program is how to let the uninstallation program delete itself after deleting the target program file and related directories. In addition, the uninstall program must be available on all Windows operating systems (Windows 9x, Windows NT, Windows 2000, Windows XP .....) you do not need to download any additional components. I searched the internet and found some related information about how to delete executable program files. However, most of the recommended solutions have a problem, that is, it can only work on a certain version of Windows. Some methods are implemented by modifying the thread attributes. This usually causes timing problems. There are also some methods that have serious errors during running and cannot be used at all. I am thinking about seeking a better solution to implement the self-deletion function of executable programs: using the self-deleted DLL to implement self-deleted executable programs, this breaks through the limitations of the above methods.
Rundll32.exe
Generally, the DLL code from the downloaded directory can be executed only after it is loaded to the memory. How can I execute the code exported from a DLL without creating an EXE file to load and call the DLL? The method is as follows: a system utility rundll32.exe is attached to each Windows operating system version starting with Windows 95. It can be used to execute any function output by some DLL (but not all) as follows:
Rundll32.exe dllname, exportedfnname ARGs
Exportedfnname is the name of the DLL output function. When compiling a DLL for rundll32, you can declare the output function as follows:
Extern "C" _ declspec (dllexport) void callback functionname (
Hwnd,
Hinstance,
Lptstr lpcmdline,
Int ncmdshow
)
{...}
Rundll32.exe calls a function based on the function parameter list. However, based on experience, only one parameter value is actually used, that is, the lpcmdline. This parameter receives the parameter value passed in when rundll32.exe is run; __declspec (dllexport) is used to output functions. extern "C" gives the output function name modifiers, such: _ functionname @ 16 (the function name is forced to contain the size of function parameters. For details, see the description of DLL output function calling specifications in msdn ). Rundll32.exe loads the specified DLL and calls the output function specified by the value of the lpcmdline passed in through the ARGs parameter. For more information about rundll32.exe, see the msdn Library (q164787 ):
Http://support.microsoft.com/default.aspx? SCID = KB; en-US; 164787
Implement self-deleted DLL
The following is the Demo code for self-deleting dll:
# Include <windows. h>
Hmodule g_hmoddll;
Extern "C" bool winapi dllmain (hinstance hinstdll, DWORD reason, lpvoid)
{
If (reason = dll_process_attach)
G_hmoddll = hinstdll;
Return true;
}
Extern "C" _ declspec (dllexport) void callback magicdel (hwnd,
Hinstance,
Lptstr lpcmdline,
INT)
{
// 2 seconds delay
Sleep (2000 );
// Delete the executable file that creates the process
Deletefile (lpcmdline );
// Delete the DLL itself
Char filenamedll [max_path];
Getmodulefilename (g_hmoddll, filenamedll, sizeof (filenamedll ));
_ ASM
{
Lea eax, filenamedll
Push 0
Push 0
Push eax
Push exitprocess
Push g_hmoddll
Push deletefile
Push freelibrary
RET
}
}
The above code first deletes an object and then deletes it from itself. Dllmain is the DLL entry function. This function is called when the dynamic link library is loaded for the first time. In this case, the module handle is assigned to the global variable g_hmoddll, so that you can use it to obtain the DLL file name. In the magicdel function, the lpcmdline is the name of the executable file to be deleted by the DLL (for example, the file name of the uninstall program ). It is easy to delete it -- use sleep for a delay so that the executable process can exit and call deletefile. To understand the Implementation Details of magicdel, you can pass the process handle of the executable program to magicdel and wait before calling deletefile to see what will happen?
To allow the DLL to be self-deleted, you need some tips. Rundll32 calls loadmodule to load the DLL to its address space. If the DLL function can be returned, rundll32 will exit, causing the DLL to be released (not deleted ). To solve this problem, we can execute the following code:
Freelibrary (DLL module handle );
Deletefile (DLL filename );
Exitprocess (0 );
Magicdel functions cannot be called directly in this order, because freelibary will invalidate the code page. To this end, magicdel pushes equivalent Assembly commands into the stack, then executes them, followed by a RET command, and finally calls exitproccess to prevent the process from continuing to execute. I wrote an assembly code block for reference in Gary nebbit's article in the Windows Development Magazine (WDJ) "Tech Tips. If you use Visual Studio to generate a DLL with the default option, the final binary file is about 40 K. Since we plan to use dll as the resource of the executable program, the smaller the size, the better. Therefore, we must slim down it. The idea is to remove useless C Runtime code from the DLL. The specific method is as follows:
This document uses Visual Studio. NET 2003 Chinese version to compile and generate a DLL. First, set the project compilation/link options:
Project (p) | [project name] attribute (p )... | linker | input | ignore all default libraries: Yes (/nodefalib Lib). This setting passes the/nodefaultlib option to the linker to filter out runtime codes.
The DLL entry point is usually provided by the Runtime Library (dllmain by default, you must also explicitly set the DLL entry point to dllmain:
Project (p) | [project name] attribute (p)... | linker | advanced | entry point: dllmain.
If the DLL is compiled at this time, the compiler reports the following two unresolved externals errors:
Error lnk2019: external symbol that cannot be parsed ___ security_cookie, which is referenced in function _ magicdel @ 16
Error lnk2019: unresolved external symbol __ security_check_cookie @ 4, which is referenced in function _ magicdel @ 16
The solution is to perform the next step.
Project (p) | [project name] attribute (p)... | C/C ++ | code generation | buffer security check: No,
This setting does not pass the/GS flag to the compiler to get rid of the unresolved externals error.
Now, compile and generate the DLL. The final DLL size is 3 kb, and the actual file size is only 2.5 kb.
Execute programs that can be deleted
The main idea used here is to save a self-deleted dll as a resource in the executable program to be self-deleted, and then recreate it as needed, start a rundll32.exe process for deletion.
Below are the header files and resource files used to store dll as resources. A resource type value can be greater than 256, which is reserved for user-defined types. In addition, an optional method is to store the DLL binary file in a byte array directly in the source:
Contains a file in the resource.
// Selfdelete. h
# Define rc_binarytype 256
# Define id_magicdel_dll 100
// Selfdelete. RC
# Include "selfdelete. H"
Id_magicdel_dll rc_binarytype magicdel. dll
The following is the key code of the executable program:
# Include <windows. h>
# Include "selfdelete. H"
Void writeresourcetofile (hinstance,
Int idresource,
Char const * filename)
{
// Access Binary Resources
Hrsrc hresinfo = findresource (hinstance, makeintresource (idresource ),
Makeintresource (rc_binarytype ));
Hglobal hgres = loadresource (hinstance, hresinfo );
Void * pvres = lockresource (hgres );
DWORD cbres = sizeofresource (hinstance, hresinfo );
// Write binary resources to files
Handle hfile = createfile (filename, generic_write, 0, 0, create_always,
File_attribute_normal, 0 );
DWORD cbwritten;
Writefile (hfile, pvres, cbres, & cbwritten, 0 );
Closehandle (hfile );
}
Void selfdelete (hinstance)
{
Writeresourcetofile (hinstance, id_magicdel_dll, "magicdel. dll ");
// generate a command line
// 1. find rundll32.exe
char CommandLine [max_path * 3];
getwindowsdirectory (CommandLine, sizeof (CommandLine);
lstrcat (CommandLine, "\ rundll32.exe ");
If (getfileattributes (CommandLine) = invalid_file_attributes)
{< br> getsystemdirectory (CommandLine, sizeof (CommandLine);
lstrcat (CommandLine, "\ rundll32.exe");
}< br> // 2. add the rundll32.exe parameter
lstrcat (CommandLine, "magicdel. DLL, _ magicdel @ 16 ");
// 3. add this file name
char thisname [max_path];
getmodulefilename (hinstance, thisname, sizeof (thisname);
lstrcat (CommandLine, thisname );
// Execute Command Line
process_information procinfo;
startupinfo startinfo;
memset (& startinfo, 0, sizeof (startinfo ));
startinfo. dwflags = startf_forceofffeedback;
CreateProcess (0, CommandLine, 0, 0, false, normal_priority_class, 0, 0,
& startinfo, & procinfo );
}
Int winapi winmain (hinstance,
Hinstance hprevinstance,
Lpstr lpcmdline,
Int ncmdshow)
{
Selfdelete (hinstance );
}
Writeresourcetofile is used to access binary resources so that dll can be rebuilt on the disk. Windows resource API provides a pointer to raw data.
Selfdelete is used to re-create the DLL and generate the following command line to start rundll32.exe:
Path \ rundll32.exe magicdel. dll, _ magicdel @ 16 path \ executablename
Rundll32.exe is located in the windows or system directory, so selfdelete checks whether its location is correct. When CreateProcess is called to execute the command line, you must set
STARTF_FORCE-OFFFEEDBACK flag to Prevent Windows from displaying an active hourglass or cursor when running rundll32.exe. After this operation, the user will not feel that a new process is running. After the new process exits, the DLL and the original executable files are lost.
To make the self-deleted executable program independent of the C Runtime DLL, the executable program must be statically linked to the Runtime library code. To do this, modify the project compilation options:
Project (p) | [project name] attribute (p )... | C/C ++ | code generation | Runtime Library: [Single-threaded (/ml)] or [multi-threaded (/mt)] (or any option value that does not contain this DLL)
This auto-deletion technology works very stably in all Windows versions. In practical use, the uninstallation program first places its copy to the temp directory of windows, so that it can delete all program files and related directories, finally, it deletes itself with the self-deleted DLL.
Compile the installer
After determining what the installer will do, create the installer. Currently, many installation programs are downloaded from the internet and run locally. The smaller the size of the downloaded file, the better. Therefore, the most effective method is to compress the file. How can users see my program rather than the installation program of other companies first? Fortunately, Windows provides such support.
Create an interactive Setup Program that displays the software license agreement, prompts the user to install the option, copies the file, and then performs other settings. Save the compressed version of the setup program as a resource in the installer. The installer only needs to re-create the binary resource of the setup program and write it back to the disk. decompress the package and start it with a new process. It is not difficult to save and read/write binary resources-the processing details and code have been described earlier in this article.
Since Windows 95, each Windows platform has a set of APIS for extracting files-lzcopy. The following is the sample code for the installer to use this API:
// Install. h
//
# Define rc_binarytype 256
# Define id_compressed_setup 100
//
// Install. RC
//
# Include "Install. H"
Id_compressed_setup rc_binarytype paietup. ex _
//
// Install. cpp
//
# Include <windows. h>
# Include "Install. H"
Void writeresourcetofile (hinstance,
Int idresource,
Char const * filename)
{
// See the preceding Code
}
Void decompressfile (char const * Source, char const * DEST)
{
Ofstruct OFS;
OFS. cbytes = sizeof (OFS );
Int zhfsource = lzopenfile (const_cast <char *> (source), & OFS, of_read );
Int zhfdest = lzopenfile (const_cast <char *> (DEST), & OFS,
Of_create | of_write );
Lzcopy (zhfsource, zhfdest );
Lzclose (zhfsource );
Lzclose (zhfdest );
}
Int winapi winmain (hinstance,
Hinstance hprevinstance,
Lpstr lpcmdline, int ncmdshow)
{
Writeresourcetofile (hinstance, id_compressed_setup, "deleetup. ex _");
Decompressfile ("deleetup. ex _", "deleetup.exe ");
Deletefile ("deleteup. ex _");
// Start etetup.exe
Process_information procinfo;
Startupinfo startinfo;
Memset (& startinfo, 0, sizeof (startinfo ));
CreateProcess (0, "etetup.exe", 0, 0, false, normal_priority_class, 0, 0,
& Startinfo, & procinfo );
}
The code shows how the compressed setup program is saved as the resources of the installer. Follow the ideas discussed earlier in this article. The decompressfile function demonstrates how to use the lzcopy API. The installer re-creates appsetup.exe and runs it. To smoothly compile and generate the installer, you need to add lz32.lib to the compilation options of the project. This file is usually in the installation directory of Visual Studio, for example:
Visual Studio. net2003:
C: \ Program Files \ Microsoft Visual Studio. NET 2003 \ vc7 \ platformsdk \ Lib
Visual c +++ 6.0:
C: \ Program Files \ Microsoft Visual Studio \ vc98 \ Lib
In Visual Studio. NET, you can add the following methods:
Project (p) | [project name] attribute (p)... | linker | additional library Directory: [add one of the above paths]
In addition, to get rid of the dependency on the C Runtime DLL, you must use a static link to the Runtime library code:
Project (p) | [project name] attribute (p )... | C/C ++ | code generation | Runtime Library: [Single-threaded (/ml)] or [multi-threaded (/mt)] (or any option value that does not contain this DLL)
Note that the installation program does not have to wait for the setup program to complete the work, because appsetup.exe can use the self-deleted DLL to delete itself after the work is completed.
The most tricky part of lzcopy API is that it can only Extract files compressed by compress.exe. Compress.exe is a compressed file command line utility provided by Microsoft with the SDK. You can also download it from Microsoft's official FPT Website: ftp://ftp.microsoft.com/softlib/mslfiles/cp0982.exe. After an EXE is run, several unpackage files, including compress.exe, can be ignored or deleted. Use compress.exe as follows:
Compress sourcename destinationname
All Windows versions have built-in compression support, which makes it easy to compile the installation program. In addition, all Windows versions also contain another utility: expand.exe. You can decompress the package in the command line.
Summary
With the help of Self-deleted DLL, binary resources, and Windows built-in decompression support, you can create your own installer and uninstall programs, so as to easily control every aspect of the screen when you install and uninstall the program ....