When a DLL is linked to the main program, only the interface, i.e. classes, functions etc. it exports are linked by their signature. The actual machine code inside the DLL is loaded only on runtime, not on compile-time; that's why you can build your app with just the DLL's lib, and put the actual DLL where you'd rather have it (for example some shared DLLs folder or whatever).
You can then swap out this DLL with any other DLL with the same interface. That is how plugin-systems usually work, as each plugin is simply a DLL that conforms to a documented pre-defined interface, and the program just loads all DLLs of that interface it finds in some "plugins" directory.
The (possibly) confusing part here is that there are effectively two kind of .lib files:
a) libs for static linking. These put all their compiled code directly into the main app, and are a fixed part of the resulting .exe file.
b) libs for dynamic linking. These contain an interface for a DLL containing the actual code, which must be available to the app only at runtime (and if it isn't, it won't start, and simply tell you which DLL it couldn't find).
Btw: you don't have to specify which one of the libs you link to is what type, it does it automatically.
Also: Some applications are built as DLLs meant to being run inside some external environment. Webservices, for example, are implemented as DLLs with a specific interface, which are being run by an Apache/IISAPI/whatever server. This works similarly to the aforementioned plugin-system, but here each DLL effectively is the application.