Note: The author of the article is Alexey
Fahrenheit Zakharenko. Good person and specialist.
With the exponential growth of memory and disk space, the creation of really small programs is rarely needed, but sometimes there are tasks when it is just a shame to lose a few hundred extra kilobytes on every small utility.
This article tells you how to get really compact programs using actual development tools - Microsoft Visual Studio 2008.
As an example, we port the 7-zip console archiver and evaluate the effect of this.
Problems and obvious solutions
The obvious decision about dynamic linking of the runtime library (CRT) is not appropriate, since the latest versions are not installed by default on Windows XP, which every self-respecting application should support now. And the delivery of a CRT in the form of a DLL, of course, is not a panacea, since they have a significant (by the standards of this article) volume.
Another solution is to use the standard library msvcrt.dll, different versions of which can be found in all current versions of Windows. However, import libraries that allow you to connect this DLL are available only in Microsoft Visual Studio 6.0 - in subsequent editions, their own libraries are used for each new version (msvcr70.dll / msvcp70.dll, msvcr71.dll / msvcp71.dll, etc.).
A simple attempt to take the import library for msvcrt.dll and connect it to a project that builds under VS2008 will only lead to success in the simplest case of Hello World type - since the release of Windows XP, significant changes have occurred in the runtime library - this is the emergence of the secure category crt ”, and changes in exception handling, and additional checks on runtime.
Solution to the problem
The solution to this problem is suggested by Koby Kahane in his article
Dynamically linking with MSVCRT.DLL using Visual C ++ 2005 .
He conducted a study of the latest versions of the Windows Driver Kit (WDK) and found an interesting feature - many applications and examples that come with the WDK use exactly the CRT we need - msvcrt.dll. In this case, the build environment in the WDK is up-to-date — the compiler version of the last WDK is the same as the compiler version in VS2008SP1.
A detailed study of project files shows that the solution lies in several object files that are connected to the project - msvcrt_win2000.obj, msvcrt_winxp.obj, msvcrt_win2003.obj. They essentially contain the “difference” between the current CRT content and the one that is present in the corresponding Windows version name. This explains the fact that the size of the object files for newer Windows is smaller than for older ones.
So, to connect the classic msvcrt.dll, you must perform two simple steps:
- Disable the connection of all standard libraries using the compiler key / NODEFAULTLIB or the project settings Linker -> Input -> Ignore All Default Libraries
- You must connect the import library msvcrt.lib, which can be found in the folder \ lib \ Crt \ i386 \ msvcrt.lib WDK and connect the object file corresponding to the minimum version of Windows on which the application should run. Let this be for example \ lib \ Crt \ i386 \ msvcrt_winxp.obj.
Everything, the project can be collected and in the simplest case it will even compile and work correctly.
For those who do not have a WDK, and do not want to download 600 MB for several megabytes of libraries, at the end of the article I gave a link to the archive, which contains the necessary files separately.
')
Practice - pitfalls and nuances
So, now let's try to convert the project to use msvcrt.dll in it and see what happens.
For example, take 7-zip - a fairly good open source archiver. Its source code is available at 7-zip.org. A little research shows that the project is currently being built using Microsoft Visual C ++ 6.0, which makes it fairly easy to port.
Let's do this magical sequence of actions:
- First, make sure that the project is properly built using Microsoft Visual C ++ 2008. To do this, run the makefile from the \ CPP \ 7zip \ folder (using nmake). Hereinafter, all the folders are relative to the folder into which the project sources have been unzipped.
- Already at the initial assembly stage, we will receive a warning (which, in accordance with the project settings, is considered an error) C4996: 'wcscpy': This function or variable may be unsafe. Consider using wcscpy_s instead.
- Well, let's make this minor fix. In the file \ CPP \ Windows \ Security.cpp in line 125 we replace
wcscpy (s, MY__SE_LOCK_MEMORY_NAME);
on
wcscpy_s (s, 128, MY__SE_LOCK_MEMORY_NAME);
128 characters is the maximum size of the immediately declared buffer. - Now the build process was successful. At the output, the executable files for all components of the 7z archiver (console version, file manager, etc.) were obtained. To test the build, copy the \ CPP \ 7zip \ UI \ Console \ O \ 7z.exe and \ CPP \ 7zip \ Bundles \ Format7zF \ O \ 7z.dll files into one folder and verify that the console archiver works. Also, for successful launch you will need to put a manifest file next to it. For this project, it is given in the accompanying files to the article.
Now the most interesting is how to make the application use classic run-time. To do this, we make several magical passes, which are understandable to everyone who read the previous section:
- In the file \ CPP \ Build.mak in the linker flags we replace the already irrelevant option -OPT: NOWIN98 with / NODEFAULTLIB (line 45)
- Now you need to add minimal effort to each project linking libraries Kernel32.lib uuid.lib. I don’t want to edit the makefile of each project separately, therefore in the same \ CPP \ Build.mak we add the necessary libraries and modules for each project to the linker settings line. Thus, we get the following settings:
- LFLAGS = $ (LFLAGS) .. \ .. \ msvcrt \ msvcrt_winxp.obj .. \ .. \ msvcrt \ msvcrt.lib Kernel32.lib uuid.lib -nologo -NODEFAULTLIB -OPT: REF -OPT: ICF
- Collect the result and check. Under Win7 and virtual XP, everything works without problems, the archives created by our build successfully open the original 7zip.
Now let's check how such porting affected performance. For comparison, take the original assembly, as well as our two assemblies - one will use MSVCR90.dll, the other - MSVCRT.dll.
The table below shows the comparison of the obtained file sizes. Porting to a new compiler automatically saved us about 10% of the size of the executable file.
| MS VC 6.0 | MS VC 9.0 | MS VC 9.0 (msvcrt.dll) |
7z.exe | 150 016 bytes | 136,704 bytes | 138,240 bytes |
7z.dll | 726 016 bytes | 655 872 bytes | 657,920 bytes |
To measure the speed, we will pack all the binary files obtained as a result of the project build (about 187 MB). Each of the assemblies will conduct 10 packing cycles to reduce the impact of disk access and caching speed, and the data obtained will be averaged.
| MS VC 6.0 | MS VC 9.0 | MS VC 9.0 (msvcrt.dll) |
Packing time | 122.86 seconds | 112.02 seconds | 113.73 seconds |
As you can see, the transition to the updated compiler gave a real performance increase (close to 10%, which is not so bad for an application of this kind). Also, the generated binary file has decreased by the same ~ 10%.
There was no significant difference between using the classic VC90 runtime and msvcrt.dll.
Method limitations
However, a couple of potential disadvantages and limitations of this method should be noted.
There is no possibility to connect the msvcrtd.dll debugging runtime
Despite the fact that the import library for it is present in the WDK, it was not possible to find the .dll itself, and, according to Koby Kahane, Microsoft is not going to include it in future versions of the WDK.
This greatly complicates debugging of applications and provokes the use of the method described in the article only before release, which, in turn, can lead to the appearance of hard-to-catch bugs in the final direct product release - because no one guarantees full runtime compatibility.
MFC, ATL
Dragging MFC or ATL to use the new-old runtime dynamically is quite a difficult task and goes beyond the scope of this article. However, for applications that use these libraries, there are usually no strict requirements on the disk space.
Files
The archive attached to the article (1.8Mb) contains:
- The 7z diff folder contains all changes to the 7zip project. Unzip the 7z465.tar.bz2 downloaded from the site and roll the given patch over the project root folder.
- The msvcrt folder contains binary files from the WDK for porting a project for using msvcrt.dll
- The output folder contains all three builds of the console version of 7-zip for those who do not trust my test results and want to double-check them.
Download the file at:
http://download.zillya.com/attach/MsVCArticlesAttach.zipUpd .
Cryptochild helped with the invite, thanks!