Razorback
NewsProjectsGuidesResourcesContact
 Guide Index Quick Links


The CopyFiles Directive

Created on March 10, 2023

Contents
  1. The Signature
  2. Define a List of Files
  3. The Installation Section
    1. Calling Files Directly
    2. Calling Sections to Copy Files
  4. Define a New Destination Directory
  5. Define a Different Source Path
  6. A Different Name for the Destination File
  7. Using Flags
  8. Comments
  9. Your First INF File is Ready!

To be able to install software, you must be able to copy files over from one location to another. The setup engine allows you to do this with a directive called CopyFiles. As with other directives which will be covered here, the name should explain itself, but there are a number of gotchas for newcomers to be mindful of.

To start off, we should create two plain text documents, TEST.TXT and OTHER.TXT. These documents can be left empty, or you can fill them with whatever garbage you can come up with, like "This is the last time I'm ever touching a glass brick". It doesn't matter what their contents are; what's important is that the filenames fit within the 8.3 character limit (not applicable if exclusively working with Windows NT).

After this, we can create another text document, which will use the INF extension. We'll call this one TEST.INF. Don't forget to show file extensions!

The Signature

Every INF file should have a valid signature for Windows to accept it. This signature is stored in a section called Version, which is enclosed in square brackets on its own line. The signature varies depending on if you're supporting Windows 9x and NT or Windows NT only. Since we're creating an INF file for Windows 95 (and other 9x versions), the section should look like this:

[Version]
Signature="$CHICAGO$"

If you're only working with Windows NT, you should use the signature "$Windows NT$". Also note the use of quote marks; a lot of strings of text, especially those which have spaces, expect to be surrounded in quote marks to be read correctly. Not all of them do, but definitely enough that you should make a habit of it when working in different parts of an INF file.

Define a List of Files

There are a couple of ways to define a list of files for an INF. One way is to write the list of files in the INF itself, and the other involves calling an external layout file. The latter method is primarily used for installing much larger software packages with many more files, such as Windows 95. For now, we'll only focus on the former method involving creating a list in the same INF file.

To start, we could create a section called SourceDisksNames. This allows you to define one or more disks to be used in executing the script. We only need to define one disk, which can be given an ID of 1 and any label we please. It should look a little something like this:

[SourceDisksNames]
1="Source Files",,0

...with 1 being the disk ID, and "Source Files" being our label we defined. This may seem convoluted, but the INF setup engine was designed to be able to work with sets of floppy disks in a series, when CD burning was not readily available to consumers and many large sets of files still took up as much as 4MB, which was more practical to distribute on three floppy disks rather than a CD-ROM that leaves about 696MB of space unused.

In your case, however, you probably won't have to use more than one disk unless you really need to use floppy disks. If you're going to write something for altering an automated installation of Windows 95, you may still wish to pick a different disk ID like 101.

The parameters for each value such as the one written above are separated by commas (the value being the text after the equal sign). The first parameter was our disk label; the second parameter could be used to define a CAB file if need be, but in the majority of cases, it should be left empty (i.e. immediately followed by another comma). The next parameter did have a function in Windows 9x, but has since been deprecated, so it should always be set to 0.

Now, we must create the section SourceDisksFiles, which will be used to create an index of files and which disks they belong to. In our case, we're only using one disk, so every file will point to the same disk ID. We want both our text files to be copied, so our section will look like this:

[SourceDisksFiles]
test.txt = 1
other.txt = 1

In most cases, you may choose to add whitespace around the equal sign or not. Doing so makes INFs easier to read, but there are rare cases where doing so may be bad if you need to cram as much information as possible into one file using the 16-bit setup engine, which is capped at a limit of 64KB.

Because Windows filesystems are not case sensitive, you can also write the filenames in uppercase if you want. In fact, names for sections, directives, and other things are case insensitive as well, although inconsistent casing in INF sections is bad practice.

One thing you have to look out for is that you cannot prepend the filename with the source path; something like C:\WINDOWS\TEST.TXT = 1 will not get you the result you may expect. If anything, when working with INF files, you should try to avoid hard pathnames whenever possible, as the setup engine has a mechanism to account for more varied paths a user may have installed Windows to. Not all installations reside on the C: drive!

For now, your INF and source files should be located in the same directory as each other. Defining other directories will be covered further down in this page.

The Installation Section

Once that's done, you should create a new section where your directives will be listed. This will serve as the entry point to your INF, and another program will call it. By default, when you right click the INF and click the Install option from the context menu, a section called DefaultInstall will be called. This may be different if you require multiple entry points for driver installation or a more elaborate procedure, or your software expects something different; Windows 95 Setup would call the Install section in an automated installation.

In this case, it's best to stick to DefaultInstall so it's easy to execute an installation from this file with the mouse. The installation section can contain multiple types of directives for calling other sections in the INF, but for now, we'll only create one, CopyFiles.

Of course, the INF needs to know where the files will actually go. The list of destination directories for each section is defined under one section called DestinationDirs. For now, we'll assign a default destination directory for all files:

[DestinationDirs]
DefaultDestDir=10

DefaultDestDir is a special name used in this section to define a fallback directory for any files residing in a section not defined under here.

More importantly, though, note that a number was used here. This is a logical disk identifier, or LDID. Windows uses this number to determine where a certain common directory is located. In this case, 10 is used to represent the path of the Windows directory. It's usually C:\WINDOWS, but it could also be E:\WIN95. LDIDs ensure you get consistent results no matter which path you've installed Windows to. I've seen a lot of INFs use LDID 25 in place of 10; this represents the "shared directory", though it seems to most often be the same as the Windows directory in practice.

Below is a list of the LDIDs listed in the Windows 95 Resources Kit; some of these are obsolete due to being DOS/Windows 9x relics, or even used for Windows 95's ill-fated Remoteboot feature.

  • 00 - Null LDID; this LDID can be used to create a new LDID
  • 01 - Source drive:\ pathname
  • 02 - Temporary Setup directory; this is valid only during Windows 95 Setup
  • 03 - Uninstall directory
  • 04 - Backup directory
  • 10 - Windows
  • 11 - SYSTEM
  • 12 - IOSUBSYS
  • 13 - COMMAND
  • 14 - Control Panel
  • 15 - Printers
  • 16 - Workgroup
  • 17 - INF
  • 18 - Help
  • 19 - Administration
  • 20 - Fonts
  • 21 - Viewers
  • 22 - VMM32
  • 23 - Color
  • 25 - Shared
  • 26 - Winboot
  • 27 - Machine specific
  • 28 - Host Winboot
  • 30 - Root directory of the boot drive
  • 31 - Root directory for host drive of a virtual boot drive
  • 32 - Old Windows directory if it exists
  • 33 - Old MS-DOS directory if it exists

There's also another LDID that's strangely not listed in the Windows 95 Resource Kit, being 24; this is used to represent the root directory of the drive where Windows is installed. If the install path was C:\WINDOWS, this LDID would mean C:\. This is not to be confused with LDID 30, which is the root directory of the boot drive; a bootloader could reside in a different drive from where Windows is installed.

Calling Files Directly

It is possible to call upon files directly by prepending them with an @ (at) symbol in your CopyFiles directive. You could write your installation section like this:

[DefaultInstall]
CopyFiles=@test.txt,@other.txt

Subvalues, being files in this case, are separated by commas. This is not a very efficient way to define files to be copied, and has mostly been used for copying single driver files for IDE or SCSI controllers.

Calling Sections to Copy Files

Instead, you should define new sections containing lists of files you wish to copy. You can name these whatever you want so as long as they don't conflict with any reserved names, such as the ones we used earlier. As section names can be ambiguous, it helps to use a convention to distinguish what sections are used for which directives. For CopyFiles, we could append section names with .Copy or .CopyFiles.

We'll create a new section labeled Files.Copy - a very generic name. You'll want something more descriptive when dealing with more files going to multiple destinations, but this will do for now. The section will look like this:

[Files.Copy]
test.txt
other.txt

And you can update the DefaultInstall section:

[DefaultInstall]
CopyFiles=Files.Copy

As before, you can specify multiple sections separated by commas, and you can add whitespace in between the names if you wish. This looks a lot more manageable!

You can also add the section to DestinationDirs. You can assign a different LDID to this as needed.

[DestinationDirs]
DefaultDestDir	= 10
Files.Copy	= 10

By now, the INF should be complete and ready to execute, but make sure to leave at least one byte of whitespace (or a comment) at the end of the file; a newline at the end of the file will do. If you do not do this, the setup engine may not get the last character of your last filename, thereby pointing to some other file like other.tx, which we're not using.

By default, any existing files in the destination directory will be overwritten by your script when called. You can control this behavior with flags, which will be covered further down this page. Check that you don't have anything important in your destination directory (I used filenames that will almost certainly not conflict with anything existing in a normal Windows directory), then right click your INF and choose Install. Now, check your Windows directory; you should see the copied files have appeared there.

Define a New Destination Directory

Often, you won't want to copy your software to the Windows directory. The DestinationDirs section allows you to specify a subdirectory after an LDID, and if this subdirectory does not exist, it will be created. Let's say we wanted to copy the files to NEWDEST\FILES in the Windows directory... our line would then look like this:

Files.Copy = 10,NEWDEST\FILES

Then, your files will be copied to that subdirectory, which would usually be C:\WINDOWS\NEWDEST\FILES.

Define a Different Source Path

When creating INF files, it's generally best to keep all your source files in one directory, but the setup engine permits specifying a different one nonetheless. This requires going back to the SourceDisksNames directory and adding another parameter at the end of the line.

1 = "Source Files",,0,FILES

You can specify either a relative or absolute path. You should not use absolute paths since they rely on you explicitly specifying a drive letter. Unfortunately, you can't use LDIDs for this parameter. Here, I put the two source documents in a new directory called FILES relative to the INF's location in the filesystem.

A Different Name for the Destination File

Sometimes, you may need to change the name of the file when it is copied to the destination, particularly if you have files in a source that are meant to use the same name - mashing all the 3dfx driver files together comes to mind. This requires the use of a second parameter for each line in a CopyFiles section, hence the practice of listing your files to copy in a section becomes mandatory.

What I'd want to do here is rename OTHER.TXT to OFFLINE.TXT when it is copied by the setup engine. To accomplish this, the line should look like this:

offline.txt,other.txt

The first parameter actually indicates the destination file, not the source. It is the second parameter that holds the name of the source file. If this is left empty by an immediate comma or newline, the setup engine interprets this as the source name being the same as the destination name.

Using Flags

Depending on what files you're copying and where they're going, you may want to set flags to control how they are copied. This is useful in situations where, say, you're wanting to copy a distributable DLL some program uses but don't want to override a newer version.

Modify the contents of your source files with some different text. Then, in the CopyFiles section, assign the flag 16 to both files to tell the engine to not overwrite a file if it already exists at the destination. This would be the fourth parameter, so it would come after the occurrence of three commas, like so:

[Files.Copy]
test.txt,,,16
offline.txt,other.txt,,16

Execute the script. Assuming such files have already been copied over to the destination previously, you should see that those files there remain unchanged.

Some flags can be combined with each other as they are essentially adding multiple bits of different positions together, but flag 16, COPYFLG_NO_OVERWRITE cannot be combined with anything else.

Comments

As in countless other forms of source code, comments are not executed by the setup engine and are only used to help describe various parts of an INF. They can be located either on their own line or on an existing line after its actual contents. You'll see how they're used in the full sample at the bottom of this page.

Your First INF File is Ready!

If all went well, you should now have a basic understanding of how INF files work in Windows 95 (and later versions). Remember, in Windows 9x, you have to make files conform to the 8.3 filename constraint unless you are invoking a 32-bit setup engine (which will often not be the case). Should anything go wrong, double check your syntax! Generally, Windows does not have a straightforward means of automatically reporting errors in INF files, so you have to manage it all yourself. Last but not least, make sure your file ends with some whitespace or a comment.

Here is the complete INF file:

[Version]
Signature="$CHICAGO$" ; For use in Windows 95

[SourceDisksNames]
; Remove ,FILES if source files are
; in the same directory as the INF.
1="Source Files",,0,FILES

[SourceDisksNames]
test.txt	= 1
other.txt	= 1

[DestinationDirs]
DefaultDestDir	= 10
Files.Copy	= 10,NEWDEST\FILES

[DefaultInstall]
; Invokved by right clicking INF
; and selecting Install
CopyFiles=Files.Copy

[Files.Copy]
test.txt,,,16
offline.txt,other.txt,,16

; End file with whitespace!

Comments

flatrute - March 17, 2023 at 10:29 AM

There are a few specification documents on the compression algorithm which in theory would let me be able to build a replacement that scales better on modern systems but I am so busy in college at the time I am typing this...

1 comment on this page

Sort: Ascending | Descending

Leave a Comment

Name: (required)

Website: (optional)

Maximum comment length is 1000 characters.
First time? Read the guidelines

SORRY NOT GONNA SHOW THIS IN TEXT BROWSER
Enter the text shown in the image: