【问题标题】:Where does MFC COleControl::DoPropExchange store persistent properties?MFC COleControl::DoPropExchange 在哪里存储持久属性?
【发布时间】:2018-11-21 06:58:24
【问题描述】:

我已经接管了 C++ 中遗留的 MFC OCX 控件的维护工作。该项目现在在VS2013中。我试图了解 DoPropExchange 方法的功能。此方法似乎正在为控件中的几乎所有数据成员调用 PX_?????(member) ????是类型(Bool,Short,Long ...)我的理解是这些是为了提供属性的持久存储而调用的。但是根据我对 OCX 控件操作的理解,没有持久属性。还有其他理由打电话给PX_????对于 DoPropExchange 中的所有数据成员,除了支持持久属性?我还试图了解这些持久属性的加载/存储位置。用于加载/存储持久属性值的序列化文件在哪里指定?

这是 DoPropExchange 的来源

// CSigPlusCtrl::DoPropExchange - Persistence support
void CSigPlusCtrl::DoPropExchange(CPropExchange* pPX)
{
    DWORD Version;
    long BaudRate;
    short ComPort;
    BOOL Rv;

    LOG(("DoPropExchange Entry"));

    ExchangeVersion(pPX, MAKELONG(_wVerMinor, _wVerMajor));
    COleControl::DoPropExchange(pPX);
    Version = pPX->GetVersion();

    if (pPX->IsLoading())
    {
        LoadDefaultProperties();
        LoadIniParameters();
    }

    if ((Version & 0xFFFF0000) == (DWORD)MAKELONG(0, _wVerMajor))
    {
        Rv = PX_Short(pPX, _T("ImageFileFormat"), ImageFileFormat, 0);
        Rv = PX_Short(pPX, _T("ImageXSize"), ImageXSize, 0);
        Rv = PX_Short(pPX, _T("ImageYSize"), ImageYSize, 0);
        Rv = PX_Short(pPX, _T("ImagePenWidth"), ImagePenWidth, 1);
   . . .
        Rv = PX_Short(pPX, _T("ZoomY"), ZoomY, 0);
        Rv = PX_Short(pPX, _T("ZoomPower"), ZoomPower, 1);

        if (pPX->IsLoading())
        {
            if (SigBlob != NULL)
            {
                GlobalFree(SigBlob);
                SigBlob = NULL;
            }
        }
        else
        {
            if (SigBlob == NULL)
            {
                SigBlobType* SigBlobPtr;

                SigBlob = GlobalAlloc(GMEM_MOVEABLE, sizeof(DWORD));
                SigBlobPtr = (SigBlobType*)GlobalLock(SigBlob);
                SigBlobPtr->Size = 0;
                GlobalUnlock(SigBlob);
            }
        }

        if ((Version & 0xFFFF) == Version223)
        {
            Rv = PX_Blob(pPX, _T("SigBlob"), SigBlob, NULL);
        }
        if ((Version & 0xFFFF) >= Version224)
        {
            CString SigStr;

            if (!pPX->IsLoading())
            {
                SigStr = BlobToString();
            }
            Rv = PX_String(pPX, _T("SigStringStored"), SigStr, _T(""));
            if (pPX->IsLoading())
            {
                BlobFromString(SigStr);
            }
        }

    }
    else
    {
        SigMessageBox("Warning Incompatable Versions of SigPlus Control");
    }
    LoadTabletParameters();
    LOG(("DoPropExchange Exit"));
}

编辑于 2018 年 6 月 21 日添加

在调试器中运行我观察到,当调用 DoPropExchange 时,VS2013 会显示堆栈并显示下面的堆栈帧可能不正确的消息。而上面调用 DoPropExchange 的一帧来自 mfc120d.dll,它没有可用的符号文件 mfc120d.i386.pdb。

这个Microsoft Forum Post 似乎表明符号文件不适用于VS2015,我想知道VS2013 是否也是这种情况。到目前为止,我还没有找到下载 MFC120 符号进行调试的地方。

今天开始赏金寻找可以解释通常如何以及在何处为 OLE 控件序列化属性以及使用哪些方法来指定序列化数据存储位置/媒体的人。这是值得关注的,因为此控件在终端感知程序中的 Citrix ZenDesk 网络环境中运行,如果属性存储在某处,则每个客户端都需要指定该客户端唯一的位置。

【问题讨论】:

  • 我不明白这个问题。显然,您正在调用COleControl::DoPropExchange(pPX),因此您正在序列化基类的持久属性。您还在序列化自定义属性。你为什么认为没有持久属性?我不知道,除了持久化信息之外,您是否正在使用序列化支持。但它也可以用于跨公寓编组控制状态。我并不是说这种情况正在发生,但流不仅仅代表磁盘上的文件。
  • "你为什么假设没有持久属性..." 因为我从用例中知道 OCX 控件的操作,它没有持久的属性。我试图了解上面的代码是否试图将属性存储在某个地方,如果是,那么在哪里。或者它只是用于在启动时加载默认值。这是旧版 OCX,专门使用 INI 文件,不使用注册表。执行 OCX 时,INI 文件不会更改。因此,如果它试图持久化属性,那么这些值的存储位置就是一个谜。
  • 不清楚,为什么您认为知道数据存储在哪里会帮助您理解代码。它显然正在存储持久数据(例如ImageFileFormat)。如果您想了解有关代码的更多信息,请在调试器下运行它,设置断点/跟踪点等。您提出的问题似乎并没有让您更接近您的目标,这显然是理解您的代码。
  • 控件所有属性的值要么由 LoadDefaultProperties() 方法中的硬编码值设置,要么在称为 LoadIniProperties 的方法中从 INI 文件中读取。来自容器应用程序的任何修改属性的调用都不会在控制实例或系统重新启动之间持续存在。并且 OCX 控件不会将任何值写回 INI 文件。所以我试图理解为什么最初的开发者创建了一个看起来正在加载/存储属性的 DoPropExchange。
  • 我已经在调试器中广泛运行了代码。挑战在于对 DoPropExchange 的调用来自 MFC 框架,目前 VS2013 不允许我进入或跟踪调用者 MFC 代码。因此,此时无法访问调用 DoPropExchange 之前和调用 PX_Short/Long 等之后发生的情况。

标签: c++ mfc persistence ocx


【解决方案1】:

DoPropExchange 主要用于在设计和运行时之间实现控制属性的持久化。实际的目标接收器由 OCX 的客户端传递。

在 VC 中设置存储在 RC 文件中,而在 VB 中存储在 frm 和 frx 文件中。如果您在记事本中打开 FRM,您可能会看到包含此​​控件属性的部分。 附带说明一下,如果在 HTML 中使用控件并在 html 本身中使用内联设置,则有类似的实现。

除非您的 VB 客户端通过直接调用属性包函数在外部保存设置,否则您在这里不太可能遇到问题,因为上述属性不会在运行时存储。

【讨论】:

  • 我还是不明白。 OCX 是 C++ 语言。您说属性值存储在 .rc 文件中。但是 .rc 文件在运行时不可用。你说目标接收器是由客户端传递的。在哪里?如何?我没有看到任何要求这样做的电话。在 VB 客户端中实例化 OCX 时,没有参数支持此传递。设置 m_ZoomSigPlus2 = CreateObject("SIGPLUS.SigPlusCtrl.1")
  • CreateObject 示例实际上并未将任何内容传递给您的组件。但是,这不是 VB 窗体初始化子控件的方式。您的客户端实际使用的接口是 IPersistStream、IPersistStorage、IpersistPropertyBag。 MFC 实现调整调用并调用 DoPropertyExchange。有关 IpersistPropertyBag 的 MFC 实现,请参阅以下链接 fpc.org/fpc32/VS6Disk1/VC98/MFC/SRC/CTLPBAG.CPP
  • VB 客户端中没有对 IPersistStream、IPersistStorage、IPersistProperyBag 或 DoProperyExchange 的调用。我有使用 MFC Dialog Windows 进行数据交换的经验,在这种情况下,我了解正在交换的内容。与对话框中给定控件关联的类成员变量的数据正在与基础 Windows 控件进行交换。但是,在这种情况下,我不了解数据交换的两个端点是什么。一个是 OCX 控件类的数据成员,但我还没有听到关于交换的另一端是什么的解释。
  • 归根结底,当我在 VS Debugger 中运行 OCX 时,我看到 DoPropExchange 正在为 OCX 类中的每个数据成员调用 PX_type,但我看不到数据来自哪里或在现场运行时完成此交换时进行。我也没有在 VB 客户端中看到任何指定从何处检索或存储此数据的调用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-01-26
  • 2011-08-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-01-16
  • 2016-10-19
相关资源
最近更新 更多