In my last post, I mentioned that I configured the C++ DLL to compile with the
/clr option. I found this to be necessary in order to use the Visual Studio
Add Reference command, so that the C# app could find the DLL at runtime. For some reason, Add Reference failed unless I used
/clr. But strangely, I found that if I compiled without
/clr and copied the DLL into my bin/Debug folder, everything still worked. And that isn’t too surprising: P/Invoke lets you call plain old Win32 DLLs.
But why wasn’t Add Reference working? Well, it turns out that Add Reference only lets you reference assemblies, and a win32 dll is not an assembly; you need CLR support for that. I had assumed Add Reference was some kind of generic build dependency, but I guess it’s something more specific. This is kind of disappointing. Who ever heard of an IDE without some way of managing project dependencies?
At first I thought my only option was to use Pre- and Post-Build commands. If you go to the
Build Events tab of the C# properties window, you can see spaces for defining DOS commands that run before and after your build. VS supplies a bunch of macros you can use for important paths of other bits of information. So one way of solving my problem is to copy the DLL by hand. In the pre-build box, I entered this command:
copy "$(SolutionDir)$(Configuration)/MathDll.dll" "$(TargetDir)"
But that seems sort of like a hack. It leaves Visual Studio completely innocent of the project dependency. Later I found a slightly better (but still not so great) way of doing it. I got rid of this pre-build step, and under the C# project I used the
Add Existing Item command. Then I navigated to the dll and added it to the C# project. I set its
Copy to Output Directory property to
Copy if newer. I left the
Build Action property as
Content, which seems to mean that Visual Studio just leaves it untouched. So now, as long as I’ve set up the dll portion to run first, the dll gets copied properly. This seems a little better than the DOS way, because at least Visual Studio is somewhat aware of that the C# project uses the dll, and that fact is also less hidden from developers (i.e. me, six months later).
UPDATE: On second thought, my first approach seems better. Because of the $(Configuration) macro, it works whether I’m building a Debug or Release version. But the Add Existing Item approach forces me to point to a dll in either the Debug or Release directory.
Perhaps the best solution is simply to compile with
/clr. But I wanted to get both ways working, because I want to try both versions to see if either will resolve another error I’m getting, to be explained later. . . .
UPDATE 2: One problem I’ve found with the Pre-Build shell code is that even with $(Configuration), one line doesn’t work for all configurations. This is because if you set up, say, an “x64” configuration in addition to the default “Any CPU” one, then the folder structure isn’t parallel. The default configuration has a
bin/Debug folder, whereas the x64 configuration has a
bin/x64/Debug folder. This difference is not captured in the $(Configuration) macro. You can work around this because Visual Studio maintains separate property values for each configuration, so you can set up different pre-build shell commands for different configurations. But this is sort of annoying.