Падает Visual Studio 2008.
От: P.loo.t  
Дата: 17.12.10 08:54
Оценка:
У меня проблема, помогите разобраться или посоветуйте как лучше сделать. Цель написать прогу которая бы фиксировала открываемые и создаваемые окна в винде и записывала их в файлик. Проще говоря Мониторинг исполняемых файлов. Программа будет состоять из двух частей. Первый файл — запускаемый — будет загружать другой файл — динамическую библиотеку — в память. Так вот, в запускаемой проге (exe) мы будем загружать библиотеку и запускать ловушку. exe файлик создаём с помощью проета Win32. Листинг фрагмента кода (остальное нас не интересует):

// ...
    LONG      lResult;
    HINSTANCE hModule;
  
    // Создаем новый указатель на функцию
    typedef void (RunStopHookProc)(bool, HINSTANCE, HANDLE);
   
    RunStopHookProc* RunStopHook = 0;
   
    // Load the DLL file (Чтение DLL-библиотеки)
    hModule = ::LoadLibrary(L"VSysKey.dll");

    // Получить адрес функции в библиотеке
    RunStopHook = (RunStopHookProc*)::GetProcAddress(
            (HMODULE) hModule, "RunStopHook");

    // Выполнить функцию
    (*RunStopHook)(true, hInstance, hModule);

    // Цикл основного сообщения:
    while (GetMessage(&msg, NULL, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
// ...


В нём проблем нету и работает на ура и без ошибок. Вот код самого VSysKey.dll который мы описываем:

// FileMonitor.cpp : Defines the entry point for the DLL application.
//

#include <windows.h>
#include "stdafx.h"
#include "VSysKey.h"
#include "stdio.h"

HHOOK SysHook;
HINSTANCE hInst;

BOOL APIENTRY DllMaina( HANDLE hModule, 
                       DWORD  ul_reason_for_call, 
                       LPVOID lpReserved
                     )
{
    hInst=(HINSTANCE)hModule;
    return TRUE;
}

LRESULT CALLBACK SysMsgProc(

    int code,    // hook code
    WPARAM wParam,    // removal flag
    LPARAM lParam     // address of structure with message
   )
{
    //Передать сообщение другим ловушкам в системе
    CallNextHookEx(SysHook, code, wParam, lParam);

        // Код проверки удачного создания файлика Log.log в который по планам мы и должны будем всё записывать
    FILE *f=fopen("c:\\log.log","w");    
    fprintf(f,"hello");
    fclose(f);
        // Фрагмент окончен

    if (code == HCBT_ACTIVATE)
    {
        char windtext[255];
        HWND Wnd=((tagMSG*)lParam)->hwnd;
        GetWindowTextA(Wnd, windtext, 255);
        // Here you can save active window title
        // (Здесь можно сохранить заголовок активного окна)
        //MessageBoxA(0,windtext,windtext,0);
    }

    if (code == HCBT_CREATEWND)
    {
        char windtext[255];
        HWND Wnd=((tagMSG*)lParam)->hwnd;
        GetWindowTextA(Wnd, windtext, 255);        
        
        // Here you can save New file title
        // (Здесь можно сохранить заголовок нового окна)
    }
    //fclose(fuck);
    return 0;
}

///////////////////////////////////////////////////////////////////

DllExport void RunStopHook(bool State, HINSTANCE hInstance, HANDLE hModule)
{
    hInst=(HINSTANCE)hModule;
    if (true)
        SysHook = SetWindowsHookEx(WH_CBT, &SysMsgProc, hInst, 0);
    else
        UnhookWindowsHookEx(SysHook);
}


Без фрагмента с файлом (три строки создания файловой переменной, запись в файл и его закрытия) код работает на ура.

Теперь проблема:
1) Как только мы пытаемся создать файл и записать туда что-то, наша прога и библиотека компиляться без ошибок, только при запуске выскакивает Только мы жмём на кнопку ПРЕРВАТЬ — падаетвесь Visual Studio 2008. Просто вылетает прога. Что то может быть? Как этого можно избежать и добиться желаемой цели? Примечание: те же три строки если их прописать в exe файле работают на ура, создают и записывают в файл.
2) В фрагменте обработки события HCBT_ACTIVATE в коментарии есть строка MessageBoxA(0,windtext,windtext,0) где по идее windtext должна содержать заголовок активированного приложения. MessageBoxA работает, но выводит пустую строку... В чём проблема? Где я ошибся?
ПРИМЕЧАНИЕ: точка остановки при отладке не работает. Код выполняется не останавливаясь. В дебаге не могу посмотреть по шагово действия которые выполняет DLL и значения переменных. Опять же то уже третья проблема. Почему? В чём загвоздка?

P.S. У меня стоит Wondows 7 Home, до этого в течении полу года работал на Visual studio 2008 и не разу не сталкивался с подобными проблемами. Прошу не оставте без внимания...


20.12.10 17:27: Перенесено модератором из 'C/C++' — Odi$$ey
Re: Падает Visual Studio 2008.
От: swingus  
Дата: 17.12.10 09:28
Оценка:
Здравствуйте, P.loo.t, Вы писали:

...

В документации вроде написано, что хук процедура должна выглядеть так:


LRESULT CALLBACK HookProc(
  int nCode, 
  WPARAM wParam, 
  LPARAM lParam
)
{
   // process event
   ...

   return CallNextHookEx(NULL, nCode, wParam, lParam);
}


http://msdn.microsoft.com/en-us/library/ms644959(v=vs.85).aspx#wh_cbthook

то есть сперва обработка, потом вызов следующей ловушки
Re[2]: Падает Visual Studio 2008.
От: P.loo.t  
Дата: 17.12.10 09:41
Оценка:
Здравствуйте, swingus, Вы писали:

S>то есть сперва обработка, потом вызов следующей ловушки


Вообще-то на сколько я знаю — это не принципиально, но всё равно я попробовал как посоветовал ты — ни одна из перечисленных проблем не решена. Всё осталось без изменений.
Re[3]: Падает Visual Studio 2008.
От: swingus  
Дата: 17.12.10 09:49
Оценка:
Здравствуйте, P.loo.t, Вы писали:

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


S>>то есть сперва обработка, потом вызов следующей ловушки


PLT>Вообще-то на сколько я знаю — это не принципиально, но всё равно я попробовал как посоветовал ты — ни одна из перечисленных проблем не решена. Всё осталось без изменений.


Э... а у Вас не совпадает тип хука и трактовка параметров.

   if (code == HCBT_ACTIVATE)
    {
        char windtext[255];
        HWND Wnd=((tagMSG*)lParam)->hwnd;
        GetWindowTextA(Wnd, windtext, 255);
        // Here you can save active window title
        // (Здесь можно сохранить заголовок активного окна)
        //MessageBoxA(0,windtext,windtext,0);
    }


Для HCBT_ACTIVATE lParam это CBTACTIVATESTRUCT *

    if (code == HCBT_CREATEWND)
    {
        char windtext[255];
        HWND Wnd=((tagMSG*)lParam)->hwnd;
        GetWindowTextA(Wnd, windtext, 255);        
        
        // Here you can save New file title
        // (Здесь можно сохранить заголовок нового окна)
    }


для HCBT_CREATEWND — CBT_CREATEWND *
Re: Падает Visual Studio 2008.
От: okman Беларусь https://searchinform.ru/
Дата: 17.12.10 09:56
Оценка:
Здравствуйте, P.loo.t.

Я бы вообще для этой задачи не использовал хуки.
Re[4]: Падает Visual Studio 2008.
От: P.loo.t  
Дата: 17.12.10 09:59
Оценка:
Здравствуйте, swingus, Вы писали:
S>Для HCBT_ACTIVATE lParam это CBTACTIVATESTRUCT *
S>для HCBT_CREATEWND — CBT_CREATEWND *

Вообще-то я делал основу по учебнику и всё компилировалось на ура. Ну всё же попробовал твой совет и При компиляции выдаёт следуещее:
error C2065: CBT_ACTIVATESTRUCT: необъявленный идентификатор
error C2275: CBT_CREATEWND: недопустимое использование этого типа в качестве выражения
Пробовал менять в свойствах проекта Юникод кодировку на мультибайтовую, один фиг не помогает.
Re[5]: Падает Visual Studio 2008.
От: swingus  
Дата: 17.12.10 10:01
Оценка:
Тогда Вам пока рановато писать такие сложные программы

Здравствуйте, P.loo.t, Вы писали:

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

S>>Для HCBT_ACTIVATE lParam это CBTACTIVATESTRUCT *
S>>для HCBT_CREATEWND — CBT_CREATEWND *

PLT>Вообще-то я делал основу по учебнику и всё компилировалось на ура. Ну всё же попробовал твой совет и При компиляции выдаёт следуещее:

PLT>error C2065: CBT_ACTIVATESTRUCT: необъявленный идентификатор
PLT>error C2275: CBT_CREATEWND: недопустимое использование этого типа в качестве выражения
PLT>Пробовал менять в свойствах проекта Юникод кодировку на мультибайтовую, один фиг не помогает.
Re[6]: Падает Visual Studio 2008.
От: P.loo.t  
Дата: 17.12.10 10:06
Оценка:
Здравствуйте, swingus, Вы писали:

S>Тогда Вам пока рановато писать такие сложные программы


Возможно ты прав, но всё равно хотелось бы закончить эту прогу. Согласись глупо будет бросить её. Я даже совета твоего послушаю и не буду браться за подобные прги если только мне поможете с ней разобраться)))
Re: Падает Visual Studio 2008.
От: npak Россия  
Дата: 17.12.10 10:07
Оценка:
Здравствуйте, P.loo.t, Вы писали:

PLT>У меня проблема, помогите разобраться или посоветуйте как лучше сделать. Цель написать прогу которая бы фиксировала открываемые и создаваемые окна в винде и записывала их в файлик. Проще говоря Мониторинг исполняемых файлов. Программа будет состоять из двух частей. Первый файл — запускаемый — будет загружать другой файл — динамическую библиотеку — в память. Так вот, в запускаемой проге (exe) мы будем загружать библиотеку и запускать ловушку. exe файлик создаём с помощью проета Win32. Листинг фрагмента кода (остальное нас не интересует):


PLT>В нём проблем нету и работает на ура и без ошибок. Вот код самого VSysKey.dll который мы описываем:


PLT>
PLT>// FileMonitor.cpp : Defines the entry point for the DLL application.
PLT>//


PLT>        // Код проверки удачного создания файлика Log.log в который по планам мы и должны будем всё записывать
PLT>    FILE *f=fopen("c:\\log.log","w");    
PLT>    fprintf(f,"hello");
PLT>    fclose(f);
PLT>        // Фрагмент окончен

PLT>


PLT>Без фрагмента с файлом (три строки создания файловой переменной, запись в файл и его закрытия) код работает на ура.


PLT>Теперь проблема:

PLT>1) Как только мы пытаемся создать файл и записать туда что-то, наша прога и библиотека компиляться без ошибок, только при запуске выскакивает Только мы жмём на кнопку ПРЕРВАТЬ — падаетвесь Visual Studio 2008. Просто вылетает прога. Что то может быть? Как этого можно избежать и добиться желаемой цели? Примечание: те же три строки если их прописать в exe файле работают на ура, создают и записывают в файл.

Из общих соображений — хук выполняется в контексте процесса, к которому он прикреплен. Соответственно, с правами этого процесса. Может быть, у него недостаточно прав, чтобы создать файл?
Потом, вы ведь собираетесь писать в этот файл из нескольких процессов? fopen с флагом "w" будет всегда очищать содержимое файла. Надо как минимум "w+". Предлагаю вам вообще отказаться от стандартных сишных функций в коде хука и пользоваться только Win32 API — CreateFileA, WriteFileA — дабы обезопасить себя от проблем с линковкой. Например, процесс, который вы мониторите, собран со статической MSVCRT, ваша библиотека собрана с динамической MSVCRT — результат вызова сишных функций может быть своеобразный.

PS: за связку fopen-fprintf без проверки результата fopen на NULL я бы вынес подчиненному моск. Чтобы впредь неповадно было.
Re[2]: Падает Visual Studio 2008.
От: P.loo.t  
Дата: 17.12.10 10:07
Оценка:
Здравствуйте, okman, Вы писали:

O>Я бы вообще для этой задачи не использовал хуки.


Скажи, а как бы ты реализовал это? Я разберусь.
Re[7]: Падает Visual Studio 2008.
От: swingus  
Дата: 17.12.10 10:10
Оценка:
Выложите последнюю версию кода

Здравствуйте, P.loo.t, Вы писали:

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


S>>Тогда Вам пока рановато писать такие сложные программы


PLT>Возможно ты прав, но всё равно хотелось бы закончить эту прогу. Согласись глупо будет бросить её. Я даже совета твоего послушаю и не буду браться за подобные прги если только мне поможете с ней разобраться)))
Re[2]: Падает Visual Studio 2008.
От: P.loo.t  
Дата: 17.12.10 10:14
Оценка:
Здравствуйте, npak, Вы писали:

N>Из общих соображений — хук выполняется в контексте процесса, к которому он прикреплен. Соответственно, с правами этого процесса. Может быть, у него недостаточно прав, чтобы создать файл?


С павами всё нормально, проверено.

N>Потом, вы ведь собираетесь писать в этот файл из нескольких процессов? fopen с флагом "w" будет всегда очищать содержимое файла. Надо как минимум "w+". Предлагаю вам вообще отказаться от стандартных сишных функций в коде хука и пользоваться только Win32 API — CreateFileA, WriteFileA — дабы обезопасить себя от проблем с линковкой. Например, процесс, который вы мониторите, собран со статической MSVCRT, ваша библиотека собрана с динамической MSVCRT — результат вызова сишных функций может быть своеобразный.


Про флаг — не в этом суть. Я это знаю. Флаг "w" был поставлен для примера, как и строка "Hello". Вот про API дельный совет, попробую сейчас переделать прогу.

N>PS: за связку fopen-fprintf без проверки результата fopen на NULL я бы вынес подчиненному моск. Чтобы впредь неповадно было.


А проверка не используеться ЛИШЬ ОТ ТОГО чтобы показать пример cоздания файла в упрщённой форме))) Я обычно пользуюсь проверкой, так что просьба не критиковать! Только что с проверкой, что без, исход один.
Re[2]: Падает Visual Studio 2008.
От: npak Россия  
Дата: 17.12.10 10:16
Оценка:
N>Здравствуйте, P.loo.t, Вы писали:

PLT>>
PLT>>// FileMonitor.cpp : Defines the entry point for the DLL application.
PLT>>//


PLT>>        // Код проверки удачного создания файлика Log.log в который по планам мы и должны будем всё записывать
PLT>>    FILE *f=fopen("c:\\log.log","w");    
PLT>>    fprintf(f,"hello");
PLT>>    fclose(f);
PLT>>        // Фрагмент окончен

PLT>>


PLT>>Без фрагмента с файлом (три строки создания файловой переменной, запись в файл и его закрытия) код работает на ура.


PLT>>1) Как только мы пытаемся создать файл и записать туда что-то, наша прога и библиотека компиляться без ошибок, только при запуске выскакивает


N>PS: за связку fopen-fprintf без проверки результата fopen на NULL я бы вынес подчиненному моск. Чтобы впредь неповадно было.


Так и есть. Вам MSVCRT черным по белому написало, что проблема в 55-й строке файле fprintf.c . Сходили бы, посмотрели:
int __cdecl fprintf (
        FILE *str,
        const char *format,
        ...
        )
/*
 * 'F'ile (stream) 'PRINT', 'F'ormatted
 */
{
    va_list(arglist);
    REG1 FILE *stream;
    REG2 int buffing;
    int retval=0;

/*55*/    _VALIDATE_RETURN( (str != NULL), EINVAL, -1);


У вас файл не открылся! И дебаггер здесь ни к чему, достаточно глазками посмотреть, о чем ошибка.
Re[8]: Падает Visual Studio 2008.
От: P.loo.t  
Дата: 17.12.10 10:18
Оценка:
Здравствуйте, swingus, Вы писали:

S>Выложите последнюю версию кода


Без ошибки при компиляции:

LRESULT CALLBACK SysMsgProc(

    int code,    // hook code
    WPARAM wParam,    // removal flag
    LPARAM lParam     // address of structure with message
   )
{
    
    //FILE *f=fopen("c:\\log.log","w");
    
    //fprintf(f,"hello");

    //fclose(f);
    //freopen("log.log","w",stdout);
    //char a[100]={"Бла бла бла\n"};
    //fprintf(f," %s \n ",a);
    //fclose(f);
    //printf(" %s \n ",a);

    if (code == HCBT_ACTIVATE)
    {
        char windtext[255];
        HWND Wnd=((tagMSG*)lParam)->hwnd;
        GetWindowTextA(Wnd, windtext, 255);
        // Here you can save active window title
        // (Здесь можно сохранить заголовок активного окна)
        MessageBoxA(0,windtext,windtext,0);
    }

    if (code == HCBT_CREATEWND)
    {
        char windtext[255];
        HWND Wnd=((tagMSG*)lParam)->hwnd;
        GetWindowTextA(Wnd, windtext, 255);        
        
        // Here you can save New file title
        // (Здесь можно сохранить заголовок нового окна)
        MessageBoxA(0,windtext,windtext,0);
    }
    //fclose(fuck);
    //Передать сообщение другим ловушкам в системе
    CallNextHookEx(SysHook, code, wParam, lParam);

    return 0;
}


С ошибкой:

LRESULT CALLBACK SysMsgProc(

    int code,    // hook code
    WPARAM wParam,    // removal flag
    LPARAM lParam     // address of structure with message
   )
{
    
    //FILE *f=fopen("c:\\log.log","w");
    
    //fprintf(f,"hello");

    //fclose(f);
    //freopen("log.log","w",stdout);
    //char a[100]={"Бла бла бла\n"};
    //fprintf(f," %s \n ",a);
    //fclose(f);
    //printf(" %s \n ",a);

    if (code == CBT_ACTIVATESTRUCT)
    {
        char windtext[255];
        HWND Wnd=((tagMSG*)lParam)->hwnd;
        GetWindowTextA(Wnd, windtext, 255);
        // Here you can save active window title
        // (Здесь можно сохранить заголовок активного окна)
        MessageBoxA(0,windtext,windtext,0);
    }

    if (code == CBT_CREATEWND)
    {
        char windtext[255];
        HWND Wnd=((tagMSG*)lParam)->hwnd;
        GetWindowTextA(Wnd, windtext, 255);        
        
        // Here you can save New file title
        // (Здесь можно сохранить заголовок нового окна)
        MessageBoxA(0,windtext,windtext,0);
    }
    //fclose(fuck);
    //Передать сообщение другим ловушкам в системе
    CallNextHookEx(SysHook, code, wParam, lParam);

    return 0;
}
Re[3]: Падает Visual Studio 2008.
От: npak Россия  
Дата: 17.12.10 10:19
Оценка:
Здравствуйте, P.loo.t, Вы писали:

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



N>>PS: за связку fopen-fprintf без проверки результата fopen на NULL я бы вынес подчиненному моск. Чтобы впредь неповадно было.


PLT>А проверка не используеться ЛИШЬ ОТ ТОГО чтобы показать пример cоздания файла в упрщённой форме))) Я обычно пользуюсь проверкой, так что просьба не критиковать! Только что с проверкой, что без, исход один.


Да не открылся у вас файл. Вам об этом черным по белому пишуть: не выполнена проверка str != NULL. Идете в исходники MSVCRT и смотрите, что такое str — это указатель FILE*. Нет файла — нет вывода.

Покажите код с проверкой.
Re[3]: Падает Visual Studio 2008.
От: P.loo.t  
Дата: 17.12.10 10:30
Оценка:
Здравствуйте, npak, Вы писали:


    FILE *f=fopen("c:\\log.log","w");
    
    if (f!=NULL) 
        fprintf(f,"hello");

    fclose(f);


Ничего не меняет. Теперь ему ненравиться не fprintf а условие в скобках и Visual всё равно падает
Re[9]: Падает Visual Studio 2008.
От: swingus  
Дата: 17.12.10 10:45
Оценка:
LRESULT CALLBACK CBTProc(

    int code,    // hook code
    WPARAM wParam,    // removal flag
    LPARAM lParam     // address of structure with message
   )
{
    
    //FILE *f=fopen("c:\\log.log","w");
    
    //fprintf(f,"hello");

    //fclose(f);
    //freopen("log.log","w",stdout);
    //char a[100]={"Бла бла бла\n"};
    //fprintf(f," %s \n ",a);
    //fclose(f);
    //printf(" %s \n ",a);

    if (code == HCBT_ACTIVATE)
    {
        char windtext[255];
        HWND Wnd= reinterpret_cast<CBT_ACTIVATESTRUCT *>(lParam)->hWndActive;
        GetWindowTextA(Wnd, windtext, 255);
        // Here you can save active window title
        // (Здесь можно сохранить заголовок активного окна)
        MessageBoxA(0,windtext,windtext,0);
    }

    if (code == HCBT_CREATEWND)
    {
        // char windtext[255];
        CBT_CREATEWND *pCrtWnd = reinterpret_cast<CBT_CREATEWND *>(lParam);
        
        // Here you can save New file title
        // (Здесь можно сохранить заголовок нового окна)
        MessageBoxA(0, pCrtWnd->lpcs->lpszName, pCrtWnd->lpcs->lpszName, 0);
    
   }
    //fclose(fuck);
    //Передать сообщение другим ловушкам в системе
    return CallNextHookEx(SysHook, code, wParam, lParam);
}


Здравствуйте, P.loo.t, Вы писали:
...

Вы, видимо, сменили тип ловушки во время програмирования но, либо забыли учесть то, что lParam и wParam означают разные вещи для разных типов ловушек, либо не знаете об этом. Затем вы попытались впихнуть точные типы параметров CBT ловушки вместо кода операции, что не сделает ни один человек, прочитавший хотя бы C++ для чайников. Мне кажется, что пока это слишком сложный материал для изучения для Вас. Или Вы собираетесь выйти с получившейся программой на рынок?
Изменённые строчки я выделил жирным. Компилятором я не проверял, так что могут быть синтаксические ошибки. И там Вам написали по поводу открытия файла.
Re[10]: Падает Visual Studio 2008.
От: P.loo.t  
Дата: 17.12.10 10:56
Оценка:
Спасибо за исправленный код, сейчас попробую его протестить. И тебе совет: чуть терпимей к людям относись. Спроси ты меня об уровне моих познаний Visual я бы тебе честно ответил, что не ас и пальцы бы гнуть не стал. Пишу прогу не для рынка, а для себя. Если указываешь на ошибки, постарайся в следующий раз не опускать людей, не поддевать их о не знании. Всё впереди. Со всем уважением. Я только учусь, чего ты хотел от меня? Всё равно я её не брошу пока не добью.
Re[11]: Падает Visual Studio 2008.
От: swingus  
Дата: 17.12.10 11:01
Оценка:
Да я объясняю Вам, это сложный материал. Это всё равно что первоклассник стал бы поднимать штангу весом в 100 килограмм. Ладно, пишите если что не пойдёт

Здравствуйте, P.loo.t, Вы писали:

PLT>Спасибо за исправленный код, сейчас попробую его протестить. И тебе совет: чуть терпимей к людям относись. Спроси ты меня об уровне моих познаний Visual я бы тебе честно ответил, что не ас и пальцы бы гнуть не стал. Пишу прогу не для рынка, а для себя. Если указываешь на ошибки, постарайся в следующий раз не опускать людей, не поддевать их о не знании. Всё впереди. Со всем уважением. Я только учусь, чего ты хотел от меня? Всё равно я её не брошу пока не добью.
Re[4]: Падает Visual Studio 2008.
От: npak Россия  
Дата: 17.12.10 11:35
Оценка:
Здравствуйте, P.loo.t, Вы писали:

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



PLT>
PLT>    FILE *f=fopen("c:\\log.log","w");
    
PLT>    if (f!=NULL) 
PLT>        fprintf(f,"hello");

PLT>    fclose(f);
PLT>


PLT>Ничего не меняет. Теперь ему ненравиться не fprintf а условие в скобках и Visual всё равно падает


Я не телепат. И телепатов не встречал. Как вы узнали, что "ему" (кому??) не нравится "условие в скобках"? Дайте полную симптоматику, пожалуйста.

ИМХО, теперь должно валиться в fclose, который получает нулевой указатель. У него тоже есть проверка
    _VALIDATE_RETURN((stream != NULL), EINVAL, EOF);


PS: За такую проверку я бы тоже вынес "молодому талантливому программисту" моск. fclose должен вызываться только в том случае, если fopen вернул не NULL
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.