IPB

Добро пожаловать, гость ( Вход | Регистрация )

9 Страниц V « < 3 4 5 6 7 > »   
Reply to this topicStart new topic
> Delphi, Asm, C, WinAPI, PHP, ..., FAQ
-=CHE@TER=-
Sep 6 2012, 20:32
Сообщение #81


Walter Sullivan
***

Группа: Root Admin
Сообщений: 1,361
Регистрация: 4-February 08
Пользователь №: 3
Спасибо сказали: 314 раз(а)



Все, наверное, помнят, что в .BMP формате строки изображения записываются снизу вверх и от этого, если читать его по обычному, то оно вверх ногами?
Сегодня ковыряясь с .BMP и читая справку (Win32.hlp за 1996 год, которой я постоянно пользуюсь), натолкнулся на откровение (в описания структуры BITMAPINFOHEADER):
QUOTE
If biHeight is positive, the bitmap is a bottom-up DIB and its origin is the lower left corner. If biHeight is negative, the bitmap is a top-down DIB and its origin is the upper left corner.
Проверил - задал высоту отрицательной и... работает, зараза! Даже ACDSee 5.0 понимает.
Программы, которые вяло следуют (или вообще не следуют) стандартам, конечно, накроются тазом, если читают поля как беззнаковые, но в остальном даже обидно как-то - всю жизнь маялся и перевёрнутым записывал. Эх, где ж это всё раньше было?..
В msdn это тему расширили - см. описание biHeight.


Спасибо сказали:
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
jTommy
Sep 8 2012, 20:52
Сообщение #82


Наблюдающий
***

Группа: CTPAX-X
Сообщений: 197
Регистрация: 4-February 08
Из: деревня Москва
Пользователь №: 6
Спасибо сказали: 19 раз(а)



Ради интереса полез в свою "библию" - книгу "Энциклопедия форматов графических файлов". И действительно, начиная с версии 2.х формата, там в точности все так и написано. А ведь раньше и не замечал этого. smile.gif


Спасибо сказали:
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
-=CHE@TER=-
Sep 9 2012, 12:43
Сообщение #83


Walter Sullivan
***

Группа: Root Admin
Сообщений: 1,361
Регистрация: 4-February 08
Пользователь №: 3
Спасибо сказали: 314 раз(а)



Ты имеешь ввиду GFF?
Я нашёл там вот такое описание (оно за 1994 год), но там нет упоминания про отрицательное значение.
Видимо, стандарт 2.0 был введён между 1994 и 1996. (*улыбается*)
А, всё, дошло - ты про книгу 1997 года, которая является переводом более поздней версии этого сайта?
В Интернете, увы, нигде .PDF версии не нашёл, только купить предлагают.
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
jTommy
Sep 9 2012, 21:25
Сообщение #84


Наблюдающий
***

Группа: CTPAX-X
Сообщений: 197
Регистрация: 4-February 08
Из: деревня Москва
Пользователь №: 6
Спасибо сказали: 19 раз(а)



Да, она самая, издание от 1997 года.
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
-=CHE@TER=-
Dec 15 2012, 09:29
Сообщение #85


Walter Sullivan
***

Группа: Root Admin
Сообщений: 1,361
Регистрация: 4-February 08
Пользователь №: 3
Спасибо сказали: 314 раз(а)



Поделюсь двумя вещами.

1) Во-первых, в Delphi 7 как-то через одно место сделана работа с позиционированием формы.
CODE
procedure TForm1.FormActivate(Sender: TObject);
begin
  // в LoadDataFromFileAndChangeFormSize читаем данные из файла
  // указанного как параметр командной строки и меняем размер формы
  LoadDataFromFileAndChangeFormSize;
  // пытаемся отцентрировать форму с новым размером на экране
  Form1.Position:=poScreenCenter; // <-- вот тут мы падаем и программа зависает
end;

Прилетает ошибка "Cannot change Visible in OnShow or OnHide", потому что нельзя менять позицию (при чём тут Visible кто-нибудь, кроме Borland, знает?!) во время события onActivate. А проблема там в том, что при изменении позиции зачем-то пересоздаётся форма (долбанный стыд!). Решения этой проблемы в Интернете меня вообще убили - доходило до того, что советовали создать таймер в onActivate, который будет через секунду центрировать форму (когда она уже появится). Ну офигеть просто.
Исправить же ситуацию быстро и просто можно через WinAPI:
CODE
  MoveWindow(
    Form1.Handle,
    (Screen.Width - Form1.Width) Div 2,
    (Screen.Height - Form1.Height) Div 2,
    Form1.Width,
    Form1.Height,
    TRUE
  );



2) А тут поделюсь одним интересным приёмом. В последнее время наделал кучу PHP-скриптов, для автоматизации всего и вся. Проблема с ними в том, что их постоянно нужно либо:

а) Вызывать вручную:
CODE
php -f file.php somefile.ext


б) Либо таскать вместе со скриптом .BAT файл, где это безобразие будет прописано:
CODE
@php -f file.php %1


Но есть способ проще! Пишем PHP-скрипт, затем переименовываем его в .BAT и добавляем в начало:
CODE
@php -f %0 %1 %2 %3 %4 %5 %6 %7 %8 %9 & goto :EOF
<?php
// а тут у нас программа на PHP, которая что-нибудь делает
?>

И всё! У нас один .BAT файл и никакого головняка!
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
-=CHE@TER=-
Apr 6 2013, 18:25
Сообщение #86


Walter Sullivan
***

Группа: Root Admin
Сообщений: 1,361
Регистрация: 4-February 08
Пользователь №: 3
Спасибо сказали: 314 раз(а)



Товарищи, у меня вопрос - может кто-нибудь помочь?
Есть 16-ти битное Windows приложение (NE - New Executable) в котором есть окно с текстом.
Пытаюсь достать текст через WM_GETTEXTLENGTH и WM_GETTEXT. Всё бы ничего, но текст специально отформатированный каким-то своим форматом, так что там есть символ 0x00 (т.е. ноль). Из-за чего WM_GETTEXTLENGTH обламывается на первом таком символе и возвращает длинну меньше чем надо. Я пробовал специально делать буфер больше, но через WM_GETTEXT один фиг возращается всё до первого нуля (и размер скопированных данных соответственный).
Кто-нибудь знает, есть ли какая-нибудь функция, которая позволяет:
- получить заголовок окна как бинарные данные
- или получить указатель на начало буфера с эаголовком окна
- или кто-нибудь может сказать, в какой структуре из недокументированных функций можно найти как получить адрес буфера caption?
Заранее спасибо.
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
-=CHE@TER=-
Apr 9 2013, 16:17
Сообщение #87


Walter Sullivan
***

Группа: Root Admin
Сообщений: 1,361
Регистрация: 4-February 08
Пользователь №: 3
Спасибо сказали: 314 раз(а)



Нашёл вот такую штуку:

QUOTE
typedef struct _WND32 {
...
WORD windowTextOffset; // 40h Offset of the window's text in atom heap
...
} WND32, *PWND32;

Это из исходных кодов статьи "Windows 95 System programming SECRETS" by MATT PIETREK.
Исходные коды можно нагуглить, могу выложить, если понадобятся.
Скомпилил в 6-ой студии программку и запустил на Windows 98 (в XP она, понятное дело, не заработает) - действительно, работает и показывает.
Остался один вопрос: кто-нибудь знает, как получить адрес "atom heap", чтобы прибавить к нему смещение и добраться до текста в окне?
Буду очень-очень признателен.
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
-=CHE@TER=-
Apr 18 2013, 16:48
Сообщение #88


Walter Sullivan
***

Группа: Root Admin
Сообщений: 1,361
Регистрация: 4-February 08
Пользователь №: 3
Спасибо сказали: 314 раз(а)



QUOTE(-=CHE@TER=- @ Apr 9 2013, 16:17) *
Исходные коды можно нагуглить, могу выложить, если понадобятся.
showwnd.zip - вот оно.
Скорее всего под что-то старше 6-ой студии компилиться не будет.
В качестве 16-ти битного приложения в 98-ой можно запустить PROGMAN.EXE и попытаться получить его заголовок.
Может кто-нибудь хотя бы по исходным кодам скажет мне как получить адрес "atom heap"?..
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
-=CHE@TER=-
Apr 27 2013, 15:06
Сообщение #89


Walter Sullivan
***

Группа: Root Admin
Сообщений: 1,361
Регистрация: 4-February 08
Пользователь №: 3
Спасибо сказали: 314 раз(а)



Я, походу, перестану скоро Delphi пользоваться, во всяком случае VCL компонентами.
И вот из-за чего.
Написал одну программу на заказ, довольно сложную, так что для упрощения работы сделал всё на VCL (Delphi 7).
Через некоторое время ко мне обращается человек, который сейчас её допиливает и говорит что она зависает в неопределённый момент. Тупо висит и всё. Посмотрел исходные коды и дописал туда логирование. Там суть в том, что вызываются по очереди два таймера (класс TTimer), один останавливается и запускает другой. Хер знает что происходит, но иногда таймер включается (Timer1.Enabled = True) успешно, но тупо стоит. Из-за чего программа виснет, так как не может дождаться когда он сработает. Когда я это обнаружил, то переписал код на WinAPI функции SetTimer() / KillTimer() и зависон пропал.
Самое смешное, что код с классом TTimer точно также вис и на последнем Delphi X2 (или как он там называется).
Блин, сраные классы, сраные объекты, сраное ООП - такое ощущение, что всё это придумали не только для запутывания в коде программы, но и чтобы программа вообще переставала работать нормально.

P.S. Вопрос выше с получением текста из 16-ти битного приложения всё ещё актуален.
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
Siberian GRemlin
Apr 29 2013, 13:48
Сообщение #90


Advanced Member
***

Группа: CTPAX-X
Сообщений: 537
Регистрация: 4-February 08
Пользователь №: 2
Спасибо сказали: 221 раз(а)



Моя прога по работе использует TTimer и пока проблем с ним не было. Не знаю что у тебя и как делается, но у меня TTimer отвечает за запуск потока, и когда в потоке всё отработает в конце запускается Timer и он через заданное время вновь создаёт поток. Причём таких связок целый массив.

P. S. С получением текста помочь не могу, к сожалению. Не пойму, там он динамический что-ли? Нельзя ли самостоятельно выскрести этот текст оттуда откуда прога его берёт?
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
-=CHE@TER=-
Apr 30 2013, 11:40
Сообщение #91


Walter Sullivan
***

Группа: Root Admin
Сообщений: 1,361
Регистрация: 4-February 08
Пользователь №: 3
Спасибо сказали: 314 раз(а)



QUOTE(Siberian GRemlin @ Apr 29 2013, 13:48) *
P. S. С получением текста помочь не могу, к сожалению. Не пойму, там он динамический что-ли? Нельзя ли самостоятельно выскрести этот текст оттуда откуда прога его берёт?
Да, увы, динамический - поэтому нужно именно то, что написано на окне, потому что это сгенерированный, пересчитанный и обработанный текст. Т.е. на основе входных данных программа обсчитывает и выводит результат. Проблема только в том, что программу писали через одно место и ни распечатать, ни сохранить в файл результат работы нельзя, разве что PrintScreen делать, но с многостраничными отчётами, которые не помещаются на экран - это сизифов труд. Я пытался написать программу, которая бы получала канву окна и делала автоматические снимки экрана в файл, но там, почему-то, всегда оказывалась известная работа Казимира Малевича - выяснилось что нужно сворачивать все окна, кроме нужного, делать снимок всего рабочего стола, затем выковыривать оттуда по координатам изображение с окна... короче, я забил на такой способ.
Программа 1997 г.в., аналогов нет, плюс 16-ти битные приложения же ещё хрен отладишь - Олька, например, с ними не работает. А то что работает, идёт только под реальной 98-ой, и ни на XP, ни под виртуальную машину с 98 не запускается (тупо вешает).
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
-=CHE@TER=-
May 1 2013, 11:54
Сообщение #92


Walter Sullivan
***

Группа: Root Admin
Сообщений: 1,361
Регистрация: 4-February 08
Пользователь №: 3
Спасибо сказали: 314 раз(а)



Ну, ладно, пока никто с получением текста не помог - поделюсь другими своими интересными наработками...

1) Многие программы в Windows не работают с относительными путями.
К примеру, как я уже говорил выше, чтобы функции [Get|Write]PrivateProfile*() работали как надо, а не сохраняли в %SystemRoot%, нужно либо указывать абсолютный путь, либо писать в начале ".\" перед именем файла.
Однако, есть куда более серьёзные проблемы - например, при попытке открыть через OLE-объект документ Word или Excel, нужно обязательно указывать абсолютный путь до файла иначе труба.
Кстати говоря, относительный путь может сыграть злую шутку - если, к примеру, имя файла для записи результата было указано как ".\filename.ext", то, когда программа сменит рабочий каталог через SetCurrentDirectory(), файл будет сохраняться не в то место где он был до этого, а в новый каталог, который только что стал текущим!
И тут на выручку приходит WinAPI функция GetFullPathName(), документацию на которую рекомендую посмотреть на MSDN, а я только ограничусь примером, который из относительного пути делает абсолютный:
CODE
Function ExpandFullPath(FileName: String): String;
Var P: PChar;
Begin
  // get filename size (include null character)
  SetLength(result, GetFullPathName(PChar(FileName), 0, Nil, P));
  // get full filename path
  GetFullPathName(PChar(FileName), Length(result), PChar(result), P);
End;

Сунуть в функцию можно что угодно (предоложим, что мы сейчас в C:\Folder):
filename.ext => C:\Folder\filename.ext
..\..\filename.ext => C:\filename.ext
C:\Folder\..\.\Folder\filename.ext => C:\Folder\filename.ext
И так далее. Хочу заметить, что файл filename.ext, как и путь до него, не обязан существовать - функция работает только со входной строкой приделывая к ней текущий каталог.
Чертовски удобно.

2) Не так часто меню обои на рабочем столе, последний раз делал это пару лет назад - сложно найти хорошие фото. Проблема тут в том, что часто фотографии леса, водопадов и гор красивые, но слишком яркие - белый текст ярлыков на них не видно, а тень Windows XP как-то криво отбрасывает, в результате чего текст становится абсолютно нечитаемым и приходится напрягать глаза. Включать непрозрачный фон для надписей не хочу, так как смотрится это весьма коряво. Пробовал было в PhotoShop 7 делать эфект фонарика, чтобы по краям фото яркость уменьшалась, но PS7 одновременно поднимает яркость в середине, из-за чего изображение в центре "выцветает". Попытка откалибрировать ни к чему хорошему не привела и тогда я написал свою утилиту (за 5 минут, так что она не особо оптимальна) для затемнения фото от центра к краям, где, обычно, иконки и распологаются. При этом центр не осветляется, а остаётся как прежде. Вот код - может ещё кому-нибудь пригодится.
Можно сделать белый .BMP файл размером с экран и запустить:
darkener white.bmp fade.bmp 0
Чтобы посмотреть как происходит затенение.
Величина затенения (FadeValue) будет таковой только в самых удалённых от центра точках - т.е. в углах.
CODE
Program darkener;
{$APPTYPE CONSOLE}
Uses Windows, Graphics;
Var
  I, J, X, Y: Integer;
  D, FadeVal: Longword;
       K, KK: Real;
          TB: TBitmap;
Begin
  WriteLn('Image Darkener');
  WriteLn('(c) CTPAX-X Team 2013');
  WriteLn('http://www.CTPAX-X.org/');
  WriteLn;
  If ParamCount <> 3 Then
  Begin
    WriteLn('Usage: darkener.exe input.bmp output.bmp FadeValue[0..255]');
    Exit;
  End;
  J:=0;
  Val(ParamStr(3), I, J);
  If ((I < 0) Or (I > 255) Or (J <> 0)) Then
  Begin
    WriteLn('Invalid FadeValue[0..255]!');
    Exit;
  End;
  Write('Working...');
  FadeVal:=I;
  TB:=TBitmap.Create;
  TB.LoadFromFile(ParamStr(1));
  X:=TB.Width Div 2;
  Y:=TB.Height Div 2;
  K:=(255 - FadeVal) / sqrt((X*X) + (Y*Y));
  For J:=0 To TB.Height - 1 Do
    For I:=0 To TB.Width - 1 Do
    Begin
      D:=TB.Canvas.Pixels[I, J];
      KK:=K * sqrt(((I - X)*(I - X)) + ((J - Y)*(J - Y)));
      KK:=255 - KK;
      D:=RGB(
        Trunc((GetRValue(D)/255)*KK),
        Trunc((GetGValue(D)/255)*KK),
        Trunc((GetBValue(D)/255)*KK)
      );
      TB.Canvas.Pixels[I, J]:=D;
    End;
  TB.SaveToFile(ParamStr(2));
  TB.Free;
  Write('done!');
  WriteLn;
End.


3) И, наконец, последнее чем хочу поделиться - файл для программ не работающих с маской из командной строки:
do.bat
CODE
@echo off
for %%a in (%2) do %1 "%%a" %3 %4 %5 %6 %7 %8 %9

Помещаем этот файл куда-нибудь в %PATH% (например в C:\Windows) и запускаем, к примеру, так:
do pngout.exe *.bmp /c0
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
-=CHE@TER=-
Jun 6 2013, 17:11
Сообщение #93


Walter Sullivan
***

Группа: Root Admin
Сообщений: 1,361
Регистрация: 4-February 08
Пользователь №: 3
Спасибо сказали: 314 раз(а)



Сделал себе замечательный srvrun.bat для перезапуска локальных служб (положить в PATH):
CODE
@echo off
if _%1 == _ goto quit
net start %1 2>nul
if errorlevel 2 goto stop
goto quit
:stop
net stop %1
:quit

При первом вызове запускает службу, при втором останавливает:
QUOTE
C:\Work\>srvrun mysql
Служба "MySQL" запускается.
Служба "MySQL" успешно запущена.


C:\Work\>srvrun mysql
Служба "MySQL" останавливается.
Служба "MySQL" успешно остановлена.

Немного поясню как работает: при вызове пытается запустить службу и, если она уже запущена (errorlevel = 2), то пытается её остановить. Строчка "2>nul" подавляет вывод ошибок о том, что служба уже запущена. К слову сказать если имя службы указано неверно (например myslq), то errorlevel тоже будет равен 2, однако, и сообщение вылезет соответствующее при остановке службы:
QUOTE
C:\Work\>srvrun myslq
Системная ошибка 1060.

Указанная служба не установлена.

Не забываем, что службы содержащие пробелы в имени необходимо заключать в двойные кавычки:
srvrun "Gene6 FTP Server"
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
Siberian GRemlin
Jun 22 2013, 08:06
Сообщение #94


Advanced Member
***

Группа: CTPAX-X
Сообщений: 537
Регистрация: 4-February 08
Пользователь №: 2
Спасибо сказали: 221 раз(а)



Возникла внезапная потребность использовать socks proxy. Для этого требовалось обновить Indy до 10-ой версии (Delphi 7), но сделать это не удалось, т.к. последняя версия просто не захотела на последнем этапе компилироваться. После трёх безуспешных попыток решить данную проблему решил попробовать библиотеку Synapse. Работать с ней оказалось в разы удобнее и надёжнее (в Indy socks proxy предполагается через пень-колоду). Рекомендую. Пошёл переписывать весь проект.

http://parsing-and-i.blogspot.ru/2010/02/s...irst-steps.html
http://www.webdelphi.ru/tag/synapse/page/2/

P. S. Если кто в курсе как запускать локальный proxy сервер Tor без встренного firefox portable — буду рад советам: копаться пока совсем нет времени.
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
-=CHE@TER=-
Jun 28 2013, 11:09
Сообщение #95


Walter Sullivan
***

Группа: Root Admin
Сообщений: 1,361
Регистрация: 4-February 08
Пользователь №: 3
Спасибо сказали: 314 раз(а)



Мне не так давно было ещё одно откровение, которым спешу поделиться.
Представьте, что у нас есть вот такой код в какой-то программе, которую мы хотим изменить:

CODE
.00401581: 7513  jne   .000401596

Как известно jump'ы правят двумя способами:
1) Когда надо переходить, то меняем 0x7? на 0xEB.
2) Когда НЕ надо переходить, то заменяем оба байта на 0x90.
Но я всегда стараюсь сократить количество изменяемых байт, чтобы проще было исправлять, да и самому не путаться.
В первом случае, как можно заметить, мы меняем 1 байт, а во втором 2.
Хотелось бы сократить издержки производства, но как это сделать?
Оказывается достаточно просто - нужно всего лишь заменить второй байт (смещение для прыжка прибавляемое к адресу после команды jump) на 00, вот так:

CODE
.00401581: 7500  jne   .000401583

Куда будет переходить такой прыжок?
Правильно, на следующую после jump'а инструкцию.
Изменяется всего один байт - очень аккуратно, компактно и элегантно.


Спасибо сказали:
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
-=CHE@TER=-
Sep 14 2013, 14:32
Сообщение #96


Walter Sullivan
***

Группа: Root Admin
Сообщений: 1,361
Регистрация: 4-February 08
Пользователь №: 3
Спасибо сказали: 314 раз(а)



Часто пишу программы, которые со временем довольно сильно меняются.
Ставить на свою машину SVN, GIT или другие репозитории не хочу, да и задачи не такие глобальные.
Так что сделал себе вот такой вот файлик.

backup.bat
CODE
@echo off
del "%DATE:~-4%%DATE:~3,2%%DATE:~0,2%.zip" >nul 2>&1
cd ..
zip.exe -9 -X -D "%~d0%~p0%DATE:~-4%%DATE:~3,2%%DATE:~0,2%.zip" "*.*" -x "*.dcu" -x "*.bak"
cd /d "%~d0%~p0"

1) Файл должен находиться в "%КАТАЛОГ_ПРОЕКТА%\backup\", где вместо backup может быть каталог с любым другим именем.
2) Ключ "-x" позволяет исключить ненужные файлы.
3) Нельзя писать "..\*.*" для указания маски файлов для упаковки, потому что это чревато тем, что в архив файлы с таким именем и будут сложены: "..\Project1.dpr" - что при распаковке вывалит их на каталог выше чем надо.
4) Ключ -D отключает обработку каталогов (чтобы самого себя не запаковать).
5) "-9" - максимальное сжатие.
6) "-X" - отключает всякую ненужную информацию, которая только раздувает размер архива.
7) "%DATE:~-4%%DATE:~3,2%%DATE:~0,2%" превратится в текущую дату: "20130914" (запускаем и делаем копию за сегодня).
8) "%~d0%~p0" - каталог, откуда был запущен .BAT файл.
9) Бесплатный "zip.exe" брал тут: Info-ZIP, распаковать и поместить в %PATH% (например, в C:\WINDOWS\). Прошу обратить внимание, что в этой программе ключи регистрочуствительные, так что -d и -D, а также -x и -X - это разные ключи!


Спасибо сказали:
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
-=CHE@TER=-
Nov 23 2013, 17:38
Сообщение #97


Walter Sullivan
***

Группа: Root Admin
Сообщений: 1,361
Регистрация: 4-February 08
Пользователь №: 3
Спасибо сказали: 314 раз(а)



Пишу в последнее время на Си, так что недавно мне довольно долго выедал мозг один мой косяк.
Короче, есть дефайн:
CODE
#define STRUCT_SIZE(z) (z * sizeof(mystruct))

mystruct - некая структура, которая в данном случае нам не интересна.
Вызывается оно примерно так:
CODE
WriteFile(fl, pstruct, STRUCT_SIZE(count), &dw, NULL);

где:
pstruct - указатель на начало структуры;
count - количество элементов mystruct в этой структуре.
Логично, что для получения размера всего массива в байтах нужно размер одного элемента стуктуры умножить на их количество.
Всё было отлично, пока я не дошёл до места, где в структуре больше элементов, чем count - есть ещё несколько зарезервированных, которые идут в конце:
CODE
WriteFile(fl, pstruct, STRUCT_SIZE(count + 2), &dw, NULL);

В результате структура пишется в файл не целиком, а очень куцо - только самое начало.
Я довольно долго тупил удивляясь "чудесам на виражах", пока до меня не дошло, что определённый мною макрос STRUCT_SIZE(), на самом деле разворачивается в:
CODE
(z * sizeof(mystruct)) => (count + 2 * sizeof(mystruct))

Что при приоритете операции умножить даёт:
CODE
count + (2 * sizeof(mystruct))

Так что не наступайте на эти грабли и, если пишите макрос, то не забывайте всунуть переменную макроса (z) в скобки:
CODE
#define STRUCT_SIZE(z) ((z) * sizeof(mystruct))
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
-=CHE@TER=-
Nov 27 2013, 15:42
Сообщение #98


Walter Sullivan
***

Группа: Root Admin
Сообщений: 1,361
Регистрация: 4-February 08
Пользователь №: 3
Спасибо сказали: 314 раз(а)



Недавно я тут писал про утилиту COMPRESS.EXE, сжимающую файлы.
Конечно, сжатие весьма посредственное (мы сейчас рассматриваем классический алгоритм LZSS, а не MS-ZIP, который туда добавили позже) и хорошо жмёт только файлы где есть длинные последовательности из повторяющихся байт, но в общем и целом для некоторых вещей вполне себе подойдёт. Тут главный плюс в том, что распаковщик короткий, легко пишется и не требует дополнительной памяти (ну 4 Кб на циклический буфер - это крохи). Так что можно использовать этот архиватор и созданные им файлы для своих нужд.
Но, для начала, давайте взглянем на то, как сжатые файлы создаются.

Итак, делаем:
COMPRESS.EXE FILENAME.EXT FILENAME.EX_

Что происходит после выполнения этой команды?

1) Создаётся файл FILENAME.EX_

2) Туда пишется следующий заголовок:
CODE
CHAR[8] - signature "SZDD\x88\xF0\x27\x33"
CHAR - compression algo type - always 'A' (0x41)
CHAR - last char of uncompressed file name (example: "test.xyz" -> 'z'); may be null
DWORD - unpacked file size

Итого, заголовок у нас занимает 14 байт.
Отдельно хочу сделать замечание по поводу поля "algo type" (алгоритм сжатия). Если кто-то видел в Интернете исходные коды Windows 2000, то там можно было увидеть такое:
CODE
#define ALG_FIRST ((BYTE) 'A') // first version algorithm label for Lempel-Ziv
#define ALG_LZ    ((BYTE) 'B') // new Lempel-Ziv algorithm label
#define ALG_LZA   ((BYTE) 'C') // Lempel-Ziv with arithmetic encoding algorithm label

Однако, сразу после этого шёл define объявлявший валидным только первый алгоритм. Так что на практике первые два поля в заголовке архива - константы и можно смело проверять даже не 8, а первые 9 байт.
Поле "last char" утилитой EXPAND.EXE тоже не используется, а COMPRESS.EXE пишет туда ноль (или я так и не понял, как сделать, чтобы оно работало). Так что его можно игнорировать, если, конечно, вы сами не создавали архив и сами же его не распаковываете (точно зная, что поле не пустое).
Далее идёт размер распакованного файла и всё. Хочу обратить на это внимание - в архиве нигде не содержится размер сжатых данных - файл читается, пока размер распакованного буфера не достигнет размера распакованного файла из заголовка.

3) В файл пишется упакованное содержимое.

4) Файлу-архиву ставится дата исходного файла - таким "оригинальным образом" в Microsoft сохраняют дату создания файла. Поясню: при распаковке извлечённому файлу выставляется (восстанавливается) дата с файла-архива.

Ну вот, теперь мы знаем как это работает и устроено, так что можем делать архивы, линковать в ресурсы к своим программам и "на лету" распаковывать. Вот небольшой код на Delphi делающий распаковку (я переписал его с Си - см. ссылку).
Вызывать так: MSLZSSExpandBuff(PackedBuffer, UnpackedBuffer, PackedBufferSize, UnpackedBufferSize);

CODE
{ ported from: http://gnuwin32.sourceforge.net/packages/mscompress.htm }

Function MSLZSSGetByte(Var P: Pointer; Var Ps: DWORD): Integer;
Begin
  If Ps > 0 Then
  Begin
    result:=Byte(P^);
    Ps:=Ps - 1;
    Inc(DWORD(P));
  End
  Else result:=-1;
End;

Function MSLZSSPutByte(Var U: Pointer; Var Us: DWORD; B: Byte): Boolean;
Begin
  result:=(Us > 0);
  If result Then
  Begin
    Byte(U^):=B;
    Us:=Us - 1;
    Inc(DWORD(U));
  End;
End;

Procedure MSLZSSExpandBuff(P, U: Pointer; Ps, Us: DWORD);
Var
  I, J, Len, M, B: Integer;
             Buff: Array[0..$FFF] Of Byte;
Begin
  FillChar(buff[0], $FFF + 1, 32);
  I:=($FFF + 1) - 16;
  While ((Ps > 0) And (Us > 0)) Do
  Begin
    B:=MSLZSSGetByte(P, Ps);
    If B = -1 Then Break;
    For M:=0 To 7 Do
    Begin
      If (B And 1) = 0 Then
      Begin
        J:=MSLZSSGetByte(P, Ps);
        If J = -1 Then Break;
        Len:=MSLZSSGetByte(P, Ps);
        If Len = -1 Then Break;
        J:=J + ((Len And $F0) ShL 4);
        Len:=(Len And $0F) + 3;
        While Len > 0 Do
        Begin
          Buff[I]:=Buff[J];
          If MSLZSSPutByte(U, Us, Buff[I]) = False Then Break;
          J:=(J + 1) And $FFF;
          I:=(I + 1) And $FFF;
          Len:=Len - 1;
        End;
      End
      Else
      Begin
        J:=MSLZSSGetByte(P, Ps);
        If J = -1 Then Break;
        Buff[I]:=J;
        If MSLZSSPutByte(U, Us, Buff[I]) = False Then Break;
        I:=(I + 1) And $FFF;
      End;
      B:=B ShR 1;
    End; // For
  End; // While
End;


Спасибо сказали:
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
-=CHE@TER=-
Feb 13 2014, 14:49
Сообщение #99


Walter Sullivan
***

Группа: Root Admin
Сообщений: 1,361
Регистрация: 4-February 08
Пользователь №: 3
Спасибо сказали: 314 раз(а)



Натолкнулся на то, что в нескольких играх используется LZSS, описанный в моём сообщении выше.
Но, есть одно отличие MS-LZSS (Microsoft LZSS) от классического LZSS (который, похоже, используется во многих играх), без которого на выходе получится не файл, а что попало.
Итак, строчки из MS-LZSS:
CODE
  FillChar(buff[0], $FFF + 1, 32);
  I:=($FFF + 1) - 16;

в классическом LZSS должны выглядеть вот так:
CODE
  FillChar(buff[0], $FFF + 1, 0);
  I:=($FFF + 1) - 16 - 2;

Т.е. кольцевой буфер заполняется нулём, а не пробелом (походу изначально MS жали текстовые файлы, вот пробелом (32) и заполняли), а также отступ от начала буфера должен быть $FEE (поэтому ещё "-2" добавляется).

Ещё хочу заметить, что сжатые этим алгоритмом данные очень хорошо выявляются "на глаз":
CODE
_FORMMIDI_1234LIST_...

Где "_" - это символ $FF (255), так как в начале файлов ещё нет повторений, то первые блоки по 8 байт идут в упаковонный поток без изменений. В общем, если в файле видны блоки по 9 байт, первый из которых $FF (и, собственно, с $FF файл и начинается), то с большой долей вероятности можно утверждать, что файл ужат классическим LZSS.
В некоторых совсем уж старых играх буфер может быть не 4Кб, а 1Кб, но это не так сложно подобрать опытным путём.


Спасибо сказали:
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
-=CHE@TER=-
Aug 22 2014, 11:14
Сообщение #100


Walter Sullivan
***

Группа: Root Admin
Сообщений: 1,361
Регистрация: 4-February 08
Пользователь №: 3
Спасибо сказали: 314 раз(а)



QUOTE(-=CHE@TER=- @ Jul 9 2007, 12:10) *
Нет, всё гораздо хуже - программа при загрузке компьютера загружается и "садится" в SystemTray. А из-за чего я делаю такую хрень с её перезагрузкой - так это из-за того, что при загрузке компа, иногда, проявляется такой баг: программа загрузилась, в процессах есть, а в трее иконки нет. Х.з. из-за чего это. Приходится её перезапускать.
QUOTE(jTommy @ Jul 9 2007, 20:09) *
Бардак на корабле! © Попугай из Пиратов Карибского моря. smile.gifНадо разбираться, так не должно быть.
Как оказалось, это не я дурак, а кривая реализация в Windows (выделение моё):
QUOTE
First is a timeout of 4 seconds, raised to 7 in Windows Vista. Second is that delivery is aborted if the target process seems hung. According to Microsoft's documentation, this means that the target hasn't been seen to pick up any messages for at least 5 seconds.
<...>
Experiments with Windows XP confirm that the taskbar window can be busy for more than a minute during startup, most notably because of network discovery.
© Missing Icons in Notification Area
User is offlineProfile CardPM
Go to the top of the page
+Quote Post

9 Страниц V « < 3 4 5 6 7 > » 
Reply to this topicStart new topic
5 чел. читают эту тему (гостей: 5, скрытых пользователей: 0)
Пользователей: 0 -

 



Упрощённая версия Сейчас: 1st November 2024 - 12:23