Удаление указателя TStreamAdapter
WB->Document->QueryInterface(IID_IPersistStreamInit,(void **)&psi);
TMemoryStream *olds = new TMemoryStream();
TStreamAdapter *sa1 ;
sa1 = new TStreamAdapter(olds, soReference);
psi->Save(*sa1, true);
...
...
...
delete sa1; // тут AV
как правильно удалить sa1?
sa1->Free() тоже не проходит
про TMemoryStream ниче незнаю. ))
Кстати для работы с COM очень удобно юзать ATL-ные обертки типа
class CComPtr
и
template<class T, const IID* piid = &__uuidof(T)>
class CComQIPtr: public CComPtr<T>
WB->Document->QueryInterface(IID_IPersistStreamInit,(void **)&psi);
void *buff1 = malloc (DM->Query1BodyDoc->BlobSize);
TStream *s = DM->Query1->CreateBlobStream(DM->Query1->FieldByName("BodyDoc"), bmRead);
s->Seek(0, soFromBeginning);
s->Read(buff1, DM->Query1BodyDoc->BlobSize);
s->Seek(0, soFromBeginning);
TMemoryStream *olds = new TMemoryStream();
TStreamAdapter *sa1 ;
sa1 = new TStreamAdapter(olds, soReference);
//delete sa1; если тут удалять, то все ОК!
//return;
psi->Save(*sa1, true);
void *buff2 = malloc (olds->Size);
olds->Seek(0, soFromBeginning);
olds->Read(buff2,olds->Size);
olds->Seek(0, soFromBeginning);
if (olds->Size == DM->Query1BodyDoc->BlobSize && memcmp(buff1, buff2, olds->Size) == 0) // doc wasn't changed
{
free(buff1);
free(buff2);
psi->Release();
delete sa1;
delete olds;
return;
}
мешает psi->Save(...)
sa1 = NULL;
delete sa1;
так правильно вообще будет?
sa1 = NULL;
delete sa1;
так правильно вообще будет?
Так 100% не правильно. Ты же реальный указатель тут теряешь, а вместо него пытаешся обнулить NULL.
Там сказано, что не всегда метод Release приводит к удалению объекта.
В твоём примере, если не удалён psi, он может мешать удалять sal.
delete psi;
delete sa1;
один хрен
delete psi;
delete sa1;
один хрен
Почитай документацию по COM.
таким образом: delete psi;
ты не освободишь COM объект.
//delete sa1; если тут удалять, то все ОК!
//return;
psi->Save(*sa1, true);
т.е. прежде чем пытаться удалить этот указатель, поудаляй вначале все зависимости, скорее всего в psi, есть какой нибудь метод или функция, типа Close, которая будет закрывать этот sa1.
Или, возможен такой вариант: что сама функция Save затирает ту область памяти..т.е. освобождает объект. надо вообще почитать документацию для более конкретного ответа. Я уже сталкивался с такими фишками борланда...что save удаляет самостоятельно объект, тоже долго мучался в поиске решения...
в т.ч. и так:
psi->Release(); //отсвобождение COM объекта
delete olds; // удаление "связанной" с sa1 памяти
delete sa1;
один фиг AV и все тут
В справке по IPersistStream::Save написано
HRESULT Save(
IStream *pStm, //Pointer to the stream where the object is to be saved
BOOL fClearDirty //Specifies whether to clear the dirty flag
);
а в своём примере ты передаёшь не АДРЕС: sa1, а ОБЪЕКТ: *sa1 !
Возможно это всё и портит!
Плюс как написал nikipelovav - странный способ передачи значения в метод Save. Конечно оперетор * может быть переопределен для класса TStreamAdapter, а я не знаю что это за класс, так как не пользуюсь Borland-ом. Но ты все же уверен, что *sa1, возвращает именно то что нужно для передачи в Save() ?
S_OK
вся эта конструкция работает абсолютно правильно...
т.е. из IPersistStreamInit в TMemoryStream посредством TStreamAdapter передается поток, передается полностью, правильно и 100% работает...
единственный косяк с удалением этого промежуточного звена TStreamAdapter.
в общем и целом это не критично, однако, если в течении работы проги открыть около 100 документов, тогда потребляемая память возрастает на 5-10 метров, т.е. на 20-50 %, что не есть гуд...
он там определяется
а подключается к проекту судя по всему вот так:
#include <atl\atlvcl.h>
Посмотри как определён класс в ocidl.h
....
#if defined(__cplusplus) && !defined(CINTERFACE)
MIDL_INTERFACE("7FD52380-4E07-101B-AE2D-08002B2EC713")
IPersistStreamInit : public IPersist
{
public:
virtual HRESULT STDMETHODCALLTYPE IsDirty( void) = 0;
virtual HRESULT STDMETHODCALLTYPE Load(
/* [in] */ LPSTREAM pStm) = 0;
virtual HRESULT STDMETHODCALLTYPE Save(
/* [in] */ LPSTREAM pStm,
/* [in] */ BOOL fClearDirty) = 0;
virtual HRESULT STDMETHODCALLTYPE GetSizeMax(
/* [out] */ ULARGE_INTEGER *pCbSize) = 0;
virtual HRESULT STDMETHODCALLTYPE InitNew( void) = 0;
};
...
Ты пробовал вместо *sai передавать в метод Save - sai ?
Как Я понял, метод QueryInterface внутри себя вызывает метод IUnknown::AddRef , который ИНКРИМЕНТИРУЕТ счётчик вызовов интерфейса ( копий указателей ).
The IUnknown::AddRef method increments the reference count
for a the calling interface on an object. It should be called for
every new copy of a pointer to an interface on a given object.
Метод IUnknown::Release() , наоборот, ДЕКРИМЕНТИРУЕТ счётчик вызовов интерфейса ( копий указателей ). И лишь в случае СЧЁТЧИК = 0 очищает память!
Decrements the reference count for the calling interface on a object.
If the reference count on the object falls to 0, the object is freed from memory.
Возможно, ты в другом месте программы делаешь ещё вызов
и в твоем проблемном месте, поток sai занят?
P.S.: Для меня эта тема новая, свои мысли высказываю только на основе анализа справки ;)
2. QueryInterface вызываю всего один раз (видно по коду который выкладывал, он полный, за исключением записи в файл, но того что выложил достаточно для понятия проблемы)
3. psi->Release() делал столько раз сколько и QueryInterface (т.о. счетчик должен быть обнулен)
4. в качестве ЗЫ: облазил весь инет, нашел кучу примеров сохранения WB в поток/файл, но нигде в С++ вариантах не удаляется TStreamAdapter, отднако в дельфевских кодах вызывается sa.Free(); (через нее тоже пробовал, монопенисуально delete)
В справке по деструктору TStreamAdapter написано
Instead, it is freed automatically when its reference count drops to 0.
__property int RefCount = { read=FRefCount };
Description
RefCount defines the lifetime of the object.
When RefCount is zero, the object is destroyed.
RefCount is incremented by calls to the _AddRef method
and decremented by the _Release method.
Выходит что после psi->Release(); - sai->RefCount = 0 и sai автоматически удаляется!
откуда так много ума не приложу, когда РЕАЛЬНО вызывается ОДИН раз..
а весь гемор в том что память жрет и не освобождает
Это очистит RefCount в 0 и возможно освободит память.
уже лучше )))
TCppWebBrowser *WB;
Я обратил внимание на то, что создавая поток BLOB данных ты его не удаляешь!
...
if (olds->Size == DM->Query1BodyDoc->BlobSize && memcmp(buff1, buff2, olds->Size) == 0) // doc wasn't changed
{
free(buff1);
free(buff2);
psi->Release();
delete sa1;
delete olds;
return;
}
нет строки "delete s;". Возможно здесь причина засорения памяти.
Ты проверяешь изменение по таймеру? Если да, то отслеживаешь ли ты ситуацию повторного запуска обработчика, т.е. когда один обработчик выполняется, а второму "уже время".
TStream виртуальный класс ему не требуется new\delete
1) Насчёт
отслеживаю на OnBeforeNavigate2
В справке написано, что это событие вызывается перед переходом на другую страницу, вызванному методом Navigate или кликом на ссылку. Но ведь в этот момент WB отображает туже страницу что и какое-то время назад, по всей видимости, ту страницу, которая сохранена у тебя в BLOB поле.
По идее надо использовать событие OnDocumentComplet, т.е. когда страница полностью загружена,
и в этот момент сравнивать с предидущим состоянием.
2) Насчёт
TStream виртуальный класс ему не требуется new\delete
пожалуйста обоснуй.
Я работаю в своей БД с BLOB полями и удаляю поток данных после использования, поскольку так написано в справке:
The following example copies the data in the Notes field of Table1 to the Remarks field of ClientDataSet1.
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TBlobStream *Stream1;
TStream *Stream2;
Stream1 = new TBlobStream(Table1Notes, bmRead);
try
{
ClientDataSet1->Edit();
// here’s another way to create a blob stream
Stream2 = ClientDataSet1->CreateBlobStream(ClientDataSet1->FieldByName("Remarks"), bmReadWrite);
try
{
Stream2->CopyFrom(Stream1, Stream1->Size);
ClientDataSet1->Post();
}
__finally
{
delete Stream2;
}
}
__finally
{
delete Stream1;
}
}
Вот так, например, Я копирую содержимое BLOB поля в поток в памяти
TStream *BS = myDataSet->CreateBlobStream( myDataSet->FieldByName( myFieldName ), bmRead );
MS->CopyFrom( BS, BS->Size );
delete BS;
...
далее работаю с потоком в памяти MS
...
по поводу проверки на изменение документа:
прога организована т.о. что перед тем как загрузить новую страницу, она проверяет старую(сравнивает с БД), т.е. получается редактировался документ, юзер захотел перейти на другой, автоматом вызывается проверка перед тем как загрузится новый документ (т.е. перед тем как состояние БД изменится)
И как ситуация с sai. Ты писал о другом сообщении об ошибке. Пробовал моё предложение while ( psi->Release() )?
но все это вопрос не сильно решает... как росла память так и растет
while ( psi->Release() ) пробовал после этого и вылезла уже другая ошибка.
короче через ComQIPtr буду дклать, так поменьше жрет памяти