我在项目中使用 activeX 组件时遇到内存泄漏。我正在使用 Embarcadero Rad Studio 10.2 并开发一个需要与同一台机器上的 Codesys 软 PLC 通信的 C++ 工业程序。
所以,我有一个 ActiveX 组件可以处理我的程序和软 PLC 之间的通信部分。
我导入了 ActiveX,一切看起来都还不错,但我发现了一个每小时大约 20MB 的内存泄漏......要导入库,我遵循了官方指南: http ://docwiki.embarcadero.com/RADStudio/Tokyo/en /Registering_a_COM_Object
我做了很多测试,我意识到每次使用涉及变体的 ActiveX 方法时都会发生内存泄漏。看起来该程序无法释放组件使用的某种临时变体。
我已经测试了 Visual Studio 示例并且一切正常,所以我认为问题是由 Rad Studio 在我导入 activeX 组件时生成的类型库产生的。ActiveX 开发人员还声称一切都适用于 Visual Studio。
我还使用了 Dr. Memory 和其他确认泄漏存在但无法提供详细信息的工具,因为我认为 ActiveX 不是为调试而编译的。
关于这种行为的原因有什么想法吗?
RAD studio 中的 ActiveX 可能存在一些不兼容问题?
提前致谢
编辑
显示 ActiveX 用法的示例。
单元1.cpp
#include <vcl.h>
#pragma hdrstop
#pragma package(smart_init)
#pragma resource "*.dfm"
#include <System.IOUtils.hpp>
#include "Unit1.h"
TForm1 *Form1;
// ---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner)
{
counter = 0;
// Setting the path for communication setting files required for later connection
if (TFile::Exists("PLCHandler.ini"))
{
iniPath = (wchar_t*)L"PLCHandler.ini";
logPath = (wchar_t*)L"Log.txt";
}
iResult = PLCHandler->MCreate(&iHandle);
try
{
// Creating the component and retrieving the handle for other methods
iResult = PLCHandler->MCreate(&iHandle);
if (iResult == 0)
{
iResult = PLCHandler->MConnect(iHandle, 0, iniPath, logPath);
if (iResult == 0)
{
connected = true;
LabeledEdit1->Text = "CONNECTED";
long int numeroSimboli = 0;
PLCHandler->MGetNumberOfSymbols(iHandle, &numeroSimboli);
LabeledEdit2->Text = numeroSimboli;
PLCHandler->MGetPlcStatus(iHandle, &iPLCStatus);
LabeledEdit3->Text = iPLCStatus;
}
}
else
{
LabeledEdit2->Text = "ERROR: " + (String)iResult;
}
}
catch (...)
{
LabeledEdit2->Text = "ERROR";
}
}
// ---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
// Timers for testing purposes, they launch the next method every ms. Changing timing only delays the problem
Timer1->Enabled = !Timer1->Enabled;
Timer2->Enabled = !Timer2->Enabled;
}
// ---------------------------------------------------------------------------
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
// Asking to the PLC Handler the value of a PLC variable, identified by name
Variant varReceived;
BSTR name = SysAllocString(L"Test.GVL.Test_INT");
try
{
counter++;
LabeledEdit1->Text = counter;
// This is where i suppose the memory leak happens; the problem vanishes commenting the next line
varReceived = PLCHandler->MSyncReadVarFromPlc(iHandle, &iResult, name, 2);
LabeledEdit3->Text = varReceived.GetElement(0);
SysFreeString(name);
VarClear(varReceived);
}
catch (...)
{
VarClear(varReceived);
SysFreeString(name);
}
}
// ---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
Timer1Timer(this);
}
// ---------------------------------------------------------------------------
void __fastcall TForm1::Button3Click(TObject *Sender)
{
// Other test: destroy the component and recreates it: the memory usages remains the same, no deallocation happens
try
{
PLCHandler->MDelete(&iHandle);
iResult = PLCHandler->MCreate(&iHandle);
if (iResult == 0)
{
iResult = PLCHandler->MConnect(iHandle, 0, iniPath, logPath);
if (iResult == 0)
{
connected = true;
LabeledEdit1->Text = "CONNECTED";
long int numeroSimboli = 0;
PLCHandler->MGetNumberOfSymbols(iHandle, &numeroSimboli);
LabeledEdit2->Text = numeroSimboli;
PLCHandler->MGetPlcStatus(iHandle, &iPLCStatus);
LabeledEdit3->Text = iPLCStatus;
}
}
else
{
LabeledEdit2->Text = "ERROR: " + (String)iResult;
}
}
catch (...)
{
LabeledEdit2->Text = "ERROR";
}
}
// ---------------------------------------------------------------------------
单元1.h
#ifndef Unit1H
#define Unit1H
// ---------------------------------------------------------------------------
#include <System.Classes.hpp>
#include <Vcl.Controls.hpp>
#include <Vcl.StdCtrls.hpp>
#include <Vcl.Forms.hpp>
#include <Vcl.ExtCtrls.hpp>
#include <Vcl.OleCtrls.hpp>
#include "PLCHANDLERXLib_OCX.h"
// ---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published: // IDE-managed Components
TTimer * Timer1;
TButton * Button1;
TLabeledEdit *LabeledEdit1;
TTimer * Timer2;
TLabeledEdit *LabeledEdit2;
TButton * Button3;
TPLCHandlerX *PLCHandler;
TLabeledEdit *LabeledEdit3;
void __fastcall Button1Click(TObject *Sender);
void __fastcall Timer1Timer(TObject *Sender);
void __fastcall Button2Click(TObject *Sender);
void __fastcall Button3Click(TObject *Sender);
private:
// User declarations
public: // User declarations
long int counter;
wchar_t* iniPath;
wchar_t* logPath;
long int iPLCStatus;
long int iHandle;
long int readSize;
long int writeSize;
long int iResult;
Byte unbyte;
bool connected;
__fastcall TForm1(TComponent* Owner);
};
// ---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
// ---------------------------------------------------------------------------
#endif
并根据要求在导入 ActiveX 时由 RAD Studio 生成 TLB
.cpp 文件
// ************************************************************************ //
// WARNING
// -------
// The types declared in this file were generated from data read from a
// Type Library. If this type library is explicitly or indirectly (via
// another type library referring to this type library) re-imported, or the
// 'Refresh' command of the Type Library Editor activated while editing the
// Type Library, the contents of this file will be regenerated and all
// manual modifications will be lost.
// ************************************************************************ //
// $Rev: 87174 $
// File generated on 14/03/2018 11:22:13 from Type Library described below.
// ************************************************************************ //
// Type Lib: C:\PLCHandler_SDK_Windows_v16\bin\Windows\PLCHandlerX.ocx (1)
// LIBID: {BB4C0C2B-D94B-4F5C-A774-4DF59A2227FF}
// LCID: 0
// Helpfile: C:\PLCHandler_SDK_Windows_v16\bin\Windows\PLCHandlerX.hlp
// HelpString: PLCHandlerX ActiveX Control module
// DepndLst:
// (1) v2.0 stdole, (C:\Windows\SysWOW64\stdole2.tlb)
// SYS_KIND: SYS_WIN32
// ************************************************************************ //
#include <vcl.h>
#pragma hdrstop
#include "PLCHANDLERXLib_TLB.h"
#if !defined(__PRAGMA_PACKAGE_SMART_INIT)
#define __PRAGMA_PACKAGE_SMART_INIT
#pragma package(smart_init)
#endif
namespace Plchandlerxlib_tlb
{
// *********************************************************************//
// GUIDS declared in the TypeLibrary
// *********************************************************************//
const GUID LIBID_PLCHANDLERXLib = {0xBB4C0C2B, 0xD94B, 0x4F5C,{ 0xA7, 0x74, 0x4D,0xF5, 0x9A, 0x22,0x27, 0xFF} };
const GUID DIID__DPLCHandlerX = {0xA51B6208, 0x4C76, 0x4E79,{ 0xAC, 0x93, 0xB4,0x15, 0x7D, 0x6D,0x97, 0xC5} };
const GUID DIID__DPLCHandlerXEvents = {0xF2CC045D, 0x93E1, 0x4FE1,{ 0xA1, 0x5F, 0xE6,0x48, 0x18, 0x85,0x35, 0x5A} };
const GUID CLSID_PLCHandlerX = {0x99036BDD, 0x9A94, 0x4ED2,{ 0x89, 0x61, 0x42,0x0C, 0x74, 0xDD,0x51, 0xCE} };
};
.h 文件对于问题主体来说太长(此处为完整代码),但 MSyncReadVarsFromPlc 方法是
VARIANT __fastcall MSyncReadVarsFromPlc(long lHandle, long* plResult, BSTR pszSymbols, VARIANT SizeList, long lNumOfVars)
{
_TDispID _dispid(/* MSyncReadVarsFromPlc */ DISPID(45));
TAutoArgs<5> _args;
_args[1] = lHandle /*[VT_I4:0]*/;
_args[2] = plResult /*[VT_I4:1]*/;
_args[3] = pszSymbols /*[VT_BSTR:0]*/;
_args[4] = SizeList /*[VT_VARIANT:0]*/;
_args[5] = lNumOfVars /*[VT_I4:0]*/;
OleFunction(_dispid, _args);
return _args.GetRetVariant();
}
正如您在 TLB 中看到的那样,MSyncReadVars 方法返回一个 VARIANT,该 VARIANT 实际上包含具有请求的变量值的字节数组。
Variant varReceived 存储返回的 VARIANT,但在完成时使用 VarClear 解除分配。
知道什么会产生内存泄漏吗?
我的感觉是从 MSyncReadVarsFromPlc 返回的 VARIANT 在方法执行后没有被释放。但我看不到任何解决此问题的方法,也因为 Visual Studio 示例中的相同用法可以正常工作。
ActiveX 能否在 Visual Studio 而不是 RAD Studio 中正常工作?