Look into virtual abstract base classes. The dll can return a pointer to a descendant in a factory function and the host can use it because it knows the base class. (EDIT: I see Wyrframe's link talks about them, so read that).
The main reason for this is to support dlls made in different languages and with different compilers than yours.
Do make sure you define calling conventions and data (struct) alignment. For calling conventions, cdecl and stdcall are good choices.
Is this, in fact, not what COM technology is all about? Although it includes additional features such as reference counting as well.