Search code examples
c#c++pinvokecalling-convention

How to fix 'EntryPointNotFoundException'


I'm trying to import external c++ methods into my C# code.

I have modified a Windows driver which I'm using to access memory. To invoke the driver, I'm using c++ interface. Finally, to invoke the the interface connecting me to the driver, I use C# code.

The problem I'm facing is that during the runtime, I get following error System.EntryPointNotFoundException: Unable to find an entry point named 'GetTargetPid' in DLL 'API.dll'.

Now, The interface itself consists only of single header file. I thought that maybe that is the problem, however from what I've read online, using single header file even for implementation is perfectly fine.

This is my import in C#

[DllImport("API.dll")]
public static extern IntPtr GetTargetPid();

and here I Invoke the method

IntPtr processID = IntPtr.Zero;
...
ProcessID = GetTargetPid();

So my C# code is nothing special.

Now here is my API.dll

extern "C"
{
...
class CDriver
{
public:
    //Handle to the driver
    HANDLE hDriver; 
    //Initialization of the handle              
    CDriver::CDriver(LPCSTR RegistryPath)
    {
        hDriver = CreateFileA(RegistryPath, GENERIC_READ | GENERIC_WRITE,
            FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
    }
    ...

    __declspec(dllexport)
    ULONG_PTR GetTargetPid()
    {
        if (hDriver == INVALID_HANDLE_VALUE)
            return false;

        PVOID Id = 0;
        ULONG_PTR Result;
        DWORD Bytes;

        if (DeviceIoControl(hDriver, IO_GET_PROCESS_ID, NULL, NULL,
            Id, sizeof(Id), &Bytes, NULL)) {

            Result = (ULONG_PTR)Id;
            return Result;
        }

        else
            return false;
    }

Most of the examples I'v read online are using static methods, is that of any importance? what I need is working import, i think this should be trivial, however I can't figure it out.


Solution

  • You have two problems. First problem __declspec(dllexport) ULONG_PTR GetTargetPid() compiles just fine and exports CDriver::GetTargetPid. You don't want that.

    In reading your CDriver code I'm convinced that it's not a singleton. If you really want to P/Invoke:

    extern "C" {
    __declspec(dllexport)
    CDriver *CreateCDriver(LPCSTR RegistryPath)
    {
        return new CDriver(RegistryPath);
    }
    
    __declspec(dllexport)
    ULONG_PTR GetTargetPid(CDriver *driver)
    {
        return driver->GetTargetPid();
    }
    
    __declspec(dllexport)
    CDriver *DestroyCDriver(CDriver *driver)
    {
        delete driver;
    }
    } // extern "C"
    

    Second problem: you are P/Invoking a C function. Need Cdecl declarations in C#:

    [DllImport("API.dll", CallingConvention=Cdecl, CharSet=CharSet.????)]
    public static extern IntPtr CreateCDriver(string name);
    
    [DllImport("API.dll", CallingConvention=Cdecl)]
    public static extern IntPtr GetTargetPid(IntPtr cdriver);
    
    [DllImport("API.dll", CallingConvention=Cdecl)]
    public static extern IntPtr DestroyCDriver(IntPtr cdriver);
    

    I can't tell from your code whether you compile ANSI or Unicode; fill in CharSet.???? correctly.

    The usage of this stuff is like this:

    IntPtr cdriver = null;
    try {
        cdriver = CreateCDriver("whatever");
        var pid = GetTargetPid(cdriver);
        // do whatever with pid
    } finally {
        DestroyCDriver(cdriver);
    }
    

    The moment you have to move a cdriver reference off the stack you need Dispose() and Finalize().

    internal class CDriver : IDisposable {
        private IntPtr cdriver;
    
        public CDriver(string registry)
        {
            cdriver = CreateCDriver("whatever");
        }
    
        public void Dispose()
        {
            Dispose(true);
            GC.SupressFinalize(this);
        }
    
        protected virtual void Dispose(bool disposing)
        {
            DestroyCDriver(cdriver);
            cdriver = IntPtr.Zero;
        }
    }