Проблема с ReadFile в СОМ
От: DT  
Дата: 31.01.02 14:02
Оценка:
Непонятная закавыка — при вызове ReadFile (COM-клиент на VB) в нижеприведенном методе происходит неожиданное завершение работы клиента (если сконфигурировать в Release) или выдается сообщение "Debug assertion failed!" c комментарием "_CrtIsValidHeapPointer(pUserData)" в файле dbgheap.c (если сконфигурировать в Debug), которого, кстати, на моей машине в принципе нет. Причем последнее сообщение появляется при освобождении памяти из-под буфера free (buffer), а если эту строчку убрать, то происходит то же самое, что и в Release. Если закомментировать ReadFile, ошибка исчезает, но и необходимые функции тоже. Что делать?

STDMETHODIMP CActiveXTest::LoadFromFile(BSTR FileName)
{
USES_CONVERSION;
DWORD Len = ::SysStringLen(FileName) + 2;
DWORD NoB = 0;
char *buff;
buff = (char*)malloc(Len);

buff = W2A(FileName);

HANDLE hFile = ::CreateFile(LPCTSTR(buff), GENERIC_READ, FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
MessageBox(NULL, "Invalid file name!", "ERROR!", MB_OK);
return E_FAIL;
}
free(buff);

Len = GetFileSize(hFile, NULL);
char *buffer = new char [Len + 1];

::SetFilePointer(hFile,0,NULL,FILE_BEGIN);
::ReadFile(hFile, &buffer, Len, &NoB, NULL);

m_bstrBody = A2BSTR(buffer);

free(buffer);
::CloseHandle(hFile);

return S_OK;
}
Re: Проблема с ReadFile в СОМ
От: Lexey Россия  
Дата: 31.01.02 14:39
Оценка:
Здравствуйте DT, Вы писали:

DT> m_bstrBody = A2BSTR(buffer);


Проблема скорее всего вот здесь.

Про это где-то говорил Alex Fedotov. Нельзя макросы конвертации использовать для больших объемов данных (для файлов в данном случае), т.к. они резервируют буфер в стеке. А стек не резиновый.
Re[2]: Проблема с ReadFile в СОМ
От: DT  
Дата: 31.01.02 14:50
Оценка:
Здравствуйте Lexey, Вы писали:

DT>> m_bstrBody = A2BSTR(buffer);

L>Проблема скорее всего вот здесь.
L>Про это где-то говорил Alex Fedotov. Нельзя макросы конвертации использовать для больших объемов данных
Попробовал вовсе выбросить эту строчку из кода. Результат нулевой.
Re[3]: Проблема с ReadFile в СОМ
От: DT  
Дата: 31.01.02 15:47
Оценка:
Задачу решил весьма извратным методом. Привожу код — вдруг у кого такая же проблема возникнет:

USES_CONVERSION;
CRITICAL_SECTION cs;

DWORD NoB;

InitializeCriticalSection(&cs);
EnterCriticalSection(&cs);

HANDLE hFile = ::CreateFile(LPCTSTR(W2A(FileName)), GENERIC_READ,
FILE_SHARE_READ, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
MessageBox(NULL, "Invalid file name!", W2A(FileName), MB_OK);
return E_FAIL;
}


DWORD Len = ::GetFileSize(hFile, NULL);
char buffer[MAX_BUFFER_SIZE + 1];
m_bstrBody = "";
DWORD CountLen = (DWORD)(Len / MAX_BUFFER_SIZE);
DWORD CountEnd = Len — CountLen * MAX_BUFFER_SIZE;
DWORD Offset, i, j;

for (i = 0; i < CountLen; i++) {
for (j = 0; j < MAX_BUFFER_SIZE + 1; j++) buffer[j] = 0;
Offset = i * MAX_BUFFER_SIZE;
::SetFilePointer(hFile, Offset, NULL,FILE_BEGIN);
::ReadFile(hFile, &buffer, MAX_BUFFER_SIZE, &NoB, NULL);
m_bstrBody += A2BSTR(buffer);
}
for (j = 0; j < MAX_BUFFER_SIZE + 1; j++) buffer[j] = 0;

Offset = CountLen * MAX_BUFFER_SIZE;
::SetFilePointer(hFile, Offset, NULL, FILE_BEGIN);
::ReadFile(hFile, &buffer, CountEnd, &NoB, NULL);
m_bstrBody += A2BSTR(buffer);

::CloseHandle(hFile);

LeaveCriticalSection(&cs);
DeleteCriticalSection(&cs);


Правда, почему одно работает, а другое нет, я понятия не имею. Может, объяснит кто?
Re: Проблема с ReadFile в СОМ
От: George_Seryakov Россия  
Дата: 31.01.02 15:54
Оценка:
Здравствуйте DT, Вы писали:


DT>STDMETHODIMP CActiveXTest::LoadFromFile(BSTR FileName)


DT> buff = (char*)malloc(Len);


DT> buff = W2A(FileName);


...

DT> free(buff);


Что ты освобождаешь, аллокированную память или входной BSTR?
GS
Re: Проблема с ReadFile в СОМ
От: VVV Россия  
Дата: 31.01.02 19:36
Оценка:
Здравствуйте DT, Вы писали:

DT> USES_CONVERSION;

DT> DWORD Len = ::SysStringLen(FileName) + 2;
DT> DWORD NoB = 0;
DT> char *buff;
DT> buff = (char*)malloc(Len);

DT> buff = W2A(FileName);


DT> free(buff);


W2A использует alloca — отводит память на стеке, поэтому free(buff) ведёт к печальным последствиям, т.е. надо убрать malloc и free
char *buff=W2A(FileName); — память освободится автоматически при выходе из функции.
Re[2]: Проблема с ReadFile в СОМ
От: DT  
Дата: 01.02.02 09:12
Оценка:
Здравствуйте George_Seryakov, Вы писали:

DT>>STDMETHODIMP CActiveXTest::LoadFromFile(BSTR FileName)

DT>> buff = (char*)malloc(Len);
DT>> buff = W2A(FileName);
GS>...
DT>> free(buff);
GS>Что ты освобождаешь, аллокированную память или входной BSTR?
Аллокированную память, конечно.
Re: Проблема с ReadFile в СОМ
От: Dront Россия  
Дата: 01.02.02 12:53
Оценка:
Здравствуйте DT, Вы писали:

DT>Непонятная закавыка — при вызове ReadFile (COM-клиент на VB) в нижеприведенном методе происходит неожиданное завершение работы клиента (если сконфигурировать в Release) или выдается сообщение "Debug assertion failed!" c комментарием "_CrtIsValidHeapPointer(pUserData)" в файле dbgheap.c (если сконфигурировать в Debug), которого, кстати, на моей машине в принципе нет. Причем последнее сообщение появляется при освобождении памяти из-под буфера free (buffer), а если эту строчку убрать, то происходит то же самое, что и в Release. Если закомментировать ReadFile, ошибка исчезает, но и необходимые функции тоже. Что делать?


<skipped>
DT> char *buffer = new char [Len + 1];
DT> ::SetFilePointer(hFile,0,NULL,FILE_BEGIN);
DT> ::ReadFile(hFile, &buffer, Len, &NoB, NULL);
<skipped>

еще бы не вылетало надо ::ReadFile(hFile, buffer, Len, &NoB, NULL);
buffer — уже указатель.
WBR, Andrey Reznik (2:5020/2999, Andrey_Reznik@rambler.ru)
Re[2]: Проблема с ReadFile в СОМ
От: DT  
Дата: 01.02.02 14:55
Оценка:
Здравствуйте Dront, Вы писали:

DT>>Непонятная закавыка — при вызове ReadFile (COM-клиент на VB) в нижеприведенном методе происходит неожиданное завершение работы клиента (если сконфигурировать в Release) или выдается сообщение "Debug assertion failed!" c комментарием "_CrtIsValidHeapPointer(pUserData)" в файле dbgheap.c (если сконфигурировать в Debug), которого, кстати, на моей машине в принципе нет. Причем последнее сообщение появляется при освобождении памяти из-под буфера free (buffer), а если эту строчку убрать, то происходит то же самое, что и в Release. Если закомментировать ReadFile, ошибка исчезает, но и необходимые функции тоже. Что делать?


D><skipped>

DT>> char *buffer = new char [Len + 1];
DT>> ::SetFilePointer(hFile,0,NULL,FILE_BEGIN);
DT>> ::ReadFile(hFile, &buffer, Len, &NoB, NULL);
D><skipped>

D>еще бы не вылетало надо ::ReadFile(hFile, buffer, Len, &NoB, NULL);

D>buffer — уже указатель.

Интересно, а почему тогда у меня все замечательно работало и с указателем?
Re[3]: Проблема с ReadFile в СОМ
От: Janis Baumanis Латвия  
Дата: 16.04.02 07:58
Оценка:
Здравствуйте DT, Вы писали:

DT>Здравствуйте Dront, Вы писали:


DT>>>Непонятная закавыка — при вызове ReadFile (COM-клиент на VB) в нижеприведенном методе происходит неожиданное завершение работы клиента (если сконфигурировать в Release) или выдается сообщение "Debug assertion failed!" c комментарием "_CrtIsValidHeapPointer(pUserData)" в файле dbgheap.c (если сконфигурировать в Debug), которого, кстати, на моей машине в принципе нет. Причем последнее сообщение появляется при освобождении памяти из-под буфера free (buffer), а если эту строчку убрать, то происходит то же самое, что и в Release. Если закомментировать ReadFile, ошибка исчезает, но и необходимые функции тоже. Что делать?


D>><skipped>

DT>>> char *buffer = new char [Len + 1];
DT>>> ::SetFilePointer(hFile,0,NULL,FILE_BEGIN);
DT>>> ::ReadFile(hFile, &buffer, Len, &NoB, NULL);
D>><skipped>

D>>еще бы не вылетало надо ::ReadFile(hFile, buffer, Len, &NoB, NULL);

D>>buffer — уже указатель.

DT>Интересно, а почему тогда у меня все замечательно работало и с указателем?


Так сказали уже, что макросы конвертации изпользует стек и из-за этого всё рушится, если конвертируемий буфер больше стека. Сам когда то на это налетел (где то тут даже про это упомянуто). Из-за этого макросы A2W, W2A, A2BSTR, BSTR2A итд. лучше не изпользовать для строк, которые длиннее 8 Kb. Я обошёлся путем выковыряния нужного кода из исходников ATL. То что получилось, можно посмотреть ниже. Работает нормально, самая длинная строка, которая попалась, была ~30 Mb

STDMETHODIMP CEDSDatabase::EDSWriteDUFFile(long lRecID, BSTR bstrFile, long lWriteMode, VARIANT_BOOL *pbResult)
{
.....
    TCHAR szFile[_MAX_PATH];
    BSTR bstrDUF = NULL;
    
    USES_CONVERSION;
    _tcscpy(szFile, OLE2T(bstrFile));

    // Load DUF from file.
    HANDLE hFile = CreateFile(szFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,  NULL);
    if (hFile != INVALID_HANDLE_VALUE) 
    {
        DWORD dwLen = ::GetFileSize(hFile, NULL);
        DWORD dwBytesRead;    
        char *pszDUF = new char[dwLen + 1];        // array + terminator char
        
        ReadFile(hFile, pszDUF, dwLen, &dwBytesRead, NULL);
        CloseHandle(hFile);

        if(dwLen == dwBytesRead)
        {
            char *pStartDUF = NULL;
            pszDUF[dwLen] = '\0';    // zero terminator
            // Search loaded file for DUF declaration start position.
            pStartDUF = lstrstr(pszDUF, "<DeclarationFile ");
            if(pStartDUF)
                bstrDUF = Ansi2BSTR(pStartDUF, -1, CP_BALTIC);
        }    
        delete [] pszDUF;
    }

    if(bstrDUF == NULL)
        return E_FAIL;
.....

}


// =====[ функции конвертации строк ]=====

// Convert ANSI string to unicode OLE string (BSTR). 
// Code page optional, system code page is used by default.
// Return converted BSTR or NULL, if error occurs.
inline BSTR Ansi2BSTR(LPCSTR lpsz, int nLen, UINT uiCP = CP_ACP)
{
    BSTR bstr = NULL;
    int nCnvLen = MultiByteToWideChar(uiCP, 0, lpsz, nLen, NULL, NULL);
    bstr = ::SysAllocStringLen(NULL, nCnvLen);
    if (bstr != NULL)
        MultiByteToWideChar(uiCP, 0, lpsz, -1, bstr, nCnvLen);
    return bstr;
}

// Convert unicode OLE string (BSTR) to ANSI string. 
// Code page optional, system code page is used by default.
// Return converted ANSI string and length (terminating zero 
// is not included) or zero, if conversion failed.
inline int BSTR2Ansi(BSTR bstr, LPSTR *lpsz, UINT uiCP = CP_ACP)
{
    int iChars = SysStringLen(bstr);
    int iRet = 0;
    if(iChars)
    {
        *lpsz = new char[++iChars];
        iRet = WideCharToMultiByte(uiCP, 0, bstr, -1, *lpsz, iChars, 0, 0);
    }
    return (iRet == 0) ? 0 : --iRet;
}
Re[3]: Проблема с ReadFile в СОМ
От: DarkGray Россия http://blog.metatech.ru/post/ogni-razrabotki.aspx
Дата: 16.04.02 08:12
Оценка:
Здравствуйте DT, Вы писали:

DT>Интересно, а почему тогда у меня все замечательно работало и с указателем?


А почему ему не работать?

Ты ему дал кусок стека (&buffer), ReadFile в этот кусок считал данные, GPF(General Protection Fault) само собой не возникает, т.к. обращение идет по памяти, которая тебе принадлежит.
А то, что при это гробится куча данных в стеке (когда важных, когда не очень) ты узнаешь много позже самого вызова ReadFile в виде разнообразнейших ошибок, что и было в твоем случае.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.