It is possible but not trivial. And yes, this is a very dirty hack.
In some cases loading the EXE file with LoadLibrary is enough. The returned HMODULE is actually the base address of the loaded EXE. Cast it to a suitable int type, add your relative function address to that, cast it back to a function pointer and call the function through that pointer.
Unfortunately, the EXE file may have its relocation info stripped. It means that the EXE will be expecting to run from a specific address. In this case, you have to change your own program's base address to avoid conflict. Check out your linker's docs, there should be an option to do that. After that, LoadLibrary will load the EXE in its preferred base address and hopefully all should work fine.
There is some very useful info on this here. Make sure to check the update at the end of the page for a different technique that may work better in some cases.
Edit: As Alex correctly stated in the comment below, if the function relies on some initialized value, or it calls such a function, including most C runtime functions, it will be much harder to make it work. One can identify the initialization functions and call them beforehand but using debug API may be your best bet in those situations.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…