There are different kinds of DLLs, you'll need to treat them differently when you use them in a C# project. The 3 main kinds are:
- DLLs that contain unmanaged code and were designed to be used by a program written in unmanaged code. You cannot use such a DLL directly, there is no way to add a reference to them in a C# project. You must use P/Invoke, the [DllImport] attribute is required to declare the exported functions in the DLL. Glut32.dll is such a DLL. A very basic test you can use to see if you've got such a DLL is to run Dumpbin.exe /exports on that DLL. It lists the names of the functions that are exported.
- DLLs that implement an in-process COM server. They are written in unmanaged code as well. .NET has very good support for using such servers, as long as you have a type library for them. The type library is usually embedded in the DLL, Visual Studio expects to find it when you add a project reference, either through the COM tab or the Browse tab. A very basic test is Dumpbin.exe /exports again, an in-process COM server has 4 exported functions. The DllGetClassObject function is the important one. You can view the type library embedded in the DLL with OleView.exe, File + View Typelibrary. A good example is c:\windows\system32\shell32.dll
- DLLs that were created by a managed compiler. They don't contain machine code like the other types, they contain IL code and metadata. It is the native kind of DLL for managed code, you simply use Project + Add Reference to add a reference, the compiler automatically knows the types that are available in the DLL.
The first kind is the one you'll encounter a lot for DLLs in the wild. There's a lot of code written for Windows in an unmanaged language. It isn't a kind of DLL that's particularly easy to use from managed code. Glut32.dll for example has a lot of exported functions, writing a P/Invoke declaration for every single one of them is painful.
Tools you can use to help with this are SWIG and PInvoke Interop Assistant. The former is required when the DLL was written in the C++ language. C++ classes are not directly usable from a C# program, they need a wrapper written in the C++/CLI language. The latter tool is useful for DLLs written in C, including the Windows API.
Beware that those tools don't usually give you a clean and guaranteed-to-work interop solution. Declarations in unmanaged code are ambiguous, you'll need to know the exact semantics of the arguments of an unmanaged function to pick the right one. Getting the wrong one can be hard to diagnose, the best place to get help is a forum or Q+A site. Like stackoverflow.com