First, let's examine building the projects as modules and then linking them via the Assembly Linker. In Figure 1, we see the contents of the BuildWithAL.bat batch file. First, I add the path to the desired version of the Framework (1.1 in this example). Then I invoke the VB.NET Compiler (vbc.exe), providing it with what is called a response file. Response files are simply a plain text file with compiler switches in them. You specify them with the VB.NET and C# compilers by prefixing the response file name with the @ sign.
Figure 2 shows the contents of the VBModule.rsp file. As you can see, I specify /nologo to suppress the compiler informational blurb, /t:module, which tells it to output a module, /r to specify the assemblies I'm referencing in my code, /out to specify the target filename, and finally I specify the files to include in the compilation. Since we only have one, I just put its name there (as opposed to using wildcards). We could also have put all of these switches on the command line, but I use response files for readability and managability. The key here is the /t[arget] switch. This is what tells the compiler to output a module instead of an assembly. "Module" is the .NET term for a file containing Microsoft Intermediate Language code. It cannot be used or executed on its own because it lacks assembly metadata required by the Common Language Runtime (CLR). It must be included in an assembly, either by using the Assembly Linker or by using /addmodule in a compiler that supports that functionality.
The next significant line in Figure 1 is Line 6 where we are invoking the C# Compiler to compile the C# project as a module. Again, we see that I am using a response file, CSModule.rsp, whose contents (Figure 3) are much the same as VBModule.rsp, except that I use a wildcard (*.cs) to specify all class files in the CSLibrary directory. Again, the key here is to target a module. You may also notice the /noconfig switch on Line 6 of Figure 1. This simply instructs the compiler not to use the csc.rsp response file (located in the same directory as csc.exe) that automatically references all of the Framework System assemblies.
The last significant lines in Figure 1 are Lines 8-9. These are really just one command that I used the line continuation character (^) on to make more readable. The command invokes the Assembly Linker, which will create an assembly file as specified in the /out switch. Notice the first arguments are the modules to link, which are the two modules just created on Lines 4 and 6 of Figure 1. Again I specify /nologo to suppress the informational blurb, and /target tells it to output a code library (DLL), which is actually the default. You can also specify exe (console app) and win (windows app), but for those you should add a /main switch to specify the entry point method for the executable.
And that is it. It really just boils down to a few command lines, so while it's not as easy as clicking Build in VS.NET, it's not that difficult either. The main difficulty would be keeping the project items and options synchronized between the VS.NET project and your build script.
Using this method leaves you with two .netmodule files containing your two projects' code and one .dll file that is only the assembly manifest. You must deploy all modules in the assembly into the same directory on the target computer, and you need to reference the file containing the assembly manifest (typically the DLL file), which in our case is MultifileAssembly.dll. Also note that you would likely want to use this method if you were deploying the application over the net, as mentioned above as the second reason that you might want a multifile assembly.