Total Commander Forum Index Total Commander
Форум поддержки пользователей Total Commander
Сайты: Все о Total Commander | Totalcmd.net | Ghisler.com | RU.TCKB
 
 RulesRules   SearchSearch   FAQFAQ   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Single Post  Topic: "умный" компрессор NTFS файлов для total 
Author Message
wOxxOm



PostPosted: Sun Feb 12, 2006 23:30    Post subject: "умный" компрессор NTFS файлов для total Reply with quote

Написал свою первую прогу на winApi в delphi - 50 кило. Ногами не бить. Вешается на горячую клавишу в меню запуска тотала или на кнопку. Параметр запуска: "%L" - это файл-список выделенного в панели тотала. Сжимает на ntfs диске то, что ужмется больше, чем в 1.2 раза. То есть сжимается все подряд (те что уже сжатые, те не трогаются), а потом прога смотрит на разницу в размерах и разжимает, если плохо сжалось. Неэффективно, но вписать в прогу анализатор сжимаемости наподобие winrar'а не умею. Настроек ini нету, потом напишу. А лучше если интересно и лучше меня знаете winApi/Delphi то допишите до ума. Интерфейс кодировать еще не умею Wink поэтому что получилось, то и ловите.

Предистория
1. У меня давно все диски NTFS
2. на NTFS можно прозрачно сжимать файлы на уровне системы
3. Сейчас процы гораздо быстрее веников и сжатый файл грузится быстрее, чем несжатый
4. Софт жмется в полтора разы, некоторый - в три(!!) - это gta san andreas - с 4.5 гигов до 1.5 гигов
5. Пользоваться стандартным способом сжатия через свойства файла/папки долго, нудно, неудобно
6. Пользоваться горячей клавишей для запуска compact /c /s:"%p" * можно, но в любом случае : нельзя этой тупой проге передать список файлов, да и главное - если она сжимает, то подряд, а часто бывает, что файл в 1 гиг ужимается до 900 мег - кому надо такая экономия?

Code:

program CompressFile;
uses
  windows, messages, sysUtils, strUtils;

{$R *.res}
{$V-,B-,X+,H+,J+}
const
  prcOK = 1.2;
  WndTitle='Compress File on NTFS';
  WndClass='ntfsCompress';
  wndW:word=200;
  wndH:word=50;

var
  Wnd:HWND; Msg:TMSG; Wc:TWndClassEx; log, font: HWND; DC: HDC;
  h:dword; BytesReturned,fattr:DWORD; Buffer:Short;
  fsize,fsize2,totalS,totalS2: longword; i,j:integer;
  dirsToDo: boolean;
  t:text; fname,ss:string; logS: string; tWH:TSize;
  ScreenH, ScreenW: word; offsH, ttlH: word; wndR,clR: TRECT;

function _str(const v:double; field,digits:word):string;
  begin str(v:field:digits,result); end;

function WindowProc( Wnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM ): LRESULT; stdcall;
  begin Result:=0;
    case Msg of
      WM_DESTROY: begin PostQuitMessage(0); Exit; end;
    end;
    Result:=DefWindowProc( Wnd, Msg, wParam, lParam );
  end;

procedure adjustWindowSize(const s:string; logAdded:boolean);
  var reposition:boolean; newH:word;
  begin
    if GetTextExtentPoint32(dc, pchar(s), length(s), tWH) then begin
      reposition:=false; newH:=ttlH+offsH+20+tWH.cy*word(logAdded);
      if (tWH.cx>wndW-20) and (tWH.cx<ScreenW/1.3) then
        begin wndW:=tWH.cx+20; reposition:=true; end;
      if logAdded and (newH>wndH) and (newH<ScreenH div 2) then
          begin wndH:=newH; reposition:=true; end;
      while tWH.cy*word(logAdded)+offsH>wndH-20-ttlH do begin //scroll
        ss:=copy(logS,1,pos(#13#10,logS)-1);
        logS:=copy(logS,length(ss)+3,$7FFF);
        GetTextExtentPoint32(dc, pchar(ss), length(ss), tWH);
        dec(offsH, tWH.cy);
      end;
      if logAdded then begin
        inc(offsH,tWH.cy);
        if not reposition and (tWH.cx>wndW-20) then
          inc(offsH,tWH.cy*(tWH.cx div (wndW-20)));
      end;
      if reposition then begin
        SetWindowPos(log,0,10,10,wndW-20,wndH-ttlH-20,SWP_NOZORDER or SWP_NOREDRAW);
        SetWindowPos(wnd,0,(ScreenW-wndW)div 2,(ScreenH-wndH)div 2,wndW,wndH,SWP_NOZORDER);
      end;
    end;
  end;

function formatSize(S:longword):string;
  begin
    if S>$40000000 then result:=_str(S/$40000000,7,2)+'GB' else
    if S>$100000 then result:=_str(S/$100000,7,2)+'MB' else
    if S>$400 then result:=_str(S/$400,7,2)+' kB'
    else begin result:=_str(S,7,0)+' B '; exit; end;
    S:=length(result)-3;
    while result[S]='0' do begin result[S]:=' '; dec(S); end;
  end;

function fileSize(fH:DWORD; var S:longword; const cFN:string):string;
  var L:cardinal;
  begin result:='';
    if cFN<>'' then L:=GetCompressedFileSize(pchar(cFN),@S)
      else L:=GetFileSize(fH,@S);
    if L=$FFFFFFFF then exit;
    S:=S shl 32 or L;
    result:=formatSize(S);
  end;

procedure processMsg;
  begin
    if PeekMessage(Msg, wnd, 0, 0, 0) then
      begin TranslateMessage(Msg); DispatchMessage(Msg); end;
    if getAsyncKeyState(VK_ESCAPE)<>0 then
      begin postquitmessage(0); halt; end;
  end;

procedure processFile(const fname:pchar; fattr:dword);
  var fd:_WIN32_FIND_DATA; fh:dword; s:string; fa:dword;
  begin
    processMsg;

    if fattr and FILE_ATTRIBUTE_DIRECTORY <> 0 then begin
      fh:=FindFirstFile(pchar(fname+'*'),fd);
      if fh=INVALID_HANDLE_VALUE then exit;
      repeat
        if (pchar(@fd.cFileName)<>'.') and (pchar(@fd.cFileName)<>'..') then begin
          s:=string(fname)+pchar(@fd.cFileName);
          fa:=GetFileAttributes(pchar(s));
          if fa and FILE_ATTRIBUTE_DIRECTORY<>0 then s:=s+'\';
          processFile(pchar(s),fa);
        end;
      until not findNextFile(fh,fd);
      windows.findClose(fh);
    end;

    if (fattr and FILE_ATTRIBUTE_DIRECTORY=0)
       and (fattr and FILE_ATTRIBUTE_COMPRESSED <> 0) then exit;

    H:=CreateFile(PChar(fname), GENERIC_READ or GENERIC_WRITE,
           FILE_SHARE_READ, nil, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);

    ss:='Size: '+fileSize(H,fsize,'')+#9': ';
    logS:=logS+ss+fname+#13#10;
    setWindowText(log, pchar(logS));
    adjustWindowSize(ss+fname,true);
    GetClientRect(wnd,wndR); InvalidateRect(wnd,@wndR,false); updateWindow(wnd);

    if H<>INVALID_HANDLE_VALUE then
    try Buffer:=1;
      DeviceIoControl(h, $9C040, @Buffer, 2, nil, 0, BytesReturned, nil);
      processMsg;
      ss:=fileSize(0,fsize2,fname);
      inc(totalS,fsize); inc(totalS2,fsize2);
      if totalS2=0 then s:='' else s:=_str(totalS/totalS2,6,3);
      setWindowText(wnd,pchar('NTFS Compress files. '+formatSize(totalS)+' --> '+
        formatSize(totalS2)+'.  ratio is '+s));
      if ss<>'' then begin if fsize2=0 then fsize2:=1;
        ss:='-->'+ss+#9': '+_str(fsize/fsize2,5,2)+#9;
        i:=length(logS)-4; while (i>0) and (logS[i]<>#13) do dec(i);
        if (logS[i]=#13) and (i>0) then inc(i,2) else inc(i);
        j:=posEx(#9': ',logS,i);
        if j=0 then j:=i+2;
        ss:=ss+copy(logS,j+1,$7FFF);
        logS:=copy(logS,1,j-3)+ss;
        ss:=copy(logS,i,$7FFF);
        setWindowText(log, pchar(logS));
        adjustWindowSize(ss,false);
        if fsize/fsize2<prcOK then begin
          logS:=copy(logS,1,length(logS)-2)+' [no gain: DECOMPRESS]'#13#10;
          adjustWindowSize(ss+' [no gain: DECOMPRESS]',false);
          buffer:=0;
          DeviceIoControl(h, $9C040, @Buffer, 2, nil, 0, BytesReturned, nil);
          inc(totalS2,fsize-fsize2);
          setWindowText(wnd,pchar('NTFS Compress files. '+formatSize(totalS)+' --> '+
            formatSize(totalS2)+'.  ratio is '+_str(totalS/totalS2,6,3)));
        end;
      end;
    finally
      CloseHandle(h);
    end;
  end;

begin
  with wc do begin
    cbSize:=SizeOf(wc); style:=CS_HREDRAW or CS_VREDRAW;
    lpfnWndProc:=@WindowProc;
    cbClsExtra:=0; cbWndExtra:=0; hInstance:=hInstance;
    hIcon:=LoadIcon( 0, IDI_APPLICATION );
    hCursor:=LoadCursor( 0, IDC_ARROW );
    hbrBackground:=COLOR_WINDOW;
    lpszMenuName:=nil; lpszClassName:=WndClass;
  end;
  RegisterClassEx(Wc);

  ScreenW:=GetSystemMetrics(SM_CXSCREEN); ScreenH:=GetSystemMetrics(SM_CYSCREEN);
  Wnd:=CreateWindowEx(0, WndClass, WndTitle, WS_OVERLAPPEDWINDOW,
          (ScreenW-wndW)div 2, (ScreenH-wndH)div 2, wndW, wndH, 0, 0, hInstance, nil);
  getClientRect(wnd,clR); ttlH:=wndH-clR.Bottom;
  log:=CreateWindowEx(0, 'Static', '', WS_CHILD or WS_VISIBLE
          or WS_EX_LEFT or SS_LEFTNOWORDWRAP,
          10, 10, wndW-20, wndH-20-ttlH, Wnd, 1, hInstance, nil );
  font:=CreateFont(16,0,0,0,0,0,0,0,ANSI_CHARSET,0,0,0,0,'Arial');
  if font=INVALID_HANDLE_VALUE then font:=CreateFont(16,0,0,0,0,0,0,0,ANSI_CHARSET,0,0,0,0,'MS Sans Serif');
  if font=INVALID_HANDLE_VALUE then font:=getStockObject(ANSI_VAR_FONT);
  SendMessage(log, WM_SETFONT, font, 1);

  DC:=getDC(0); logS:=''; offsH:=0;
  ShowWindow(Wnd, SW_SHOWNORMAL);

  fname:=paramStr(1); if fname[1]='"' then fname:=copy(fname,2,length(fname)-2);
  assign(t,fname); try reset(t) except exit end;
  dirsToDo:=false; totalS:=0;totalS2:=0;

  repeat
    readln(t,fname); fattr:=GetFileAttributes(pchar(fname));
    if fattr and FILE_ATTRIBUTE_DIRECTORY <> 0 then
      begin DirsToDo:=true; continue; end;
    if fattr and FILE_ATTRIBUTE_COMPRESSED <> 0 then continue;

    processFile(pchar(fname),fattr);
  until eof(t);

  if DirsToDo then begin reset(t);
    while not eof(t) do begin readln(t,fname);
      fattr:=GetFileAttributes(pchar(fname));
      if fattr and FILE_ATTRIBUTE_DIRECTORY <> 0 then
        processFile(pchar(fname),fattr);
    end;
  end;
  Halt(Msg.wParam);
end.


если нужна иконка для файла, то в CompressFile.rc пишем:
IDR_MAINFRAME ICON DISCARDABLE "CompressFile.ico"

и копируем любую иконку с именем CompressFile.ico, затем компилируем .rc файл: "brcc32.exe CompressFile.rc"
затем билдим в дельфи, все.
P.S. Ясное дело когда в delphi делаешь новый проект он создает форму и юнит, их надо закрыть, не сохраняя, потом в Project->View source и вставить туда код.
View user's profile Send private message Send e-mail


Powered by phpBB © 2001, 2005 phpBB Group