0

I cannot share the actual project code, but I will provide an exact code example for the problem I face. I am trying to implement my own separate component (project) in the solution (Visual Studio 2022), which should be able to access anything outside, manipulate variables, and run functions from other components. The problem I face is that some functions or classes are not declared in the original header files, and I try to create a "fake header file" for those. Using this header file, I can build my own component successfully, access variables from the original instances, change, etc. But! It is impossible to call a function in the specific case (I get LINK2019 error when I try to call a function from the struct). Please help me to find a way to call functions, and I would be happy to see any other hacky ways to access external dll class instances, variables, functions, etc.

Common stuff

Note: CommonClass, CommonContainer are shared classes accross the Solution CommonContainer can return reference to anything inside with .GetReference function

Common.h

  • Just a wrapper classes for the system
#pragma once

class CommonClass {

};

template<class T>
class CommonContainer {
private:
    T* reference;

public:
    CommonContainer(T* newReference)
        : reference(newReference)
    {
    }

    T* GetReference() const {
        return reference;
    }
    
    CommonContainer& operator=(const CommonContainer& other)
    {
        reference = other.GetReference();
        return *this;
    }
    
    template<class OtherClass>
    CommonContainer& operator=(const CommonContainer<OtherClass>& other)
    {
        reference = other.GetReference();
        return *this;
    }
    
    CommonContainer& operator=(T* newReference)
    {
        reference = newReference;
        return *this;
    }
};

SharedComponent.h

  • Component which is shared across components and store those inside
#pragma once
#include "Common.h"
#include <map>
#include <string>

template<class T>
class SharedComponent {
    private:
    std::map<std::string, T*> componentsMap = {};
        
    public:
    T* Get(std::string name) {
        return componentsMap[name];
    }
    
    T* Set(std::string name, T* component) {
        componentsMap[name] = component;
        return component;
    }
};

External dll

Handler.h

  • Handler component (built in separate dll)
#pragma once

namespace outside
{
class HandlerComponent : public CommonClass
{
public:
    HandlerComponent();

private:
    void GetData(std::vector<std::string>* data);
    void Initialize(SharedComponent<CommonClass>* shared);
    CommonContainer<CommonClass> implementation;
};
}

Handler.cpp

#include "Common.h"
#include "SharedComponent.h"
#include <vector>
#include <string>
#include "Handler.h"

namespace outside
{

struct HandlerComponentImplementation : CommonClass
{
    void Initialize();
    std::vector<std::string> GetJsonData();

    struct InternalData
    {
        std::vector<std::string> requests;
        std::vector<std::string> responses;
        int counter;

        InternalData() : counter(0)
        {
            Update();
        }

        void Update()
        {
            // Update internal data
        }
    };

    InternalData internal;
};

void HandlerComponentImplementation::Initialize() {
    // Some init code
    internal = InternalData();
}

std::vector<std::string> HandlerComponentImplementation::GetJsonData() {
    internal.Update();
    return internal.responses;
}

void HandlerComponent::GetData(std::vector<std::string>* data) {
    auto componentImplementation = static_cast<HandlerComponentImplementation*>(implementation.GetReference());
    *data = componentImplementation->GetJsonData();
}

void HandlerComponent::Initialize(SharedComponent<CommonClass>* shared) {
    CommonContainer<HandlerComponentImplementation> componentImplementation = new HandlerComponentImplementation();
    implementation = componentImplementation;
    
    shared->Set("Handler", this);
}

}

My component

HandlerFake.h

  • It has fake header to make everything public for Handler classes
#pragma once
#include "Common.h"
#include "SharedComponent.h"
#include <vector>
#include <string>

namespace outside
{
struct HandlerComponentImplementation : CommonClass
{
public:
    std::vector<std::string> GetJsonData();

    struct InternalData
    {
    public:
        std::vector<std::string> requests;
        std::vector<std::string> responses;
        int counter;

        InternalData();

        void Update();
    };

    InternalData* internal;
};

class HandlerComponent : public CommonClass
{

public:
    HandlerComponent();
    void GetData(std::vector<std::string>* data);
    void Initialize();
    CommonContainer<CommonClass> implementation;
};

// Component to manipulate Handler instance
class ManipulatorComponent : public CommonClass
{
    void Initialize(SharedComponent<CommonClass>* shared);
};
}

HandlerFake.cpp

  • Here we get Handler component from shared and manipulate it
#include "Common.h"
#include "SharedComponent.h"
#include <vector>
#include <string>
#include "HandlerFake.h"

namespace outside {
    void ManipulatorComponent::Initialize(SharedComponent<CommonClass>* shared) {
        auto handler = shared->Get("Handler");
        auto handlerComponent = (HandlerComponent*) handler;
        
        // Can access whatever from handlerComponent
        auto implementation = handlerComponent->implementation;
        auto handlerComponentImplementation = static_cast<HandlerComponentImplementation*>(implementation.GetReference());
        auto handlerComponentImplementationInternal = handlerComponentImplementation->internal;
        
        // Get error if use this (LNK2019)
        handlerComponentImplementationInternal->Update();
    }
}

I have tried different tricks for redifining private and struct. I researched a lot about the problem, but eventually it seems like the issue is that those struct instances are declared in the runtime and Linker cannot resolve it. Somehow members of the instances are accessible, though. So I am still confused and would like to have some kind of solution and learn some hacky tricks to overcome my issue because I need to call these functions of structs inside.

5
  • If you need access to private members, consider making ManipulatorComponent a friend of HandlerComponentImplementation. This allows ManipulatorComponent to access private members directly. In Handler.cpp, add " friend class ManipulatorComponent;" in the HandlerComponentImplementation struct. Like below; struct HandlerComponentImplementation : CommonClass { friend class ManipulatorComponent; }; Commented Oct 9, 2024 at 7:04
  • @ZhenningZhang Unfortunately, I cannot make any changes to HandlerComponent; that is why I have to create HandlerFake.h in the first place and make the compiler think that members and functions are public. Commented Oct 9, 2024 at 8:31
  • Whether a member function is private or public is baked into the function's mangled name. The names of the functions you are trying to call therefore don't match the names exported from the DLL; hence the linker error. In addition, members of HandlerComponentImplementation are likely not exported from the DLL at all, so there's nothing to link with. Your fake header file is useless to the compiler because there are no .obj files to go with it; the symbol information that was in the original source code was lost back when the DLL was linked. Commented Oct 12, 2024 at 17:33
  • @IgorTandetnik Thank you for the comment. I figured out that those struct definitions inside HandlerComponentImplementation were never exported from the header file, so that might be an issue, but! Any instance members from HandlerComponentImplementation can be accessed in my case (from HandlerFake); expect functions. The question is how I could access those functions the easiest way, or maybe with some kind of tricky approach. Commented Oct 14, 2024 at 19:46
  • Think about it this way. Somewhere inside the DLL, there's the body of a member function of HandlerComponentImplementation, at some address. But there's no longer any symbolic name associated with that address; instead, the address is hard-coded (by the linker, at the time the DLL was linked) into the body of HandlerComponent members calling that function. If you can't rebuild the DLL from the source code, then the only other option is to reverse-engineer the function at the low, binary level, and call it at that level. That's not for the faint of heart. Commented Oct 14, 2024 at 23:40

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.