JustasMasiulis / lazy_importer

library for importing functions from dlls in a hidden, reverse engineer unfriendly way
Apache License 2.0
1.65k stars 222 forks source link

C++ Unable to determine which instance of D2D1CreateFactory is intended. #38

Closed rohybnol closed 3 years ago

rohybnol commented 3 years ago

Using this LI_FN(D2D1CreateFactory)(D2D1_FLAGS, FactoryOptions, &d2d_factoryPtr); leads me to this compiler output: C++ Unable to determine which instance of D2D1CreateFactory is intended. The Direct2D system library provides 4 overloaded versions for the D2D1CreateFactory function, how can I choose the right one for the LI_FN macro? Thanks

JustasMasiulis commented 3 years ago

2 solutions:

If those overloads were actually functional and weren't just a wrapper for the C API, you'd be in a world of pain, as the library would need the mangled name, but thankfully that's not the case.

rohybnol commented 3 years ago

Method 1 results into error C2365: "D2D1CreateFactory": redefinition; previous definition was "function".

JustasMasiulis commented 3 years ago

Method 1 results into error C2365: "D2D1CreateFactory": redefinition; previous definition was "function".

#include <Windows.h>
#include <d2d1.h>
#include "lazy_importer.hpp"

int main() {
    LoadLibraryA("d2d1.dll");
    using D2D1CreateFactory =
        HRESULT(WINAPI*)(
            _In_ D2D1_FACTORY_TYPE factoryType,
            _In_ REFIID riid,
            _In_opt_ CONST D2D1_FACTORY_OPTIONS* pFactoryOptions,
            _Out_ void** ppIFactory
            );

    void* pFactory;
    LI_FN_DEF(D2D1CreateFactory)(D2D1_FACTORY_TYPE_SINGLE_THREADED, __uuidof(ID2D1Factory), nullptr, &pFactory);
}

This compiles and runs perfectly fine for me.

rohybnol commented 3 years ago

Following exactly your example, however overhere there seem to be an issue, I am not really able to figure it out on my own whats happening.


        using D2D1CreateFactory =
            HRESULT(WINAPI*)(
                _In_ D2D1_FACTORY_TYPE factoryType,
                _In_ CONST D2D1_FACTORY_OPTIONS& factoryOptions,
                _Out_ void** ppFactory
                );

        LI_FN_DEF(D2D1CreateFactory)(D2D1_FLAGS, FactoryOptions, &d2d_factoryPtr);

Compiler output:
LazyImports.hpp(534,48): error C2664: "HRESULT (D2D1_FACTORY_TYPE,const D2D1_FACTORY_OPTIONS &,void **)" : conversion of argument 3 from "_Ty" to "void **" not possible
1> with
1> [
1> _Ty=ID2D1Factory2 **
1> ]
LazyImports.hpp(532,1): error C3169: "decltype(auto)": cannot derive type for "auto" from "HRESULT
Main.cpp(100,1): error C3779: "li::detail::lazy_function<1634981206,D2D1CreateFactory>::operator ()": a function that returns "decltype(auto)" must not be used before it is defined.```
JustasMasiulis commented 3 years ago

&d2d_factoryPtr is ID2D1Factory2 ** which cannot be converted to void ** - you'd simply need to explicitly cast the pointer (reinterpret_cast or C style cast in this case).

⚠️ IMPORTANT: I think you copied the C++ function definition and not the C definition. This wont work. There is only a single exported variant of this function and that is the C version with 4 parameters.

// this is just a wrapper from windows headers that I am assuming you copied - you want to use the real function that it's calling
template<class Factory>
COM_DECLSPEC_NOTHROW
HRESULT
D2D1CreateFactory(
    _In_ D2D1_FACTORY_TYPE factoryType,
    _In_ CONST D2D1_FACTORY_OPTIONS &factoryOptions,
    _Out_ Factory **ppFactory
    )
{
    return
        D2D1CreateFactory(
            factoryType,
            __uuidof(Factory), // <- the extra parameter that you need to have
            &factoryOptions,
            reinterpret_cast<void **>(ppFactory)); // <- the cast that you were missing
}

This is what your code should look like I think.

    using D2D1CreateFactory =
        HRESULT(WINAPI*)(
            _In_ D2D1_FACTORY_TYPE factoryType,
            _In_ REFIID riid,
            _In_opt_ CONST D2D1_FACTORY_OPTIONS* pFactoryOptions,
            _Out_ void** ppIFactory
            );

    LI_FN_DEF(D2D1CreateFactory)(D2D1_FLAGS, __uuidof(ID2D1Factory2), FactoryOptions, (void**)&d2d_factoryPtr);
rohybnol commented 3 years ago

You was right I've got that solved now. I have spot another issue as well when trying to use DefWindowProc like that:

LRESULT CALLBACK winproc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp)
{
    return LI_FN(DefWindowProcW)(hwnd, wm, wp, lp);
}

user32.dll is already loaded so thats not the issue, test env. just crashes when calling it.

thank you very much, great lib btw!

JustasMasiulis commented 3 years ago

thank you very much, great lib btw!

Thanks

I have spot another issue as well when trying to use DefWindowProc like that:

It's a forwarded export. The handling of them is explicit in this library.

  1. define LAZY_IMPORTER_CASE_INSENSITIVE. This is technically not mandatory but pretty much all forwarded exports will have the dll name in wrong case, so it's rather unlikely to work without this.
  2. Explicitly use forwarded export handling with one of 2 choices:
    • Enable it globally be defining LAZY_IMPORTER_RESOLVE_FORWARDED_EXPORTS, so most functions will try to resolve forwarded exports by default.
    • Use it on case-by-case basis with .forwarded() member function that returns the pointer, instead of the operator() overload. IE. LI_FN(DefWindowProcW).forwarded()(...)

As a small side note, you might want to cache the pointers to functions that will continuously be called. Either by your own mechanism or just using the built in functionality (take a look at the table in the readme), as these lookups aren't exactly free.

rohybnol commented 3 years ago

Alright I fully understand it now, thanks for explaining everything detailed like that - I got it all solved now with your instructions. By any chance, do you know a good way to get rid of all other imports like CRT as well? thanks again this lib is really great and you are a great person too, have a good one!

JustasMasiulis commented 3 years ago

By any chance, do you know a good way to get rid of all other imports like CRT as well?

Well it depends on what features you want to use. Getting STL work without exceptions enabled doesn't take too long (maybe a few hundred lines of copy pasting from google). But essentially you just don't link with CRT -> /nodefaultlib in linker options (it's called ignore default libraries or something like that) and then just implement missing functions as you go.

have a good one

You too 👍