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.
HandlerComponentImplementationare 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.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 ofHandlerComponentmembers 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.