DLLs in .NET are quite different to native DLLs. .NET DLLs contain bytecode, called CIL, which has enough information for other programs - such as the csc compiler - to work out the classes, types, interfaces etc that are contained within.
This is quite different to native DLLs. Native DLLs contain binary instructions & mostly unstructured data, and there is (in general) no way to work out what the data means - for example somewhere in the DLL may be the two bytes (in hex) 43 23
and there's no way to tell whether the program will interpret these as the characters C#
or as the integer 17187
or a memory address, or even an instruction to feed to the CPU.
So, moving on to your questions:
- The
symbol table
is a piece of metadata for the DLL; it tells the compiler/linker how to convert void myDllFunc (int bar)
into an address in the DLL. It's basically a look-up table. Exporting functions from the DLL is how you tell which functions you'd like to end up in that look up table - these are the functions that other code will be able to call, because it will be able to find them. Remember - without other information, there's no way to tell where myDllFunc
starts.
extern C
is recommended because of the process of name resolution, and in particular, how C++ handles function overloading. When you have two functions:
int square (int x);
double square (double x);
the compiler needs some way to distinguish between them - the name "square" is now ambiguous, and can't be resolved to a single code address. C++ handles this by name mangling - the compiler takes the name of your function, square
, and then adds some magic strings corresponding to the types in the function signature. So, for example, the compiler could see your two functions as:
int int+square+int (int x);
double dbl+square+dbl (double x);
and they're now no longer ambiguous (real compilers don't use such a simple scheme). There are now two problems here:
- You want to refer to that function as "square", not "int+square+int", and, worse,
- Different C++ compilers use different name-mangling rules.
In order to make it easier to interoperate, people generally mark functions they export with extern C
, which makes the compiler use C's naming rules, in which the function's name is not mangled.
Edit to address comments:
Declaring the function signatures extern C
resolves the name mangling problem because C doesn't have name mangling. C can get away without name mangling because declaring two functions
int square (int x);
double square (double x);
is an error; the compiler/linker doesn't have to - and won't - handle that ambiguity.
Exporting a function from a DLL
is nothing more than adding the function to the symbol table. This makes it possible for code outside your DLL to call the function, because now external code can look up where the function starts. Thus, the function is "exported" in that other people can call it.