Creating compact applications in VC ++

    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 simply a pity to lose several hundred extra kilobytes on each small utility.
    This article tells how to get really compact programs using the latest development tools - Microsoft Visual Studio 2008.
    As an example, we port the 7-zip console archiver and evaluate the effect of this.

    Issues and obvious solutions


    The obvious decision to dynamically link the runtime library (CRT) is not suitable, since the latest versions are not installed by default on Windows XP, which every self-respecting application should now support. A CRT supply 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 msvcrt.dll library, different versions of which can be found in all current versions of Windows. However, the import libraries that allow you to connect this DLL are only available in Microsoft Visual Studio 6.0 - in subsequent editions they use their libraries 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 is going to be built under VS2008 will only succeed in the simplest case, such as Hello World - since the release of Windows XP, significant changes have occurred in the runtime library - this is the appearance of functions of the “secure” category crt ”, and changes to exception handling, and additional runtime checks.

    Solution


    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 out an interesting feature - many applications and examples that come with WDK use exactly the CRT we need - msvcrt.dll. Moreover, the build environment in the WDK is current - the compiler version of the latest WDK is the same as the compiler version in VS2008SP1.
    A detailed study of the 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, in essence, contain the “difference” between the current CRT content and the one that is present in the corresponding version name of Windows. This explains the fact that the size of object files for newer Windows is smaller than for older ones.
    So, to connect the classic msvcrt.dll you need to follow two simple steps:
    • Disable connection of all standard libraries using the compiler key / NODEFAULTLIB or project settings Linker -> Input -> Ignore All Default Libraries
    • You need to connect the msvcrt.lib import library, which can be found in the \ lib \ Crt \ i386 \ msvcrt.lib WDK folder and connect the object file corresponding to the minimum version of Windows on which the application should run. Let this for example be \ lib \ Crt \ i386 \ msvcrt_winxp.obj.

    Everything, the project can be assembled and in the simplest case, it will even compile and work correctly.
    For those who do not have WDK, and do not want to download 600 MB for the sake of several megabytes of libraries, I gave a link to the archive at the end of the article, 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 sources are 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 easy enough to port.
    We will carry out such a magic sequence of actions:
    • First, make sure that the project is correctly built using Microsoft Visual C ++ 2008. To do this, run the makefile from the \ CPP \ 7zip \ folder (using nmake). Hereinafter, all folders are indicated relative to the folder into which the project sources are unzipped.
    • Already at the initial assembly stage we get 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 small correction. In the \ CPP \ Windows \ Security.cpp file on line 125, 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. The output received executable files for all components of the 7z archiver (console version, file manager, etc.). To verify the assembly’s operability, copy the files \ CPP \ 7zip \ UI \ Console \ O \ 7z.exe and \ CPP \ 7zip \ Bundles \ Format7zF \ O \ 7z.dll to one folder and make sure the console archiver is working. Also, for a successful launch, you need to put a manifest file next to it. For this project, it is given in the accompanying files for the article.

    Now the fun part is how to make the application use classic run-time. To do this, we make several magical passes that are clear to everyone who read the previous section:
    • In the \ CPP \ Build.mak file in the flags of the linker, we replace the already irrelevant option -OPT: NOWIN98 with / NODEFAULTLIB (line 45)
    • Now you need to add the link of the Kernel32.lib uuid.lib libraries to each project with minimal effort. I don’t feel like editing the makefile of each project separately, so in the same \ CPP \ Build.mak we add the libraries and modules necessary for each project to the linker settings line. Thus, we get the following settings string:
    • LFLAGS = $ (LFLAGS) .. \ .. \ msvcrt \ msvcrt_winxp.obj .. \ .. \ msvcrt \ msvcrt.lib Kernel32.lib uuid.lib -nologo -NODEFAULTLIB -OPT: REF -OPT: ICF
    • We collect the result and check. Under Win7 and virtual XP everything works without problems, the archives created by our assembly successfully open with the original 7zip.

    Now let’s check how such porting affected performance. For comparison, we take the original assembly, as well as our two assemblies - one will use MSVCR90.dll, the other - MSVCRT.dll.
    The table below compares the resulting file sizes. Porting to a new compiler automatically saved us about 10% of the size of the executable file.
    MS VC 6.0MS VC 9.0MS VC 9.0 (msvcrt.dll)
    7z.exe150 016 bytes136,704 bytes138,240 bytes
    7z.dll726 016 bytes655,872 bytes657,920 bytes

    To measure the speed, we will pack all the binary files obtained as a result of the assembly of the project (about 187Mb). Each of the assemblies will conduct 10 packing cycles to reduce the impact of disk access and caching speeds, and average the data obtained.
    MS VC 6.0MS VC 9.0MS VC 9.0 (msvcrt.dll)
    Packing time122.86 seconds112.02 seconds113.73 seconds

    As you can see, the switch to the updated compiler gave a real performance boost (close to 10%, which is not so bad for an application of this kind). Also, the generated binary file decreased by the same ~ 10%.
    There was no significant difference between using the classic runtime for VC90 and msvcrt.dll.

    Method Limitations


    However, a couple of potential disadvantages and limitations of this method should be noted.

    There is no way to connect the debug runtime msvcrtd.dll


    Despite the fact that the import library for it is included in the WDK, the .dll itself could not be found, and, according to Koby Kahane, Microsoft is not going to include it in future versions of the WDK.
    This significantly complicates the 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 elusive bugs at the final direct release of the product - after all, no one guarantees full compatibility of runtimes.

    MFC, ATL


    Drag and drop an MFC or ATL to use the new-old runtime dynamically is a rather difficult task and is beyond the scope of this article. However, for applications that use these libraries, there are usually no strict requirements for disk space.

    Files


    The archive attached to the article (1.8Mb) contains:
    1. The 7z diff folder contains all the changes to the 7zip project. Unzip the 7z465.tar.bz2 downloaded from the site and roll the patch above the project’s root folder.
    2. The msvcrt folder contains binaries from the WDK to port the project to using msvcrt.dll
    3. 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.

    You can download the file at: http://download.zillya.com/attach/MsVCArticlesAttach.zip

    Upd . Cryptochild helped with invite, thanks!

    Also popular now: