пробежаться по считанному файлу в памяти

Тема в разделе "WASM.BEGINNERS", создана пользователем varnie, 2 окт 2005.

  1. varnie

    varnie New Member

    Публикаций:
    0
    Регистрация:
    2 янв 2005
    Сообщения:
    1.785
    здравжелаю!

    разбирался тут с работой CreateFile, GlobalAlloc, ReadFile, CloseHandle и GlobalFree на Си++, но, в отличие от асма, что-то застопорилась тут работа.

    есть сл. кусок кода:



    //cut here///////////////////////////////////////////////



    WIN32_FIND_DATA findData;

    HANDLE fHandle = FindFirstFile( "*", &findData );



    DWORD fSize = findData.nFileSizeHigh * MAXDWORD + findData.nFileSizeLow;

    DWORD bytesRead;

    LPVOID pAlloc;



    if ( INVALID_HANDLE_VALUE == fHandle )

    return;



    do

    {



    HANDLE hFile;



    if ( ( hFile = CreateFile( findData.cFileName, GENERIC_READ, FILE_SHARE_READ, NULL,OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_HIDDEN, NULL )

    ) != INVALID_HANDLE_VALUE )



    {



    if ( ( pAlloc = (LPVOID ) GlobalAlloc( GMEM_FIXED, fSize ) ) != NULL )

    {

    if ( ReadFile( hFile, pAlloc, fSize, &bytesRead , NULL ) )

    {



    CloseHandle( hFile );



    /*подскажите, как можно теперь в цикле (или еще как-нить более продвинуто) пробежаться по всему куску памяти, на которую указывает указатель pAlloc и проверить ее на наличие опред. последовательности символов. бьюсь, бьюсь, а никак не могу 'выйти' на содержимое куска памяти, как бы я ни старался...:-(

    */



    }

    GlobalFree( pAlloc );

    }



    }



    } while ( FindNextFile( fHandle, &findData ) );



    FindClose( fHandle );





    //cut here///////////////////////////////////////////////



    заранее пасиба. мэтров 'жанра' очень прошу сильно не пинаться, т.к. я прежде чем постить сюда этот вопрос, пробежался по Рихтеру, и не нашел похожих примеров с GlobalAlloc; там только описывается работа с CreateFileMapping, MapViewOfFile и HeapAlloc. может поясните, почему GlobalAlloc проигнорили?
     
  2. IceStudent

    IceStudent Active Member

    Публикаций:
    0
    Регистрация:
    2 окт 2003
    Сообщения:
    4.300
    Адрес:
    Ukraine
    varnie

    1) нет проверки, не каталог ли это - сэкономишь на вызове CreateFile.



    2) fSize нужно получать внутри цикла и не учитывать nFileSizeHigh: 4 Гб памяти всё равно не выделишь. Или лучше проверять — если файл слишком большой, то пропускать.



    3) Сдалась тебе эта GlobalAlloc, она морально устарела ещё при Win98. Используй VirtualAlloc.



    4) Закрывай хэндл файла вне зависимости от того, прочитался ли файл — главное, что он открылся, значит, нужно закрыть.



    5) Для доступа к памяти используется оператор *. Учти, что он зависит от типа переменной:


    Код (Text):
    1. LPVOID pAlloc;
    2. BYTE b = *((LPBYTE)pAlloc);  // обращение к байту
    3. DWORD d= *((LPDWORD)pAlloc); // обращение к дворду




    Для "пробега" по памяти просто инкреминтируй указатель, но запомни, что ((LPBYTE)pAlloc)++ переходит на 1 байт, а ((LPDWORD)pAlloc) — сразу на 4, то есть



    ((LPTYPE)pAlloc)++ == ((LPTYPE)pAlloc) += sizeof(TYPE);



    Иначе потом долго будешь думать, почему работает не так, как в асме.







    Это простым перебором:
    Код (Text):
    1.  
    2. LPBYTE p = (LPBYTE)pAlloc;
    3. while(p++ < (((LPBYTE)pAlloc) + fSize)){
    4.   if(*p == 0xCC)
    5.     break;
    6.   }




    А насчёт более продвинутого — копай литературу по поиску.
     
  3. varnie

    varnie New Member

    Публикаций:
    0
    Регистрация:
    2 янв 2005
    Сообщения:
    1.785
    IceStudent,

    большущий сенкс за существенную помощь! попробую прокомментировать твои пунты.

    1) проверка есть, она в куске кода, кот-й идет у меня перед этим, запостенным. а этот (запостенный вначале) кусок получает управление, только если текущий файл - не каталог.тут все норма.

    2) не очень понял. ты имеешь ввиду, что надо считать fSize только как findData.nFileSizeLow ? поясни, пожалуйста. или же через GetFileSize лучше? а насчет того, что надо fSize в цикле получать, согласен, я что-то упустил этот момент. спасибо за fix!

    3) я в курсе=) но вот решил реализовать именно через GlobalAlloc, так сказать, для сравнения, как это было на асме, а теперь 'переносится' на Си++.

    4) а вот это очень существенное замечание. я и подумать не мог, что у меня недочет тут.



    пасиба!



    очень признателен за твою помощь.
     
  4. IceStudent

    IceStudent Active Member

    Публикаций:
    0
    Регистрация:
    2 окт 2003
    Сообщения:
    4.300
    Адрес:
    Ukraine
    varnie

    Я имею ввиду, что nFileSizeLow - это DWORD, то есть, в нём поместится 4 гб-ый размер файла. Смею думать, такой файл ты не сможешь обработать, загрузив целиком его в память. Поэтому нет смысла в этой строке: findData.nFileSizeHigh * MAXDWORD + findData.nFileSizeLow, нужно делать проверку на размер файла, который ты сможешь обработать: if(nFileSizeLow > MAX_FILE_SIZE) continue;





    Так и на асме использовать GlobalAlloc нет смысла. Для небольший объёмов есть HeapAlloc, для больших - VirtualAlloc.
     
  5. varnie

    varnie New Member

    Публикаций:
    0
    Регистрация:
    2 янв 2005
    Сообщения:
    1.785
    я что-то опять запутался. попробовал переписать свою писанину заменив GlobalAlloc/GlobalFree на VirtualAlloc/VirtualFree.

    Делаю PVOID pAlloc = VirtualAlloc( NULL, fSize, MEM_RESERVE, PAGE_READWRITE ); .

    VirtualAlloc выполняется успешно, затем я считываю в полученный зарезервированный регион адресного пространства физич. данные из файла, (CreateFile/ReadFile) - все ок, все считывается в память, но вот переменная pAlloc почему-то после этого дела заменяется адресом памяти, куда считан самый "конец файла". Поэтому в VirtualFree( pAlloc, 0, MEM_RELEASE ); вместо базового адреса зарезервированного региона в кач-ве первого аргумента передается ерундень, и есессно ничего не освобождается.

    как быть? и где я не прав?

    пробовал сдублить в др переменную рез-т от VirtualAlloc, а затем уже в VirtualFree сувать эту вторую переменную-дубликат, тогда все хорошо идет, и VirtualFree делает свое дело. но нутром чувствую, что это как-то ненормально так действовать.

    можете объяснить мне, почему исходная pAlloc затирается? или так и должно быть?

    заранее спасибо.
     
  6. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    > "после этого дела заменяется адресом памяти, куда считан самый "конец файла"

    Ну дык, ты наверное в целях "пробежаться по считанному файлу" инкрементируешь сам адрес pAlloc, вот он у тебя и убегает на конец файла ;) Нужно pAlloc "хранить как зеницу ока", а вот "бегать" с помощью вспомогательной переменной LPBYTE p = (LPBYTE)pAlloc, как тебе и посоветовал IceStudent
     
  7. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    Давненько собираюсь уточнить некоторые вопросы с VirtualAlloc vs GlobalAlloc\HeapAlloc, а то многие "доброжелатели" легко и просто рекомедуют использовать Virtual, не упоминая о том на какие грабли тут можно наступить ;)

    Ес-но GlobalAlloc тормозная и устаревшая, но эквивалентной заменой ей является HeapAlloc, а не Virtual. Дело в том, что экономная винда при запросе MEM_COMMIT не торопиться выделять физическую память, а ждет первого обращения на чтение\запись к странице памяти. При обращении к невыделенной странице ес-но возникает PageFault само по себе пожирающее немеряное число тиков, как и любое исключение. Винда обрабатывает исключение, выделяет физическую память, да еще и старательно забивает ее нулями (из соображений конфиденциальности - чтобы мы не сперли какую-нить секретную информацию ;). Ес-но при первом запросе достаточно большого блока памяти этих тормозов не избежать что при HeapAlloc, что при Virtual. Но если требуется использовать блок памяти многократно (как в рассматриваемой задаче), то не нужно каждый раз наступать на эти виндовые грабли. Ес-но лучше один раз выделить достаточно большой блок памяти и использовать его для чтения всех файлов (а не освобождать и выделять новый для каждого файла). В этом случае VirtualAlloc дает преимущества, т.к. можно зарезервировать достаточно большой диапазон адресов и наращивать MEM_COMMIT по мере необходимости (если размер очередного файла превышает размер commited). А вот если каждый раз выделять и освобождать память, то даже GlobalAlloc может работать быстрее Virtual, т.к. для блоков в несколько страниц GlobalFree\HeapFree просто помечают блок кучи как свободный, но оставлюят его commited, поэтому при повторных выделениях тормозов с PageFault'ами не возникает. Так-что куча придумана не зря и для многократных выделений небольших блоков лучше использовать HeapAlloc. Ну а Virtual - для больших блоков и для возможности увеличения размера блока on-place (т.к. HeapRealloc не гарантирует увеличения размера "на месте" и при реаллокации переписывает все данные в новый блок - дополнительные тормоза).



    PS: А читать файлы размером > 64Кб лучше с флагом FILE_FLAG_NOBUFFERING или блоками по 16-64К (см.тут)