C++ C++;COM对象热补丁?
背景: 我正在连接windows COM对象 所使用的方法是可变修改。假设我们有一个名为instance的接口A实例,它在接口中包含oldmethod,我将其替换为newmethod。但是,在我的newmethod中,我需要知道oldmethod的地址,以便在完成自己的事情后调用oldmethod 在全局变量中存储oldmethod的地址是不安全的,因为接口a后面可能有多个实现,假设有两个实现,类A1和类A2。因此,我的newmethod需要同时存储A1->oldmethod和A2->oldmethod,并根据实例类型调用适当的函数C++ C++;COM对象热补丁?,c++,com,closures,hook,C++,Com,Closures,Hook,背景: 我正在连接windows COM对象 所使用的方法是可变修改。假设我们有一个名为instance的接口A实例,它在接口中包含oldmethod,我将其替换为newmethod。但是,在我的newmethod中,我需要知道oldmethod的地址,以便在完成自己的事情后调用oldmethod 在全局变量中存储oldmethod的地址是不安全的,因为接口a后面可能有多个实现,假设有两个实现,类A1和类A2。因此,我的newmethod需要同时存储A1->oldmethod和A2->oldme
- 实现这一点的一种方法是保留一个映射,它存储(vtable->oldmethod的地址)。因为vtable的地址可以作为A1类和A2类的区分符。在mynewmethod中,将为当前实例检查映射是否为正确的oldmethod。但是,这会使程序每次都检查映射,这会增加成本,而映射上的线程安全会增加成本
- 另一种方法是创建一个闭包,我分配一块可执行内存,并将我的newmethod的二进制代码写在里面(它可以减小到最小大小,因此大小不是问题)。我在二进制代码中为每个实例修改oldmethod的地址。在这种情况下,地图上没有搜索成本
第二种方法是,我创建的闭包包含特定于类的数据,即oldmethod指针。如果我需要在我的newmethod中存储特定于实例的数据,除了保留(此指针->数据)映射之外,还有其他策略吗?我尽了最大努力,却找不到办法。您可能没有类A1的源代码,但您是否可以控制它何时被实例化(通过“new”、CoCreateInstance或其他一些工厂函数)?如果是这样,那么只需实现一个实现接口a的类,并将接口a上的所有调用转发给实际对象,并截取您关心的方法 在下面的示例中,我们展示了一个替换的示例
class InterfaceA : public IUnknown
{
public:
virtual int M1() = 0;
virtual int M2(int x, int y) = 0;
virtual int M3() = 0;
};
class CMyWrapperClass : public InterfaceA
{
public:
int _refcount;
InterfaceA* _pInner;
CSomeClass2(InterfaceA* pInner)
{
_pInner = pInner;
_pInner->AddRef();
_refcount = 1;
}
~CSomeClass2()
{
_pInner->Release();
}
virtual int M1() {return _pInner->M1();}
virtual int M2(int x, int y) {printf("CSomeClass2::M2(x=%d, y=%d)\n", x, y); return _pInner->M2(x,y); }
virtual int M3() {return _pInner->M3();}
// not shown - addRef, release, queryinterface
};
// example instantiation
hr = CoCreateInstance(CLSID_A1, NULL, CLXCTX_ALL, IID_InterfaceA, (void**)&pInterfaceA);
// now do the wrap
pInterfaceA = new CMyWrapperClass(pInterfaceA);
如果您无法控制要热补丁的类的实例化,那么我确实有代码可以共享。但这显然有点复杂。如果这不起作用,我将发布另一个与热补丁COM vtable直接相关的答案。我花了很长时间才理解这个问题。实际上,我写了一段很好的代码来演示如何修补vtable,然后在包装器类中调用原始方法。修补vtable很容易 然后我发现了你所指的问题。也就是说,当调用经过修补的vtable方法(newmethod)时,即使它是在另一个类中定义的,“this”是原始对象,并且您没有调用哪个实例的任何上下文。因此,您不能简单地引用一个成员变量来返回您保存的“oldmethod”
所以经过一番思考,我认为全球地图是最安全的方法。您可能只需要一个锁(critical_部分)来保护在映射中插入、删除或查找函数指针。在从映射中安全地检索旧方法后,调用该方法时可能不需要持有锁。因此,执行此操作的运行时开销可以忽略不计。是否有某种特定的原因使您不只是从现有实现派生一个coclass,并使用签名必须匹配的“newmethod”虚拟地覆盖“oldmethod”,或者根据定义,您是,违反COM发布IID的合同和pin的接口。当然,除非这不是您的代码(旧方法),并且您基本上是在尝试钩住其他人的coclass。您也可以对alias进行共类,但听起来您更感兴趣的是偷偷地这样做。我正在挂接现有的COM对象以修改其行为,我没有实现。@WhozCraig和我没有直接调用该方法,我也没有调用方的代码。“然而,这将使程序每次都检查地图,这会增加成本,而地图上的线程安全将增加成本。“这是一个非常无效的假设。我不明白为什么这是无效的,你的意思是成本是可忽略的吗?我已经考虑过了,但是我认为它不安全,在调用方知道的不仅仅是接口中的功能。在这种情况下,包装器将失败。我正试图找到一个通用的方法来处理所有的案件,避免危险。我对你们的另一个解决方案很感兴趣。给我一个例子,说明上面提到的“不安全”的地方。类别A:InterfaceA。ClassA拥有InterfaceA拥有的所有虚拟功能。但是,ClassA还有一个方法M4(),软件的作者可能知道M4(),并在某处调用M4(),但提供的头文件可能不包含M4,因为它是私有的。我知道这是一种不好的做法,在编写自己的程序时,我不会这样做。我说我正在试图找到一种完全安全的方法,程序在挂接之前和之后都能正常工作。换句话说,你担心你试图破解的软件的内部,可能会违反COM的一般原则,并在不经过QueryName的情况下将接口指针安全地向下转换回其内部类