Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/127.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ C++;COM对象热补丁?_C++_Com_Closures_Hook - Fatal编程技术网

C++ C++;COM对象热补丁?

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

背景:

我正在连接windows COM对象

所使用的方法是可变修改。假设我们有一个名为instance接口A实例,它在接口中包含oldmethod,我将其替换为newmethod。但是,在我的newmethod中,我需要知道oldmethod的地址,以便在完成自己的事情后调用oldmethod

在全局变量中存储oldmethod的地址是不安全的,因为接口a后面可能有多个实现,假设有两个实现,类A1类A2。因此,我的newmethod需要同时存储A1->oldmethodA2->oldmethod,并根据实例类型调用适当的函数

  • 实现这一点的一种方法是保留一个映射,它存储(vtable->oldmethod的地址)。因为vtable的地址可以作为A1类和A2类的区分符。在mynewmethod中,将为当前实例检查映射是否为正确的oldmethod。但是,这会使程序每次都检查映射,这会增加成本,而映射上的线程安全会增加成本

  • 另一种方法是创建一个闭包,我分配一块可执行内存,并将我的newmethod的二进制代码写在里面(它可以减小到最小大小,因此大小不是问题)。我在二进制代码中为每个实例修改oldmethod的地址。在这种情况下,地图上没有搜索成本

问题1

第二种方法是安全的,还是第一种方法更好?它们中的任何一个都有潜在的安全问题吗

问题2


第二种方法是,我创建的闭包包含特定于类的数据,即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的情况下将接口指针安全地向下转换回其内部类