DLL Import Failure

Started by
31 comments, last by myvraccount 9 years, 9 months ago

Add reference is used to add .NET assemblies, such as the Class Library project type. You should not add a reference to a non-.NET dll. Instead, you can add it to your project (Add existing item) and then just modify its properties to "Copy If Newer" (or "Copy Always"). That way your C++ DLL(s) will be copied into your output directory (bin\Debug or bin\Release). You can probably also use the solution properties to setup a dependency if both the C++ project and the C# project is in the same solution, but I've never tried this.

You should only copy the .DLL, the .LIB is used if you want to import it from another C or C++ project and the .pdb is just debug information (which might be useful when debugging, but is not required or used when running without debugger).

[DllImport("SomeLibrary")] can take the filename with or without .dll. The reason why you may want to do it without the .dll suffix is that it plays better with Mono should you ever want to support Linux or whatever.

Edit:

When you add a platform and/or a configuration, you basically create a whole copy of the set of settings, which just some single settings changed. If you check your project settings, you can find the target platform under the build tab.

I've never really checked what happens when it can find a .DLL for the right platform, but the .DLL does not contain the requested function. Maybe you get a DllNotFoundException in that case as well? How did you export your function in your C++ code? Did you use extern "C"? Did you use a .def file or __declspec( dllexport )?

Advertisement

Well that's all good to know but the only reason I was copying everything to everywhere was as a precaution, on the off chance that it was not finding it because it was in the wrong place. And it can't hurt, can it?

I just want to make absolutely sure nothing can go wrong, but somehow it still is.

What you say about not using references for non-.NET DLLs, assuming it's correct, it would rule out that as a possible cause of the error, and leave me fresh out of ideas! So now what?

As I wrote in my edit, how do you export the function? With a .def file or __declspec( dllexport )? Also, you need to use extern "C". Please confirm you have done this..?

[Edit]

Also, did you tell it to use stdcall in the C++ code? Otherwise you'll need to tell the C# code to import it using Cdecl, unless you are using Linux or Win64. :)

Well I couldn't find the settings, because there's a project->properties menu item but not project->settings, and I couldn't find any build tab in the properties one. I had been just using the Configuration Manager, but wondering why the settings weren't available there. I can right-click on the project in the Solution Explorer and click properties, but that doesn't have a build tab either. Of course I'm not at my computer now so I can't use VS2012 and am using VS2010 right now instead. I'm not sure if that makes a difference. (I'm developing this project on VS2012 express though).

I don't think it would have a DllNotFoundException problem if it can't find the function, but then I didn't even think that the problem was that it can't find the function. Do you think that's what the problem is? The way I interpreted it, once the error changed to BadImageFormatException, I thought maybe it could find the DLL but it just couldn't read it for some reason.

I'm using Win64, it might be nice to have it compatible with Win32 also but it's not essential, and I don't care at all about Linux or Mac etc. for this.

How did I export them? Let's see... I set up the project to make a DLL automatically and then I just added my code to it, but I read through the auto-generated code (this is from memory), and it looked like it was #defining something as "__declspec(ddlexport)" or something extremely similar to that. I'm pretty sure that was the exact text, and then it used it in the declaration of all functions. I also thought I saw an extern "C" at some point but I'm not certain of that - I'd have to check, I'm pretty sure though. I don't think there was any .def file at all. I think I'd remember that.

Also, I tried putting the 3rd party DLL (DLL A) directly into C# instead of using my DLL (DLL B), and that may actually work better in the long run. I think I got it to even change the exception from DllNotFoundException to BadImageFormatException (which I consider progress). But if I do it that way I don't know whether it used __declspec(dllexport) or extern "C" or a .def file, but would it even matter? All they give me is a DLL and a LIB.

I've tried my DllImport with .dll extension and with .lib (that didn't work), but I haven't tried it without an extension. I'm not sure that would make a difference though.

I probably need to somehow set the settings for the build mode (32 vs 64 bit) more specifically than I have already. Either that or I must be overlooking some tiny detail somewhere.

Just go with 32 bits for everything, unless you really need 64 bits for something (very much memory usage).

I created two projects, using VS2010 though. First, I created a C# Console Application project, then I created a Win32 project which I changed in the create project wizard to Dll project and checked the Exports symbols.

TheDll.h:


#ifdef THEDLL_EXPORTS
#define THEDLL_API __declspec(dllexport)
#else
#define THEDLL_API __declspec(dllimport)
#endif


extern "C"
THEDLL_API int fnTheDll(void);

TheDll.c:


#include "stdafx.h"
#include "TheDll.h"



// This is an example of an exported function.
extern "C"
THEDLL_API int fnTheDll(void)
{
	return 42;
}

Program.cs:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.Write("It returns {0}", fnTheDll());
        }

        [DllImport("TheDll", CallingConvention = CallingConvention.Cdecl)]
        private static extern int fnTheDll();
    }
}

When I created these projects, the x86 platform was the default for the C# project and Win32 was the default for the C++ DLL project. This showed up as Mixed Platforms. I had to manually copy the TheDll.dll to the Debug folder of my C# project, that is next to my ConsoleProject.exe. Only the .dll and the .exe is needed.

If I try to import a function that does not exist, I get an EntryPointNotFoundException. If I change the C# project to build for x64 instead, without changing the C++ DLL to 64 bits, I get a BadImageFormatException. If I don't copy the C++ dll manually to the same folder as the .exe, I get a DllNotFoundException.

Edit:

Do note the extern "C" lines. They were added by me, and they are required.

Well this is interesting. Most of that code looks similar to what mine auto-generated (like your definition of THEDLL_API). I don't think I ever set the CallingConvention parameter, and it was just using the default setting, because I was following an example that did it that way. I wonder if that's what caused the problem? That's definitely worth looking into! But I never got an EntryPointNotFoundException, and I know that I spelled the function name correctly, so that's probably not causing the problem, but it's good to know in case I ever do get that exception. I think the extern "C" is there but I'll double check to make sure.

Anyway, I won't have time until Saturday but I think this might just solve the problem. If you have any other ideas though I'm all ears, in case this doesn't work. I'll try everything. I've been working on all this DLL stuff a little at a time on Saturdays for a couple months now and I'm so close that I hope it finally all works this weekend. So I'll keep my fingers and my eyes crossed wacko.png and thanks for all your help!

If you get a DllNotFoundException, the dll is in the wrong place. If you get a BadImageFormatException, it can find the dll, but it's corrupt or more likely for the wrong platform (ie a 64-bit dll when you need a 32-bit one). If you get an EntryPointNotFoundException, you've misspelled the name of the function or similar. If it crashes and/or throws some exception about memory and/or stack violations, you may be using the wrong calling convention.

The problems will appear in that order, so if you get a BadImageFormatException, it won't even try to find the function.

I highly recommend using Win32 and x86 for everything, that is 32 bits. Once you've figured that out you can start moving into 64 bits. You can make a program that runs in 64 bits if possible and otherwise in 32 bits, but it's slightly messier since you need to check at runtime which DLL and/or function to use.

Well that's unfortunate, because it means I can't fix it by setting the CallingConvention parameter. I'll just start a new project with 32 bit instead of 64, and hope it works.

In the long run though, do you think that 32 bit will be future compatible?

I doubt 32-bit code will stop working on Windows anytime soon. When it does, I'm sure we'll have plenty of Dosbox-like solutions which will let people run the programs/games anyway so I wouldn't worry about that.

If you mean will 32 bits be enough in the long run, then it greatly depends on the application. There are applications/games today that needs or can greatly benefit from the additional memory capacity of 64 bits. On the other hand, for some applications the lower memory usage of 32 bits is beneficial and can sometimes speed up execution. There are also other trade offs such as number of registers and memory fragmentation issues, but in the end it really depends on your game/application.

I doubt 32-bit code will stop working on Windows anytime soon.

In terms of lifespan, I am sure it could be argued that 32-bit .exes may well end up outliving the .NET framework on Windows ;)

Good point though, I completely forgot about the __declspec, __cdecl stuff! Writing bindings is so fiddly, no wonder nothing has managed to fully replace C or C++ yet.
Perhaps the following small example may help (http://www.dotnetperls.com/dllimport).

I may be wrong but I am quite sure that the 64-bit version of .NET has no problem calling 32-bit dlls. This should all be abstracted and marshalled anyway.
http://tinyurl.com/shewonyay - Thanks so much for those who voted on my GF's Competition Cosplay Entry for Cosplayzine. She won! I owe you all beers :)

Mutiny - Open-source C++ Unity re-implementation.
Defile of Eden 2 - FreeBSD and OpenBSD binaries of our latest game.

This topic is closed to new replies.

Advertisement