1

Тема: [C/C++, winApi] Файловий менеджер, simple file manager, file explorer

Проект аж з 2008 року, але досі актуальний, нині студікі все ще за нього питають в мене, буду відправляти сюди.
Опис:
  Це простий, двохпанельний файловий менеджер з базовим функціоналом, до ФАРу далеко, але я намагався :)

Що можна робити з файлами:
  Копіювати, переіменовувати, переміщати, видаляти, і переглядати (в разі текстових файлів).

Командний рядок, WinApi і Visual Studio, от і всі технології.

Має один файл MainX.cpp, в якому все це і написане.

Там якийсь прикол був з юнікодом, і фактично проект має дві версії, але версію з юнікодом я Вам не дам, бо з нею якась біда була, і я вже не пам'ятаю яка і де, тому ANSI версії функцій до Ваших послуг.

Скрін:
http://replace.org.ua/misc.php?action=pun_attachment&item=1744&download=0

Code
/**
Description
Version 0.0.04 (beta) (ANSI-edition)
*/

#include <windows.h> //Здесь все АПИ-шные ф-ции и модули
#include <iostream> //тут стандартные потоки ввода/вывода
#include <CONIO.H> //тут getch();

using namespace std;

const short WIDTH    =    80; //константа ширины поля (кол-во символов по горизонтали)
const short HEIGHT  =    25; //константа высоты поля (кол-во символов по вертикали), нужно для установки панели и некоторых других вычислений

/*Дерективы компилятора*/
#define DIALOG_OK        0;
#define DIALOG_YES_NO    0;
#define DIALOG_WAIT        0;
#define DIALOG_GET        0;

#define PN_LEFT        0;
#define PN_RIGHT    0;
#define PN_FULL        0;


static HANDLE _ConsoleOut = ::GetStdHandle(STD_OUTPUT_HANDLE); //объявляем переменную, будет хранить хандл окна консоли (ВИНАПИ)


/**Устанавливаем глобальные переменные*/
    byte nP=1;                //хранит номер выбраной панели (1 - слева, 2 - справа)
    char backUpDir[MAX_PATH]="FILE SYSTEM";    //хранит временный путь, для того чтоб сделать откат, если произойдет ошибка составления файлов (к примеру в СД-РОМе нет диска, или путь блокирован ОС-кой)        

    /**для панели 1*/
    char fList1[2][450][MAX_PATH];//глобальная переменная, которая хранит список файлов и подкаталогов в выбраном каталоге
    char dir1[MAX_PATH]=("FILE SYSTEM");        //глобальная переменная, которая хранит каталог
    int dx1=0;                //хранит смещения для вывода на экран списка файлов (для первой панели)
    int countFd1=0;            //хранит количество подкаталогов в выбраном каталоге
    int countFf1=0;            //хранит количество файлов в выбраном каталоге
    int selItem1=1;            //хранит номер выделенного елемента списка    
    /*-------------*/

    /**для панели 2*/
    char fList2[2][450][MAX_PATH];//глобальная переменная, которая хранит список файлов и подкаталогов в выбраном каталоге
    char dir2[MAX_PATH]= ("FILE SYSTEM");        //глобальная переменная, которая хранит каталог
    int dx2=0;                //хранит смещения для вывода на экран списка файлов (для второй панели)
    int countFd2=0;            //хранит количество подкаталогов в выбраном каталоге
    int countFf2=0;            //хранит количество файлов в выбраном каталоге
    int selItem2=1;            //хранит номер выделенного елемента списка    
    /*-------------*/
    
/*------------------------------------*/

/**Объявняем все функции, кроме main() используемые в программе (чтобы из любой функции можно было вызывать любую функцию*/
    //pdDialog тоже можем не объявлять, так как это первая функция (видится всеми итак)
void getDrive(byte nPanel);//Формирование списка логических дисков в системе 
int fileList( byte nPx);//Формирование списка файлов в каталоге. nPx - номер панели, для которой составляем список файлов 1 -слева; 2-справа
BOOL SetConsoleSize(SHORT x, SHORT y);//Установка размера консоли (x, y)
BOOL setCol(int text, int bg);//установить цвет текста + фона
BOOL setPos(SHORT x, SHORT y);//ф-ция которая устанавливает курсор в заданную позицию экрана, с координатами (x, y)
bool isCoren();//Определяет находимся ли мы в корне накопителя (отдельного логического диска). Нужна для того чтоб убрать лишние точки вначале списка файлов (или поставить таковые)
bool root(char *c);//Определяет находимся ли мы на уровне, на котором выводятся все логические диски в системе
void paint(int as);//Отрисовка панелей с файлами. 


/**Отрисовка окна диалога*/
//mes - текст сообщения
//dgType - тип диалога: 
//    0 - предупреждение, либо ошибка (одна кнопка ок), 
//    1 - диалог подтверждения (2 кнопки (да, нет)), 
//    2 - сообщение(ждите и т.п.) не требует нажатия, 
//    3 - диалог ввода (получает текст с клавиатуры и передает в inText)
bool pdDialog(char* mes, byte dgType, char* inText="null")
{
    setlocale (LC_CTYPE,".866"); //устанавливаем кодовую страницу в кодировку ,866 (DOS). Это нужно для того, чтоб у нас была возможность рисовать символами рамки окон и панелей
    
    setCol(0,7);//Устанавливаем цвет, текста и фона, соответственно
    
    setPos((80-strlen(mes))/2,11); //устанавливаем курсор в позицию экрана. Это сделано для того, чтоб вывести сообщение "mes" посередине окна
    for (int i=0;i<strlen(mes)+2;i++) //рисуем горизонтальную двойную линию, которая на 2 символа больше чем длина сообщения, strlen(mes)=колво символов в mes
        cout<<(char)205;            //выводим символ "═"
    
    setPos((80-strlen(mes))/2,13); //устанавливаем курсор в позицию экрана
    for (int i=0;i<strlen(mes)+2;i++) //рисуем горизонтальную двойную линию
        cout<<(char)205; //выводим символ "═"
    
    setlocale (LC_CTYPE,".1251"); //устанавливаем кодовую страницу в кодировку .1251 (ANSI). Это нужно для того, чтоб у нас была возможность выводить текст кириллицей
    
    setPos((80-strlen(mes))/2,12); //устанавливаем курсор в позицию экрана
    cout<<" "<< mes <<" "<<endl; //выводим сообщение
    
    switch(dgType){ //в зависимости от типа сообщения переходим по метке
    case 0:
        {
            setPos((80-strlen(" OK (Enter) "))/2+1,14); //устанавливаем позицию
            cout<<" OK (Enter) ";    //рисуем кнопку
            
            break; //выходим из тела switch, чтоб не выполнять других операций
        }
    case 1:
        {
            setPos((80-strlen(" Yes (Enter)   No(Esc) "))/2+1,14);//устанавливаем позицию
            cout<<" Yes (Enter)   No(Esc) "; //рисуем кнопку
            
            break;//выходим из тела switch, чтоб не выполнять других операций
        }
    case 3:
        {
            setPos((80-strlen(mes))/2,14);
            for (int i=0;i<strlen(mes)+2;i++)
                cout<<" ";
            setPos((80-strlen(" Confirm (Enter) "))/2+1,15);//устанавливаем позицию
            cout<<" Confirm (Enter) "; //рисуем кнопку
            setPos((80-strlen(mes))/2+1,14);
            
            cin>>inText; //получаем строку от пользователя и передаем за пределы функции

        }
    }
    
    if (dgType<2) //если dgType<2, то нам нужно перехватывать нажатие клавишь с клавиатуры
    {
        int key = getch(); //получаем код нажатой клавиши
        switch (key)
        {
        case 13:        //Enter
            return true; //возвращаем значение этой функции в истину
            break;
        case 27:        //Esc
            return false;//возвращаем значение этой функции в ложь
        }
    }
}


/**Формирование списка логических дисков в системе. nPanel - номер панели: 1- слева; 2-справа*/
void getDrive(byte nPanel)
{
    // Взято от сюда http://articles.org.ru/cn/showdetail.php?cid=6002 , переделано под С++
    
    DWORD x=GetLogicalDrives(); // Функция возвращает битовую маску установленных логических дисков.
    // Бит 0 определяет наличие диска А:, бит 1 - диска B и т.д.
    int bit; 
    

    if (x==0) { //проверяем успешное выполнение функции, если x=0 то то функция не выполнена как следует
        pdDialog("Ошибка составление списка логич. дисков",0);//Поэтому выводим сообщение об ошибке (тип которого, сообщение с одной кнопкой ОК)
        exit(0); //и выходим за пределы getDriv
    }

    char dn[4]=" "; //массив из 4-х элементов, будет хранить строку, в которой будет проверятся путь корня диска (например С:\, D:\, E:\ и т.д.)
    strcpy(dn, " :\\\0"); //копируем 3 последних символа (двоеточие, слешь, и нулевой), так как они не буду изменять своего значения на протяжении всей жизни функции
    char tip[31]; //В этом массиве символов будет хранится тип долического диска (например жесткий, сьемный, СДРОМ, и т.п.)
    char tempCh[33]; //В этом массиве символов будет хранится все имя (которое в последствии будет выведено)
    int j=0; //эта переменная будет хранить фактическое количество логических дисков в системе
    for (int i=1;i<=64;i++) //в этом цикле пробегаем все возможные варианты
    {
        bit=x & 1; // Накладываем битовую маску для выделения бита с поряковым номером 0
        
        if (bit==1) //если такое диск найден
        {
            dn[0]=(char)(64+i); //получаем его букву (первый символ пути к корню)

            UINT tp =GetDriveTypeA(dn); //получаем тип диска
            
            //и сохраняем в текстовую переменную
            if (tp==DRIVE_UNKNOWN) 
                strcpy(tip,"Drive type cannot be determined");
            else if (tp==DRIVE_NO_ROOT_DIR)
                strcpy(tip,"Drive no root dir");
            else if    (tp==DRIVE_REMOVABLE)
                strcpy(tip,"Removable");
            else if (tp==DRIVE_FIXED)
                strcpy(tip,"Fixed");
            else if (tp==DRIVE_REMOTE)
                strcpy(tip,"remote (network)");
            else if (tp==DRIVE_CDROM)
                strcpy(tip,"CDROM");
            else if (tp==DRIVE_RAMDISK)
                strcpy(tip,"RAM disk");

        
            //Вспомогательный маретиал
            //#define DRIVE_UNKNOWN     0
            //#define DRIVE_NO_ROOT_DIR 1
            //#define DRIVE_REMOVABLE   2
            //#define DRIVE_FIXED       3
            //#define DRIVE_REMOTE      4
            //#define DRIVE_CDROM       5
            //#define DRIVE_RAMDISK     6
            
            
            //формируем строчку (с путем к корню + в квадратных скобках тип диска)
            strcpy(tempCh,dn); //скопировать строчку dn в tempCh
            strcat(tempCh,"  ["); //конкатенация (объеденение строчки tempCh и "  [")
            strcat(tempCh,tip); //конкатенация (объеденение строчки tempCh и tip)
            strcat(tempCh,"]"); //конкатенация (объеденение строчки tempCh и "]")
            
                
            switch(nPanel){ //в зависимости от сообщаемой панели записываем результаты в глобальный массив списка папок
            case 0:            
            case 1:                
                strcpy(fList1[0][j],tempCh);
                if (nPanel==1) break;
            case 2:
                strcpy(fList2[0][j],tempCh);
            }
            j++; //увеличиваем найдетное количество дисков на еденицу (инкремент)

        }
        x>>=1; //сдвиг в право на 1 бит (всеравно что x=x*2, только намного шустрее)
    }

    switch(nPanel){ //в зависимости от сообщаемой панели записываем значения (количество папок и файлов)
    case 0:
    case 1:
        countFf1=0; //количество файлов панели 1 здесь приравниваем к нулю
        countFd1=j; //сохраняем количество
        if (nPanel==1) break; //если, только панель 1 - выходим
    case 2:
        countFf2=0; //количество файлов здесь приравниваем к нулю
        countFd2=j; //количество файлов панели 2 здесь приравниваем к нулю
    }
}

/**Формирование списка файлов в каталоге. nPx - номер панели, для которой составляем список файлов 1 -слева; 2-справа*/
int fileList( byte nPx){  //функция которая заносит в массив файлы и каталоги, взято из "www.msdn.microsoft.com"
    
    char *dir; //указатель на папку в которой формируем список файлов

    if (nPx==1) //в зависимости от номера панели
        dir=dir1; //сообщаем указатель на переменную папки панели 1, или
    else
        dir=dir2; //панели 2

    if ( root(dir) ) //если мы находимся на уровне, где должны сформировать список всех функций в системе
        getDrive(nPx); //то формируем это список
    else //иначе формируем список файлов и папок
    {    
        WIN32_FIND_DATAA ffd; //структура с параметрами поиска файлов и папок
        char szDir[MAX_PATH]; //формируем строчку с путем для поиска
        HANDLE hFind = INVALID_HANDLE_VALUE; //хандл поиска
        DWORD dwError=0; //номер ошибки
        
        strcpy(szDir,dir); //копируем строчку по адресу dir в переменную szDir
        strcat(szDir,"\\*"); //добавляем слешь и звездочку

        hFind = FindFirstFileA(szDir, &ffd); //сообщаем хандл

        if (INVALID_HANDLE_VALUE == hFind) //если по каким либо причинам, от нас не зависящим, хандл не присвоен - выводим сообщение об ошибке
        {
            pdDialog("Произошла ошибка, при составлении списка файлов",0); //выводим сообщение (тип с одной кнопкой ОК)
            strcpy(dir,backUpDir);    //возвращаем из бэкАп папку что была до выполнения над ней модификаций
            return dwError; //сообщаем сообщение об ошибке
        } 
       
       // List all the files in the directory with some info about them.

        int j1=1; //будет хранить количество папок
        int j2=0; //будет хранить холичество файлов 
        setlocale (LC_CTYPE,"Russian"); //устанавливаем кодовую страницу в .1251 (ANSI) чтоб коректно сохранять папки и файлы содержащире кириллицу
        
        if (nPx==1) //в хависимости от выбраной панели 
            strcpy(fList1[0][0],".."); //сохраняем первый элемент списка файлов в "..", чтоб иметь возможность "путешествовать" по дереву каталогов
        else
            strcpy(fList2[0][0],"..");


        
        if (!isCoren()) //если мы не в корне то делаем 2 пустых операции поиска файлов чтоб избавится от ненужных точек
        {
            FindNextFileA(hFind, &ffd); //следующий найденый файл
            FindNextFileA(hFind, &ffd); //следующий найденый файл
        }
        
        do //цикл, в котором будем формировать список файлов и папок
        {
            if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) //если найденый элемент - папка, то
            {
                int i = 0; //будет хранить номер копируемого символа
                while (true) //в этом цикле посимвольно копируем строку найденой папки в общий список папок
                {
                    if (nPx==1) //в зависимости от панели 
                        fList1[0][j1][i] = ( ffd.cFileName[i] ); //закидваем имя найденого элемента в глобальный массив списка папок первой панели
                    else
                        fList2[0][j1][i] = ( ffd.cFileName[i] ); //закидваем имя найденого элемента в глобальный массив списка папок второй панели

                    if (ffd.cFileName[i]=='\0') break; //находим нулевой символ, и выходим из цикла
                    i++; //увеличиваем значение копируемого символа на единицу (инкремент)
                }
                j1++; //увеличиваем значение номера папки на еденицу (инкремент)
            }
            else //иначе , все тоже самое, но только для файлов
            {
                int i = 0;
                while (true/*ffd.cFileName[i]!=0*/)
                {
                    if (nPx==1)
                        fList1[1][j2][i] = ( ffd.cFileName[i] );
                    else
                        fList2[1][j2][i] = ( ffd.cFileName[i] );

                    if (ffd.cFileName[i]=='\0') break;
                    i++;
                }
                j2++;


            }

        } while (FindNextFileA(hFind, &ffd) != 0 && j1<450 && j2<450); //если элементы в каталоге еще есть и количество каталогов не превышает 450 и количество файлов не превышает 450,
        //то выполняем цикл, иначе идем дальше
            
        if (nPx==1) { //в зависимости от выбраной панели , 
            countFd1 = j1; //сохраняем количество найденых файлов и
            countFf1 = j2; //каталогов в глобальные переменные
        } else {
            countFd2 = j1;
            countFf2 = j2;
        }

        dwError = GetLastError(); //получаем номер ошибки (если успешьно - то ERROR_NO_MORE_FILES)
        if (dwError != ERROR_NO_MORE_FILES) //если ERROR_NO_MORE_FILES, значит произошла ошибка, 
        {
            pdDialog("Произошла ошибка, при составлении списка файлов",0); //выводим сообщение о ней (тип которого с одной кнопкой ОК)
            strcpy(dir,backUpDir); //возвращаем из бэкАпа строчку с путем
        }

        FindClose(hFind); //высвобождаем память
        return dwError; //возвращаем значение функции
    }
    
}




/**Установка размера консоли (x, y)*/
BOOL SetConsoleSize(SHORT x, SHORT y)
{
    COORD size = {x, y}; //получаем объект типа "точка"
    return ::SetConsoleScreenBufferSize(_ConsoleOut, size);//сообственно устанавливаем размер консоли
}

/**установить цвет текста + фона*/
BOOL setCol(int text, int bg)//АПИ-шная функция установки цвета соответственно текста и фона
{
    return::SetConsoleTextAttribute(_ConsoleOut, text+(bg<<4)); //устанавливаем цвет текста+фона , подробнее  cmd.exe "color()"
}

/**ф-ция которая устанавливает курсор в заданную позицию экрана, с координатами x, y*/
BOOL setPos(SHORT x, SHORT y) //функция установки курсора в позицию с координатами соответственно х и у
{
    COORD pos = {x, y}; //определяем переменную типа "точка"
    return ::SetConsoleCursorPosition(_ConsoleOut, pos); //устанавливаем курсор в точку с координатами "pos"
}


/**Определяет находимся ли мы в корне накопителя (отдельного логического диска). Нужна для того чтоб убрать лишние точки вначале списка файлов (или поставить таковые)*/
bool isCoren() //небольшая функция которая определяет является ли каталог в котором мы находимся корневым
{ 
    //определяем переменные, и выделяем под них память
    int i=0; //количество символов в строке
    int j=0; //количество слешей в строке
    
    while(true) //бесконечный цикл
    {
        if (nP==1)
        {
            if (dir1[i]==0) break; //если символ с индексом i равен нулю - это конец строки, выходим из цыкла
            else i++; //иначе увеличиваем i на еденицу
            if (dir1[i]=='\\')j++; //если символ с индексом i равен "\" - увеличиваем j на еденицу
        } else {
            if (dir2[i]==0) break; //если символ с индексом i равен нулю - это конец строки, выходим из цыкла
            else i++; //иначе увеличиваем i на еденицу
            if (dir2[i]=='\\')j++; //если символ с индексом i равен "\" - увеличиваем j на еденицу
        }
    }
    if (j>1) return false;    //если j>0 - значит мы не в корне - возращаем значение функции "ложь"
    else return true;        //иначе мы в корне, возвращаем возращаем значение функции "истина"
}




/**Определяет находимся ли мы на уровне, на котором выводятся все логические диски в системе*/
bool root(char *c) //с - путь каталога
{
    int j=0; //будет хранить номер проверяемого симовла
    for (int i=0;i<11;i++) //в этом цикле посимвольно проверяем, 
        if (c[i]=="FILE SYSTEM"[i])//является ли "с" строчкой с надписью "FILE SYSTEM", и
            j++;//увеличивам значение проверяемого символа на еденицу (инкремент)

    if (j==11) return true; //если все 11 символов совпадают, возращаем значение как истина
    else return false;//иначе возвращаем "ложь"
}

/**Выводит на экран окно файлового показывателя)))*/
void paintFW(char* fn) //fn - file name
{
    
    FILE* f=fopen(fn,"r"); //открываем файл
    if(!f)    //если неудачно, 
        exit(0);//то выходим
    
    fpos_t dxF=0;    //смещение при отображении файла, оно же - позиция начала чтения файла

    int i=0;    //хранит номер символа, выводимого в отдельной строке
    fpos_t hh=0;//хранит смещение при чтении файла (установки позициичтеия) 
    fpos_t dxFx=79;    
    size_t xxx=1;

dx:
    system("cls");//очищаем экран

    setlocale (LC_CTYPE,".1251"); //устанавливаем кодовую страницу (для корректного отображения кириллицы)
    setPos(0,0); //устанавливаем курсор в позицию
    setCol(7,3);//устанавливаем цвет текста и фона символов
    cout<<"Text file viewer version 0.03;                  WIN                             "; //выводим строку
    char b[80]=""; //массив символов, который будет использоватся для вывода содержиого текстового файла

    setCol(11,1);//устанавливаем цвет
    hh=dxF; //присваиваем смещение
    for (int j=0; j<23/*23*/; j++)//в этом цикле будем перебирать строчки
    {
        
        fsetpos(f,&hh); //перемещаем начало чтения на hh байт
        
        xxx=fread(b,sizeof(b),1,f);    //читаем файл
        if (xxx==0) //если xxx==0 - значит достигнут конец файла, выводим последнюю строчку.
        {
            for (i=0;i<80; i++)
                b[i]=0;
            fsetpos(f,&hh); //перемещаем начало чтения на hh байт
            fread(b,sizeof(b),1,f);    //читаем файл
            cout<<b;
            break;
        }

        for (i=0;i<80;i++) //тут посимвольно выводим строчку
        {
            
            if (b[i]==9) cout<<" ";
            else cout<<b[i];
            
            if (b[i]==10 || b[i]==0) //10 = "\n" 
            {
                break;
            }
        }
        
        
        if (j==0) //сохраняем смещение (первой строчки)
        {
            dxFx=i+2;
        }
        hh+=i+2;
    }
        





    int key = getch(); //Получаем код клавиши
    if (key==224)
        key = getch();
    
    switch(key)    {
    case 80://Down (если нажата клавиша "вниз", то)
        {
            if  ( dxF+dxFx+23<ftell( f ) )
                dxF+=dxFx;
            
            break;
        }
    case 72://Up (если нажата клавиша "вверх", то)
        {
            dxF=0;
            break;
        }
    case 27: //esc
    case 61: //F3
    case 68: //F10

        goto dxc;
    }
    
    goto dx;
dxc:;
    dxF=0;
    fclose(f); //закрываем файл

}

/** Отрисовка панелей с файлами */
void paint(int as) //функция отрисовки (вывода на экран) 2-х панелей со списками каталогов и файлов
// as - определяет как именно рисовать экран: 
        //as=0 - полностью; 
        //as=1 - только левую панель; 
        //as=2 - только правую панель;
{
    

    setCol(11,1); //устанавливаем цвет соответственно текста и фона
    setlocale (LC_CTYPE,".866"); //устанавливаем кодировку символов в .866 (DOS)
    setPos(0,0);//устанавливаем курсор в начало координат
    
    switch(as) //в зависимости от того как именно рисовать переходим к отдельным частям кода
    {    
    case 0: //Если полностью
        
        /* тут занимаемся форматированием (красивое обрамление рамочек и тому подобное)*/
        cout<<(char)(201); //выводим символ "╔" (обрамление верхнего левого угла)
        for (int i=0;i<78;i++) //в этом цикле проходим от начала до конца по горизонтали и ортображаем верхнюю часть рамки
            if (i==38) cout<<(char)(187);//выводим символ "╗"
            else if (i==39) cout<<(char)(201);//выводим символ "╔"
            else 
            {
                //if (length(dir1)>i) cout<<(char)(205); 
                cout<<(char)(205);  //выводим символ "═"   
            }

        cout<<(char)(187);//выводим символ "╗"
        
        setlocale (LC_CTYPE,".1251");
        setPos( (40-strlen(dir1))/2,0); //утанавливаем курсор чтоб написать текущий путь в котором сейчас находимся
        cout<<dir1;    //выводим пусть к каталогу над первой панелькой

        setPos( 40+(40-strlen(dir2))/2,0); //утанавливаем курсор чтоб написать текущий путь в котором сейчас находимся
        cout<<dir2;    //выводим пусть к каталогу над второй панелькой

        setlocale (LC_CTYPE,".866");
        for (int i=1;i<=22;i++) //в этом цикле выводим обрамления рамки в вертикали
        {
            setPos(0,i);    //устанавливаем курсор в позицию (0,i)
            cout<<(char)(186);//выводим символ "║"

            setPos(40,i);    //устанавливаем курсор в позицию (40,i)
            cout<<(char)(186);//выводим символ "║"
        }

        setPos(0,23);            //устанавливаем курсор в позицию (0,22)
        cout<<(char)(200);        //выводим символ "╚"
        for (int i=0;i<78;i++)    //в этом цикле отрисовываем нижнюю часть обрамления рамки панелей (горизонтальная)
            if (i==38) cout<<(char)(188);    //если номер символа по горизонтали 38, то выводим символ "╝"
            else                            //иначе
                if (i==39) cout<<(char)(200);    //если номер символа 39, то выводим символ "╚" 
                else cout<<(char)(205);            //иначе, выводим символ "═"
        cout<<(char)(188); //выводим символ "╝"


        //------теперь строка с кнопочками (менюшка внизу)------//
        setCol(7,0);//устанавливаем цвет соответственно текста и фона шрифта
        cout<<"ENTER";//выводим стоку

        setCol(0,3);//устанавливаем цвет соответственно текста и фона шрифта
        cout<<"RUN";//выводим стоку

        setCol(7,0);//устанавливаем цвет соответственно текста и фона шрифта
        cout<<" F3";//выводим стоку

        setCol(0,3);//устанавливаем цвет соответственно текста и фона шрифта
        cout<<"VIEW";//выводим стоку

        setCol(7,0);//устанавливаем цвет соответственно текста и фона шрифта
        cout<<" F5";//выводим стоку

        setCol(0,3);//устанавливаем цвет соответственно текста и фона шрифта
        cout<<"COPY";//выводим стоку

        setCol(7,0);//устанавливаем цвет соответственно текста и фона шрифта
        cout<<" F6";//выводим стоку

        setCol(0,3);//устанавливаем цвет соответственно текста и фона шрифта
        cout<<"REMAME";//выводим стоку

        setCol(7,0);//устанавливаем цвет соответственно текста и фона шрифта
        cout<<" F7";//выводим стоку

        setCol(0,3);//устанавливаем цвет соответственно текста и фона шрифта
        cout<<"REMOVE";//выводим стоку

        setCol(7,0);//устанавливаем цвет соответственно текста и фона шрифта
        cout<<" F8";//выводим стоку

        setCol(0,3);//устанавливаем цвет соответственно текста и фона шрифта
        cout<<"DELETE";//выводим стоку
        
        setCol(7,0);//устанавливаем цвет соответственно текста и фона шрифта
        cout<<" F10";//выводим стоку

        setCol(0,3);//устанавливаем цвет соответственно текста и фона шрифта
        cout<<"EXIT";//выводим стоку
        
        setCol(7,0);//устанавливаем цвет соответственно текста и фона шрифта
        cout<<"                      ";//выводим стоку (это сделано для того чтоб небыло АртИФактов во время перехода между окнами)
        /*-----------------------------------------------------------------------------*/
        
    case 1: //если рисуем только первую,
    case 2: //или вторую панель
        int* dx=0; //условное смещение, для вывода списка файлов, большего чем максимально допустимо строчек по высоте
        int* selItem = 0;//условный номер выбраной строчки
        int* countFd = 0;//условное количество папок
        int* countFf = 0;//условное количество файлов
        for (int c=1;c<=2;c++) //с в данном случае является номером панели
        {
            if (as==1 && c==2) continue; //тут при некотором условии досрочно выходим из цикла (сделано для того, чтоб не тратить время на лишьние, ненужные отрисовки экрана)
            else if (as==2 && c==1) continue;

            if (c==1) { //если номер панели, которая отрсовывается на экране = 1, то
                dx      = &dx1; //приравниваем к условному смещению истинное смещение панели 1(Это значит что указатель dx получает адрес в памяти в зависимости от номера панели с которой мы работаем)
                selItem = &selItem1; //также далее (все условное делаем истинным)
                countFd = &countFd1;
                countFf = &countFf1;
            }else{ //иначе
                dx      = &dx2; //тоже самое для панели 2
                selItem = &selItem2;
                countFd = &countFd2;
                countFf = &countFf2;
            }

            if (*selItem-*dx>=22) *dx=*selItem-22+1; //это сделано для того чтобы, если выбраный файл выходит за область отображения, список смещался
            else if (*selItem-*dx<=-1) *dx=*selItem;
            

            setPos(0,1); //устанавливаем курсор в позицию с координатами (0,1)
            int k=0; 
            int z=0;
            int countF=0;

            for (int i=*dx;i<=(*countFd+*countFf)-1;i++) //в этом цикле выводим список файлов и каталогов
            {
                if (c==1) //если номер панели 1, то 
                    setPos(/*4*/1,i+1-*dx); //устанавливаем курсор в 
                else
                    setPos(41,i+1-*dx);

                if (i-*dx>=24-1-1) break;
                
                k=0;
                
                if (nP==1) //если мы работаем с панелью... тут мы ищем выделенную строчку, и соответственно рисуем ее
                    if (c==1){ 
                        if (i == *selItem ) setCol(7,3); 
                        else setCol(11,1);
                    } else {
                        if (i == *selItem ) setCol(8,1); 
                        else setCol(11,1);
                    }
                else
                    if (c==1){
                        if (i == *selItem ) setCol(8,1); 
                        else setCol(11,1);
                    } else {
                        if (i == *selItem ) setCol(7,3); 
                        else setCol(11,1);
                    }
                
                if (i> *countFd-1) //если i> *countFd-1 значит мы в списке папки закончили, теперь будем отображать файлы
                {
                    z=1;
                    countF=*countFd;
                }

                setlocale (LC_CTYPE,".1251");
                if (c==1) //в зависимости от выбраной панели выводим список файлов
                    while (fList1[z][i-countF][k]!=0)
                    {
                        if (k<38)
                            cout<<fList1[z][i-countF][k];
                        k++;
                    }
                else
                    while (fList2[z][i-countF][k]!=0)
                    {
                        if (k<38)
                            cout<<fList2[z][i-countF][k];
                        k++;
                    }
                
                for (int m=k;m<38;m++) //Здесь выводим пробелы, если имя файла или каталога меньше 39 символов
                    cout<<' '; //сообственно выводим пробелы

                setlocale (LC_CTYPE,".866");
                setCol(11,1);
                if (k>38) { //если длина имени папки или файла больше чем 39 символов ( то выводим справа соответствующий символ)
                    k=38; 
                    setCol(11,1);
                    cout<<"}"; //выводим символ "}"
                    
                } else                
                    cout<<(char)(186); //выводим симовл "║"
                
                k=i;
            }

            int kk = *countFd+*countFf;
            if (kk<20) //к это высота (колво строчек по вертикали)
                for (int m=k+2;m<23;m++) //дорисовываем пустые строчки
                {
                    if (c==1)
                        setPos(1,m);
                    else
                        setPos(41,m);
                    cout<<"                                      ";
                    cout<<(char)(186)<<endl;
                    
                }
    }
    }
}


/***/
void main () //главная функция, управление ей передается в начале выполнения программы
{
    SetConsoleSize(WIDTH, HEIGHT); //устанавливаем размер буфера консоли

    //Маленькая подпрограммка для определения кода клавиши от Геч()
    /*mx:
    cout<<getch()<<endl;
    goto mx;
    
    Маленькая подпрограммка для проверки работоспособности ф-ции геч()
    mx:
    if (getch()!=13) goto mx ;
    */
    
    fileList(1);//формируем список файлов панели 1
    fileList(2);//формируем список файлов панели 1

    paint(0); //рисуем интерфейс, полностью
    
mx:
    
    //указатени
    int* selItem=0; 
    int* countFf=0;
    int* countFd=0;
    char* dir;
    char* fList[2][450][MAX_PATH];

    if (nP==1)
    {
        selItem=&selItem1;
        countFf=&countFf1;
        countFd=&countFd1;
        dir=dir1;
        
        for (int j=0;j<2;j++)
            for (int k=0;k<450;k++)
                for (int i=0;i<MAX_PATH/*strlen(fList1[j][k])+1*/;i++) 
                    fList[j][k][i]=&fList1[j][k][i];
    } else 
    {
        selItem=&selItem2;
        countFf=&countFf2;
        countFd=&countFd2;
        dir=dir2;

        for (int j=0;j<2;j++)
            for (int k=0;k<450;k++)
                for (int i=0;i<MAX_PATH/*strlen(fList2[j][k])+1*/;i++) 
                    fList[j][k][i]=&fList2[j][k][i];
    }

    int key = getch(); //получаем номер символа с клавиатуры
    
    if (key==80) //Down (если нажата клавиша "вниз", то)
    {
        if (*selItem<*countFf+*countFd-1) ++*selItem;//если выделена не последняя строчка, увеличиваем значение, хранящееся по адресу *selItem
    }
    else
    if (key==72) //Up (если нажата клавиша "вверх", то)
    {
        if (*selItem>=1) --*selItem; //если выделеный елемент больше чем нулевой, то уменьшаем значение, хранящееся по адресу *selItem
    }
    else if (key==79) //End
    {
        *selItem=*countFd+*countFf-1; //устанавливаем выделение на последний элемент
    } 
    else if (key==71) //Home
    {
        *selItem=0; //устанавлдиваем выделение на первый елемент
    }
    else if (key==81) //Page Donw
    {
        //пролистываем на 1 страницу (22 строчки) вниз
        if (*selItem+22<*countFd+*countFf-1) *selItem+=22;
        else *selItem=*countFd+*countFf-1;
    } 
    else if (key==73) //Page Up
    {
        //пролистываем на 1 страницу (22 строчки) вверх
        if (*selItem-22>0) *selItem-=22;
        else *selItem=0;
    }
    else if (key == 13) //Enter (если нажата клавиша "Ввод", то)
    { 
        strcpy(backUpDir,dir); //сохраняем в бэкАп директорию которая была
        if (root(dir)) //проверяем, если мы на уровне где показаны все логические диски в системе, то
        {
            //берем первых четыре символа и записываем в путь dir
            for (int i=0;i<4;i++)
                dir[i]=*fList[0][*selItem][i];
            dir[3]=0;
            fileList(nP); //после чего составляем список фалов
            paint(0);  //и отображаем их
            *selItem=0; //номер выделения ставим в нуль
        }
        else //иначе
            if (*selItem<*countFd)
            {
                if (*selItem==0)//здесь отрезаем от пути две точки, таким образом переходя на уровень вверх
                {
                    
                    int i=0;
                    int j=0;
                    while(true) // в этом цикле находим длину (i) и количество слешей (j) строки dir
                    {
                        if (dir[i]==0) break; //ищем нулевой символ в конце строки
                        else i++;
                        if (dir[i]=='\\')j++; //считаем слеши
                    }
                    
                    if (j>1)
                    {
                        for (i;i>1;i--)
                            if (dir[i-2]=='\\')
                            {
                                dir[i-1]=0;
                                break;
                            }
                            else dir[i-1]=0;
                    } else 
                    {
                        strcpy(dir,"FILE SYSTEM");
                    }
                } 
                else //иначе добавляем выделенный каталог
                {
                    strcat(dir, *fList[0][*selItem]); //конкатинация
                    strcat(dir,  ("\\")); //добавляем замыкающий слеш
                }    
            
                fileList(nP); //выполняем перестройку массива с папками и файлами
                *selItem = 0; //устанавливаем выделение в начало (0)
                paint(0); //Рисуем результат
            
            } else {
                ShellExecuteA(0, (""),(*fList[1][*selItem-*countFd]), (""),dir,SW_SHOWNORMAL); //API, запускает связаную с файлом програму, в которую сообщает параметр - "открыть этот файл"
            }
        
    }
    else if (key == 27) { //esc (просто перерисовка экрана, если возникли АртИФакты (впринуипе можно удалить))
        setPos(0,0);
        paint(0); //Рисуем
    } 
    else if (key == 68) goto lx; //F10 (если нажата клавиша "F10", то выходим)
    else if (key == 61) //F3 //просмотр текстовых файлов
    {
        if(*selItem>=*countFd) //если выделение больше чем количество каталогов в списке то переходим к файловому просмотрщику
        {
            //тут получаем путь, соеденяем с выделенным элементом и сообщаем все это в paintFW
            char x11[MAX_PATH];
            strcpy(x11,dir);
            strcat(x11,*fList[1][*selItem-*countFd]);
            paintFW(x11);
        }
        paint(0); //перерисовываем панельки
            
    }
    else if (key >= 63 && key <= 66) //F5,F6,F7,F8 //операции копирования, переименования, перемещения, удаления файлов и папок (пустых и непустых)
    {
        if (!root(dir) && *selItem!=0) //если мы находимся не на уровне отображения логических дисков и выбраный елемент не равен нулю(переход вверх) то можем выполнять операции с файлами и папками
        {
            char mes[4][MAX_PATH]; //здесь хранится сообщение которое будет выводится в зависимости от нажатой клавиши
            strcpy(mes[0],"Вы уверены что хотите скопировать?");
            strcpy(mes[1],"Вы уверены что хотите переименовать?");
            strcpy(mes[2],"Вы уверены что хотите переместить?");
            strcpy(mes[3],"Вы уверены что хотите удалить?");

            if (pdDialog(mes[key-63],1 )) //ввыводим сообщение подтверждения, в зависимости от нажатой кнопоки
            {
                char x11[MAX_PATH]; //откуда
                char x12[MAX_PATH]; //куда
                
                char* dirx;//куда
                char* fn; //от куда
                char fn2[MAX_PATH];
                if (nP==1) //в зависимости от выделенной панели
                {
                    if (key == 64) //F6 (если нажато F6, то мы переименовываем, и нам не нужно перемещать элемент в другую панель)
                        dirx=dir1;
                    else
                        dirx=dir2;

                    if (selItem1<countFd1)
                        fn=fList1[0][selItem1]; //сообщаем в указатель "от куда брать" элемент из списка
                    else
                        fn=fList1[1][selItem1-countFd1];
                }
                else
                {
                    if (key == 64) //F6 (если нажато F6, то мы переименовываем, и нам не нужно перемещать элемент в другую панель)
                        dirx=dir2;
                    else
                        dirx=dir1;
                    
                    if (selItem2<countFd2)
                        fn=fList2[0][selItem2];
                    else
                        fn=fList2[1][selItem2-countFd2];
                }
                
                paint(0); //перерисовываем панельки
                strcpy(fn2,fn);
                if (key == 64 ) //если F6
                {
                    pdDialog("Пожалуйста введите новое имя (включая разширение)",3,fn2);
                    OemToCharA(fn2,fn2);
                }
                else ;
                

                strcpy(x11,dir); //копируем строку из dir1 в x11
                strcat(x11,fn); //конкатинация
                strcat(x11,"\0"); //конкатинация
                    
                
                for (int i=strlen(x11);i<MAX_PATH;i++) //в этом цикле обнуляем все символы с которые находятся после конца строки (зачем не знаю, но если не обнулить - Виндуз ругается)
                    x11[i]=0; 
            
                strcpy(x12,dirx); //копируем строку из dirx в x12
                strcat(x12,fn2); //конкатинация
                strcat(x12,"\0"); //конкатинация
                
                for (int i=strlen(x12);i<MAX_PATH;i++) //в этом цикле обнуляем все символы с которые находятся после конца строки (зачем не знаю, но если не обнулить - Виндуз ругается)
                    x12[i]=0;             

                /*Вот наша структура (и чтоб никто не посмел сказать что у нас ее нет))))*/
                //АПИ, взято от сюда http://msdn.microsoft.com/en-us/library/bb762164(VS.85).aspx
                SHFILEOPSTRUCTA lpFileOp; //связываем значения переменной, SHFILEOPSTRUCTA - структура файловых операций
                //устанавливаем разного рода параметры
                lpFileOp.hwnd=NULL; //хандл
                
                switch(key)
                {
                case 63:
                    lpFileOp.wFunc=FO_COPY; //копировать
                    break;
                case 64:
                    lpFileOp.wFunc=FO_RENAME; //переименовать
                    break;
                case 65:
                    lpFileOp.wFunc=FO_MOVE; //переместить
                    break;
                case 66:
                    lpFileOp.wFunc=FO_DELETE; //удалить
                    break;
                }
                
                lpFileOp.pFrom=x11; //от куда берем //конченный pFrom терпеть не может символы с номером -52 (я полагаю и любой другой отрецательный)
                lpFileOp.pTo=x12; //куда копируем/перемещаем/переименовываем и т.п.
                lpFileOp.fFlags=FOF_NOERRORUI|FOF_NOCONFIRMATION|FOF_SILENT; //устанавливаем параметре - без сообщения об ошибках|без подтверждения|тихо(все надо делать молча)
                lpFileOp.fAnyOperationsAborted=FALSE; //откат операции = ложь
                lpFileOp.hNameMappings=NULL; //маска имен = нуль(пустой указатель, который ни на что не указывает)
                lpFileOp.lpszProgressTitle=NULL; //чегото связанное с процессами = нуль(пустой указатель, который ни на что не указывает)
                
                pdDialog("подождите...",2);

                int is = SHFileOperationA(&lpFileOp);//здесь собственно выполняем операцию, и ее результат записываем в is
                
                //перестраиваем списки файлов, обоих панелей
                fileList(2);
                fileList(1);
                
                paint(0); //рисуем пенельки (обе + рамка)
                
                char mes2[4][MAX_PATH]; //текст сообщения
                strcpy(mes2[0],"Скопировано.");
                strcpy(mes2[1],"Переименовано.");
                strcpy(mes2[2],"Перемещено.");
                strcpy(mes2[3],"Удалено.");

                char mes3[4][MAX_PATH]; //текст сообщения
                strcpy(mes3[0],"Объект не скопирован");
                strcpy(mes3[1],"Объект не переименован");
                strcpy(mes3[2],"Объект не перемещен");
                strcpy(mes3[3],"Объект не удален");

                if (is == 0 ) 
                {
                    if (key == 66 || key == 65) //если нажата кнопка F7 или F8 (перемещение и удаление) то необходимо уменьшить выделение на 1 (потому, что после удачной операции исчезнет из списка)
                        --*selItem;    //уменьшаем на еденицу (декремент)
                    pdDialog(mes2[key-63],0);//если is=0, то выводим сообщение, про удачно проведенную операцию

                }else pdDialog(mes3[key-63],0);//если is не равно 0, то выводим сообщение про НЕудачно проведенную операцию

                
        }
            paint(0); //перерисовываем панельки (всё, включая обрамление)
        }

    }
    else if (key == 9) { //Tab, если нажат "Таб", переходим к панели (не той что была, а другой)
        if (nP==1) 
            nP=2;
        else 
            nP=1;
        
        paint(0); //рисуем
        goto mx; //уходим в место "mx"

    } 


    paint(nP);//рисуем главную панель файлового исследователя (только одну и без обрамления)


    setPos(0,0); //устанавливаем курсор в начало координат
    goto mx;/**/
    
    lx:;

    /*это для того чтобы отображать в конце программы строчку "для завершения нажмите любую клавишу..." белым шрифтом и посередине окна*/
    setCol(0,15); 
    setPos(15,HEIGHT/2);
} 

/** Коды клавишь полученые с помощью ф-ции getch();
key        |    code
Up        |    224; 72;
left    |    224; 75;
down    |    224; 80;
right    |    224; 77;
Enter    |    13;
Escape    |    27;
F1..F10    |    0; 59 .... 0; 68;
F11        |    224; 133;
F12        |    224; 134;
Tab        |    9;
0..9    |    48 .. 57;
PageUp    |    224; 73;
PageDown|    224; 81;
Home    |    224; 71;
End        |    224; 79;
Ins        |    224; 82;
Delete    |    224; 83;

На дополнительной клавиатуне при выключеном NumLock
коды теже, но первый флаг = 0
*/

/** Номера символов в кодировке DOS
        0
☺       1
☻       2
♥       3
♦       4
♣       5
♠       6
        7
        8
        9
        10
♂       11
♀       12
        13
♫       14
☼       15
►       16
◄       17
↕       18
‼       19
¶       20
§       21
▬       22
↨       23
↑       24
↓       25
→       26
←       27
∟       28
↔       29
▲       30
▼       31
        32
!       33
"       34
#       35
$       36
%       37
&       38
'       39
(       40
)       41
*       42
+       43
,       44
-       45
.       46
/       47
0       48
1       49
2       50
3       51
4       52
5       53
6       54
7       55
8       56
9       57
:       58
;       59
<       60
=       61
>       62
?       63
@       64
A       65
B       66
C       67
D       68
E       69
F       70
G       71
H       72
I       73
J       74
K       75
L       76
M       77
N       78
O       79
P       80
Q       81
R       82
S       83
T       84
U       85
V       86
W       87
X       88
Y       89
Z       90
[       91
\       92
]       93
^       94
_       95
`       96
a       97
b       98
c       99
d       100
e       101
f       102
g       103
h       104
i       105
j       106
k       107
l       108
m       109
n       110
o       111
p       112
q       113
r       114
s       115
t       116
u       117
v       118
w       119
x       120
y       121
z       122
{       123
|       124
}       125
~       126
⌂       127
А       128
Б       129
В       130
Г       131
Д       132
Е       133
Ж       134
З       135
И       136
Й       137
К       138
Л       139
М       140
Н       141
О       142
П       143
Р       144
С       145
Т       146
У       147
Ф       148
Х       149
Ц       150
Ч       151
Ш       152
Щ       153
Ъ       154
Ы       155
Ь       156
Э       157
Ю       158
Я       159
а       160
б       161
в       162
г       163
д       164
е       165
ж       166
з       167
и       168
й       169
к       170
л       171
м       172
н       173
о       174
п       175
░       176
▒       177
▓       178
│       179
┤       180
╡       181
╢       182
╖       183
╕       184
╣       185
║       186
╗       187
╝       188
╜       189
╛       190
┐       191
└       192
┴       193
┬       194
├       195
─       196
┼       197
╞       198
╟       199
╚       200
╔       201
╩       202
╦       203
╠       204
═       205
╬       206
╧       207
╨       208
╤       209
╥       210
╙       211
╘       212
╒       213
╓       214
╫       215
╪       216
┘       217
┌       218
█       219
▄       220
▌       221
▐       222
▀       223
р       224
с       225
т       226
у       227
ф       228
х       229
ц       230
ч       231
ш       232
щ       233
ъ       234
ы       235
ь       236
э       237
ю       238
я       239
Ё       240
ё       241
Є       242
є       243
Ї       244
ї       245
Ў       246
ў       247
°       248
∙       249
·       250
√       251
№       252
¤       253
■       254
        255
*/

Все це було написане в Visual Studio 2005 дев'ятнадцятирічним мною для моєї дівчини (тоді), з російськими коментарями, уж вибачайте.

В додатках,
file_explorer.zip - готовий ехешник;
mainX.zip - cpp файл з кодом.
Проект з студії не додаю, 

бо...

якщо ви настільки дегенеративний студент, що не можете зробити проект, то мені вам нічим допомогти.

Post's attachments

file_explorer.zip 64.65 kb, 360 downloads since 2018-02-18 

mainX.zip 15.35 kb, 439 downloads since 2018-02-18 

ScreenShot_fe4.png 83.01 kb, 167 downloads since 2018-02-18 

Подякували: LoganRoss, /KIT\, Fox, Betterthanyou, cheappi3865

2

Re: [C/C++, winApi] Файловий менеджер, simple file manager, file explorer

Прихований текст

"дев'ятнадцятирічним мною для моєї дівчини"
Як мило :3

А для чого студакам ця штука потрiбна ?

3 Востаннє редагувалося VTrim (18.02.2018 21:12:23)

Re: [C/C++, winApi] Файловий менеджер, simple file manager, file explorer

vtorgashov написав:
Прихований текст

"дев'ятнадцятирічним мною для моєї дівчини"
Як мило :3

А для чого студакам ця штука потрiбна ?

> Як мило :3
//////
Шо ж тут милого? Стосунки закінчилися після отримання нею програми :D

Прихований текст

або після сесії

Подякували: taburyak, cheappi3862

4

Re: [C/C++, winApi] Файловий менеджер, simple file manager, file explorer

VTrim написав:
vtorgashov написав:
Прихований текст

"дев'ятнадцятирічним мною для моєї дівчини"
Як мило :3

А для чого студакам ця штука потрiбна ?

> Як мило :3
//////
Шо ж тут милого? Стосунки закічилися після отримання нею програми :D

Оу.... :С

5

Re: [C/C++, winApi] Файловий менеджер, simple file manager, file explorer

vtorgashov написав:

А для чого студакам ця штука потрiбна ?

На курсач.

VTrim написав:

Шо ж тут милого? Стосунки закінчилися після отримання нею програми :D

Ви помиляєтесь.

6

Re: [C/C++, winApi] Файловий менеджер, simple file manager, file explorer

хіба дівкам подобаються такі проєкти?

7

Re: [C/C++, winApi] Файловий менеджер, simple file manager, file explorer

Chemist-i написав:
vtorgashov написав:

А для чого студакам ця штука потрiбна ?

На курсач.

VTrim написав:

Шо ж тут милого? Стосунки закінчилися після отримання нею програми :D

Ви помиляєтесь.

Звісно помиляється. Дівчині ще лишалося 3-4 роки навчатися, нащо одразу стосунки розривати?

Подякували: Chemist-i, taburyak, varkon, /KIT\, Fox, cheappi3866