It\'s a little hard to resume it in a single title, so here my situation.
I\'m building a C# application that loads a C++ library.
I call functions from that C++ DLL
This can be done, and overall not that big of a deal. However there are several points of consideration.
Since you said C++ and not C, note that besides static class, instance methods, friend functions, etc, there are still some functions that are not loadable via DllImport due to name mangling. Avoiding COM, wrapping C++ in C is occasionally used as a more portable strategy to allows libraries to be wrapped by other languages .Net or otherwise. Similarly some of these same considerations apply to callbacks from the wrapped library as in your case. Although the prototypes from your DLL don't seem to likely be a problem, if it really was built with a C++ compiler it might be worth looking at the exported symbol names to make sure.
Tell help with your searching, you need to know the vocabulary. Generally when a function takes as a parameter another function to be called either during the function invocation or later on, that is refereed to as a callback. In the world of C and C++ this is based around a language feature known as function pointers (which also serve purposes other than callbacks). Often in MS documentation the overall process of letting different functions be dynamically bound to different callers at runtime is called delegation. The C# equivalent of a function pointer are objects known as delegates created with the C# keyword delegate. I would recommend first creating some experiment programs in just C# using this feature to first understand how that works.
Also DllImport is part of the implementation, the actual .Net facility you are using is pinvoke. That should also when looking for more info. What you then want to do is to export your delegate with pinvoke by marshaling it as a function pointer. That is, .Net will create a shim function for the native code to run. Two big problem areas that often take several tries are 1) making sure this shim function / marshaled delegate itself has the correct calling convention, and 2) that the object lifetime of this behind-the-scenes shim function is such that it still exists when the native DLL is going to use it. This curve ball can sometimes require manually overriding the garbage collection behavior, but usually this means just keeping a reference to it.
I actually think that the Mono Documentation is way better than MSDN in this area also.