Освобождение памяти - Динамический массив из записей

SQL-Talker
Дата: 07.06.2019 16:13:59
Есть динамический массив записей
type
TSubjectItem = record
  Name: string;
  Value: string;
end;
TSubjectArray = array of TSubjectItem;
...
FItems: TSubjectArray;

Во время работы добавляю в него элементы:
procedure TSubject.Add(Item: TSubjectItem);
begin
  FItems := FItems + [Item];
end;

При выходе, освобождаю выделенную под нее память память :
SetLength(FItems, 0);

Так вот, иногда на этом SetLength вываливается ошибка "Invalid pointer operation"
В чем проблема? Записи нужно как-то специально зачищать?
_Vasilisk_
Дата: 07.06.2019 16:37:28
SQL-Talker
При выходе, освобождаю выделенную под нее память память :
SetLength(FItems, 0);
Это не обязательно. Он сам убьется как только выйдет из зоны видимости


SQL-Talker
Так вот, иногда на этом SetLength вываливается ошибка "Invalid pointer operation"
В чем проблема?
Где-то портится память.

Подключайте FastMM в FullDebugMode и смотрите
SQL-Talker
Дата: 07.06.2019 17:20:13
_Vasilisk_,

Подключил. Получил сообщение об утечке
Проблемы в этой процедуре:
function Cert_GetSubjectStr(aCertContext: PCCERT_CONTEXT): string;
type
  CERT_RDN_ARRAY = array of CERT_RDN;
var
  i: Integer;
  pvStructInfo: PVOID;
  cbStructInfo: DWORD;
  pCertNameInfo: PCERT_NAME_INFO;
  vRDNAttr: PCERT_RDN_ATTR;
  pszValue: PWIdeChar;
  RDNValueSize: DWORD;
  pInfo: PCCRYPT_OID_INFO;
  szName, szValue: string;
begin
  // декодируем Subject
  if CryptDecodeObject(
    X509_ASN_ENCODING or PKCS_7_ASN_ENCODING,
    X509_UNICODE_NAME, //X509_NAME,
    aCertContext.pCertInfo.Subject.pbData,
    aCertContext.pCertInfo.Subject.cbData,
    CRYPT_DECODE_NOCOPY_FLAG,
    nil,
    cbStructInfo
  )
  then
  begin
    GetMem(pvStructInfo, cbStructInfo);
    try
      CryptDecodeObject(
        X509_ASN_ENCODING or PKCS_7_ASN_ENCODING,
        X509_UNICODE_NAME, //X509_NAME,
        aCertContext.pCertInfo.Subject.pbData,
        aCertContext.pCertInfo.Subject.cbData,
        CRYPT_DECODE_NOCOPY_FLAG,
        pvStructInfo,
        cbStructInfo
      );

      pCertNameInfo := PCERT_NAME_INFO(pvStructInfo);

      // перебираем массив атрибутов Subject-а
      for i := 0 to (pCertNameInfo.cRDN - 1) do
      begin
        vRDNAttr := CERT_RDN_ARRAY(pCertNameInfo.rgRDN)[i].rgRDNAttr;
        CertRDNValueToStr(vRDNAttr.dwValueType, @vRDNAttr.Value, nil, RDNValueSize);
        if RDNValueSize > 0 then
        begin
          GetMem(pszValue, RDNValueSize);       // <<<<<----- Строка 1323
          try
            CertRDNValueToStr({vRDNAttr.dwValueType} CERT_RDN_UNICODE_STRING, @vRDNAttr.Value, pszValue, RDNValueSize);
            pInfo := CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY, PChar(vRDNAttr.pszObjId), 0);
            szName := PWideChar(pInfo.pwszName);
            szValue := PWideChar(pszValue);
            Result := Result + szName + '=' + szValue + #13#10;
          finally
            FreeMem(pszValue, RDNValueSize);        // <<<<<----- Строка 1331
          end;
        end;
      end;
    finally
      FreeMem(pvStructInfo, cbStructInfo);
    end;
  end;

  Result := Trim(Result);
end;

Проблемы связаны с вызовом CertRDNValueToStr в строке 1325 - если этот вызов закомментировать, то утечки не будет...
Но что тут не так??
SQL-Talker
Дата: 07.06.2019 17:21:07
Скрин сообщения об утечке:
DmSer
Дата: 07.06.2019 17:21:58
"Invalid pointer operation" - это ошибка, которую генерирует дельфёвый менеджер памяти, если ему подсовывают левый (неизвестный ему) указатель (или nil).

TSubject.Add случайно не вызывается одновременно из параллельных потоков?
SQL-Talker
Дата: 07.06.2019 17:23:00
DmSer,

Нет, тут все в одном потоке
cptngrb
Дата: 07.06.2019 17:32:15
FreeMem(pszValue, RDNValueSize);


замени на
FreeMem(pszValue); 
alekcvp
Дата: 07.06.2019 17:43:07
SQL-Talker,

В FullDebugMode он ещё и лог-файл создаёт, а если включить map-файл, то он даже строчки кода туда засунет где что происходит.
SQL-Talker
Дата: 07.06.2019 17:46:07
cptngrb,

ничего не изменилось
_Vasilisk_
Дата: 07.06.2019 18:05:21
SQL-Talker
Скрин сообщения об утечке:

Картинка с другого сайта.
Это не утечка. Это Memory Corrupted - повреждение памяти. О чем я и говорил
SQL-Talker
Но что тут не так??
То, что вы используете случайный размер памяти

SQL-Talker
 CertRDNValueToStr(vRDNAttr.dwValueType, @vRDNAttr.Value, nil, RDNValueSize);
https://docs.microsoft.com/en-us/windows/desktop/api/wincrypt/nf-wincrypt-certrdnvaluetostra
psz

A pointer to a buffer to receive the returned string.

csz

Size, in characters, allocated for the returned string. The size must include the terminating NULL character.

Return Value
Returns the number of characters converted, including the terminating NULL character. If psz is NULL or csz is zero, returns the required size of the destination string.

Remarks
If psz is not NULL and csz is not zero, the returned psz is always a possibly empty null-terminated string.

SQL-Talker
CertRDNValueToStr(vRDNAttr.dwValueType, @vRDNAttr.Value, nil, RDNValueSize);