Который день бьюсь с возникшей проблемой, в части освобождения памяти!
Опишу что происходит в рабочем проекте.
Суть проблемы в следующем, есть функция void test(VString &arg), в которой из ранее созданного объекта pObj(pObj это класс который является обверткой вокруг COM объекта) вызывается функция GetVersionSoft, которая возвращает указатель(LPCWSTR ) на строку(версия софта). В тестовой функции вызов функции pObj->GetVersionSoft(d); закомметарен. Затем в функции WideCharToVString мы конвертируем в строку и полученную строку записываем в
переменную VString* a2. Выходим из функции WideCharToVString. После чего проводим присваивание arg = v8. В процесс выхода из функции вызывается деструктор класса VString переменной v8. Внутри которого происходит освобождение памяти и при вызове функции free происходит ексепшен с кодом 0x80000003. Другие деструкторы класса VString отрабатываются без проблем.
Для воспроизведения ошибки написал тестовый пример(исходники привел ниже), отличие от рабочего проекта только в закомментаренном вызове функции pObj->GetVersionSoft(d). Ошибка не возникает.
Может кто подскажет в чем проблема с освобождение памяти. При условии что другая прога(исходников нет) которая работает с этим COM объектом отрабатывает без ошибок.
Исходный код класса VString и тестовый пример ниже.
VString.h
#ifndef __VSTRING_H__
#define __VSTRING_H__
#include <windows.h>
class VString
{
public:
VString();
VString(int );
VString(char *pSrc);
VString(VString &pStr);
virtual ~VString();
void Internal_delete(char *pData);
char* Internal_new(unsigned int uiSize);
char* c_str() const;
unsigned int erase();
unsigned int length();
VString& operator=( const VString &rh);
VString& operator=( const char *szSrc);
public:
char* m_pString; //+4 указатель на строкуint m_LenString; //+8 дилина строкиint m_CountChar; //+C количество символов за минусом последнегох[\0 конец строки]
CRITICAL_SECTION m_CriticalSection;//+10
};
#endif
Здравствуйте, wbear, Вы писали:
W>Который день бьюсь с возникшей проблемой, в части освобождения памяти!
С таким кодом это надолго...
W>Для воспроизведения ошибки написал тестовый пример(исходники привел ниже), отличие от рабочего проекта только в закомментаренном вызове функции pObj->GetVersionSoft(d). Ошибка не возникает.
Это странно, потому что функция CoTaskMemFree получает указатель на константную строку L"1.4.d". что должно приводить к порче памяти, по идее.
W>Может кто подскажет в чем проблема с освобождение памяти. При условии что другая прога(исходников нет) которая работает с этим COM объектом отрабатывает без ошибок.
Скорее всего проблема связана с тем, как заказывается память для параметра GetVersionSoft.
PS В коде много ошибок, ключая утечку памяти и падение при некоторых сценариях использования VString.
Здравствуйте, wbear, Вы писали:
W>переменную VString* a2. Выходим из функции WideCharToVString. После чего проводим присваивание arg = v8. В процесс выхода из функции вызывается деструктор класса VString переменной v8. Внутри которого происходит освобождение памяти и при вызове функции free происходит ексепшен с кодом 0x80000003. Другие деструкторы класса VString отрабатываются без проблем.
Даже если пропустить весь код, до WideCharToVString в котором масса нюансов, скажем так:
int cchMultiByte = wcslen(lpWideCharStr) + 1; // Вычисление длины буфера неправильно
Внимательно посмотри документацию на функцию WideCharToMultiByte — это "комбайн", который и преобразовывает широкую строку в нужную тебе кодировку и она же вычисляет размер необходимого буфера. У тебя в качестве кодировки указана ANSI, ты уверен что это то, что тебе нужно?
delete(lpMultiByteStr); // Удаление константного буфера, непонятно как выделенного !!!
У тебя в примере буфер выделен вообще на стеке. На лицо порча хипа и привет.
А зачем критическая секция, объект класса используется в нескольких потоках? И зачем критическая секция в деструкторе, объект может удаляться из нескольких потоков???
W> int cchMultiByte = wcslen(lpWideCharStr) + 1;
W>
W> char* lpMultiByteStr;
W> lpMultiByteStr = new char[cchMultiByte];
Если в lpWideCharStr есть символы с кодом больше 127 и конвертируется в UTF-8, то длины буфера не хватит.
Здравствуйте, kov_serg, Вы писали:
_>ps: Нафига там CriticalSection?
Класс VString является частью большой библиотеки по работе с COM серверами и службами. В связи с этим и используется блокировка доступа.
Здравствуйте, B0FEE664, Вы писали:
BFE>С таким кодом это надолго...
Согласен что код корявый. Убрал из исходников все лишнее(проверки, код основных функций), чтобы можно было увидеть в чем может быть ошибка
BFE>Это странно, потому что функция CoTaskMemFree получает указатель на константную строку L"1.4.d". что должно приводить к порче памяти, по идее.
После константной строки находиться закомментаренная функция pObj->GetVersionSoft(d); Именно в этой функции я так понимаю какраз выделяется память и записывается строка с версией ПО.
W>>Может кто подскажет в чем проблема с освобождение памяти. При условии что другая прога(исходников нет) которая работает с этим COM объектом отрабатывает без ошибок. BFE>Скорее всего проблема связана с тем, как заказывается память для параметра GetVersionSoft.
BFE>PS В коде много ошибок, ключая утечку памяти и падение при некоторых сценариях использования VString.
Согласен что код очень сырой. НО моя задача на текущем этапе в проекте помочь, людям создать из ассемблеровских фрагментов набросок будущей SDK.
Думаю что код после меня еще не раз измениться.
Если укажите возможные сценарии возникновения ошибки — буду благодарен.
Здравствуйте, AleksandrN, Вы писали:
AN>А зачем критическая секция, объект класса используется в нескольких потоках? И зачем критическая секция в деструкторе, объект может удаляться из нескольких потоков???
Класс VString является частью большой библиотеки по работе с COM серверами и службами. В связи с этим и используется блокировка доступа.
AN>
W>> int cchMultiByte = wcslen(lpWideCharStr) + 1;
W>>
W>> char* lpMultiByteStr;
W>> lpMultiByteStr = new char[cchMultiByte];
AN>
AN>Если в lpWideCharStr есть символы с кодом больше 127 и конвертируется в UTF-8, то длины буфера не хватит.
AN>
AN>Почему потребовался свой велосипед вместо std::string?
Велосипед не мой. Это система импортная АСУ. Разраб АСУ за бугром, в связи с последними событиями выкатил отказ в поддержке ПО. Поставили задачу разобраться где и какие функции взаимодействуют с COM серверами И написать прототип будущего SDK. Вот и ковыряюсь.
Здравствуйте, wbear, Вы писали:
W>Добрый день, коллеги!
W>Который день бьюсь с возникшей проблемой, в части освобождения памяти!
W>Опишу что происходит в рабочем проекте. W>Суть проблемы в следующем, есть функция void test(VString &arg), в которой из ранее созданного объекта pObj(pObj это класс который является обверткой вокруг COM объекта) вызывается функция GetVersionSoft, которая возвращает указатель(LPCWSTR ) на строку(версия софта). В тестовой функции вызов функции pObj->GetVersionSoft(d); закомметарен. Затем в функции WideCharToVString мы конвертируем в строку и полученную строку записываем в W>переменную VString* a2. Выходим из функции WideCharToVString. После чего проводим присваивание arg = v8. В процесс выхода из функции вызывается деструктор класса VString переменной v8. Внутри которого происходит освобождение памяти и при вызове функции free происходит ексепшен с кодом 0x80000003. Другие деструкторы класса VString отрабатываются без проблем.
W>Для воспроизведения ошибки написал тестовый пример(исходники привел ниже), отличие от рабочего проекта только в закомментаренном вызове функции pObj->GetVersionSoft(d). Ошибка не возникает.
W>Может кто подскажет в чем проблема с освобождение памяти. При условии что другая прога(исходников нет) которая работает с этим COM объектом отрабатывает без ошибок.
W>Исходный код класса VString и тестовый пример ниже.
Что заметил:
1)
VString a("123456");
a=a; // сперва удалит строку, а потом попытается ее скопировать...
2)
Если Internal_new вернет nullptr, то мы никогда не выйде из критической секции:
Здравствуйте, wbear, Вы писали:
BFE>>Это странно, потому что функция CoTaskMemFree получает указатель на константную строку L"1.4.d". что должно приводить к порче памяти, по идее. W>После константной строки находиться закомментаренная функция pObj->GetVersionSoft(d); Именно в этой функции я так понимаю какраз выделяется память и записывается строка с версией ПО.
Сигнатуру функции GetVersionSoft можете показать?
Уверены, что память выделяется вызовом CoTaskMemAlloc ?
BFE>>PS В коде много ошибок, ключая утечку памяти и падение при некоторых сценариях использования VString. W>Согласен что код очень сырой. НО моя задача на текущем этапе в проекте помочь, людям создать из ассемблеровских фрагментов набросок будущей SDK. W>Думаю что код после меня еще не раз измениться.
Если работу стоит делать, то её стоит делать хорошо.
W>Если укажите возможные сценарии возникновения ошибки — буду благодарен.
VString a("asdf");
const char* p = NULL;
a = p; // утечка
VString a("asdf");
a = a; // падение
А зачем писать свою VString? Возьмите что-нибудь готовое, std::string или CComBSTR ...
Здравствуйте, wbear, Вы писали:
W>Который день бьюсь с возникшей проблемой, в части освобождения памяти!
Стандартно для российского форума будут советы не по теме.
W>Для воспроизведения ошибки написал тестовый пример(исходники привел ниже), отличие от рабочего проекта только в закомментаренном вызове функции pObj->GetVersionSoft(d). Ошибка не возникает.
Может так и оставить?
W>Может кто подскажет в чем проблема с освобождение памяти.
Проблема в классе VString. Ненужная критическая секция (да, я читал мотивацию, но она не верная), ненужные ручные управления памятью, для буфера всегда можно использовать std::vector<char>.
W>При условии что другая прога(исходников нет) которая работает с этим COM объектом отрабатывает без ошибок.
Очевидно, там нет VString и сомнительного использования CoTaskMemFree.
W>Исходный код класса VString и тестовый пример ниже.
Это класс который писали вы сами или от евреев пришло? Что-то я не ожидал от них такого . Как сказали выше, лучше использовать std::string и пару функций конверсии без CoTaskMemFree внутри.
W>void WideCharToVString( LPCWSTR lpWideCharStr, VString* a2, int a3)
Это должен быть один из конструкторов VString, но без CoTaskMemFree внутри, SRP и всё такое.
Здравствуйте, B0FEE664, Вы писали:
BFE>А зачем писать свою VString? Возьмите что-нибудь готовое, std::string или CComBSTR ...
Кстати, отличная идея. Есть же ATL с его классами именно для всех этих кейсов.
Здравствуйте, wbear, Вы писали:
W>Класс VString является частью большой библиотеки по работе с COM серверами и службами. В связи с этим и используется блокировка доступа.
Потоков несколько?
Зачем блокировка доступа в деструкторе? ~VString вызывается из одного потока?
Как объекты VString используются в разных потоках, как сырые указатели?
Здравствуйте, wbear, Вы писали:
W>После константной строки находиться закомментаренная функция pObj->GetVersionSoft(d); Именно в этой функции я так понимаю какраз выделяется память и записывается строка с версией ПО.
В первом сообщении написано, что GetVersionSoft() возвращает LPCWSTR.
В MSDN написано
An LPCWSTR is a 32-bit pointer to a constant string of 16-bit Unicode characters, which MAY be null-terminated.
Передаёт ли GetVersionSoft() владение выделенной памятью вызывающей функции или управление памятью остаётся в pObj?
Какой сценарий использования для этой строки и, созданным из неё, объектом VString?