The problem is that when you call a .dll that resides in a different project then the calling .dll the calling .dll needs to be able to locate the .dll to call.
This can be done either by placing the .dll to be called in the same folder as the calling .exe (which is what Copy Local does) or by setting the PATH environment variable to include the location of the .dll to be called.
If you are running outside of the IDE then you also need to set this up so that the .dll to be called can be located by the calling program.
Please look at the following tutorial on Managed calling native code (p/invoke)
The answer to your other question is that you can create a native project that will create a .dll for each program in the project instead of linking them all together into one .dll.
This is called a multiple output project and can be set under Project-->Properties-->Application tab-->Output to multiple .executables.
Then as long as the program-id is the same as the .dll name you can call it without setting a procedure pointer.
Thanks.