Версия для печати темы

Нажмите сюда для просмотра этой темы в оригинальном формате

CTPAX-X _ Статьи _ Delphi, Asm, C, WinAPI, PHP, ...

Автор: -=CHE@TER=- Jan 5 2007, 00:35

http://www.angelfire.com/hi5/delphizeus/index.html.
На английском.
Очень подробно всё описано, плюс есть исходные коды готовых юнитов.
Например SmallUtil, в котором есть все частоиспользуемые подпрограммы, и который в размере меньше, чем SysUtils (автор так утверждает - я не проверял).

Автор: Siberian GRemlin Jul 5 2007, 16:36

Может не в тему, но не решился создавать новую тему. Как в Delphi для указанного EXE выставить в WinXP совместимость с той или иной виндой?

Автор: -=CHE@TER=- Jul 5 2007, 16:44

QUOTE(Siberian GRemlin @ Jul 5 2007, 04:36 PM) *
Может не в тему, но не решился создавать новую тему. Как в Delphi для указанного EXE выставить в WinXP совместимость с той или иной виндой?

Например так:
CODE
[HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers]
"C:\GAMES\MYGAME\MAINFILE.EXE"="WIN95"


А вообще - поставь на .EXE какую хочешь совместимость (или там 256 цветов) и в этой ветке смотри, что за ключи будут появляться.

Автор: -=CHE@TER=- Jul 7 2007, 16:37

Вопрос - кто знает, как по MUTEX'у процесса получить его путь и имя файла? Что-то типа ParamStr(0), только у другого процесса...
Суть в чём: при перезапуске моей программы, нужно чтобы она завершила свою старую копию (если есть) перед загрузкой, чтобы не убивать процесс вручную в TaskManager'е и затем снова запускать.

Для убиения процесса используется такая, вот, нелёгкая процедура (потому что TerminateProcess() оставляет иконку в трее - т.е. она просто хлопает процесс, а мне нужно, чтобы он корректно завершился):

CODE
Function EnumThreadWndProc(Handle: Cardinal; lParam: Integer): LongBool; stdcall;
Begin
  SendMessage(Handle, $0010{WM_CLOSE}, 0, 0);
  Result:=(lParam<>0);
End;

Function ProcessClose(FileName: String; bCloseAll: Boolean): Boolean;
Var
    ThreadSearch, ModuleSearch: Cardinal;
                   ThreadEntry: TThreadEntry32;
                   ModuleEntry: TModuleEntry32;
Begin
  result:=False;
  FileName:=ExtractFileName(FileName);
  ThreadSearch:=CreateToolHelp32Snapshot(TH32CS_SNAPTHREAD, 0);
  If ThreadSearch > 0 Then
    Begin
      ThreadEntry.dwSize:=SizeOf(TThreadEntry32);
      If Thread32First(ThreadSearch, ThreadEntry) Then
        Repeat
          ModuleSearch:=CreateToolHelp32Snapshot(TH32CS_SNAPMODULE, ThreadEntry.th32OwnerProcessID);
          If ModuleSearch > 0 Then
            Begin
              ModuleEntry.dwSize:=SizeOf(TModuleEntry32);
              If Module32First(ModuleSearch, ModuleEntry) Then
                Repeat
                  If lstrcmpi(PChar(ExtractFileName(ModuleEntry.szExePath)), PChar(FileName)) = 0 Then
                    Begin
                      EnumThreadWindows(ThreadEntry.th32ThreadID, @EnumThreadWndProc, Integer(bCloseAll));
                      result:=True;
                    End;
                Until (Not Module32Next(ModuleSearch, ModuleEntry)) Or (result);
              CloseHandle(ModuleSearch);
            End;
        Until (Not Thread32Next(ThreadSearch, ThreadEntry)) Or (result);
      CloseHandle(ThreadSearch);
    End;
End;


Имя файла нужно для сравнения с ModuleEntry.szExePath - вдруг, мою программу, переименовали, так что я не могу спускать в функцию ParamStr(0) от текущего процесса.
Т.е. была, скажем, программа "A1.EXE", сделали копию "A2.EXE". Теперь пускают "A1.EXE", затем "A2.EXE" - т.к. MUTEX у них один, то вторая должна как-то (т.к. имя уже не совпадает) зная только MUTEX завершить предыдущую.

Вообще, пытаюсь сделать такой алгоритм:
1) Производится попытка создать MUTEX с определённым именем
2) Если пришла ошибка GetLastError = ERROR_ALREADY_EXISTS, то убить этот процесс (корректно, как в коде выше, через посылание WM_CLOSE, иначе иконка в трее останется)

Кто-нибудь знает как или, возможно, более простой путь решения?..

Автор: jTommy Jul 8 2007, 16:01

QUOTE(-=CHE@TER=- @ Jul 7 2007, 08:37 PM) *
Вопрос - кто знает, как по MUTEX'у процесса получить его путь и имя файла? Что-то типа ParamStr(0), только у другого процесса...
Суть в чём: при перезапуске моей программы, нужно чтобы она завершила свою старую копию (если есть) перед загрузкой, чтобы не убивать процесс вручную в TaskManager'е и затем снова запускать.
Если я правильно понял ситуация такая: загружена твоя программа, пользователь изменил, например, настройки и надо перезагрузить программу, так?

В таком случае, может-быть так: пользователь жмет "применить", программа создает mutex, запускает второй экземпляр и заканчивает работу (при завершении, в самом конце удаляет mutex). Второй экземпляр находит mutex и ждет, пока он не пропадет.

P.S.: А закрыть (корректно) процесс наверное можно, если послать ему сообщение WM_CLOSE или WM_DESTROY. В этом я не уверен - надо проверять.

P.P.S.: Кажется я вернулся smile.gif

Автор: -=CHE@TER=- Jul 9 2007, 12:10

QUOTE(jTommy @ Jul 8 2007, 04:01 PM) *
Если я правильно понял ситуация такая: загружена твоя программа, пользователь изменил, например, настройки и надо перезагрузить программу, так?
Нет, всё гораздо хуже - программа при загрузке компьютера загружается и "садится" в SystemTray. А из-за чего я делаю такую хрень с её перезагрузкой - так это из-за того, что при загрузке компа, иногда, проявляется такой баг: программа загрузилась, в процессах есть, а в трее иконки нет. Х.з. из-за чего это. Приходится её перезапускать. В принципе, если бы всё было нормально и иконка всегда появлялась, то с перезагрузкой проги бы проблем не было - я бы просто не давал запускать ещё одну копию и сразу выходил. У меня есть подозрение, что это из-за того, что я держу автозапуск "Run" в HKCU, а не в HKLM, однако, ставить программу на автозапуск для всех пользователей - тупо. Каждый сам должен выбирать нужна она ему или нет.


QUOTE(jTommy @ Jul 8 2007, 04:01 PM) *
P.S.: А закрыть (корректно) процесс наверное можно, если послать ему сообщение WM_CLOSE или WM_DESTROY. В этом я не уверен - надо проверять.
Handle'у процесса посылал WM_CLOSE - без толку. Это только формам посылать надо - как я сейчас и делаю. WM_DESTROY не пробовал.


QUOTE(jTommy @ Jul 8 2007, 04:01 PM) *
P.P.S.: Кажется я вернулся smile.gif
Welcome back! Хорошо отдохнул? (*улыбается*)

Автор: jTommy Jul 9 2007, 20:09

QUOTE(-=CHE@TER=- @ Jul 9 2007, 04:10 PM) *
Нет, всё гораздо хуже - программа при загрузке компьютера загружается и "садится" в SystemTray. А из-за чего я делаю такую хрень с её перезагрузкой - так это из-за того, что при загрузке компа, иногда, проявляется такой баг: программа загрузилась, в процессах есть, а в трее иконки нет.
Бардак на корабле! © Попугай из Пиратов Карибского моря. smile.gif
Надо разбираться, так не должно быть.

QUOTE(-=CHE@TER=- @ Jul 9 2007, 04:10 PM) *
У меня есть подозрение, что это из-за того, что я держу автозапуск "Run" в HKCU, а не в HKLM, однако, ставить программу на автозапуск для всех пользователей - тупо. Каждый сам должен выбирать нужна она ему или нет.
Из-за этого маловероятно, но и проверить это легко - поробуй позапускать программу из HKLM.

QUOTE(-=CHE@TER=- @ Jul 9 2007, 04:10 PM) *
Handle'у процесса посылал WM_CLOSE - без толку. Это только формам посылать надо - как я сейчас и делаю. WM_DESTROY не пробовал.
Но винда как-то умудряется корректно закрывать приложения...

QUOTE(-=CHE@TER=- @ Jul 9 2007, 04:10 PM) *
Welcome back! Хорошо отдохнул? (*улыбается*)
Да я не отдыхал, лень такая жуткая навалилась, в ресурсокопании ничего не получается + комп новый собирал.

Автор: Xplorer Jul 10 2007, 09:44

QUOTE(-=CHE@TER=- @ Jul 9 2007, 04:10 PM) *
Handle'у процесса посылал WM_CLOSE - без толку. Это только формам посылать надо - как я сейчас и делаю. WM_DESTROY не пробовал.
Все сообщения посылаются только окнам (HWND). Достаточно послать WM_CLOSE только главному окну. WM_DESTROY посылает сама Windows при уничтожении окна.

QUOTE(jTommy @ Jul 10 2007, 12:09 AM) *
Но винда как-то умудряется корректно закрывать приложения...
Есть сообщения WM_QUERYENDSESSION и WM_ENDSESSION.

-=CHE@TER=-
Попробуй такой вариант:
CODE
program Project1;

uses
  Windows,
  Messages,
  Forms,
  Unit1 in 'Unit1.pas' {Form1};

{$R *.res}

var
  Mutex: THandle;
  Wnd: hWnd;

procedure ErrHalt(const Msg: string);
begin
  MessageBox(0, PChar(Msg), 'Error', MB_ICONHAND or MB_OK);
  Halt;
end;

begin
  Mutex := OpenMutex(MUTEX_ALL_ACCESS, false, '851137EC-3D96-4EA6-817B-30969CCF477B');
  if Mutex <> 0 then
  begin
    Wnd := FindWindow('AFDF47C5-D586-47F2-8091-C6FBAEF8780A', nil);
    if Wnd = 0 then ErrHalt('Window not found');

    SendMessage(Wnd, WMU_MYCLOSE, 0, 0);

    repeat
      CloseHandle(Mutex);
      Mutex := OpenMutex(MUTEX_ALL_ACCESS, false, '851137EC-3D96-4EA6-817B-30969CCF477B');
    until Mutex = 0;
  end;

  Mutex := CreateMutex(nil, false, '851137EC-3D96-4EA6-817B-30969CCF477B');
  if Mutex = 0 then ErrHalt('CreateMutex failed');
  if GetLastError = ERROR_ALREADY_EXISTS then ErrHalt('Mutex already exists');

  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.Run;

  CloseHandle(Mutex);
end.

CODE
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

const
  WMU_MYCLOSE = WM_USER + 1;

type
  TForm1 = class(TForm)
    Label1: TLabel;
    Label2: TLabel;
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    procedure wmuMyClose(var Msg: TMessage); message WMU_MYCLOSE;
  protected
    procedure CreateParams(var Params: TCreateParams); override;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.CreateParams(var Params: TCreateParams);
begin
  inherited;

  Params.WinClassName := 'AFDF47C5-D586-47F2-8091-C6FBAEF8780A';
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Label1.Caption := ParamStr(0);
  Label2.Caption := FormatDateTime('d-mm-yyyy hh:mm:ss.zzz', now);
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Sleep(1000);
end;

procedure TForm1.wmuMyClose(var Msg: TMessage);
begin
  Application.Terminate;
end;

end.

Автор: -=CHE@TER=- Jul 10 2007, 12:51

QUOTE(jTommy @ Jul 9 2007, 08:09 PM) *
Бардак на корабле! © Попугай из Пиратов Карибского моря. smile.gif
Надо разбираться, так не должно быть.
Угу. Я тоже так думаю. Пока что идей нет.

QUOTE(jTommy @ Jul 9 2007, 08:09 PM) *
Из-за этого маловероятно, но и проверить это легко - поробуй позапускать программу из HKLM.
Попробовал этот вариант - не помогло...

QUOTE(jTommy @ Jul 9 2007, 08:09 PM) *
Да я не отдыхал, лень такая жуткая навалилась, в ресурсокопании ничего не получается + комп новый собирал.
Ну, тоже надо. (*улыбается*)


QUOTE(Xplorer @ Jul 10 2007, 09:44 AM) *
Все сообщения посылаются только окнам (HWND). Достаточно послать WM_CLOSE только главному окну. WM_DESTROY посылает сама Windows при уничтожении окна.
А вот, кстати, интересный тогда впорос возникает - если у меня главная форма отлавливает ON_CLOSE событие и вместо закрытия сворачивается в трей - то что делать?


QUOTE(Xplorer @ Jul 10 2007, 09:44 AM) *
Есть сообщения WM_QUERYENDSESSION и WM_ENDSESSION.
Кстати, может быть тогда их попробовать? И кому их отсылать?


QUOTE(Xplorer @ Jul 10 2007, 09:44 AM) *
-=CHE@TER=-Попробуй такой вариант:
О спасибо тебе большое - работает!

Тогда сразу напрашивается пара вопросов:

1) Mutex нигде не закрывается? Это вообще хорошо? Я у себя в главном .DPR файле в конце, перед "END." делал так:
CODE
  ReleaseMutex(Handle);
  CloseHandle(Handle);

Тут тоже так делать? Или см. 2)

2) Я так понял, что программа убивается, посылкой WM_CLOSE - вопрос, а MUTEX, что-ли, передаётся той копии, которая запустилась?.. Или что вообще происходит я не понял...

3) MUTEX обязательно должен называться '851137EC-3D96-4EA6-817B-30969CCF477B' или можно своё имя сунуть?..

Автор: Xplorer Jul 10 2007, 13:54

Я изменил код.

QUOTE(-=CHE@TER=- @ Jul 10 2007, 04:51 PM) *
А вот, кстати, интересный тогда впорос возникает - если у меня главная форма отлавливает ON_CLOSE событие и вместо закрытия сворачивается в трей - то что делать?
Смотри в коде.

QUOTE(-=CHE@TER=- @ Jul 10 2007, 04:51 PM) *
Кстати, может быть тогда их попробовать? И кому их отсылать?
Эти сообщения посылает Windows при Log off/Turn off.

QUOTE(-=CHE@TER=- @ Jul 10 2007, 04:51 PM) *
1) Mutex нигде не закрывается? Это вообще хорошо? Я у себя в главном .DPR файле в конце, перед "END." делал так:
CODE
  ReleaseMutex(Handle);
  CloseHandle(Handle);

ReleaseMutex вызывать не надо, т.к. мьютекс не был захвачен. При завершении процесса мьютекс автоматически закрывается.

QUOTE(-=CHE@TER=- @ Jul 10 2007, 04:51 PM) *
3) MUTEX обязательно должен называться '851137EC-3D96-4EA6-817B-30969CCF477B' или можно своё имя сунуть?..
Можно и своё.

Автор: -=CHE@TER=- Jul 10 2007, 22:04

QUOTE(Xplorer @ Jul 10 2007, 01:54 PM) *
Я изменил код.

Смотри в коде.
Хе-хе, ты своё сообщение делаешь. В принципе, да, выход. Что-то я туплю...


QUOTE(Xplorer @ Jul 10 2007, 01:54 PM) *
ReleaseMutex вызывать не надо, т.к. мьютекс не был захвачен. При завершении процесса мьютекс автоматически закрывается.
Я просто сторонник того, что сколько памяти выделил, сколько хэндлов занял - чтобы в конце программы всё освободил... Типа, на ОС полагайся, а сам не плошай. (*улыбается*)

Кстати, а зачем в FormClose стоит Sleep(1000)?.. Это просто так или имеет какое-то важное стратегическое значение?

Ещё раз спасибо за помощь!

Автор: -=CHE@TER=- Jul 16 2007, 16:40

Думаю, тут будет полезно приводить некоторые полезные куски кода.

Вот пара из них. Handle - не обязательно какой-то элемент вашей программы - это может быть и хэндл чужой программы.

Эмулирование нажатия какой-нибудь клавиши на клавиатуре:

CODE
SendMessage(Handle, WM_KEYDOWN, VK_SPACE, 0);
SendMessage(Handle, WM_CHAR, VK_SPACE, 0);
SendMessage(Handle, WM_KEYUP, VK_SPACE, 0);

В данном случае посылается код пробела элементу с хэндлом Handle. Обратите внимание, для чтобы нажатая клавиша обработалась корректно, необходимо послать все три сообщения! Т.е. сначала что нажата клавиша пробел, затем, символ клавиши, и потом, что она отжата. Это удобно делать, например, для ListView, если нужно чтобы после удаления элемента автоматом выделился тот, на котором сейчас стоит курсор.

Эмулирование нажатия клавиши мыши реализуется по схожей схеме:
CODE
SendMessage(Handle, WM_LBUTTONDOWN, MK_LBUTTON, MAKELONG(X, Y));
SendMessage(Handle, WM_LBUTTONUP, MK_LBUTTON, MAKELONG(X, Y));

В данном случае, эмулируется нажатие на левую клавишу мышки.
X, Y - координаты точки от левого верхнего угла элемента с хэндлом Handle, где "произошло" нажатие.
Сразу скажу, что я этот код не тестировал. Хотя, по идее, он должен работать.

Автор: Siberian GRemlin Jul 19 2007, 09:14

Извиняюсь, если не в тему (вроде это WinAPI). Хотелось бы вызывать стандартные диалоги поиска и замены текста, а также использовать их, т.е. брать искомую строку из поля ввода в этих диалогах и т.п. Как это правильно реализовать, если возможно?

Автор: -=CHE@TER=- Jul 19 2007, 13:51

QUOTE(Siberian GRemlin @ Jul 19 2007, 09:14 AM) *
Извиняюсь, если не в тему (вроде это WinAP). Хотелось бы вызывать стандартные диалоги поиска и замены текста, а также использовать их, т.е. брать искомую строку из поля ввода в этих диалогах и т.п. Как это правильно реализовать, если возможно?


Делаешь:
Uses CommDlg;

тогда становятся доступны вот эти две функции:

function FindText(var FindReplace: TFindReplace): HWND; stdcall;
function ReplaceText(var FindReplace: TFindReplace): HWND; stdcall;

http://msdn2.microsoft.com/en-us/library/ms646918.aspx
http://msdn2.microsoft.com/en-us/library/ms646946.aspx

Если же размер исполняемого .EXE файла не интересует, то используй модуль Dialogs - там уже готовые классы есть, типа FindDialog, ReplaceDialog.

Автор: -=CHE@TER=- Jul 20 2007, 01:30

А теперь моя очередь задавать вопрос. (*улыбается*)
Кто-нибудь знает, как получить количество переданных/полученых байт подключения?

В общем суть задачи - есть некоторое Интернет-соединение, горящее в трее иконкой с двумя перемигивающимися мониторами. Так вот - там отображается значение полученых/переданных байт. Я перепробовал несколько функций из iphlpapi.dll (GetIfTable(), GetIpStatistics(), GetTcpStatistics()) - всегда либо больше, либо меньше того, что там (в трее) отображается. Кто-нибудь знает, как получить точно такое же число?

Если что, то у меня есть хэндл этого подключение - брал через RasEnumConnectionsA()/RasGetConnectStatusA().

Автор: Xplorer Jul 20 2007, 06:08

Для 2000/XP/2003/Vista - RasGetConnectionStatistics
Для 95/98/Me - ключ HKEY_DYN_DATA

Автор: -=CHE@TER=- Jul 20 2007, 13:48

Xplorer!
RasGetConnectionStatistics - это как раз в точности то, что мне нужно было!
Большое, нет просто ГРОМАДНОЕ спасибо!

Автор: -=CHE@TER=- Jul 23 2007, 19:01

Переименовал тему - теперь тут можете обсуждать что хотите (см. название).

Всё ещё вожусь с Турком. Делать враппер для всех 101-ой функции, которые использует игра, из kernel32.dll очень и очень влом.
Киньте в меня, пожалуйста, ссылкой с доками по hook'ам или с примером, скажем, как захукать MessageBoxA() (а то я сколько в сети не нахожу - они все нерабочие; может только под 9х пашут?..)...
Вообще - есть примеры, где хук ставится нормально, через стандартные API-функции типа SetWindowsHookEx() / UnhookWindowsHookEx() ? Без записи в чужой процесс и изменения точек входа и без создания отдельной .DLL? По-человечески это вообще реально сделать? Потому что писать в чужой процесс не есть гуд из соображений конфликта с антивирями.

Собственно задача: запустить программу, чтобы она работала пока её не закрыли и перехватывала произвольную функцию (все вызовы этой функции), пусть, скажем, это будет MessageBoxA() вызванный любым процессом.

Автор: -=CHE@TER=- Aug 11 2007, 13:54

Кстати, насчёт ReadKey / KeyPressed - вот, неплохая реализация юнита CRT для Windows:
http://www.bsdg.org/SWAG/DELPHI/0305.PAS.html
Там, конечно, не все процедуры / функции, но основная часть. Остальное, думаю, кто захочет сделает сам.

Автор: -=CHE@TER=- Apr 7 2008, 17:03

Вопрос ко всем - возможно ли при помощи класса TFileStream обрубить файл?
Варианты с SetEndOfFile(FS.Handle) и прочее не интересует - т.к. используется модуль Windows, а значит пример не будет мультиплатформенным.

Автор: Xplorer Apr 8 2008, 19:30

CODE
S := TFileStream.Create('x.x', fmOpenReadWrite);
S.Size := 123;
S.Free;

Автор: -=CHE@TER=- Apr 9 2008, 10:09

Xplorer!
Ой, спасибо большое!
Что-то я стормозил - думал это свойство только на чтение...

[OffTopic]

Остальные сообщения скрыл, чтобы не позориться... (*улыбается*)

Автор: -=CHE@TER=- Jun 8 2008, 11:49

Ещё пара глупых вопросов:

1. В Си можно сделать такой финт ушами (кусок кода, структура tlist где-нибудь выше объявлена - не суть важно):

CODE
int main(int argc, char *argv[]){
struct tlist *list;
int i;
  list = (struct tlist *)malloc(10*sizeof(list[0])); /* 10 элементов */
  for(i = 0; i<10; i++){
    list[i].par_int = i; /* допустим заполняем как-то поля структуры */
  }
  free(list);
  return(0);
}

Собственно, вопрос тупой - можно ли сделать что-нибудь подобное на Delphi?
CODE
Var T: ^TStruct;
I: Integer;
Begin
  GetMem(T, 10*SizeOf(TStruct));
  For I:=0 To 9 Do
    T^[I].par_int:=I; { вот тут оно ругается, как только не пиши }
  FreeMem(T, 10*SizeOf(TStruct));
End.

Есть ли какой-нибудь способ и чтобы он был не сильно через одно место?..
Заводить ещё один указатель и делать Inc(T2, SizeOf(TStruct)); просьба не предлагать.

2. Второй вопрос такой же. Видел в одной программе примерно такой код:
CODE
Var A,B,C,D: Byte;
...
If (A = Byte('R')) And (B = Byte('I')) And (C = Byte('F')) And (D = Byte('F')) Then

В связи с этим вопрос - можно ли сделать как-нибудь так, чтобы по-короче было:
CODE
Var dwSign: Cardinal;
...
If dwSign = Cardinal('RIFF') Then { к сожалению, здесь оно ругается}

А то писать $46464952, вместо 'RIFF' не очень наглядно, да и лазить в HEX режим каждый раз, чтобы посмотреть как там сигнатура архива выглядит - лень.

Автор: Grom PE Jun 8 2008, 12:28

-=CHE@TER=-, вопросы нормальные =)

1. Если я правильно понял, то так:

CODE
Type
  PStructArray=^TStructArray;
  TStructArray=array [0..0] of TStruct;
Var
  T: PStructArray;
  I: Integer;
Begin
  GetMem(T, 10*SizeOf(TStruct));
  For I:=0 To 9 Do
    T[I].par_int:=I;
  FreeMem(T, 10*SizeOf(TStruct));
End.


2. Как-то так:
CODE
type T4Char=array [0..3] of char;
var dwSign: cardinal;
begin
  dwSign:=$46464952;
  if T4Char(dwSign) = 'RIFF' then
    WriteLn('yes') else WriteLn('no');
end.

Более оптимизированный код получим при:
CODE
type T4Char=array [0..3] of char;
const RIFF_SIG:T4Char='RIFF';
var dwSign: cardinal;
begin
  dwSign:=$46464952;
  if dwSign = Cardinal(RIFF_SIG) then
    WriteLn('yes') else WriteLn('no');
end.

Автор: -=CHE@TER=- Jun 8 2008, 17:59

Grom PE!
Спасибо большое, только создавать ещё одну структуру - тоже не очень прикольно. Но, хотя бы, от Array of <тип> избавиться можно будет.
Кстати, заменил Write2 на WriteLn в твоём посте, чтобы не вводить в заблуждение тех кто не в курсе. (*улыбается*)

Автор: -=CHE@TER=- Oct 12 2008, 19:56

Небольшая программка для перезапуска зависших приложений.
Замечание:
1) Функции GetModuleFileNameEx() и IsHungAppWindow() есть только в Windows 2000/XP и более новых.
2) GetModuleFileNameEx() - позволяет махом получить имя исполняемого файла процесса - нет нужды заниматься геммороем с CreateToolHelp32Snapshot(), Thread32First/Next(), Module32First/Next() и прочим.
3) GetModuleFileNameEx() есть в юните psapi начиная с Delphi6, но если использовать её через юнит (Uses psapi;), то в код программы добавляется куча всякого шлака (проверка на существование этой функции LoadLibrary() и т.д.), что, я считаю, лишним.
4) GetModuleFileName() есть начиная с Windows 95, но почему-то она возвращает только имя запускаемого приложения, а не того процесса, handle которого ей был передан (глючит?), поэтому и приходится использовать GetModuleFileNameEx().

CODE
Program WHungApp;
Uses Windows;

Function IsHungAppWindow(Wnd: hWnd): BOOL; stdcall; external 'user32.dll' name 'IsHungAppWindow';
Function GetModuleFileNameEx(hProcess: THandle; hModule: HMODULE; lpFilename: PAnsiChar; nSize: DWORD): DWORD; stdcall; external 'psapi.dll' name 'GetModuleFileNameExA';

Function EnumWindowsProc(Wnd: hWnd; lparam: LPARAM): BOOL; stdcall;
Var
    pid: DWORD;
     ph: THandle;
      S: String;
  PInfo: TProcessInformation;
  SInfo: TStartupInfo;
Begin
  If IsHungAppWindow(Wnd) Then
    Begin
      GetWindowThreadProcessId(Wnd, @pid);
      ph:=OpenProcess(PROCESS_ALL_ACCESS, False, pid);
      SetLength(S, MAX_PATH);
      SetLength(S, GetModuleFileNameEx(ph, 0, @S[1], MAX_PATH));
      // terminate process
      TerminateProcess(ph, 0);
      CloseHandle(ph);
      // restart process
      CreateProcess(Nil, PChar(S), Nil, Nil, False, 0, Nil, Nil, SInfo, PInfo);
    End;
  Result:=True;
End;

Begin
  EnumWindows(@EnumWindowsProc, 0);
End.

Автор: -=CHE@TER=- Oct 13 2008, 08:16

Вопрос: кто-нибудь знает как получить ключи командной строки, с которыми был запущен процесс?

Добавлено:
http://forum.sources.ru/index.php?showtopic=136611.

Автор: -=CHE@TER=- Oct 13 2008, 10:55

Вот код тру перезапускалки:

CODE

Program WHungApp;

Uses Windows;

Function IsHungAppWindow(Wnd: hWnd): BOOL; stdcall; external 'user32.dll' name 'IsHungAppWindow';
Function GetModuleFileNameEx(hProcess: THandle; hModule: HMODULE; lpFilename: PAnsiChar; nSize: DWORD): DWORD; stdcall; external 'psapi.dll' name 'GetModuleFileNameExA';
Function NtQueryInformationProcess(ProcessHandle: THandle; ProcessInformationClass: Byte; ProcessInformation: Pointer; ProcessInformationLength: ULONG; ReturnLength: PULONG): DWORD; stdcall; external 'ntdll.dll' name 'NtQueryInformationProcess';

// Как по PID процесса узнать CMDLINE?, то есть, командную строку?
// http://forum.sources.ru/index.php?showtopic=136611
// http://undocumented.ntinternals.net/

// http://forum.sources.ru/index.php?showtopic=243663
Type

  UNICODE_STRING = Packed Record
    Length       : Word;
    MaximumLength: Word;
    Buffer       : PWideString;
  end;

  PROCESS_BASIC_INFORMATION = Packed Record
    ExitStatus    : DWORD;
    PebBaseAddress: DWORD; // PPEB
    AffinityMask  : DWORD;
    BasePriority  : DWORD;
    uUniqueProcessId: ULong;
    uInheritedFromUniqueProcessId: ULong;
  End;

Function GetProcessCmdLine(hProcess: THandle): String;
Var
       PBI: PROCESS_BASIC_INFORMATION;
    pp, cb: DWORD;
        ws: WideString;
        uz: UNICODE_STRING;
Begin
  Result:='';
  If (NtQueryInformationProcess(hProcess, 0{ProcessBasicInformation},
                               @PBI, SizeOf(PROCESS_BASIC_INFORMATION),
                               @cb) = 0) Then
    Begin
      If ReadProcessMemory(hProcess, Ptr(PBI.PebBaseAddress+16), @pp, 4, cb) Then
        If ReadProcessMemory(hProcess, Ptr(pp+64), @uz, SizeOf(UNICODE_STRING), cb) Then
          Begin
            SetLength(ws, uz.Length Div 2);
            If ReadProcessMemory(hProcess, uz.Buffer, PWideChar(ws), uz.Length, cb) Then
              Result:=AnsiString(ws);
          End;
    End;
End;

Procedure WriteLog(S: String);
Var
    Fl: THandle;
    dw: DWORD;
Begin
  Fl:=CreateFile(PChar(ParamStr(0)+'.log'), GENERIC_WRITE Or GENERIC_READ, 0, Nil, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
  If Fl <> INVALID_HANDLE_VALUE Then
    Begin
      SetFilePointer(Fl, 0, Nil, FILE_END);
      WriteFile(Fl, S[1], Length(S), dw, Nil);
      CloseHandle(Fl);
    End;
End;

Function GetDateTime: String;
Begin
  SetLength(result, 19);
  GetDateFormat(LOCALE_SYSTEM_DEFAULT, 0, Nil, 'yyyy.MM.dd', @result[1], 10);
  GetTimeFormat(LOCALE_SYSTEM_DEFAULT, 0, Nil, 'HH:mm:ss', @result[12], 8);
  result[11]:=#32;
End;

Function EnumWindowsProc(Wnd: hWnd; lparam: LPARAM): BOOL; stdcall;
Var
      pid: DWORD;
       ph: THandle;
     S, C: String;
    PInfo: TProcessInformation;
    SInfo: TStartupInfo;
Begin
  If IsHungAppWindow(Wnd) Then
    Begin
      GetWindowThreadProcessId(Wnd, @pid);
      ph:=OpenProcess(PROCESS_ALL_ACCESS, False, pid);
      If ph<>0 Then
        Begin
          SetLength(S, MAX_PATH);
          SetLength(S, GetModuleFileNameEx(ph, 0, @S[1], MAX_PATH));
          C:=GetProcessCmdLine(ph);
          If Pos('"', C)<>0 Then
            Begin
              C[1]:='_';
              Delete(C, 1, Pos('"', C)+1);
            End
          Else
            Begin
              If Pos('  ', C)<>0 Then
                Delete(C, 1, Pos('  ', C)+1);
            End;
          S:='"'+S+'" '+C;
          WriteLog(GetDateTime+#32+S+#13#10);
          // terminate process
          TerminateProcess(ph, 0);
          CloseHandle(ph);
          // restart process
          FillChar(SInfo, SizeOf(SInfo), 0);
          SInfo.cb:=SizeOf(SInfo);
          CreateProcess(Nil, PChar(S), Nil, Nil, False, 0, Nil, Nil, SInfo, PInfo);
          CloseHandle(PInfo.hThread);
          CloseHandle(PInfo.hProcess);
        End;
    End;
  Result:=True;
End;

Begin
  EnumWindows(@EnumWindowsProc, 0);
End.

Автор: -=CHE@TER=- Oct 18 2008, 11:15

PHP 5.2.1 + IIS 6

PHP has encountered an Access Violation at 0117A242 (может быть другой адрес)
Затем вообще падает IIS и никакой страницы не возвращает ("внутренняя ошибка сервера").
Перезапуск службы IIS помогает, но ненадолго.

Причина: кривая реализация всех независящих от регистра (case-insensitive) функций:
str_ireplace()
strcasecmp()
etc.

Если где-то есть вызов хотя бы одной такой функции - ошибок не будет, но через некоторое время IIS упадёт.

В какой версии PHP это исправили и исправили ли вообще - не знаю.
Возможно, что это косяк только Windows-версии PHP.

Автор: -=CHE@TER=- Oct 31 2008, 15:54

Очередная херня - столько времени убил, пока нагуглил как решается.

Что устанавливалось: Apache 2.2 + PHP 5.2.6 + MySQL 4.1

Во-первых, чтобы встал PHP на Apache 2.2 надо в httpd.conf писать:

QUOTE
PHPIniDir "C:/PHP/"
LoadModule php5_module "C:/PHP/php5apache2_2.dll"

Ну и всякие там ниже по тексту (в IfModule mime_module):
QUOTE
AddType application/x-httpd-php .php


Во, вторых, всё встало, кроме MySQL - через CLI, QueryBrowser работает, через PHP, зараза, не хочет. На странице пишет:
QUOTE
Fatal error: Call to undefined function mysql_connect() in test.php on line ###

При этом в лог Apache (/logs/error.log) бодро пихается:
QUOTE
PHP Warning: PHP Startup: Unable to load dynamic library 'C:\\PHP\\ext\\php_mysql.dll' - Не найдена указанная процедура.\r\n in Unknown on line 0

Русская строчка декодирована из: \xcd\xe5\xed\xe0\xe9\xe4\xe5\xed\xe0 \xf3\xea\xe0\xe7\xe0\xed\xed\xe0\xff xef\xf0\xee\xf6\xe5\xe4\xf3\xf0\xe0.

Оказывается, надо скопировать файл libmysql.dll из C:\PHP\ в C:\Program Files\Apache Software Foundation\Apache2.2\bin\ и перезапустить Apache.

Автор: -=CHE@TER=- Nov 15 2008, 07:32

Есть на форме UpDown1, ассоциированный (Associate) с ним Edit1 и кнопка.

На кнопке такой код:

CODE
Procedure TForm1.Button1Click(Sender: TObject);
Var TI: TINIFile;
Begin
  If OpenDialog1.Execute Then
    Begin
      TI:=TINIFile.Create(OpenDialog1.FileName);
      UpDown1.Position:=TI.ReadInteger('Section', 'Value', 0);
      TI.Free;
    End;
End;


Пашет только если нажать два (!) раза подряд (с открытием файла, конечно). А если написать так:

CODE
      TI:=TINIFile.Create(OpenDialog1.FileName);
      Edit1.Text:=IntToStr(TI.ReadInteger('Section', 'Value', 0));
      TI.Free;


Или даже так:

CODE
      UpDown1.Position:=123;


То Edit1 обновляется вместе с UpDown1 с первого раза. В чём засада кто-нибудь знает? Refresh / Update у Edit1 и UpDown1 (а также вызов OnChanging) не спасли гиганта мысли. Такой код:

CODE
X:=TI.ReadInteger('Section', 'Value', 0);
UpDown1.Position:=X;


На удивление тоже не работает (вернее работает, но со второго раза).

Автор: Grom PE Nov 15 2008, 12:58

-=CHE@TER=-,
у меня с чистым созданным проектом всё работает с первого раза.
Смотри, нет ли у тебя какого-нибудь старого кода, который мешает.
Еще попробуй перезапустить Delphi.

Автор: -=CHE@TER=- Nov 15 2008, 13:08

QUOTE(Grom PE @ Nov 15 2008, 12:58) *
Еще попробуй перезапустить Delphi.
Как оно всё оказывается просто - перезапустил и прошло. Даже обидно. (*улыбается*)

Автор: -=CHE@TER=- Dec 18 2008, 18:26

Вопрос сложный, но может кто-то знает в чём прикол...
В общем DirectDraw и RTSP-поток.
IBasicVideo.GetCurrentImage - размер изображения 720x576 (и сохраняется оно таким растянутым).
IBasicVideo.GetVideoSize - 720x480.
А на самом деле оно 720x540 - когда в окне просматриваешь.
Кто-нибудь знает отчего такой расколбас и можно ли его запилить?

Для декодирования потока используются кодеки MainConcept GmbH Network Source и SDP Parser Engine версии 7.5.0.35746.

Автор: -=CHE@TER=- Jan 2 2009, 19:53

Делал тут как-то плеер. И решил сделать для него возможности:
а) Таскать щёлкнув на любом месте окна формы.
б) Переходить в полноэкранный режим и обратно двойным щелчком на форме.
Всё сделал и тут выяснилась дикая вещь: при возврате из полноэкранного режима форма "прилипала" к курсору мышки, как будто я нажал и держу левую клавишу - т.е. активировался алгоритм перетаскивания окна. Бился с этим багом целый день. Выяснил интересные вещи:

Если поставить на форме обработку событий:

CODE
OnDblClick
OnMouseDown
OnMouseUp

И писать при их возникновении в Memo соответствующие текст, то получим такую последовательность при двойном щелчке на форме:
CODE
MouseDown
MouseUp
DoubleClick
MouseDown
MouseUp

А теперь, внимание! Если в обработчике события OnDblClick делается что-то тяжёлое (какие-нибудь вычисления, работа с COM и т.д.) - то события приходят уже в таком порядке:
CODE
MouseDown
MouseUp
MouseUp
DoubleClick
MouseDown

Т.е. MouseUp приходит раньше MouseDown! Абзац!!! Причём это не глюк Delphi - потому что если вручную перехватывать соответствующие WM_ сообщения - всё будет точно также!
Однако, немного покурив доки по сообщениям решение нашлось:
CODE

type
  TForm1 = class(TForm)
    ...
  private
    procedure WMLButtonDblClk(var Msg: TMessage); message WM_LBUTTONDBLCLK;
    { Private declarations }
  public
    { Public declarations }
  end;

...

Procedure TForm1.WMLButtonDblClk(var Msg: TMessage);
Begin
  // тут делаем что-нибудь
  Msg.Result:=-1; // <-- не пускаем это сообщение дальше!!!
End;

В результате при Msg.Result:=-1 вторую пару сообщений MouseDown и MouseUp система уже не шлёт:
CODE
MouseDown
MouseUp
DoubleClick

Автор: -=CHE@TER=- Jun 25 2009, 04:10

Кто-нибудь знает, как можно проверить сокет на дохлость?
Суть проблемы: создаю подключение к серверу, а он может его закрыть - тогда на команде получения данных Recv() у меня программа навечно впадает в кому.

В Интернете вычитал, что можно попробовать получить 0 байт и проверить ошибку:

CODE
Recv(SK, result[1], 0, 0);
If WSAGetLastError = 0 Then Sz:=Recv(SK, result[1], Sz, 0) Else Sz:=SOCKET_ERROR;


Однако! Первая строчка с получением 0 байт у меня работает нормально (XP SP3), а на соседней машине (2003 SP2) появляется BSoD с DRIVER_IRQL_NOT_LESS_OR_EQUAL. При этом сама машина с 2003 работает нормально и BSoD там никогда не было.

Кто-нибудь может объяснить это вообще как понимать?

Я, конечно, попробую ещё переписать с Select(), но такое поведение меня очень сильно озадачивает...

Автор: Siberian GRemlin Jun 25 2009, 06:22

Обычно http://s60.radikal.ru/i167/0904/f5/3097282e2208.jpg вызвавший эту ошибку. Если он указан -- попробуй его обновить. В сети достаточно много информации по этой теме. Ты пробовал искать?

Автор: -=CHE@TER=- Jun 25 2009, 11:56

QUOTE(Siberian GRemlin @ Jun 25 2009, 06:22) *
Обычно http://s60.radikal.ru/i167/0904/f5/3097282e2208.jpg вызвавший эту ошибку. Если он указан -- попробуй его обновить. В сети достаточно много информации по этой теме. Ты пробовал искать?
Искал уже - тоже думаю, что драйвер на сетевуху кривой. Проблема в том, что тачка не моя, так что был ли там указан драйвер вызвавший ошибку - не знаю.
В общем, будем обновлять драйвер.

Автор: Siberian GRemlin Nov 2 2009, 10:11

http://www.docwin.ru/article855.html нашёл. Даже если ошибка на чужом компе, то можно выяснить причину. Только в сценарии и в имени папки ничего менять не надо -- сценарий обновлён, а статья нет.

Автор: -=CHE@TER=- Dec 5 2009, 16:36

QUOTE(-=CHE@TER=- @ Jun 25 2009, 04:10) *
Однако! Первая строчка с получением 0 байт у меня работает нормально (XP SP3), а на соседней машине (2003 SP2) появляется BSoD с DRIVER_IRQL_NOT_LESS_OR_EQUAL. При этом сама машина с 2003 работает нормально и BSoD там никогда не было.
Как оказалось BSoD был совсем не там...
Короче, перед CloseSocket(), особенно для UDP-соединений нужно делать http://msdn.microsoft.com/en-us/library/ms740481(VS.85).aspx, желательно с SD_BOTH, иначе на некоторых сетевых картах, драйвера к которым писали криворукие имбецилы, очень быстро забивается буфер и случается BSoD (т.к. приложение уже не забирает пакеты из буфера и вообще закрыло сокет, а тупой драйвер всё ещё продолжает их туда пихать).
На сетевых картах, драйвера к которым писали вменяемые люди, при таком раскладе буфер самоочищается и сокет самозакрывается - т.е. сделана жёсткая проверка "на дурака".

Автор: -=CHE@TER=- Jan 15 2010, 19:26

Товарищи, объясните мне кто-нибудь такой феномен:

CODE
Program bitstest;
{$APPTYPE CONSOLE}
Var X: Cardinal;
Begin
  X:=123;
  X:=X ShR 32; // Должно быть 0...
  WriteLn(X); // Но остаётся 123 как и было!..
End.


X ShR 32 (X >> 32) должно быть 0, т.к. X переменная 32-х разрядная. Но дело в том, что эта операция не работает!
Если сделать X >> 31 - то всё ок, но 32 просто ничего не делает - X не изменяется!
Сейчас даже на FASM программу написал - такой же результат.
Это что за багофича такая?!..

Кстати, если на Си такое сделать, то результат не изменится, но будет предупреждение:
warning: right shift count >= width of type

Просто есть формула:
X:=X ShR (32 - BITS);
Которая при BITS = 0, должна давать 0, но из-за этого косяка получается фигня...

Кто-нибудь может объяснить почему так? Я ещё понимаю, когда сдвиг больше размера типа, но когда равно - должен же 0 быть?..

Добавлено:
Ну, трындец просто (http://gcc.gnu.org/ml/gcc/2004-11/msg01133.html):
QUOTE
most popular CPUs have only 5 bit shift counts
Офигеть!!!
И ещё (http://gcc.gnu.org/ml/gcc/2004-11/msg01141.html):
QUOTE
shift count from 0 up to the number of bits in the register-1
Слов нет - одни эмоции.

Автор: Grom PE Jan 16 2010, 07:46

-=CHE@TER=-

Ну дык, процессор так работает, x shr y на самом деле x shr (y mod 32).
Надо обнулить переменную, так и пиши =)

Точнее, x shr (y and 11111b), что и составляет 5 бит маски.
http://www.intel.com/software/products/documentation/vlin/mergedprojects/analyzer_ec/mergedprojects/reference_olh/mergedProjects/instructions/instruct32_hh/vc283.htm

Автор: -=CHE@TER=- Jan 23 2010, 18:35

Думаю, что это лучше здесь разместить, чем в ссылках:
http://www.strchr.com/ - занятный сайт про C, WinAPI, ASM.
Примеры кода, оптимизации и времени которое будет затрачено на каждый вариант того или иного алгоритма. Есть исходные коды программ.

Автор: Siberian GRemlin Feb 15 2010, 15:45

Может не в тему, но есть ли готовый модуль\библиотека для работы с БД в SQLite?

Автор: Axsis Feb 15 2010, 16:10

пара ссылок с первой страницы http://www.google.ru/search?client=opera&rls=en&q=SQLite+delphi+unit&sourceid=opera&ie=utf-8&oe=utf-8:
http://www.delphi.int.ru/articles/43/
http://sqlitebrowser.sourceforge.net/
http://sqlite4delphi.sourceforge.net/

Автор: -=CHE@TER=- Apr 22 2010, 10:05

Чтобы долго не писать чего да как - приведу лог чата

QUOTE

******: можно тогда тебя отвлеч глупым вопросом?
-=CHE@TER=-: Давай.
******: есть девайс. типа ноут.
******: есть оборудование - типа жпс приёмник.
******: оно типа встроено и его не видно
******: покуда не будет запущена утилита и не поставлена галочка
******: вопрос
-=CHE@TER=-: Даже Everest не видит? А в BIOS девайс этот включён?
-=CHE@TER=-: А, понял.
******: можно ли как то отследить что делает эта утилита
******: тоесть покуда она не занесёт какой то битик в памяти - девайс не виден даже в эвересте
-=CHE@TER=-: Теоретически возможно всё.
******: а помню на двк была прога... типа оболочки
-=CHE@TER=-: А после закрытия утилиты и выгрузки её из памяти - девайс продолжает работать?
******: и можно было пошагово отслеживать выполнение программы
******: да. продолжает
******: если утилиту запустить повторно - то девайс исчезает :)
-=CHE@TER=-: Возможно она свой драйвер ставит, который после перезагрузки отваливается (так и задумано).
******: неее
******: драйверами не пахнет 100%
-=CHE@TER=-: Дай мне эту тулзу.
-=CHE@TER=-: И скажи чего ты там и где ставишь.
******: ща
******: я кинул
******: жпс активатор написал не знаю кто
******: изначально и этого даж не было
******: DEVICE_SWITCH.EXE поидеи запускается жпс активатором, автоматом передвигает курсор и жмёт применить
******: Enable gsc3f GPS
-=CHE@TER=-: Прикол.
-=CHE@TER=-: Оно у меня не запускается.
******: угу, вначале проверяет есть ли оборудование?
-=CHE@TER=-: Видимо, да.
-=CHE@TER=-: Т.е. оно запускается и тут же выходит.
-=CHE@TER=-: Даже ничего не сообщает.
******: поидеи папка тулс должна находиться в корне диска ц
-=CHE@TER=-: А какая разница?
******: покрайней мере когда она там... мне сообщает на обычном компе что девайс не может быть выключен
-=CHE@TER=-: У-у-у... Оно тут через DeviceIoControl работает...
******: переведи на земной язык :)
-=CHE@TER=-: Это проще будет документацию по устройству найти и написать свою программу, чем разбираться в исходном коде.
-=CHE@TER=-: Тут сильно замороченный алгоритм её включения / выключения.
-=CHE@TER=-: Нужна спецификация: в какой порт чего пихать, что должно прийти, что означает и т.д.
-=CHE@TER=-: http://mikhail.krivyy.com/2004/07/28/asus-l3c-led/
-=CHE@TER=-: Вот например - человек включает и выключает лампочку светодиода.
-=CHE@TER=-: Нужно тоже самое, но для твоего GPS.
-=CHE@TER=-: Ща тогда тебе ломанный файл дам - просто сграблю значения.
-=CHE@TER=-: Лови.
-=CHE@TER=-: Скопируй к себе на ноут и запусти, затем дай содержимое файла KERNEL.LOG, который появится.
-=CHE@TER=-: После запуска нажми, чтобы оно разрешило твоё устройство.
******: угу
******: готово
DeviceIoControl(232,2237444,INBUFF:1 1381453892 0 0 0 ,20,OUTBUFF768,)
DeviceIoControl(232,2237444,INBUFF:2 1447380049 1 8 1237008 ,20,OUTBUFF768,)
CBFARR(0 4 2)
DeviceIoControl(232,2237444,INBUFF:2 1313817683 1 8 1240192 ,20,OUTBUFF768,)
CBFARR(0 4 2)
DeviceIoControl(232,2237444,INBUFF:1 1381453892 0 0 0 ,20,OUTBUFF768,)
DeviceIoControl(232,2237444,INBUFF:2 1447380049 1 8 1237008 ,20,OUTBUFF768,)
CBFARR(0 4 2)
DeviceIoControl(232,2237444,INBUFF:2 1179599955 1 8 1240192 ,20,OUTBUFF768,)
CBFARR(0 4 2)
-=CHE@TER=-: Я сделал прогу.
******: что где тестить*?
-=CHE@TER=-: Лови gsc3fgps.exe
-=CHE@TER=-: Если просто запустишь - то включит, если с каким-либо параметром (любым) - то выключит.
******: ура! работает :)
-=CHE@TER=-: Сунь тогда прогу в автозагрузку - будет сразу при включении компьютера подключать твой GPS.


Код программы:
CODE
program gsc3fgps;
{$IFNDEF DCC32HACK}uses Windows;{$ENDIF}
// http://mikhail.krivyy.com/2004/07/28/asus-l3c-led/
// http://mikhail.krivyy.com/wp/wp-content/uploads/2006/10/asusled.cpp
// http://www.scintilla.utwente.nl/~timl/devicepower/v1.1/devicepower.c

const
  ATKACPI_IOCTL_CODE = $222404;
  // ioctl command codes
  DPWR = $52575044; // Device Power list
  QDEV = $56454451; // Query Device
  SDON = $4E4F4453; // Set Device On
  SDOF = $464F4453; // Set Device Off

type
  ioctl_arg = packed record
        dummy: word;
    arg_bytes: word;
        value: longword;
  end;
  pioctl_arg = ^ioctl_arg;

  ioctl_cmd = packed record
        ctype: longword;
     cmd_code: longword;
      has_arg: longword;
    arg_bytes: longword;
          arg: pioctl_arg;
  end;

function CtrlACPI(ATKACPIhandle: THandle; code, hasArg, arg: longword): longbool;
var
  iarg: ioctl_arg;
  icmd: ioctl_cmd;
  bout: longword;
  buff: array[0..191] of longword;
begin
  bout := 0;

  iarg.dummy := 0;
  iarg.arg_bytes := 4;
  iarg.value := 2;

  icmd.ctype := hasArg;
  icmd.cmd_code := code;
  icmd.has_arg := arg;
  icmd.arg_bytes := 8 * arg;
  icmd.arg := pioctl_arg(longword(@iarg) * arg);

  result := DeviceIoControl(ATKACPIhandle, ATKACPI_IOCTL_CODE, @icmd, sizeof(icmd), @buff[0], sizeof(buff), bout, nil);
end;

// enable gsc3f GPS
var ATKACPIhandle: THandle;
begin
  ATKACPIhandle := CreateFile('\\.\ATKACPI', GENERIC_READ or GENERIC_WRITE, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);
  if ATKACPIhandle <> INVALID_HANDLE_VALUE then
  begin
    CtrlACPI(ATKACPIhandle, DPWR, 1, 0);
    CtrlACPI(ATKACPIhandle, QDEV, 2, 1);
    if ParamCount <> 0 then
      CtrlACPI(ATKACPIhandle, SDOF, 2, 1)  // OFF
    else
      CtrlACPI(ATKACPIhandle, SDON, 2, 1); // ON
    CloseHandle(ATKACPIhandle);
  end;
end.

Автор: -=CHE@TER=- Jun 1 2010, 17:46

Иногда бывает надо при распаковке игровых архивов попутно создавать необходимые каталоги.
Например для файла: DATA\TEXTURES\ENVIRONMENT\SKY_01.TGA их нужно создать аж 3 штуки.

Вот небольшие куски кода на Delphi и C для таких вещей.
Обрабатываются обе косые черты.
В обоих случаях идентификатор S не должен являться константой.

CODE
For I:=1 To Length(S) Do
  If ((S[I] = '\') Or (S[I] = '/')) Then
  Begin
    CreateDirectory(PChar(Copy(S, 1, I - 1)), Nil);
    { Delphi понимает только слэш }
    S[I]:='\';
  End;


CODE
char *s;
...
s = filenamepath;
while(*s){
  if((*s == '\\') || (*s == '/')){
    *s = 0;
    mkdir(filenamepath);
    /* а C понимает только такой */
    *s = '/';
  }
  s++;
}

Автор: -=CHE@TER=- Jul 21 2010, 16:00

QUOTE(-=CHE@TER=- @ Jul 20 2007, 13:48) *
Xplorer!
RasGetConnectionStatistics - это как раз в точности то, что мне нужно было!
Большое, нет просто ГРОМАДНОЕ спасибо!

Опять всплыл вопрос, только на этот раз хочу получить количество принятых / переданных байт для сетевой карты. RasGetConnectionStatistics() тут уже не помогает.

Сделал программу через http://msdn.microsoft.com/en-us/library/aa365943%28v=VS.85%29.aspx, но, почему-то, если не сильно активен, там обычно на полмегабайта меньше трафика, чем отображается по команде "netstat -e". Кто-нибудь знает, в чём прикол?

Добавлено:
А! Всё, понял - "netstat -e" показывает суммарный трафик на адаптере - т.е. сколько пришло и ушло со всех интерфейсов. А т.к. интерфейса два (+loopback), то нужно было тупо трафик суммировать.
Хотя это даже удобно - если не учитывать loopback, то можно получить "чистый трафик", который уходит во внешку.

Автор: -=CHE@TER=- Jul 27 2010, 17:38

Блин, ставил по работе на удалённый сайт Joomla - застрелиться. Сама CMS в архиве .tar.gz, но хостер (сюрприз!) не даёт доступ по SSH, а также запускать через PHP exec() / system() чтобы выполнить команду распаковки. В Интернете погуглил - ничего кроме использования двух упомянутых выше команд посоветовать не могут.
Короче, распаковал я Жумлу и стал копировать по FTP на сайт... 2 часа с гаком - un-freaking-believable!
Скорость заливки-то большая, да вот при создании нового файла удалённый сайт почему-то тупил по несколько секунд, а в Жумле этих файлов, как снега зимой.
В общем, в конце копирования я уже озверел и решил написать свою программу, которая бы соединяла файлы в один (один файл быстро заливается) и чтобы этот файл можно было потом на сервере распаковать вызвав через HTTP скрипт на PHP для распаковки.

Формат файла filedump:
DWORD - размер файла (size)
DWORD - размер строки имени файла (nlen)
CHARS - имя и путь файла (nlen байт)
BYTES - содержимое файл (size байт)
Повторить до конца файла.

Маленькое замечание: т.к. надо было ещё и пустые каталоги на всякий случий паковать и создавать на сервере, то с именем файла такое дело - если оно заканчивается на '/' - то это каталог, нужно его создать. Файлы при паковке сортируются, так что ситуации, когда надо распаковать файл, каталога для которого ещё нет, по идее, не должны встречаться.

Опционально в эти скрипты можно добавить сжатие gz (если хостер поддерживает) и дату со временем.

pack.php - пакует в архив filedump всё в текущем каталоге и его подкаталогах.
unpk.php - распаковывает filedump в текущий каталог.

Т.к. скрипты дописал практически одновременно с окончанием копирования - проверить времени не было, но, по идее, должны работать. Кстати, там с правами под иксы может быть косяк - все файлы будут пользователю Apache принадлежать - это можно поковырять и chmod() при распаковке добавить для файлов.

Код программ


pack.php
CODE

<?php
  set_time_limit(0);

Function GetFileList($dirpath){
  $dirs = array();
  if(is_dir($dirpath)){
    if($dh = opendir($dirpath)){
      while($file = @readdir($dh)){
        if(($file != '.') && ($file != '..')){
          if((filetype($dirpath.$file) == 'file')){
            $dirs[] = $dirpath.$file;
          }else{
            $dirs[] = $dirpath.$file.'/';
            $dirs = array_merge($dirs, GetFileList($dirpath.$file.'/'));
          }
        }
      }
      closedir($dh);
      natcasesort($dirs);
    }
  }
  return($dirs);
}

//  optional: add filedatetime and compression
//  $tm = filemtime($file); // getfiletime
//  touch($file, $tm);      // setfiletime
//  $file = gzcompress($file, 9);
//  $file = gzdecompress($file);

  @unlink('./filedump');
  $list = GetFileList('./');
  for($i = 0; $i < count($list); $i++){
//    echo $list[$i]."\r\n";
    $fl = fopen('./filedump', 'ab');
    if($fl){
      $name = substr($list[$i], 2);
      $nlen = strlen($name);
      $size = 0;
      $file = '';
      if($name[$nlen-1] != '/'){
        $size = filesize('./'.$name);
        $file = file_get_contents('./'.$name);
      }
      $size = pack('V', $size);
      $nlen = pack('V', $nlen);
      fwrite($fl, $size, 4);
      fwrite($fl, $nlen, 4);
      $size = unpack('V', $size);
      $nlen = unpack('V', $nlen);
      fwrite($fl, $name, $nlen[1]);
      fwrite($fl, $file, $size[1]);
    }
    fclose($fl);
  }

?>




unpk.php
CODE

<?php
  set_time_limit(0);
  $fend = filesize('./filedump');
  $fl = fopen('./filedump', 'rb');
  while(ftell($fl) < $fend){
    $size = 0;
    $nlen = 0;
    $name = '';
    $file = '';

    $size = fread($fl, 4);
    $nlen = fread($fl, 4);
    $size = unpack('V', $size);
    $nlen = unpack('V', $nlen);
    $size = $size[1];
    $nlen = $nlen[1];
    $name = fread($fl, $nlen);
    if($size != 0){
      $file = fread($fl, $size);
    }
    if($name[$nlen-1] == '/'){
      @mkdir('./'.$name, 0777);
    }else{
      @file_put_contents('./'.$name, $file);
    }
  }
  fclose($fl);
?>


Автор: -=CHE@TER=- Aug 10 2010, 14:27

Вчерась ковырял одну игрушку, где данные были упакованы deflate. Как и всегда использовал puff.c + puff.h из zlib. Внезапно, при распаковке, получаю ошибку записи в память по адресу 0, программа падает. Почесав затылок извлекаю сжатый файл отдельно и делаю тестовую программку, где ничего кроме распаковки нет - не помогло. Тогда беру предыдущую (благо сохранилась - я, кстати, понял что после выхода новой версии программы, старые лучше не удалять - можно потом будет об этом сильно пожалеть) и компилирую с ней - всё работает! Написал письмо в zlib - мне товарищ Mark Adler выслал сегодня патч - применил его на последнюю версию и всё снова заработало.
Т.к. этот баг появляется редко (я уже пару программ с последней версией puff сделал и упало только здесь и то на 1 файле), то, скорее всего, обновлять файлы на сайте zlib.net будут не скоро. К тому же это не основной пакет, а дополнительный. Так что привожу инструкцию по самостоятельному патчу файла "puff.c" version 2.1, 4 Apr 2010:

1) Скачиваем http://gnuwin32.sourceforge.net/packages/patch.htm, распаковываем и ищем там файл "patch.exe".
2) Кидаем к нему в каталог файл "puff.c", который мы хотим пропатчить.
3) Создаём в том же каталоге текстовый файл "puff.diff" такого содержания:

CODE

--- ../../../zlib-1.2.5/contrib/puff/puff.c     2010-04-04 11:23:58.000000000 -0700
+++ puff.c      2010-04-25 02:04:16.000000000 -0700
@@ -525,6 +544,12 @@
         int symbol;
         short lengths[FIXLCODES];

+        /* construct lencode and distcode */
+        lencode.count = lencnt;
+        lencode.symbol = lensym;
+        distcode.count = distcnt;
+        distcode.symbol = distsym;
+
         /* literal/length table */
         for (symbol = 0; symbol < 144; symbol++)
             lengths[symbol] = 8;
@@ -541,12 +566,6 @@
             lengths[symbol] = 5;
         construct(&distcode, lengths, MAXDCODES);

-        /* construct lencode and distcode */
-        lencode.count = lencnt;
-        lencode.symbol = lensym;
-        distcode.count = distcnt;
-        distcode.symbol = distsym;
-
         /* do this just once */
         virgin = 0;
     }

4) Запускаем:
patch.exe -o puff2.c puff.c puff.diff
5) Получаем исправленный файл в "puff2.c".

Автор: Siberian GRemlin Jan 7 2011, 15:20

Сегодня у меня был очередной сеанс мозгофилии.
Пример из InnoSetup (описание внешней процедуры в IS)

CODE
procedure MyDllFuncSetup(hWnd: Integer; lpText, lpCaption: AnsiString; uType: Cardinal);
external 'MyDllFunc@files:MyDll.dll stdcall';

Пример из InnoSetup (описание процедуры в Delphi)
CODE
procedure MyDllFunc(hWnd: Integer; lpText, lpCaption: PAnsiChar; uType: Cardinal); stdcall;
begin
  MessageBoxA(hWnd, lpText, lpCaption, uType);
end;

Меня удивила разница в типе параметров - строка и явный указатель. Проверил, работает. И действительно, на стороне DLL получаем не строку, а указатель.

Далее самое главное, на стороне своей DLL я работаю с интерфейсом из другой DLL и его параметры изменить не могу, а там используется указатель на другой тип.
CODE
Reader.Open(PWideChar(FileName));
где FileName: String;
Если работать с диалогами и брать строку (имя файла) оттуда, то всё работает. В моём же случае пришлось извратиться.
CODE
S:='';
for j:=0 to Length(FileName)-1 do S:=S+FileName[j];
Reader.Open(PWideChar(S));

Но это не помогло, и в итоге получаем, что якобы "нет такого файла". А также получил предупреждение (было ли оно в оригинале уже не помню)
QUOTE
suspicious typecast of string to pwidechar
Пробовал искать в сети инфу, но либо не так запросы оформляю, либо ничего дельного там нет. Как это дело правильно преобразовать?

Автор: -=CHE@TER=- Jan 7 2011, 23:20

1) Либо Inno: AnsiString -> WideString; Delphi: PAnsiChar -> PWideChar
2) Либо http://msdn.microsoft.com/en-us/library/bb202786.aspx в Delphi
Первый вариант предпочтительнее, т.к. не будет зависеть от установленной в системе локали.

Автор: Siberian GRemlin Jan 8 2011, 10:49

Собственно, я и спрашивал как правильно перегнать PAnsiChar -> PWideChar.

Добавлено:
Сделал пока так. Насчёт локалей в системе ничего не понял. Это что такое? Желательно писать или по-русски или по-английски, чтобы понять можно было, ибо винегрет плохо усваивается организмом.

CODE
//Обычную в юникод
function StringToWideString(const s: AnsiString; codePage: Word=0): WideString;
var
  l: integer;
begin
  if s = '' then
    Result := ''
  else
  begin
    l := MultiByteToWideChar(codePage, MB_PRECOMPOSED, PChar(@s[1]), - 1, nil, 0);
    SetLength(Result, l - 1);
    if l > 1 then
      MultiByteToWideChar(CodePage, MB_PRECOMPOSED, PChar(@s[1]),
        - 1, PWideChar(@Result[1]), l - 1);
  end;
end;

Автор: -=CHE@TER=- Jan 8 2011, 12:35

http://msdn.microsoft.com/en-us/library/0h88fahh.aspx - это, грубо так говоря, язык установленный в системе (их, кстати, может быть сколько угодно, а не только 1 (EN) или 2 (EN, RU)). Например, если поставить чистую английскую винду, то вместо русских букв у тебя будут только знаки "?" (как и вместо букв любого другого не-английского языка).
Я не знаю (настолько серьёзно с Inno не работал) поддерживает ли он UNICODE, но без этого:
1) "C:\Игры\UT" превратится в "C:\????\UT" если в системе не установлена русская локаль.
2) Любой каталог, где в имени есть символы непредставимые в ANSI, будет содержать в имени "?".
Что, естественно, работать в ANSI-программе не будет.
ANSI -> UNICODE не проблема, а вот при UNICODE -> ANSI часть символов теряется (превращается в "?"). Поэтому я и написал, что лучше только с Unicode работать, если уж даже твоя библиотека её требует.
Вообще, конечно, можно на этом не запариваться - вряд ли твой руссификатор какие-нибудь китайцы ставить будут. Главное, чтобы всяких хитрых символов в имени каталога назначения не было (и в имени пользователя тоже, если ты используешь временный каталог по умолчанию, а он "C:\Documents and Settings\ВасяПупкин\Local Settings\Temp\" - мы так, если помнишь, с первой версией NFSMultiConv пролетели, т.к. утилита VAG'а работала только с ANSI именами файлов).

Автор: -=CHE@TER=- Jan 9 2011, 16:01

Ковырял тут по просьбе CMS одну. Собственно, были проблемы с отображением.

Во-первых, баннер в шапке как-то странно отваливался вниз. Причём только под IE8, под всеми остальными обозревателями, в том числе под IE6 всё было ок. Ну да не беда - всё поправилось добавлением свойства valign="top" в тег <td> где был баннер:

CODE
<td valign="top"><a href="http://www.example.com/"><img src="banner.jpg"></a></td>


Во-вторых, почему-то у таблиц с меню и прочими вещами последняя строка с картинкой линии имеющей закруглённые края по бокам всё время на 3-4 пикселя отваливалась от таблицы. Такое убожество было только под IE (версии 6 или выше). Собственно, код последней строки:
CODE
<tr><td colspan="2"><img src="bottom.gif" border="0" height="5" width="200"></td></tr>

Установка border у <td> и <img> показала, что зазор именно между ними. Т.е. получалось что это либо padding у <td>, либо margin у <img>. Однако установка всех возможных атрибутов: border, margin, padding у обоих этих элементов в 0 и даже несмотря на добавление !important зазор так и не убрала. Офигев от такого я поставил у <td> стиль background-image и background-repeat: no-repeat, а внутрь <td> всунул nbsp чтобы пустая таблица не схлопывалась, что, собственно, решило проблему. Но мне решение не понравилось и я решил ещё поковырять стили, тем более, что такой глюк с разметкой я часто видел и на других сайтах. Чтобы я не делал (даже valign="top" у <td>), какие бы стили не менял зазор всё равно оставался. И тут я подумал: отступ, должно быть, делает сам IE, т.к. сайт написан на xml, но некоторые элементы там не соответствуют стандарту (как видно у той же картинки в шаблоне нет "/" на конце - должно быть <img ... />). Я где-то читал, что если документ не соответствует строго формату xml, то во время парсинга IE переключается обратно в какой-то там режим совместимости чего-то с чем-то. И в нём он может отображать элементы как попало. Мне нужно было как-то вытащить элемент из того неизменяемого контейнера, который ему сделал IE. Можно было применить абсолютное позиционирование, но такой фокус это уж очень грязный хак, да и с изменяемым по ширине/высоте контентом не пройдёт. И тут меня осенило - я добавил у тега <img> свойство:
CODE
style="float:left;"

И всё заработало! Ведь float как раз отменяет всякие там отступы и прочие вещи, что в данном случае решило проблему.

Автор: Siberian GRemlin Apr 14 2011, 16:49

Не подскажите ка можно на JavaScript вставить (гипер)текст в страничку из внешнего файла? Что-то типа include() на PHP.

Автор: -=CHE@TER=- Apr 14 2011, 18:26

Если тебе просто что-то надо вставить, а не файл, то можно так:

CODE
var st = '<a href="http://www.google.com/">Google</a>';
var test = document.getElementById("test");
test.innerHTML = st; // содержимое элемента с id="test" будет заменено на то, что было в переменной st


Если же тебе именно файл надо загрузить, то тут смотри в сторону AJAX или jQuery. Кстати, загружать что-то на JavaScript можно только с того же домена, где выполняется скрипт. Иначе политика безопасности не позволит. Т.е. JavaScript выполняемый на сайте example.com не сможет загрузить что-либо с сайта microsoft.com.

Для XML (для HTML там надо, видимо, XML убрать) на java script:
CODE
var st = './testfile.xml';
var http_request = false;
http_request = new XMLHttpRequest();
if(!http_request){
  alert('XMLHTTPERROR');
  return;
}
http_request.open("GET", st, false);
http_request.send(null);
st = http_request.responseText; // ответ сервера - XML документ


На jQuery всё вообще в одну строчку (данный пример загружает в div с id="test" содержимое файла newpage.htm с сервера):
CODE
$('#test').load('./newpage.htm');

Если #test заменить на body - то будет заменено содержимое страницы.

Кстати, include() в PHP используется для подключения файлов с исходными кодами. Упаси тебя боже использовать его для вывода страницы, особенно, если это не твой файл, а, скажем, скачанные данные из Интернета - вредоносный PHP код туда всунуть как два байта переслать.
Насчёт же подключения в HTML я бы http://htmlbook.ru/html/frame или http://htmlbook.ru/html/iframe посоветовал, но их баннерорезки часто рубят.

Автор: Siberian GRemlin Apr 15 2011, 11:12

Мне нужно было именно на JS или jQ, ибо для AJAX нужен TomCat, а его на сервере нет, равно как и нет PHP. В сети почему-то нужную функцию найти не мог никак. Спасибо!

Автор: -=CHE@TER=- Jul 23 2011, 13:01

Поставил я тут новый colorer для FAR и моя тачка захлебнулась обрабатывая те PHP скрипты, которые раньше без проблем редактировались. Так что откатился я на colorer version 'freeCraze' за 08.03.2000.
Работает резко, но там косяки были с раскраской у PHP - пришлось вручную дожимать до рабочего состояния.

Итак:
1) В файле:
C:\Program Files\Far\Plugins\colorer\includes\net\PHP.Hrc
находим строчку:

CODE
<Block Start="/(\<\?)/" End="/(\?\>)/" Scheme="PhpScript"
Color0="PhpText" Color1="HtTag" Color2="HtTag">

меняем на:
CODE
<Block Start="/(\<\?(php)*)/" End="/(\?\>)/" Scheme="PhpScript"
Color0="LtCyan/bkBlack" Color1="HtTag" Color2="HtTag">


2) В файле:
C:\Program Files\Far\Plugins\colorer\includes\net\PhpScript.Hrc

- исправляем статичные строки (с одиночными кавычками):
находим строчку:
CODE
<RegExp Start="/([Ll]?'..??.??.??')/" Color0="dStrings">

меняем на:
CODE
<RegExp Start="/('(.*?[^\'])??')/" Color0="dStrings">


- исправляем проверку регистра у ключевых слов (чтобы подсвечивались в любом варианте):
находим строчку:
CODE
<KeyWords MatchCase="True" Color="dResWords">

меняем на:
CODE
<KeyWords MatchCase="False" Color="dResWords">


- подсвечиваем переменные - добавляем куда-нибудь вот такое:
CODE
<!-- Varibles -->
<RegExp Start="/(\$[a-zA-Z0-9_]+)/" Color0="DkGray">


После всех этих изменений новый colorer уже и не нужен. (*улыбается*)

Автор: Siberian GRemlin Jul 28 2011, 03:05

Намедни столкнулся с тем что IE не понимает InnerHTML (и некоторые другие) для таблиц. Поискав, нашёл http://webew.ru/articles/598.webew. Читать можно только последний абзац с кодом.

Автор: -=CHE@TER=- Nov 11 2011, 15:15

У Delphi такой смешной оптимизатор (компилирую программу через DCC32HACK - там глюки и косяки сразу хорошо видно, т.к. программа маленькая и без мусора):

CODE
Const Chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';

Делает копию этой строки в каждом (!) месте программы где она используется.

Если же написать так:
CODE
Const Chars: String = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';

То только в первом - все остальные на неё ссылаются.

А вот так:
CODE
Const Chars: String[36] = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';

Вообще загоняет строку отдельно в секцию DATA.

Автор: Siberian GRemlin Nov 11 2011, 15:25

Как я понимаю при конкатенации (дурацкое слово) строк создаётся новый элемент типа string, и если потерять указатели на строки из которых складывалась новая, т.е. например S:=S+S, или добавлять вручную значение, например S:=S+'s', то будет утечка памяти? Хочу вспомнить как правильно и не громоздко работать на строками на D7, а то работа на Java расслабляет...

Автор: -=CHE@TER=- Nov 11 2011, 20:39

Похоже долго ты на Java писал. (*улыбается*)

Нет, насколько я помню, Delphi хранит указатели на сложные типы данных и количество переменных ссылающихся на них. Как только количество = 0, то переменная уничтожается. Если же ты делаешь T:=S;, то у тебя в T не хранится строка S, а только указатель на неё. Реально память выделяется, если я правильно помню, только когда ты начинаешь изменять T, например: T:=T + 's';
Уже не помню, где об этом читал, где-то статья была, может даже здесь (на форуме) ссылку давали.
http://sources.ru/magazine/0804/delphistr.html - там нет якорей, так что крути вниз до главы "Как же это происходит?".
Так это происходит или нет, судить не берусь, но, вроде бы, в этой главе походит на правду.

Автор: -=CHE@TER=- Nov 14 2011, 13:41

Siberian GRemlin!

До меня, кажется, дошло, почему ты спросил. (*улыбается*)

QUOTE(-=CHE@TER=- @ Nov 11 2011, 15:15) *
CODE
Const Chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';

Делает копию этой строки в каждом (!) месте программы где она используется.

Это моё замечание (как и другие из того сообщения) относились не к динамическому размещению строк в памяти во время работы программы, а к размещению данных в .EXE файле. Т.е. в приведённом случае в .EXE файле на диске будет три раза продублированна в разных местах эта строка. Таким образом, к примеру, в случае большого массива, размером, предположим в 64 Кб (скажем, для расшифровывания чего-нибудь) на диске такое безобразие будет занимать 64*3 Кб + код программы, где 3 - количество обращений к этому массиву (в данном случае у меня их было 3).

Автор: -=CHE@TER=- Jan 14 2012, 13:26

Писал я недавно патч для включения крови в German версии Turok 2. Решил, чтобы не маяться с CreateWindow(), сделать диалог в ресурсах. Взял из FASM пример EXAMPLES\DIALOG\DIALOG.ASM и переколбасил его под свои нужды. Всё работает, но странности начались, когда я решил навести последний штрих - добавить manifest для XP/Vista/7.
Программа стала сразу выходить после запуска. Офигев от такого безобразия начал отлаживать - выяснилось, что DialogBoxParam() сразу выходит даже не отображая окно. Убираем манифест - всё снова работает. Пошёл гуглить и натолкнулся вот на такую статью:
http://yvs-it.blogspot.com/2010/04/initcommoncontrolsex.html
(кстати, там на сайте и другие статьи интересные есть - их немного, так что пробегитесь по ним, кому интересно)
В общем, суть такова: чтобы программы с манифестом и DialogBoxParam() работали как надо, нужно чтобы в импорте была библиотека COMCTL32.DLL. Это можно сделать, вызвав, к примеру, InitCommonControls(). Я решил её не вызывать (толку-то всё равно не будет), а просто перепрыгнуть:

CODE
jmp @ICC
invoke InitCommonControls
@ICC:

Мне главное, чтобы в импорте COMCTL32.DLL была, а если ни одна функция оттуда не вызывается, то компилятор удаляет её из импорта.
Вот такая фигня.

Автор: -=CHE@TER=- Mar 7 2012, 20:37

QUOTE(-=CHE@TER=- @ Aug 10 2010, 14:27) *
Вчерась ковырял одну игрушку, где данные были упакованы deflate. Как и всегда использовал puff.c + puff.h из zlib. Внезапно, при распаковке, получаю ошибку записи в память по адресу 0, программа падает. <...>
Наконец-то обновили - http://zlib.net/.

Автор: -=CHE@TER=- Mar 12 2012, 09:13

QUOTE(-=CHE@TER=- @ Nov 11 2011, 15:15) *
У Delphi такой смешной оптимизатор (компилирую программу через DCC32HACK - там глюки и косяки сразу хорошо видно, т.к. программа маленькая и без мусора):
Кто-нибудь знает, как можно сразу, без этих приседаний, загонять константы в секцию .DATA? Может переключатель {$..} какой есть? А то Delphi их дублирует, скотина, в .CODE размещать не хочу, а для каждой строки писать String[кол-во_символов] = '...'; очень нудно.

Автор: Siberian GRemlin Mar 12 2012, 11:07

QUOTE(-=CHE@TER=- @ Mar 12 2012, 17:13) *
для каждой строки писать String[кол-во_символов] = '...'; очень нудно.
Я для InnoSetup писал прогу в которой вставляешь строки, а она выдаёт код с массивом строковых констант. Возможно и тебе это будет проще делать.

Автор: Siberian GRemlin Mar 24 2012, 09:13

Кто-нибудь знает где можно взять готовый код под PHP4 для генерации картинки с текстом? Наподобие того как это сделано для телефона продавца на сайте avito.ru - нужно открыть какое-нибудь объявление и там будет "показать телефон продавца". Пробовал разные запросы, но поисковик выдаёт сайты на которых можно ввести текст, выбрать шрифт и пр. и он выдаст какую-нибудь красивую картинку.

Автор: -=CHE@TER=- Mar 24 2012, 11:58

http://lurkmore.to/%D0%94%D0%B5%D0%BC%D0%BE%D1%82%D0%B8%D0%B2%D0%B0%D1%86%D0%B8%D0%BE%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D0%BE%D1%81%D1%82%D0%B5%D1%80/%D0%93%D0%B5%D0%BD%D0%B5%D1%80%D0%B0%D1%82%D0%BE%D1%80%D1%8B (за номером 7 самый простой, и ниже - до конца, уже сложнее) натыкался когда-то на генератор на PHP, который добавляет текст на изображение. Посмотри что да как, лишнее выкини. Все эти функции описаны в справке по PHP. Если я не ошибаюсь это расширение GD2 Lib, которое на современных серверах должно быть.
Если же у тебя вообще голый PHP без расширений, то можно самому сгенерировать и выводить .BMP файл, правда занимать он будет больше (впрочем, можно немного поприседать и сделать ч/б, чтобы меньше весил).

Автор: -=CHE@TER=- Apr 14 2012, 15:39

В Си есть такая замечательная функция, как sprintf(), которая позволяет формировать строчку со всякими '%s', '%03d' и прочим. Эта функция есть в msvcrt.dll, однако, я не люблю эту библиотеку, так как на чистой Windows 98 SE у меня её не было - она ставится только с какой-нибудь программой, да и вообще, в ядро системы не входит.

Зато в ядро системы входит wsprintf(). К сожалению, в Delphi она криво объявлена, в результате чего ей нельзя пользоваться. Как мы помним, у всех таких функций может быть разное количество аргументов - это, оказывается, решается ключевым словом "varargs":

CODE
function wsprintf2(Output: PChar; Format: PChar): Integer; cdecl; varargs; external 'user32.dll' name {$IFDEF UNICODE}'wsprintfW'{$ELSE}'wsprintfA'{$ENDIF};

Вот такое объявление (я его уже не помню где нашёл в Интернете) - работает.
cdecl, как мы помним, говорит о том, что в отличие от stdcall, вызываемая функция не знает сколько параметров сложено на стек, так что об их снятии со стека после работы функции должна позаботиться вызывающая сторона, что в данном случае логично.

Нужно только помнить пару вещей:
1) В MSDN настоятельно не рекомендуют пользоваться этой функцией (небезопасна, если криво используется), но в Windows 7 и ниже она есть (в Windows 98 есть только wsprintfA).
2) В этой функции ограничение на 1025 байт для ANSI и 1025*2 для UNICODE буфера - т.е. если строчка больше, то она просто будет отрезана.
3) Она поддерживает далеко не все возможности, которые есть в sprintf (кажется, с дробными числами не работает - см. MSDN).

Пример использования (S - переменная типа String):
CODE
// максимальный размер буфера
SetLength(S, 1025);
// отрезаем по границе - сколько реально было использовано
SetLength(S, wsprintf2(PChar(S), '%03d - %s', 45, PChar('Hello World!')));


P.S. Да, в Delphi есть FmtStr(), но она подключается вместе с SysUtils и ещё кучей мусора, что для небольших программ на WinAPI просто неприемлемо.

Автор: -=CHE@TER=- Apr 15 2012, 10:32

И ещё о нескольких устаревших (по мнению MSDN, где ими не советуют пользоваться) функциях.

Часто программе нужно где-то хранить настройки. Вариантов хранения два:
1) В файле.
2) В реестре.

Microsoft настоятельно рекомендует пункт 2 и пользоваться для этих целей веткой реестра HKEY_CURRENT_USER \ Software, чтобы у каждого пользователя были свои настройки - т.е. каждый вошедший в систему пользователь мог настроить программу "под себя" (так как ему нравится). Как известно HKEY_CURRENT_USER загружается автоматически вместе с текущим профилем пользователя при входе в систему и у каждого пользователя эта ветка реестра будет своя (разумеется, кроме тех одинаковых вещей, которые по умолчанию добавляет туда сама система при создании профиля для нового пользователя). К слову, HKEY_LOCAL_MACHINE - это глобальная ветка, настройки в которой доступны всем пользователям (вернее будет сказать, что это настройки для всего компьютера), посему и писать туда не имея прав администратора вообще нельзя.

Хранение же настроек в файле чревато тем, что у всех пользователей будут одни и те же настройки, а если каждый захочет менять под себя... будет мало хорошего. В принципе, можно хранить настройки в каталоге пользователя C:\Documents and Settings\USERNAME\Application Data\<ИМЯ ПРОГРАММЫ> (т.н. каталог %APPDATA% - который, как и ветка реестра, у каждого пользователя свой).

Однако, и реестр и каталог пользователя "болеют" одной проблемой: при удалении программы, все эти вещи остаются. Т.е. система получается загажена неиспользуемыми данными - "мусором". Можно, конечно, сделать установщик, который будет все эти вещи чистить при удалении программы.
Однако же:
1) Надо писать установщик.
2) Надо писать логику и обработку удаления программы.
3) Программа перестаёт быть portable (портативной - переносной, ей обязательно нужен будет доступ для записи либо в реестр, либо на жёсткий диск).
Самый серьёзный пункт - это пункт 3.
Но даже не в этом дело, а в том, что многие пользователи до сих пор, "по привычке", удаляют программу через SHIFT+DELETE, вместо "Установки и удаления", что сводит все старания из п.п. 1 и 2 на нет.
Наконец, для программ, которые являются каким-то разовыми утилитами и которыми не пользуешься каждый день, все эти вещи просто лишние.

И вот тут нас выручают настройки сохранённые в файл в каталоге программы - по завершении работы весь каталог с программой можно безболезненно удалить. И всё вернётся на круги своя.

Настройки можно хранить и в своём формате, но гораздо удобнее, на тот случай, если программа навернётся при запуске из-за кривого файла настроек, делать это в .INI формате, который очень просто и быстро в таких ситуациях можно подправить ручками, а не писать для этого специальную программу-редактор.

Уф, ладно, со вступлением закончил. (*улыбается*)

Теперь переходим к практике. Для работы с .INI файлами есть в WinAPI следующие функции:
GetPrivateProfile* - для чтения
WritePrivateProfile* - для записи
* - означает, что есть несколько функций, которые начинаются на эту строку, но имеют разные окончания.

Для примера рассмотрим вот такой тестовый "test.ini" файл:

CODE
[Main]
Param1=123
StrVal=String!

Для того, чтобы прочитать оттуда нужно сделать следующее:
CODE

Var
  S: String;
  X: Integer;
Begin
...
// максимальный размер строки - 100 символов
SetLength(S, 100);
// меняем на тот, сколько реально было прочитано
SetLength(S, GetPrivateProfileString('Main', 'StrVal', '', @S[1], Length(S), '.\test.ini'));
X:=GetPrivateProfileInt('Main', 'Param1', 0, '.\test.ini');

Ну и писать, точно также, только через WritePrivateProfile*.
Кстати, чтобы удалить из .INI достаточно написать (Nil - в качестве значения параметра, чтобы удалить):
CODE
WritePrivateProfileString('Main', 'StrVal', Nil, '.\test.ini');

И StrVal вообще исчезнет из .INI файла.

Важные замечания:
1) Не путайте (Get/Write)PrivateProfile* и просто (Get/Write)Profile* - последние функции (без Private в названии) пишут в системный файл "WIN.INI"! А его лучше не трогать, к тому же доступа на запись в системный каталог может не быть.
2) Не забывайте писать '.\' в начале имени файла. Это важно - если написать просто 'test.ini', то этот файл будет сохранён в "C:\Windows\test.ini" - а прав записи туда может не быть.
3) Функций гораздо больше (не только *String и *Int) - см. справку по MSDN. Я, кстати, здесь не объяснил некоторые параметры (значения по умолчанию, например, если параметра в файле нет) - за всем этим, а также другими удобными вещами и прочими тонкостями - см. MSDN.
4) Как я уже говорил, MSDN настоятельно рекомендует не пользоваться этими функциями и посылает всех в реестр, однако же, они вполне себе спокойно работают и под Windows 7.

P.S. В Delphi есть IniFiles при подключении которого через uses появится класс TIniFile работающий с .INI файлами и независящий от перечисленных WinAPI функций, однако же, как я уже писал в сообщении выше, для небольших программ на WinAPI подключение этого модуля чудовищно раздувает размер кода.

Автор: -=CHE@TER=- Apr 19 2012, 13:23

Проводя очередную чистку системы, натолкнулся на файл:
C:\WINDOWS\Perflib_Perfdata_*.dat
который не хотел удаляться. Внутри была видна как-то информация от долбанного .NET.
Unlocker показал, что этот файл "держит" процесс G6FTPServer.exe (Gene6 FTP Server 3.8.0.34).
Как выяснилось, это система чего-то пишет, при запросе статистики об используемой памяти, процессорного времени и т.д.
Отключается сиё безобразие переименованием или удалением файла:
C:\Program Files\Gene6 FTP Server\Plugins\g6_log_system.dll
Не забудьте перед этим остановить службу FTP-сервера, а потом снова включить.
Там оно ещё и в реестр http://www.g6ftpserver.com/forum/?showtopic=2119 из-за этого.
В результате отключения этой .DLL информации о загрузке системы в логах не будет, но мне она и так никуда нафиг не упёрлась.

Автор: Grom PE Apr 19 2012, 19:05

Сам у себя встречал такие файлы. Полюбопытствовал, что и как.
Выяснилось, что можно взять и применить регу:

CODE
REGEDIT4

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib]
"Disable Performance Counters"=dword:00000001
Тогда ни одна программа не будет создавать эти файлы.
А последствия - http://technet.microsoft.com/en-us/library/cc737243(v=ws.10).aspx.

Автор: -=CHE@TER=- Apr 24 2012, 12:14

В очередной раз зайдя на сайт товарища Siberian GRemlin'а и увидев надпись (да, я таки поставил себе NoScript):

QUOTE
Для отображения ссылок необходимо разрешить выполнение 'JavaScript' в вашем обозревателе!
гласящую, что без JS на сайте ничего нельзя увидеть, я всё же решился дать ему ссылку тут на полезную статью, о том, как AJAX'ом пользоваться не надо: http://citkit.ru/articles/491/ (хотя бы со слов: "Вот, пожалуй, и все об этом. А теперь тех, кто не уснул за чтением первой части статьи..." и далее), если, конечно, никакая индексация в поисковых системах (т.е. попытка спрятать сайт от поисковых роботов) не является частью какого-нибудь Хитрого Плана™. (*улыбается*)

Автор: Siberian GRemlin Apr 24 2012, 13:16

Я так понимаю, ты знаешь способ создания динамичных страниц на голом HTML. Никого AJAX'а, кстати, тоже нет.

Автор: -=CHE@TER=- Apr 24 2012, 15:37

QUOTE(Siberian GRemlin @ Apr 24 2012, 13:16) *
Я так понимаю, ты знаешь способ создания динамичных страниц на голом HTML. Никого AJAX'а, кстати, тоже нет.
AJAX = Asynchronous Javascript and XML (асинхронный JavaScript и XML).
Ну, у тебя там нет XML, а ты просто кусок сайта через массивы подгружаешь - в принципе, тоже какой-то вариант хранения данных.

Я про саму мысль, что у тебя там здоровенный список игр на какой-либо странице, но через поисковую систему на твой сайт не выйдешь с запросом имени игры и перевода, так как поисковые пауки индексируют сайт без JS.

Это всё к чему: у тебя хостер PHP или какие-либо другие серверные языки не разрешает использовать? Только голый HTML? У тебя же, вроде бы, собственный домен? Просто тогда можно было бы нормально генерить страницы на стороне сервера.

Если же только голый HTML, то это жесть, честно говоря.

Есть один способ - как я на народе выкручивался: делал шаблон сайта и парсер. Парсер выдирал контент (содержимое) из *.HTM файлов рекурсивно обходя все каталоги сайта. То что считалось за тело (т.е. не шапка, не подвал и не меню) помечалось как:
CODE
<!-- Content -->
тут тело документа
<!-- /Content -->

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

Но, видишь что, у меня списков постоянно меняющихся нет, я там, в основном, статьи пишу и ручками всё правлю. Тебе, возможно, будет проще сделать БД, где хранить списки игр и при компиляции сайта вставлять в нужное место. Скажем в документе пишешь %%GAMELIST_RU%%, а уже при компиляции заменяешь это слово на список нужных игр. Тебе тогда нужно будет делать несколько шаблонов: один общий для сайта (шапка, меню, подвал), а другой для каждой страницы, ну и готовые странички в отдельный каталог складывать. Можно даже перезаписывать только те, которые изменились, чтобы по дате отсортировать и на сервер только новые закидывать.

Вот такие варианты могу предположить.
Это не наезд, просто у тебя столько работы пропадает, потому что никак не индексируется - вот что грустно.

Автор: Siberian GRemlin Apr 24 2012, 15:49

Месяц назад я только договорился с админом и мне включили PHP, жаль только что 4-ой версии, а не 5-ой. Пока не было времени код переписать. Планирую сделать PHP+XML. Если есть идеи, то готов выслушать.

Автор: -=CHE@TER=- Apr 24 2012, 16:19

О, блин, 4-ый PHP это уже здорово, чем вообще ничего!
Я не знал об этом, поэтому и предложил вот это всё.
Думаю с PHP у тебя уже проблем не будет.
Предложить особо нечего - если MySQL или какой-либо БД нет, то тогда действительно только в .XML хранить БД и парсить её при генерации страниц сайта на PHP.
simplexml_load_file(), к сожалению, только в PHP 5 появилась, смотри в сторону xml_parser_create() и других функций - они, судя по руководству к PHP, есть и в 4-ой версии.
Вот годная статья: http://grigorieff.ru/?p=183.

Автор: -=CHE@TER=- May 30 2012, 10:22

Нашёл в Delphi 7 охрененный косяк, который вынес мне мозг.

1) Создаём новую программу - пустую форму и вешаем обработчик на событие OnActivate (нам нужно показать окно с некоторой заполненной информацией в ListBox).
2) Кидаем на форму ListBox и вешаем обработчик на событие OnClick.
3) Пишем туда такой код:

CODE
procedure TForm1.ListBox1Click(Sender: TObject);
begin
  // if Sorted changed in FormActivate you'll see this only with keyboard
  ShowMessage('OnClick');
end;

procedure TForm1.FormActivate(Sender: TObject);
var i: integer;
begin
  for i:=1 to 10 do ListBox1.Items.Add(IntToStr(i));
  ListBox1.Sorted:=True // <-- bye-bye OnClick, DblClick and mouse events!
end;

4) Долбанный стыд - при изменении Sorted (неважно в True или False) внутри OnActivate - OnClick, OnDblClick у ListBox и все прочие события с мышки перестают обрабатываться. Работает только клавиатура...

Автор: -=CHE@TER=- Sep 6 2012, 20:32

Все, наверное, помнят, что в .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 понимает.
Программы, которые вяло следуют (или вообще не следуют) стандартам, конечно, накроются тазом, если читают поля как беззнаковые, но в остальном даже обидно как-то - всю жизнь маялся и перевёрнутым записывал. Эх, где ж это всё раньше было?..
В http://msdn.microsoft.com/en-us/library/windows/desktop/dd318229.aspx это тему расширили - см. описание biHeight.

Автор: jTommy Sep 8 2012, 20:52

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

Автор: -=CHE@TER=- Sep 9 2012, 12:43

Ты имеешь ввиду http://netghost.narod.ru/gff/?
Я нашёл там http://netghost.narod.ru/gff/graphics/summary/micbmp.htm описание (оно за 1994 год), но там нет упоминания про отрицательное значение.
Видимо, стандарт 2.0 был введён между 1994 и 1996. (*улыбается*)
А, всё, дошло - ты про книгу 1997 года, которая является переводом более поздней версии этого сайта?
В Интернете, увы, нигде .PDF версии не нашёл, только купить предлагают.

Автор: jTommy Sep 9 2012, 21:25

Да, она самая, издание от 1997 года.

Автор: -=CHE@TER=- Dec 15 2012, 09:29

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

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 файл и никакого головняка!

Автор: -=CHE@TER=- Apr 6 2013, 18:25

Товарищи, у меня вопрос - может кто-нибудь помочь?
Есть 16-ти битное Windows приложение (NE - New Executable) в котором есть окно с текстом.
Пытаюсь достать текст через WM_GETTEXTLENGTH и WM_GETTEXT. Всё бы ничего, но текст специально отформатированный каким-то своим форматом, так что там есть символ 0x00 (т.е. ноль). Из-за чего WM_GETTEXTLENGTH обламывается на первом таком символе и возвращает длинну меньше чем надо. Я пробовал специально делать буфер больше, но через WM_GETTEXT один фиг возращается всё до первого нуля (и размер скопированных данных соответственный).
Кто-нибудь знает, есть ли какая-нибудь функция, которая позволяет:
- получить заголовок окна как бинарные данные
- или получить указатель на начало буфера с эаголовком окна
- или кто-нибудь может сказать, в какой структуре из недокументированных функций можно найти как получить адрес буфера caption?
Заранее спасибо.

Автор: -=CHE@TER=- Apr 9 2013, 16:17

Нашёл вот такую http://blog.naver.com/35mwlee/110156146195:

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", чтобы прибавить к нему смещение и добраться до текста в окне?
Буду очень-очень признателен.

Автор: -=CHE@TER=- Apr 18 2013, 16:48

QUOTE(-=CHE@TER=- @ Apr 9 2013, 16:17) *
Исходные коды можно нагуглить, могу выложить, если понадобятся.
http://www.ctpax-x.org/uploads/showwnd.zip - вот оно.
Скорее всего под что-то старше 6-ой студии компилиться не будет.
В качестве 16-ти битного приложения в 98-ой можно запустить PROGMAN.EXE и попытаться получить его заголовок.
Может кто-нибудь хотя бы по исходным кодам скажет мне как получить адрес "atom heap"?..

Автор: -=CHE@TER=- Apr 27 2013, 15:06

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

P.S. Вопрос выше с получением текста из 16-ти битного приложения всё ещё актуален.

Автор: Siberian GRemlin Apr 29 2013, 13:48

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

P. S. С получением текста помочь не могу, к сожалению. Не пойму, там он динамический что-ли? Нельзя ли самостоятельно выскрести этот текст оттуда откуда прога его берёт?

Автор: -=CHE@TER=- Apr 30 2013, 11:40

QUOTE(Siberian GRemlin @ Apr 29 2013, 13:48) *
P. S. С получением текста помочь не могу, к сожалению. Не пойму, там он динамический что-ли? Нельзя ли самостоятельно выскрести этот текст оттуда откуда прога его берёт?
Да, увы, динамический - поэтому нужно именно то, что написано на окне, потому что это сгенерированный, пересчитанный и обработанный текст. Т.е. на основе входных данных программа обсчитывает и выводит результат. Проблема только в том, что программу писали через одно место и ни распечатать, ни сохранить в файл результат работы нельзя, разве что PrintScreen делать, но с многостраничными отчётами, которые не помещаются на экран - это сизифов труд. Я пытался написать программу, которая бы получала канву окна и делала автоматические снимки экрана в файл, но там, почему-то, всегда оказывалась известная работа Казимира Малевича - выяснилось что нужно сворачивать все окна, кроме нужного, делать снимок всего рабочего стола, затем выковыривать оттуда по координатам изображение с окна... короче, я забил на такой способ.
Программа 1997 г.в., аналогов нет, плюс 16-ти битные приложения же ещё хрен отладишь - Олька, например, с ними не работает. А то что работает, идёт только под реальной 98-ой, и ни на XP, ни под виртуальную машину с 98 не запускается (тупо вешает).

Автор: -=CHE@TER=- May 1 2013, 11:54

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

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

Автор: -=CHE@TER=- Jun 6 2013, 17:11

Сделал себе замечательный 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"

Автор: Siberian GRemlin Jun 22 2013, 08:06

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

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

P. S. Если кто в курсе как запускать локальный proxy сервер Tor без встренного firefox portable — буду рад советам: копаться пока совсем нет времени.

Автор: -=CHE@TER=- Jun 28 2013, 11:09

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

CODE
.00401581: 7513  jne   .000401596

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

CODE
.00401581: 7500  jne   .000401583

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

Автор: -=CHE@TER=- Sep 14 2013, 14:32

Часто пишу программы, которые со временем довольно сильно меняются.
Ставить на свою машину 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" брал тут: http://sourceforge.net/projects/infozip/files/, распаковать и поместить в %PATH% (например, в C:\WINDOWS\). Прошу обратить внимание, что в этой программе ключи регистрочуствительные, так что -d и -D, а также -x и -X - это разные ключи!

Автор: -=CHE@TER=- Nov 23 2013, 17:38

Пишу в последнее время на Си, так что недавно мне довольно долго выедал мозг один мой косяк.
Короче, есть дефайн:

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))

Автор: -=CHE@TER=- Nov 27 2013, 15:42

Недавно я тут писал про утилиту http://www.forum.ctpax-x.org/?showtopic=236#entry3598, сжимающую файлы.
Конечно, сжатие весьма посредственное (мы сейчас рассматриваем классический алгоритм 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;

Автор: -=CHE@TER=- Feb 13 2014, 14:49

Натолкнулся на то, что в нескольких играх используется 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Кб, но это не так сложно подобрать опытным путём.

Автор: -=CHE@TER=- Aug 22 2014, 11:14

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.
© http://www.geoffchappell.com/notes/windows/shell/missingicons.htm

Автор: Siberian GRemlin Sep 18 2014, 09:26

Кто подскажет как отловить что пишется.
Суть такая: после завершения программы на экран выводится сообщение «Except...», но выводится оно на доли секунды. Но проблема в том, что выводится оно после завершения приложения, т.е. после «end.».

CODE
  Readln;
end.

И если запустить прогу с параметром, то в файле будет пусто либо то что выводим мы сами.
CODE
test.exe >err.txt

Переписав всё в графическое приложение мы разумеется вообще ничего (сообщения об ошибке) не увидим. Как грамотный программист должен поступить в данном случае? Всё работает штатно, но это мелькание ошибки меня смущает. Есть подозрения, что шелудит «ActiveX».

Автор: Axsis Sep 18 2014, 14:15

Если не ошибаюсь, оператор перенаправления вывода ">" по-умолчанию перенаправляет только поток STDOUT, а ошибки выводятся в STDERR.
Попробуй так:

CODE
test.exe >>err.txt 2>&1

Это перенаправит STDERR в STDOUT, а его, в свою очередь, в файл err.txt
Подробности:
_ttp://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/redirection.mspx?mfr=true

Автор: -=CHE@TER=- Sep 20 2014, 22:53

Почитываю блог http://blogs.msdn.com/b/oldnewthing/, который ведёт Raymond Chen - один из программистов Microsoft. Блог интересный, оттуда можно много чего почерпнуть как из истории и архитектуры Windows, так и о том, как надо или не надо писать программы под эту систему.
Была там одна http://blogs.msdn.com/b/oldnewthing/archive/2005/02/01/364563.aspx, код в которой ввёл меня в ступор (лишнее выкинул):

CODE
BOOL Is64BitWindows() {
BOOL f64 = FALSE;
  return IsWow64Process(GetCurrentProcess(), &f64) && f64;
}

Код определяет, запущен ли процесс 32 на системе 64 (вызывать его в программе которая и так 64-битная смысла нет). Кстати, не забывайте, что IsWow64Process() есть не во всех Windows, так что LoadLibrary(Kernel32.dll) и GetProcAddress(IsWow64Process) вам в помощь.
Так вот, меня поразило "&& f64" (&& - логическое And), ведь f64 до вызова было присвоено FALSE, а значит и вся конструкция "функция AND FALSE" будет равна FALSE. Однако же, потом до меня допёрло, что это верно только в случае когда выражение состоит только из констант (т.е. может быть посчитано компилятором на этапе компиляции), иначе выражения вычисляются слева на право.
Т.к. сама функция http://msdn.microsoft.com/ru-ru/library/windows/desktop/ms684139.aspx может почему-то обламаться, то результат складывается во второй параметр (он передаётся по адресу).
Таким образом, выполнение идёт следующим образом: сначала отработает IsWow64Process() и только потом её результат вычисляется совместно с f64 - т.е. на момент "&& f64", переменная "f64" уже изменена функцией.

По той же причине будет работать и следующая конструкция:

CODE
char *s;
<...>
if (s != NULL) {
  if (s[0] == '?') {
    // todo code here
  }
}

при замене её на более короткую:

CODE
char *s;
<...>
if ((s != NULL) && (s[0] == '?')) {
  // todo code here
}

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

Но!

В математике от перестановки результат не меняется, так что конструкции "1 & 0" и "0 & 1" эквивалентны. Однако же в приведённых выше примерах при перестановке условий мы получим либо всегда FALSE в Is64BitWindows(), либо фатальную ошибку чтения памяти по адресу 0 когда s == NULL.

Тут нужно заметить, что в Pascal и Delphi есть магический переключатель $B, который по умолчанию находится в выключенном состоянии, но если его включить, то все проверки будут выполняться полностью, что чревато в примерах выше неприятными последствиями (кроме кода, где проверки разделены на две вложенные). См. http://forum.vingrad.ru/articles/topic-157378.html в статье по ссылке.
Попробуйте эту программу, а также её, но удалив "?" в коде:

CODE
{$APPTYPE CONSOLE}
var p:pchar;
begin
  p:=nil;
{?$B+}
  writeln((p <> nil) and (p^ = #10));
end.


Но, возвращаясь к упомянутому блогу, ещё одна занимательная статья, про которую хочется сказать:
http://blogs.msdn.com/b/oldnewthing/archive/2014/06/13/10533875.aspx
Название можно перевести как "когда делать что-то может быть быстрее, чем не делать этого".
Разгадка проста
Когда убираем if() - убираем condition jump в asm коде, а значит конвеер ЦП не будет сбрасывать и опять наполнять кеш инструкций (дорогостоящая по времени операция!).

Автор: -=CHE@TER=- Sep 23 2014, 14:23

Кто работает с PHP наверное знает, что в php.ini нужно обязательно указать часовой пояс, иначе функции работы с датами будут, во-первых, работать неверно, а, во-вторых, вываливать Warning'и.

Устанавливается часовой пояс так (для Москвы):

CODE
[Date]
; Defines the default timezone used by the date functions
; http://php.net/date.timezone
date.timezone = Europe/Moscow

Недавно заметил, что у меня разница в 1 час выводимого времени с тем, что должно быть.
Из-за того что в России отменили переход на зимнее/летнее время (кстати, http://lenta.ru/news/2014/07/01/wintime опять стрелки крутить будем), а в PHP, видимо, это где-то жёстко зашито, то пришлось искать способ явно указывать часовой пояс, без сохранения летнего времени.

Так вот, указывать нужно вот так (для Москвы):

CODE
date.timezone = Etc/GMT-4

У кого-то сейчас может начать трещать шаблон почему вместо "+4" стоит "-4", однако же это такая особенность (выделение моё):
QUOTE
If you disregard the above warning, please also note that the IANA timezone database that provides PHP's timezone support uses POSIX style signs, which results in the Etc/GMT+n and Etc/GMT-n time zones being reversed from common usage.

For example, the time zone 8 hours ahead of GMT that is used in China and Western Australia (among other places) is actually Etc/GMT-8 in this database, not Etc/GMT+8 as you would normally expect.
© http://php.net/manual/en/timezones.others.php

Автор: Siberian GRemlin Sep 23 2014, 14:48

Кто мне подскажет когда и как правильно использовать CoUninitialize? Пишут, что нужно в каждом потоке делать CoInitialize(nil) и CoUninitialize. А что если у меня создаётся поток, в нём проверяется возможно ли подключиться к БД чрез ADO, а в главном потоке нужно либо переходить к работе с БД или завершать работу приложения. Придумал не я, этого требует задание. Дак вот если подгрузка делается в отдельном потоке и никак иначе, то когда делать выгрузку в данном случае?

Автор: -=CHE@TER=- Sep 23 2014, 17:35

Можно одним вызовом со специальным параметром разрешить для всех потоков процесса:

QUOTE
Did you get that? If any thread in the process calls Co­Initialize­[Ex] with the COINIT_MULTI­THREADED flag, then that not only initializes the current thread as a member of the multi-threaded apartment, but it also says, "Any thread which has never called Co­Initialize­[Ex] is also part of the multi-threaded apartment."
© http://blogs.msdn.com/b/oldnewthing/archive/2013/04/19/10412399.aspx
И ещё пара полезных ссылок о подводных камнях: http://blogs.msdn.com/b/oldnewthing/archive/2004/05/20/135841.aspx и http://chrisbensen.blogspot.com/2007/06/delphi-tips-and-tricks.html.

Автор: Siberian GRemlin Oct 24 2014, 06:21

Как я понял, во всех 64-разрядных «Окнах» обращения 32-разрядных программ к реестру перенаправляется. Из

CODE
HKEY_LOCAL_MACHINE\SOFTWARE\
в
CODE
HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\
Долго понять не мог почему игра не подхватывает свои параметры, пока не сделал проверку обращений к реестру. http://www.viva64.com/ru/k/0029/.

Автор: -=CHE@TER=- Oct 24 2014, 17:54

Там не только реестр - там, вообще, очень хитрое колдунство: http://msdn.microsoft.com/en-us/library/windows/desktop/aa384249.aspx.
См. ссылки в "In this Section" - там по каждой грабле отдельная статья, объясняющая чем она грозит.
Если ты делаешь установщик и он 32-битный, то проблем не должно быть.
А вот если ты файл реестра просто экспортируешь... я не уверен, но попробуй там, в начале, написать "REGEDIT4" вместо "Windows Registry Editor Version 5.00" (или что оно там на современных системах добавляет) - может RegEdit догадается что файл старый и его нужно в ветку 32 пихать.

Автор: Axsis Oct 26 2014, 23:48

RegEdit'а тоже две версии. Из 32-битных приложений вызывается один и пишет в виртуальные ветки, из 64-битных вызывается другой.
Я однажды долго не мог понять почему .reg файл, импортируемый из тотал командера (32-битного) не показывается в реестре, и в программе (x64) не работают настройки, которые я импортирую.
А потом обнаружил, что regedit, вызванный из тотала и regedit, вызванный через "пуск->выполнить" показывают разный реестр... (в первом случае 32-ух, а во втором - 64-бинтую его версию).
Добавил тот же самый .reg файл в реестр из проводника и всё заработало как и должно было.
PS: а "REGEDIT4" вместо "Windows Registry Editor Version 5.00" писать бесполезно в данном случае...

Автор: Siberian GRemlin Oct 28 2014, 10:02

Искал как проверить наличие связи с интернетом, нашёл http://subreal-team.com/blog/2014/01/19/connected-to-internet-delphi/. Из рассмотренных в ней вариантов достоверным оказался только последний. Проблема в том, что после такой проверки перестаёт работать «http://www.ararat.cz/synapse/doku.php/download» — на все запросы приходит ответ с кодом 500. Отключаю проверку связи и всё отлично. Кто-нибудь сталкивался или может знает другие способы проверки связи?

Автор: -=CHE@TER=- Oct 28 2014, 12:47

WSACleanup() убери оттуда.
Как я понял, твой Synapse с сокетами работает, а вызов этой подпрограммы нахрен закрывает библиотеку WinSock.
И, кстати, я там не увидел, чтобы они где-то в коде WSAStartup() делали, чтобы библиотеку закрывать.

Автор: -=CHE@TER=- Nov 29 2014, 14:51

Немного интересной и полезной информации по .BAT файлам.

1) Недавно перечитывая справку по .BAT файлам в Windows, натолкнулся на такую штуку:

CODE
@echo off
if _%1 == _ goto quit
program.exe %1
:quit

Оказывается в Windows (в 98-ой не проверял, но в XP точно есть) можно сделать проще (справка по "goto /?"):
QUOTE
Изменение команды GOTO при включении расширенной обработки команд:
Команда GOTO принимает в качестве метки перехода строку :EOF, которая вызывае передачу управления в конец текущего пакетного файла. Это позволяет легко выйти из пакетного файла без определения каких-либо меток. Команда CALL /? выводит описание расширенных возможностей команды CALL, делающих эту функцию особенно полезной.
Таким образом, скрипт можно сократить:
CODE
@echo off
if _%1 == _ goto :EOF
program.exe %1

Для тех, кто не в курсе - если вместо "goto <конец_файла>" делать exit, то это прекращает выполнение вообще. К примеру, если из одного .BAT файла через call вызвали другой, то при exit произойдёт выход из обоих файлов, а не возврат в верхний.

2) А ещё команду call можно вызывать с метками и параметрами - см. справку по "call /?".

3) Но и это ещё не всё. Перечитывая FAQ к DOS Navigator 1.51 (C:\DN\DOC\DN-FAQ25.TXT) я нашёл там такое:
QUOTE
Q > Зачем в DN.HGL сделано 'comentstring ::' в .BAT-файлах".

A > Потому, что набоp '::' в начале стpоки аналогичен REM, более того, это даже более гpамотно, так как REM все же команда DOS и вся стpока пеpедается командному пpоцессоpу на обpаботку, а ':' это не команда, а метка (что, видимо, и так все знают), а вот если после ':' стоит символ, котоpый не может быть частью названия метки (точнее метка, котоpой нет), то DOS пpосто пpопускает эту стpоку, т.е. получается тот же комментаpий, только экономится запуск целой команды. Для командных файлов с многими REM'ами можно добиться некотоpого ускоpения запуска. А '::' - уже установившийся de-facto стандаpт, кpоме того так пpоще набиpать и так гоpаздо кpасивее.

Не знаю, насколько эта фича документированна, но такая штука:
CODE
@echo off
rem комментарий
:: это тоже комментарий
echo тест
Вполне себе работает.

P.S. Годная, хотя и старая, книжка http://msdosworld.ru/stati/stiven_simrin_bibliya_ms-dos (версии 3.30), особенно там интересны всякие описания по физическому устройству дисков и файловой системы. Чего я про неё пишу - там описывается, что до MS-DOS 3.30 не было команды call и прямой вызов одного .BAT файла из другого приводил к невозможности возврата, поэтому приходилось http://msdosworld.ru/stati/stiven_simrin_bibliya_ms-dos/obrashchenie_k_komandnomu_modulyu_bez_ispolzovaniya_komandi_sall.html.

Автор: -=CHE@TER=- Dec 7 2014, 17:20

С некоторого времени трахаюсь (простите за мой французский) с WordPress.
Куча какого-то хренового кода, старого как экскременты мамонта, который никто не хочет исправлять и/или удалять (см. обсуждения по ссылкам).

То, что основательно выжрало мне время:

1) Все значения в $_POST и $_GET экранируются через addslashes(), независимо от настройки get_magic_quotes_gpc() (которая уже давно протухла и не поддерживается). Сделано это для подтирания соплей криворукожопым дебилам пишущим что-то типа:

CODE
$result = $wpdb->get_results('SELECT * FROM users WHERE name='.$_POST['name']);

забы(и)вающим про(на) mysql_real_escape_string() и подобные функции, чтобы исключить SQL-инъекцию. Подробнее: https://wordpress.org/support/topic/wp-automatically-escaping-get-and-post-etc-globals и https://core.trac.wordpress.org/ticket/18322. Так что будьте готовы делать stripslashes(), если вам нужна оригинальная строка.

2) Вторая вещь, с которой вы будет как минимум полчаса сношаться не понимая что к чему - это высота <select> при свойстве multiple (множественный выбор). Она не меняется. Чтобы поменялась нужно в файле со стилями указать (auto - чтобы свойство size работало, указывающее сколько строк должно быть видно):
CODE
#wpcontent select[multiple] {
  height: auto;
}

Когда вы допрёте до этого, то наверняка узнаете что разработчики https://core.trac.wordpress.org/ticket/10331, но им глубоко пофигу на ваши проблемы и потраченное время.

3) Не WP, но рядом - плагин AutoComplete для jQuery (jQuery Autocomplete plugin 1.2.2).
Вызываем так - всё работает:
CODE
jQuery('input[name="city"]').autocomplete('cityfind.php', {
  selectFirst: true, // выбирать первый найденный
  minChars: 4 // как минимум 4 символа должны быть введены
});

Всё отлично и зашибись. Теперь, чтобы не гонять зря запросы на сервер, делаем локальный массив (citylist) и выгружаем все нужные города туда:
CODE
// короткий пример списка
// на деле там 3K городов
// в отдельном файле citylist.js
var citylist = [
"Detroit",
"Los Angeles",
"New York"
];
<...>
jQuery('input[name="city"]').autocomplete(citylist, {
  selectFirst: true,
  minChars: 4
});

И оно нихрена не работает! Мне пришлось дебажить этот плагин прямо по шагам, залезая в каждый метод, чтобы, в итоге таки найти что нужно обязательно добавить свойство:
CODE
matchContains: true,

чтобы эта херня заработала (вставить сюда тонны ненависти)! Потому что:
CODE
function request(term, success, failure) {
  if (!options.matchCase) term = term.toLowerCase();
  // term - введённая строка в нижнем регистре
  var data = cache.load(term);
  if (data) {
    <...> // нам нужно сюда!
  }
}
// смотрим cache.load():
function load(q) {
  // здесь всё нормально
  if (!options.cacheLength || !length) return null;
  // options.url == null (т.к. мы массив передали)
  // но options.matchContains == false (по умолчанию)!
  if (!options.url && options.matchContains) {
    <...> // нам нужно сюда!
  }
  return(null);
}

И гугление в Интернете вам не поможет - я там даже упоминания об этом не нашёл!

Автор: Siberian GRemlin Apr 1 2015, 13:11

У кого-нибудь есть идеи как быстро читать строки из текстового файла размером боле 1 гига (более 90,5 миллионов строк)?

Чтение строками (предполагалось, что там все строки равной длины) заняло 1,5 часа, это при том, что я весь файл загрузил в память. Но оказалось, что в них есть ошибки и длина части строк разнится. Не представляю сколько уйдёт времени на чтение посимвольно.

Есть мысль загружать часть файла в память и оттуда читать в какой-нибудь TStringList. Интересно, будет ли быстрее.

P. S. Отвечу на возможный вопрос. Это выгрузка БД в CSV, нужно сравнить свои значения со всеми значениями в ней.

Автор: -=CHE@TER=- Apr 2 2015, 11:31

Конкретизируй задачу (опиши коротко и понятно на каком-нибудь похожем примере).
Потому что если тебе просто нужно сравнить свои значения с теми что там, то всю БД в память загружать и не нужно - прочитал одну строку, распарсил, нашёл нужные занчения, сравнил, затем прочитал следующую и так далее. При таком подходе тебе нафиг грузить файл в память не упёрлось, что быстрее и проще.
Я бы читал файл блоками:
1) Скажем, известна максимальная длинна строки - пусть будет 500 символов.
2) Читал бы с запасом блок в 512 символов.
3) Парсил бы его и искал конец строки. Скажем, строка бы была длинной в 302 символа.
4) На этом шаге парсил строку и вытаскивал нужные значения.
5) Здесь перемещал бы оставшийся блок в 512-302=210 символов в начало буфера, затем читал бы из файла остаток в 302 байта и переходил бы к пункту 1. Если сделать буфер кольцевой, то даже перемещать ничего не нужно - будет быстрее, но с реализацией замороченнее.
К слову сказать, пункты 2-5 умеет делать буферизованный ввод/вывод Сишных функций https://msdn.microsoft.com/en-US/library/c37dh6kf.aspx - читает строку из файла. В Delphi можно подключить через external библиотеку msvcrt.dll и оттуда эту функцию тягать (ещё fopen() и fclose() понадобятся).
Если же тебе нужно постоянно что-то там сравнивать, то проще и быстрее будет загрузить твой .CSV обратно в какую-нибудь БД и уже с БД и работать.

Вот, держи, можешь замерить скорость - по идее, должно быть быстро. Учти только, что fgets() возвращает строку с символом перевода на конце (если это не последняя строка в файле) и строки там ASCIIZ (с нулём на конце).

CODE
program functest;
{$APPTYPE CONSOLE}

function fopen(filename: pchar; filemode: pchar): pointer;  cdecl; external 'msvcrt.dll';
function fgets(s: pchar; n: integer; fptr: pointer): pchar;  cdecl; external 'msvcrt.dll';
function fclose(fptr: pointer): integer; cdecl; external 'msvcrt.dll';

var
  f: pointer;
  s: array[0..1023] of char;
begin
  // open for (r)ead as (t)ext file = rt
  f:=fopen('functest.dpr', 'rt');
  // file open ok
  if (f <> nil) then
  begin
    // first line
    fgets(s, 1024, f);
    writeln(s);
    // second line
    fgets(s, 1024, f);
    writeln(s);
    // close file
    fclose(f);
  end;
end.

Автор: Siberian GRemlin Apr 3 2015, 10:35

Да, до меня до самого допёрло, что всё сразу в памяти держать не нужно.
Но я немножко по другому сделал. По 10-100 метров (пока не решил сколько лучше брать) гружу, догружаю до конца строки и загоняю в TStringList, и массив сам заполняется, причём достаточно быстро. После сравнения выгружаю и читаю следующий блок.

Как оказалось, длина строки меняется из-за косяка в кодировке: кириллица осталась в юникоде, когда всё остальное — в ANSI. Сообщил об этом, а они сообразить не могут, что я от них хочу.

Можно было сделать это всё через, например, SQL, но заказчик не осилит его использование.

Автор: -=CHE@TER=- Apr 3 2015, 15:30

QUOTE(Siberian GRemlin @ Apr 3 2015, 10:35) *
Можно было сделать это всё через, например, SQL, но заказчик не осилит его использование.
Тогда, как вариант, можно программу сделать, в которую вводишь сервер, порт, логин, пароль и имя базы данных, а она уже сама нужные запросы делает и выводит статистику.

Автор: Siberian GRemlin Apr 6 2015, 09:48

Доступа нет к серверу и не будет. Есть только выгрузка.

Автор: -=CHE@TER=- Jul 8 2015, 15:40

Переписывал на сях пару дней назад bnkextr, а также wav2ogg, причём в последней программе нужно было делить размеры блоков на 255. Если помните, то в OggS размер блока записывается как байт N, а затем N байтов сумма которых даёт полный размер блока.
И тут я вспомнил подсмотренный (уже не помню где) прикольный способ подсчёта.

В общем, если нам нужно разделить блок данных размером datasize байт на пакеты по packsize байт, то, обычно, делают как-то так ("/" = "div"; "%" = "mod" в Pascal/Delphi):

CODE
packets = datasize / packsize;
if (datasize % packsize) {
  packets++;
}

Собственно проверка нам нужна когда остаётся остаток от деления - т.е. какой-то пакет (обычно последний) будет неполный. Занимает всё это безобразие аж 4 строчки и смотрится уродско. А можно записать в одну и красиво:

CODE
packets = (datasize + (packsize - 1)) / packsize;

Если кто не понял что тут делается, то задам вопрос: когда у нас появляется остаток?
Когда datasize целиком не делится на packsize.
Рассмотрим на примере: пусть packsize = 5.
Тогда остаток в datasize может быть 1, 2, 3 или 4.
Если теперь увеличить datasize на packsize - 1 (т.е. на 5 - 1 = 4), то любое из этих чисел даст число большее, либо равное packsize, но (это важно!) всегда будет меньше packsize * 2 - т.е. при делении на packsize мы получим тот самый дополнительный пакет, причём он будет ровно один.
Способ красив, но не лишён недостатков, а именно переполнения (когда сумма делимого вылезает за размеры базового типа), так что использовать его следует в тех случаях, когда гарантировано сложение переполнения дать не может.
У варианта выше с двумя делениями такого недостатка нет, так что имейте это ввиду.

Автор: -=CHE@TER=- Aug 24 2015, 22:08

В последнее время больше пишу на сях, чем на Pascal/Delphi.
Когда-то, помню, тратил кучу времени на перевод примеров или кода с C/C++ (так как большинство всего этого было именно на упомянутых языках) на Pascal/Delphi. Дело в том, что си позволяет сделать много, коротко и просто. Чего Delphi/Pascal не может.
Давайте рассмотрим примеры.
Есть некий буфер в памяти который надо как-то изменить для дальнейшего использования.
Скажем, нужно занулить первый байт.
Pascal:

CODE
Var p: ^Byte; { PByte }
...
p^:=0;

C:
CODE
BYTE *p;
...
*p = 0; /* или p[0] = 0; */

Теперь усложним пример - нам нужно также занулить 4 и 8 байты.
C:
CODE
/* от нуля, так что 4-1 и 8-1 */
p[0] = 0;
p[3] = 0;
p[7] = 0;

А вот с Pascal начинается геморрой:
CODE
p^:=0;
inc(p, 3);
p^:=0;
{ 7-3 = 4 }
inc(p, 4);
p^:=0;
{ вернули назад, иначе при освобождении памяти отхватим ошибку! }
dec(p, 7);

Можно, конечно, попытаться извернуться со своим типом-массивом:
CODE
Type
  TList = Array[0..0] Of Byte;
  PList = ^TList;
...
Var p: ^Byte;
l: plist;
...
l:=Pointer(p);
l[0]:=0;
{ а тут нас ждёт "Error: Constant expression violates subrange bounds",
ибо массив был от 0 до 0, поэтому придётся заводить переменную типа integer,
скажем n, и писать n:=3; l[n]:=0; - тогда будет работать }
l[3]:=0;

Видите, сколько лишнего кода приходится писать? Конечно, можно объявить нашу перменную как Array of Byte, но большинство библиотек и программ работают именно с обычными указателями. Можно ещё все извращения с inc()/dec() обернуть в функцию. Можно ещё много чего. А можно писать на сях, что удобнее, быстрее, короче и проще. Я уж не говорю про #define'ы - эта вещь, вообще, помогает сократить код и выкинуть всё лишнее. А ещё есть "?". Просто посмотрите:
C:
CODE
#define MY_MAX(a,b) (((a) > (b)) ? (a) : (b))
...
m = MY_MAX(x,y);
/* заменить значение 123 на 0, а иначе оставить как есть */
m = (m == 123) ? 0 : m;

Pascal:
CODE
{ здесь для той же операции придётся создавать функцию;
более того - если нужно будет сравнить два значения другого типа (не integer),
то придётся делать ещё одну, отдельную функцию на каждый такой тип данных!
т.к. #define в сях это макрос, то он таких недостатков лишён }
function my_max(a, b: integer);
begin
  if (a > b) then result:=a else result:=b;
end;
...
m:=my_max(x,y);
{ здесь только полностью писать if() }
if (m = 123) then m:=0;

Или, вот, скажем, в сях любое условие считается ложным, только если оно ноль/NULL - иначе истина.
C:
CODE
BYTE *p;
int i;
bool b;
...
p = malloc(10);
/* если p не NULL; Pascal: if (p <> nil) then ... */
if (p) {
  ...
  free(p);
}
/* если b - истина (false = 0); Pascal: if b then ...
режим {$X+} в Pascal должен быть включён, иначе нужно будет
писать полностью if (b = True) then ... */
if (b) {
  ...
}
/* если i что угодно, но не ноль;
можно также записать как if (i != 0) { ... }
Pascal: if (i <> 0) then ... */
if (i) {
  ...
}
/* если i ноль (!i - "не i");
можно также записать как if (i == 0) { ... }
Pascal: if (i = 0) then ... */
if (!i) { ... }

В общем, код сокращается от лишних и ненужных операций, становится проще и понятней для чтения, а главное делать теперь это не в пример быстрей. При написании же кода также тратится куда меньше сил и времени. Pascal/Delphi хорош для изучения языка и начинающих программистов - он приучает к порядку и аккуратности. Но потом многие из особенностей языка начинают откровенно мешать. И вот тогда, в сравнении, приходит осознание насколько же си крут.
Конечно, там нет строк (в смысле, они есть, но ASCIIZ, так что будет много головняка с выделением памяти, склейкой и прочей фигнёй, которая может привести к buffer overflow если не доглядеть; вообще говоря, в C++ есть класс CString (си строчки) и прочие такие костыли), а .DLL файлы, если загружаются не динамически через LoadLibrary(), а статически, то подключаются через адовый геморрой посредством .DEF файлов (в Delphi же это делается одной строчкой...), и ещё много чего, так что будьте готовы к тому, что то что за вас раньше делал компилятор, теперь придётся делать самостоятельно, но язык того стоит.
Так что если у кого-то будут вопросы по языку - задавайте, могу помочь разобраться.

Автор: Siberian GRemlin Aug 26 2015, 06:02

QUOTE(-=CHE@TER=- @ Aug 25 2015, 05:08) *
Или, вот, скажем, в сях любое условие считается ложным, только если оно ноль/NULL - иначе истина.

Здесь есть обратная сторона: попытка изменить строку на «0» (строка с одним символом нуля) приведёт к нежелательным результатам. Иногда можно встретить ошибку в современном ПО, связанную с отсутствием проверки данного случая. В чистом «Си» может такого конечно и нет, но вот в его производных, полагаю, д. б., иначе откуда бы взялась ошибка.

Автор: -=CHE@TER=- Aug 26 2015, 09:56

QUOTE(Siberian GRemlin @ Aug 26 2015, 06:02) *
Здесь есть обратная сторона: попытка изменить строку на «0» (строка с одним символом нуля) приведёт к нежелательным результатам. Иногда можно встретить ошибку в современном ПО, связанную с отсутствием проверки данного случая. В чистом «Си» может такого конечно и нет, но вот в его производных, полагаю, д. б., иначе откуда бы взялась ошибка.
Если честно, то не совсем понял о чём идёт речь.
Смотри, есть такая штука:
CODE
char sc[20]; // строчка с 20-тью символами
char *sp; // указатель на строчку
...
/* char всегда один байт, но если изменить на WCHAR,
то здесь будет выделено нужное количество символов (а не байт)
т.к. умножается на размер одного символа,
такой способ выделения памяти универсальный */
sp = malloc(20 * sizeof(sp[0]));

/* по твоему вопросу - обе строчки ниже делают строку пустой - ""
т.к. это ASCIIZ строка - вполне легальная операция, так и делают */
sc[0] = 0;
sp[0] = 0;

// можно и так написать:
*sc = 0;
*sp = 0;
free(sp);

// а вот это - зануление указателя
sp = NULL;

/* которое будет, если ошибиться, аналогично вот такому
(потому что NULL и 0 для компилятора одно и тоже),
т.е. хотели обнулить строчку, а обнулили указатель не неё
(это ещё и утечка памяти) */
sp = 0; // это ошибка! - либо sp = NULL, либо *sp = 0;

/* проверка существования и не пустоты строки
sp - тоже самое, что и (sp != NULL) - т.е. память выделена
*sp - это тоже самое, что и (sp[0] != 0) - т.е. строка не начинается с нуля,
иными словми - не пустая (там есть хотя бы один символ)
также хочу заметить, что sp и *sp нельзя менять местами,
парой сообщений выше объяснял почему */

if (sp && *sp) {
  ...
}

А так-то, наверное, в любом языке есть конструкции, которые потенциально могут привести к ошибке.
Ну, некоторые языки, конечно, в этом смысле достаточно сильно ограничены, но вот, скажем, от ошибок в логике работы - даже самый жёсткий компилятор не защитит, ибо он не знает, а чего, собственно, хотел человек.

Автор: Siberian GRemlin Oct 12 2015, 17:20

Столкнулся с проблемой: «сишные» консольные приложения все 16-иразрядные, а у меня сейчас основная ОС это XP x64, которая не умеет их запустить. Есть ли «мини» компилятор, чтобы перегнать их в 32? Исходники в наличии.

Автор: -=CHE@TER=- Oct 12 2015, 18:57

QUOTE(Siberian GRemlin @ Oct 12 2015, 17:20) *
Столкнулся с проблемой: «сишные» консольные приложения все 16-иразрядные, а у меня сейчас основная ОС это XP x64, которая не умеет их запустить. Есть ли «мини» компилятор, чтобы перегнать их в 32? Исходники в наличии.
Самое простое - бери старенький Visual Studio C++ 6.0 и компилируй под ним - плюс в том, что получившиеся программы будут работать под любым Windows и пользователям не нужно будет специальные библиотеки для запуска устанавливать. Или MinGW попробуй, но там есть свои заморочки (DEV-CPP можешь взять - там урезанная версия, безо всяких специфических вещей, однако для компиляции большинства кода её хватает - только бери старую версию, иначе придётся с программами ещё чемодан .dll файлов тащить - в современной версии MinGW свои зависимости).
Но главное, с типами разберись, потому что если у тебя DOS-приложение, но 32-х битное (защищённый режим), то всё нормально. А если реальный режим или 16-битный защищённый, то у тебя при компиляции под 32-бита int "2 байта" превратится в int "4 байта", ибо по спецификации языка Си:
short <= int <= long
Т.е. для 16 имеем:
2 <= 2 <= 4 [16 bits]
а для 32 уже вот так:
2 <= 4 <= 4 [32 bits]
и для 64 битного компилятора:
2 <= 4 <= 8 [64 bits]
Вроде бы так. Поэтому такая простая функция:
CODE
int x;
  ...
  // читаем в x 1 элемент размером 2 байта
  fread(&x, 2, 1, fl);
  ...
  // читаем в buff 1 элеменет размером x байт
  fread(buff, x, 1, fl);

Может уронить программу если "x" не обнуляли, ибо локальные переменные в функциях НЕ инициализируются нулями. Т.е. у тебя там мусор был, например, 0x12345678 и ты из файла прочитал два байта (размер структуры, например, число 8 там) и получил, например, 0x12340008 - т.е. старшие два байта не обнулены и получилось офигенно большое число. И вот такой код:
CODE
  // читаем в x 1 элемент размером sizeof(x) байт
  fread(&x, sizeof(x), 1, fl);

Хотя и полностью заполняет все байты в x, но тоже уронит программу, потому что раньше sizeof(x) == 2, а теперь 4 - т.е. ты читаешь два лишних байта из файла.
Короче, скомпилировать программу под 32 бита - это только половина проблемы - нужно ещё проверить логику работы.

Автор: -=CHE@TER=- Nov 3 2015, 20:24

Добавил http://www.forum.ctpax-x.org/index.php?s=&showtopic=51&view=findpost&p=3519 некоторые изменения:
- теперь неважно в каком подкаталоге проекта лежит скрипт - "%~d0%~p0" решает эту проблему;
- добавлен ключ "-X" (X - заглавная), который выкидывает дополнительные атрибуты файлов при добавлении - без этого ключа получившиеся архивы даже на максимальном сжатии (-9) всегда будут жирнее тех .ZIP, что создаёт на максимальном же сжатии WinRAR.

Автор: -=CHE@TER=- Mar 3 2016, 19:44

QUOTE(-=CHE@TER=- @ Jul 23 2011, 13:01) *
Поставил я тут новый colorer для FAR и моя тачка захлебнулась обрабатывая те PHP скрипты, которые раньше без проблем редактировались. Так что откатился я на colorer version 'freeCraze' за 08.03.2000.
Работает резко, но там косяки были с раскраской у PHP - пришлось вручную дожимать до рабочего состояния.
Продолжая тему colorer'а.
У меня стоят несколько плагинов для FAR Manager, в том числе и Calculator v3.0 by Cail Lomecb © 1998-2001 (calc). Калькулятор этот при всех достоинствах адово часто падает, если в выражении встречается буква "e" (экспоненциальная запись числа). Сам я этой функцией редко пользуюсь, а вот если в шестнадцатеричном числе встретилась такая буква, а после неё ещё и цифра - всё, труба.
Что заметил, если запустить FAR и сразу запустить калькулятор вбив туда "9e999", то результат будет "1.#INF" (как и должно). А если сначала открыть любой файл на редактирование (перед этим colorer выводит окно "just wait - i'm realoading..."), то после этого упомянутое выше выражение будет приводить к обвалу калькулятора. Иногда, кстати, и colorer обваливается тоже, но при каких условиях сказать затрудняюсь, ибо это редко происходит.
В общем, если кто-то знает как это исправить - с удовольствием выслушаю, ибо задолбало.
Исходные коды есть и к colorer и к calc, но они оба на C++, с классами, XML и прочим хламом, так что вызывают зубную боль. Я бегло их просмотрел - явно ошибки не видно и нужно серьёзно дебажиться, либо вникать в код (а его там тонны), чтобы её найти.

Автор: Grom PE Mar 3 2016, 21:38

QUOTE(-=CHE@TER=- @ Mar 4 2016, 03:44) *
Калькулятор этот при всех достоинствах адово часто падает [...]
В общем, если кто-то знает как это исправить - с удовольствием выслушаю, ибо задолбало.
А почему бы не заменить этот calc скриптами на Lua? Я в восторге от скриптов.
Сейчас взял и опубликовал свои наработки: http://grompe.org.ru/#far_scripts.zip

После установки скриптов можно будет делать так:
CODE
0xFF + 14
(Ctrl+Enter на строке)
CODE
0xFF + 14=269

Или если нравится, F11 → "Execute Lua on Selection" → "0xFF + 14" введёт в редактор 269.

Автор: -=CHE@TER=- Mar 4 2016, 19:24

QUOTE(Grom PE @ Mar 3 2016, 21:38) *
А почему бы не заменить этот calc скриптами на Lua?
У меня Far 1.70 build 2087 - к нему плагин для Lua подойдёт? Вчера попробовал тройку (Far 3) - адово медленно работает, даже если отключить в нём практически всё, то та же смена каталога происходит заметно тормозней. По той же причине не стал ставить новый colorer - с ним работать ну просто невозможно, настолько всё медленно ворочается.

QUOTE(Grom PE @ Mar 3 2016, 21:38) *
После установки скриптов можно будет делать так:
CODE
0xFF + 14
(Ctrl+Enter на строке)
CODE
0xFF + 14=269

Или если нравится, F11 → "Execute Lua on Selection" → "0xFF + 14" введёт в редактор 269.
А если я структуру файла по F3 изучаю? Мне через Ctrl+Tab переключаться на текстовый файл, чтобы там посчитать, затем назад вернуться и перейти, скажем, по нужному смещению? Много лишних телодвижений получается. К тому же, ещё со времён DOS Navigator, привык я к калькулятору по Ctrl+F6. Кстати говоря, поглядел код калькулятора в исходных кодах DN - в принципе, вытащить его оттуда можно... Но заморачиваться не особо хочется. Разве что копипастнуть в проект на Delphi, но тут опять могут кривые файлы FAR SDK для Delphi/Pascal подвести, плюс Delphi антивирусы не любят. Разве что плагин для себя собрать.

QUOTE(Grom PE @ Mar 3 2016, 21:38) *
Я в восторге от скриптов. Сейчас взял и опубликовал свои наработки: http://grompe.org.ru/#far_scripts.zip
А я никогда не сомневался, что ты крут!

Добавлено:
Таки решил проблему с калькулятором - http://www.forum.ctpax-x.org/index.php?showtopic=236&view=findpost&p=4038.

Автор: Grom PE Mar 6 2016, 06:47

QUOTE(-=CHE@TER=- @ Mar 5 2016, 03:24) *
У меня Far 1.70 build 2087 - к нему плагин для Lua подойдёт? Вчера попробовал тройку (Far 3) - адово медленно работает
По-моему нет, скрипты на Lua — это фича именно Far 3. Странно, а я не замечал тормозов с ним. Colorer чуть медленней, это да, я замечал.
QUOTE
А если я структуру файла по F3 изучаю?
Можно и для просмотрщика макрос написать под удобное сочетание.
QUOTE
[...] и перейти, скажем, по нужному смещению?
Можно именно для этого сочинить макрос, будет типа как в Hiew. Кстати, а ты не используешь Hiew для просмотра бинарных файлов что ли?
QUOTE
А я никогда не сомневался, что ты крут!
Хех, спасибо =)

Автор: -=CHE@TER=- Mar 6 2016, 07:12

QUOTE(Grom PE @ Mar 6 2016, 06:47) *
По-моему нет, скрипты на Lua — это фича именно Far 3. Странно, а я не замечал тормозов с ним. Colorer чуть медленней, это да, я замечал.
У меня компьютер старый. Уже где-то на форуме обсужалось, для работы хватает, обновлять не хочу.

QUOTE(Grom PE @ Mar 6 2016, 06:47) *
Можно и для просмотрщика макрос написать под удобное сочетание.
Не, ты не понял - в просмотрщике же нет курсора. Как ты там будешь что-то писать или указывать?

QUOTE(Grom PE @ Mar 6 2016, 06:47) *
Можно именно для этого сочинить макрос, будет типа как в Hiew.
Если у тебя все файлы будут одного формата - тогда да. А так-то структура файлов разная.

QUOTE(Grom PE @ Mar 6 2016, 06:47) *
Кстати, а ты не используешь Hiew для просмотра бинарных файлов что ли?
Не, HIEW'ом-то я как раз пользуюсь, но только для редактирования файлов, а если быстро нужно что-то посмотреть/проверить, то из FAR никуда не ухожу - открыл файл на просмотр и какой-нибудь текстовичок для записи, а дальше Ctrl+Tab или через F12 переключаюсь.

Автор: Grom PE Mar 6 2016, 12:39

QUOTE(-=CHE@TER=- @ Mar 6 2016, 15:12) *
У меня компьютер старый. Уже где-то на форуме обсужалось, для работы хватает, обновлять не хочу.
Комп ещё более тормозной, чем мой постоянно перегретый нетбук? Сожалею.
Насколько я помню, я перешёл на Far 3 из-за поддержки UTF-8.
QUOTE
Не, ты не понял - в просмотрщике же нет курсора. Как ты там будешь что-то писать или указывать?
Так можно диалогами обойтись, окошком ввода да окошком сообщения результата. Или объединить их.
QUOTE
Если у тебя все файлы будут одного формата - тогда да. А так-то структура файлов разная.
Под какой надо формат, под тот и написать (отредактировать). Я ради интереса написал скрипт для прыжков по 4-байтному смещению в просмотрщике:
Viewer_HexJump.lua
CODE
local prevpos = nil;

local function hexjump()
  local start = viewer.GetInfo(nil).FilePos;
  prevpos = start;
  -- Get first bytes of the hex view
  local str = ReadConsoleOutputCharacter(12, 1, 24);
  local strb = str:gsub("[^0-9A-F]", ""):sub(1, 8); -- 4 bytes
  local offset = 0;
  for i = #strb - 1, 1, -2 do
    offset = offset * 256 + tonumber(strb:sub(i, i+1), 16);
  end;
  viewer.Select(nil, offset, 4);
end;

local function prehexjump()
  far.Timer(0, function(self) self:Close(); hexjump() end);
end;

local function backjump()
  if prevpos ~= nil then
    viewer.Select(nil, prevpos, 4);
  end;
end;

MenuItem {
  menu = "Plugins";
  area = "Viewer";
  guid = "CFBC01BA-342A-409F-A2B7-DAF874A84680";
  text = "HexJump to 4-byte offset";
  action = function(target, item)
    local F = far.Flags;
    local mode = Viewer.State;
    local needsrefresh = false;
    if band(mode, 0x20) == 0 then
      viewer.SetMode(nil, {Type=F.VSMT_VIEWMODE, iParam=F.VMT_HEX});
      needsrefresh = true;
    end;
    if needsrefresh then
      mf.postmacro(prehexjump);
    else
      mf.postmacro(hexjump);
    end;
  end;
}

Macro {
  description="Go to previous position before HexJump";
  area="Viewer";
  key="BS";
  flags="";
  action = function()
    mf.postmacro(backjump);
  end;
}

QUOTE
Не, HIEW'ом-то я как раз пользуюсь, но только для редактирования файлов, а если быстро нужно что-то посмотреть/проверить, то из FAR никуда не ухожу - открыл файл на просмотр и какой-нибудь текстовичок для записи, а дальше Ctrl+Tab или через F12 переключаюсь.
Я повесил HIEW на Alt+F4 (хех), и использую для просмотра тоже, удобно же. А сейчас только что обнаружил фичу "console detach key" в Far'е, а то всё время грустил, что для заметок нужно второе окно Far'а открывать.

Автор: -=CHE@TER=- Mar 7 2016, 04:38

QUOTE(Grom PE @ Mar 6 2016, 12:39) *
Комп ещё более тормозной, чем мой постоянно перегретый нетбук? Сожалею.
Не стоит, но всё равно спасибо за сочувствие. (*улыбается*)

QUOTE(Grom PE @ Mar 6 2016, 12:39) *
Насколько я помню, я перешёл на Far 3 из-за поддержки UTF-8.
Да, но я стараюсь с файлами в этой кодировке не связываться.

QUOTE(Grom PE @ Mar 6 2016, 12:39) *
Я повесил HIEW на Alt+F4 (хех), и использую для просмотра тоже, удобно же. А сейчас только что обнаружил фичу "console detach key" в Far'е, а то всё время грустил, что для заметок нужно второе окно Far'а открывать.
Блин, да ты нереально крут! Действительно http://forum.farmanager.com/viewtopic.php?t=5371 - единственная проблема, она расположение окна не воспринимает (приходится снова Alt+F9 два раза жать) и срабатывает в HIEW только с 3-4 нажатия (уж не знаю почему).

Автор: -=CHE@TER=- Apr 13 2016, 11:58

Давно уже Delphi 7 не использую толком, практически полностью на Си перешёл.
А тут понадобилось быстро склепать GUI-программку для поиска эксперементального значения "a" с некоторым диапазоном погрешности "b" в базе данных, где каждое значение из БД на текущем шаге равно "c". Натолкнулся на офигенный косяк:

CODE
program d7double;
{$APPTYPE CONSOLE}
var a, b, c: double;
begin
  a:=56.4;
  b:=0.2;
  c:=56.6;
  if ((c >= (a - b)) and (c <= (a + b))) then writeln('Never get this, because...')
  else writeln('Delphi 7 float-point types bug!!!');
end.

Если подумать, то 56.4 + 0.2 = 56.6, но первый writeln() никогда не выполнится для типов Double, Real и Comp. Причём, первые два типа, на самом деле, один и тот же тип (просто название разное), если переключатель в Real48 не задан. Переписал эту программу на Си (там тип double такой же) и собрал под GCC - всё отлично, первая строка выводится. Более того, если собрать эту программу под http://grompe.org.ru/#file_dcc7hack.rar от Grom PE то условие также срабатывает! Походу это косяк какой-то стандартной библиотеки, которая подключается при компиляции программы в обычном Delphi 7.

Автор: Grom PE Apr 13 2016, 23:40

А что ты хотел? Во второй проверке ты сравниваешь 56.6 и 56.6 после сложения, то есть в зависимости от типа и реализации будет либо 56.600000000000001, либо 56.599999999999999. Косяк здесь — точно сравнивать дробные числа.

Автор: -=CHE@TER=- Apr 14 2016, 14:39

QUOTE(Grom PE @ Apr 13 2016, 23:40) *
Косяк здесь — точно сравнивать дробные числа.
Это я уже понял и, т.к. в БД значения хранились с точностью до двух знаков после запятой, то просто выравниваю также введённые значения, а затем всё это перевожу в целое игнорируя точку (типа умножить на 100), а потом уже сравниваю - и быстрее, и проблем меньше.

QUOTE(Grom PE @ Apr 13 2016, 23:40) *
А что ты хотел?
А я бы хотел чтобы Delphi 7 без хаков (типа DCC32HACK) работал также как и GCC и прочие компиляторы. Потому что это - косяк, как ни крути.

Автор: -=CHE@TER=- May 27 2016, 08:29

Зацените прикол.
Ковырялся тут в чужих исходных кодах и обнаружил такую штуку:

CODE
do {
  if (! условие1) { break; }
  // кусок_кода_1
  if (! условие2) { break; }
  // кусок_кода_2
  if (! условие3) { break; }
  // кусок_кода_3
  ...
} while(0);

Думаю: что за фигня, зачем делать цикл, с постусловием, который всего один раз выполнится, ибо условие ложно (0) - можно же, вообще, без цикла обойтись. Тогда зачем? И тут, поглядев на кучу break до меня дошло, что автору кода тупо было лень его структурировать. Выглядеть код, по нормальному, должен был вот так:

CODE
if (условие1) {
  // кусок_кода_1
  if (условие2) {
    // кусок_кода_2
    if (условие3) {
      // кусок_кода_3
      ...
    }
  }
}

Ибо break выкидывает из цикла на следующую, после его конца, инструкцию.
С одной стороны такой финт ушами не запрещён. С другой - непонятно как к нему отнесутся всякие навороченные оптимизирующие компиляторы, которые короткие циклы разворачивают для линейного исполнения, а также всякие статические анализаторы кода.
Вроде, с одной стороны метки и goto не используются, так что и поругать не за что, а с другой стороны способ вызывает сомнения и прочие смешанные чувства относительно того стоит ли его применять.

Автор: Grom PE May 29 2016, 07:32

Поначалу непривычно, но потом понимаешь, что это та же функция с рядом проверок и после каждой return. Наверняка и следует это оформить в качестве отдельной функции.

Автор: -=CHE@TER=- May 29 2016, 13:09

QUOTE(Grom PE @ May 29 2016, 07:32) *
Поначалу непривычно, но потом понимаешь, что это та же функция с рядом проверок и после каждой return. Наверняка и следует это оформить в качестве отдельной функции.
Вот я тоже так думал. Но если у тебя много разных переменных проверяется, то представь какого размера будет у этой функции список входных параметров? (*улыбается*)
Хотя такие вещи, скорее всего, говорят о том, что сама архитектура программы, в целом, мягко говоря не фонтан. (*улыбается*) Но если работаешь с чужим кодом, то приходится выбирать из меньшего количества костылей, да.

Автор: -=CHE@TER=- Sep 2 2016, 08:22

Надавно выдирал один код из другой программы на asm'е и после компиляции слегка окосел от результата:

CODE
use16; компилируем 16-ти битный код
...
mov bp, sp
...

В той программе, откуда я дёрнул этот кусок, данная инструкция была представлена последовательностью байт:
8B EC
А FASM скопмилировал эту инструкцию в:
89 E5
Погуглив выяснил, что я не один http://www.cyberforum.ru/asm-beginners/thread1520058.html от происходящего. Цитата из той темы (я специально её дополнил и раскрасил, чтобы понятно было что к чему):
QUOTE
10001011 - 11 101 100 (8B - EC) bp<-sp
10001001 - 11 100 101 (89 - E5) sp->bp
Бит направления в первом случае установлен, а во втором нет.
SP = 100
BP = 101
Там же, кстати, в теме есть ссылка на другую тему с примерами - кое-что можно аж 4 разными способами закодировать.

Автор: -=CHE@TER=- Oct 25 2016, 12:40

Это не совсем к программированию, но тем не менее.
Мучил я тут одну линуксовую прогу портированную под Windows, а там всё, что начинается с "-", это ключ командной строки. Поэтому, кстати, в DOS / Windows программах ключи начинаются с "/" - этот символ в имени файлов запрещён. Проблема в чём, вызываю я программу (да, имя входного файла с "-" начинается):
thistool.exe -newfile.ext -opt1 -opt2 -opt3 ...
А в программе позиция аргумента не привязана жёстко и считается что входной файл это всё, что не начинается с символа "-". Поэтому программа ругалась, требуя имени файла и недоумевая по поводу незнакомого ей ключа командной строки. А в другой программе, где я файл использовал, он должен был начинаться с "-". Короче, задолбался я переименовывать туда-сюда и решил эту проблему таким макаром - добавил ссылку на текущий каталог (".\") и оно, наконец-то, прожевало файл:
tool.exe .\-newfile.ext -opt1 -opt2 -opt3 ...
Можно и полный путь указать:
thistool.exe C:\MYFOLDER\-newfile.ext -opt1 -opt2 -opt3 ...

Автор: -=CHE@TER=- Oct 27 2016, 15:59

https://tcrf.net/DynaMike наглядно иллюстрирующий почему если ты не чистишь память сразу после выделения (или не выделяешь с флагом автоматической очистки типа HEAP_ZERO_MEMORY / LMEM_ZEROINIT / GMEM_ZEROINIT и т.п.), то ты - http://v-vissotsky.ru/audio.php?zid=2769.

Автор: -=CHE@TER=- Nov 23 2016, 15:03

QUOTE(-=CHE@TER=- @ Feb 13 2014, 14:49) *
Натолкнулся на то, что в нескольких играх используется LZSS, описанный в моём сообщении выше.
Но, есть одно отличие MS-LZSS (Microsoft LZSS) от классического LZSS (который, похоже, используется во многих играх), без которого на выходе получится не файл, а что попало.
Нашёл ещё одну вариацию - применялась в старых играх от Blizzard:
- Warcraft: Orcs and Humans
- Blackthorne
- Lost Vikings
В ближайшее время распаковщик на сайт добавлю (там нет имён и через флаги у размера / смещения файла определяется сжат он или нет). Кому интересно, описываю изменения в том алгоритме что я приводил ранее.

Вот эти строки:
CODE
FillChar(buff[0], $FFF + 1, 32);
I:=($FFF + 1) - 16;
...
J:=J + ((Len And $F0) ShL 4);
Len:=(Len And $0F) + 3;

Заменяются этими:
CODE
FillChar(buff[0], $FFF + 1, 0);
I:=0;
...
J:=J + ((Len And $0F) ShL 8);
Len:=((Len And $F0) ShR 4) + 3;

И всё.

Автор: -=CHE@TER=- Jan 29 2017, 19:55

https://asm32.info/fossil/repo/asmbb/index
Форум написанный на FASM. Нет, вы не ослышались. Да, это Assembler.
Дизайн дикий, функционал скромный, но идея интересная - наверняка потом в какой-нибудь KolibriOS интегрируют. (*улыбается*)

Автор: Siberian GRemlin Feb 27 2017, 09:33

Установил игру на 64-разрядную систему. Программа устанавливается через 32-разрядную InstallShield 11.0, но каким-то образом пишет данные в область реестра доступную только 64-разрядным приложениям. Например, создаёт ветку, которая в 64-разрядном редакторе реестра доступна, а в 32-разрядном отсутствует.

CODE
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-21-3391727471-1739702130-609907194-1000\Components\66EEC61806F24F94D949A665B121C141


Таким образом, я из своего 32-разрядного приложения не могу получить путь к установленной игре, так как это единственное место в системе, где он указывается. Игра, кстати, 2006 года и 32-разрядная.

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

Добавлено.
Нашёл кривое решение через экспорт ветки в файл, который нужно затем читать.
CODE
REG.EXE export HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-21-3391727471-1739702130-609907194-1000\Components\66EEC61806F24F94D949A665B121C141 test.reg


Добавлено позже.
Похоже, из 32-разрядного приложения вызывается только 32-разрядная версия REG.EXE со всеми вытекающими. Идиотство.

Автор: -=CHE@TER=- Feb 28 2017, 08:16

Переместил твоё сообщение в более подходящую тему.
Как я понял, тебе поможет http://stackoverflow.com/questions/10533421/accessing-64-bit-registry-from-32-bit-application при вызове RegOpenKeyEx().
Обрати внимание, что эта штука не будет работать под Windows 2000.
Ещё снизу см. Note - есть другой способ без флагов, но "it's stable but it should be considered an implementation detail subject to change".
Такие дела.

Автор: -=CHE@TER=- Jul 2 2017, 14:16

Может кому пригодится: http://www.ctpax-x.org/uploads/libunpak.zip (~16 Kb)
Библиотека для Delphi 7, где я собрал бинарники для последних:
- puff() из zlib (распаковывает deflate)
- blast() из zlib (распаковывает PKWARE)
Теперь это библиотека, с небольшой обёрткой, которая позволяет работать обоим алгоритмам с памятью, а не жёстким диском. Плюс к blast() добавил интерфейс, так что он теперь тоже может возвращать необходимый размер буфера для распаковки. Давно хотел такую штуку для Delphi, без всяких зависимостей от "msvcrt.dll" и прочего мусора.
Также в архиве программа-пример и два файла "deflate.dat" и "pkware.dat" на которых можно потренироваться.
Через дефайны: NO_DEFLATE или NO_PKWARE можно убрать ненужные алгоритмы, для уменьшения размера.

Автор: -=CHE@TER=- Dec 11 2017, 12:54

Прошло 10 лет http://www.forum.ctpax-x.org/index.php?s=&showtopic=63&view=findpost&p=1669. Посмотрел я снова Lazarus, последнюю версию - 1.8.0 (5-го декабря, кстати, вышла). Для теста взял один не очень большой и сложный проект на Delphi 7. Сконвертировался он нормально, но поглядев как работает я всё равно продолжаю офигевать от этого ужаса:
- .EXE файлы по прежнему толстые: проект в Delphi 7 был 500 Кб, здесь - 2 Мб, и это с включённой в опциях проекта оптимизацией по размеру и отключённой отладочной информацией (без них будут десятки мегабайт);
- не смотря на то что проект сконвертировался без проблем, в FPC / Lazarus у TJPEGImage нет метода DIBNeeded, на что мне сразу вышибло ошибку - прозреваю, что это не единственная несовместимость;
- контрол TImage так за 10 лет и не починили (долбанный стыд!) - достаточно провести по нему мышкой, чтобы он начал ужасно, чудовищно и вырвиглазно моргать и перерисовываться (хотя в изображение никаких изменений не вносилось);
- все строки в UTF-8 (почему-то ключ в настройках проекта об использовании ANSIString игнорируется), поэтому загружая файлы с русскими строчками в 1251 вы будете видеть "????" вместо них;
- если у вас на компьютере стоял FPC старой версии, то сносите его перед установкой Lazarus, ибо эта скотина вместо установленного с ней 3.0.4, будет подтягивать старый, который уже стоит в системе, в результате вы ничего не сможете скомпилировать (половины юнитов не будет);
- установщик никакой - ставит всё, в том числе компоненты, которые вы никогда не использовали, не используете и использовать не будете, причём вместе с исходными кодами, так что готовьте гигабайт свободного места под всё это барахло (а ещё это всё будет долго устанавливаться), ибо выбрать только то что вам нужно во время установки вы не сможете.
Там ещё всякого по мелочи и хотя есть и положительные моменты, но, в целом, всё по прежнему очень грустно.

Автор: -=CHE@TER=- Dec 25 2017, 17:16

QUOTE(-=CHE@TER=- @ Feb 13 2014, 14:49) *
Натолкнулся на то, что в нескольких играх используется LZSS, описанный в моём сообщении выше.
Но, есть одно отличие MS-LZSS (Microsoft LZSS) от классического LZSS (который, похоже, используется во многих играх), без которого на выходе получится не файл, а что попало.
Не поверите - очень популярный алгоритм. Во многих играх используется.
Выложил сейчас на сайт распаковщик .GPF для Snake Battle / Змеиный бой (Gamos, 1992).

Вот эти строки:
CODE
I:=($FFF + 1) - 16;
...
J:=J + ((Len And $F0) ShL 4);
Len:=(Len And $0F) + 3;

Заменить такими:
CODE
I:=($FFF + 1) - 16 - 2;
...
J:=J + ((Len And $0F) ShL 8);
Len:=((Len And $F0) ShR 4) + 3;

И всё.

Автор: -=CHE@TER=- Feb 9 2018, 09:14

QUOTE(Siberian GRemlin @ Feb 8 2018, 10:27) *
Только одна проблема — не хочется тратить время на вникание в код, если ты его перепишешь на Си или на Паскале с помощью допотопных AssignFile и пр. Я года с 2004 использую потоки после того как увидел, как это делается в одном из исходников основателя extractor.ru
Чтобы не раздувать холивар постараюсь его сократить. У меня к тебе такие вопросы:
1) Обоснуй чем потоки лучше, скажем, CreateFile() + CreateFileMapping()? Ты точно также можешь весь файл в память засунуть и делать с ним что угодно.
2) В чем проблема заменить (автозаменой даже можно), скажем, "BlockRead(fl, buffer, size);" на "stream.Read(buffer, size);"?
А почему ООП вообще и потоки в частности это плохо я сейчас объяснять не буду, дабы не разводить холивар - там на отдельную большую статью хватит. http://blogerator.org/page/oop_why-objects-have-failed - обрати внимание, ООП не последние люди критикуют, хотя некоторые, конечно, личности спорные.

Автор: -=CHE@TER=- Mar 4 2018, 16:12

Интересная статья: http://blogs.phoenix.com/phoenix_technologies_bios/2010/07/bios-undercover-calling-c-from-asm-in-x64.html. Тот случай, когда технологии уже дошли до того, что нельзя вот так просто взять и что-то вызвать из ассемблера, не огребя при этом кучу проблем в ответ. Оптимизация она такая, да.

Автор: -=CHE@TER=- Mar 29 2018, 12:36

Товарищи! Возник интересный вопрос по поводу ключей командной строки.
Смотрите, для ключа, как правило, используется какой-то символ-префикс:
-param
или вот так:
/param
Проблемы тут ровно две:
1) Если использовать '-', тогда встаёт проблема с именем файлы начинающегося с этого символа. Я писал в этой теме уже, что это можно решить коротким именем файла (dir /B /X *.*), но это сработает только в том случае, если файл имеет длинное имя.
2) Если использовать '/' и ты пишешь кроссплатформенную утилиту на сях, то этот символ является вполне себе разрешённым, ибо в иксах вместо '\' для разделения путей используется именно что '/' и путь '/usr/vasia/myfile' - это обычное дело.
А идея использовать запрещённые символы в имена файла в качестве префикса - никуда не годится.
Потому что в Windows они такие: \ / : * ? " < > |
\ и / - используются для указания путей ('/' в Windows fopen и прочие иксовые функции перед использованием разворачивают в '\');
: - используется для указания потока у файла в NTFS дисках (кстати говоря, ещё и не является запрещённым символам в имени файла в иксах);
* и ? - используются для задания маски у имени файлов, а * в иксах и того хуже - если globe не отключён, то вызов "tool *" приведёт к тому, что все файлы в текущем каталоге буду сконвертированнны в аргументы argv (аналог ParamStr в Delphi);
" - используется для экранирования параметров содержащих пробел или символ табуляции;
< и > используются для перенаправления ввода и вывода;
| - используется для конвеера.

Вариантов решения, как я понимаю, немного:
1) Если у программы один обязательный входной параметр имя файла, то жёстко делать его первым, а все остальные необязательные ключи 2, 3 и т.д. так что там можно и '-' использовать и совсем без префикса.
2) Использовать ужасное с двумя минусами '--param' как в некотрых иксовых утилитах.
3) Ещё один линуксовый подход - все параметры должны предворяться ключами, т.е. нельзя написать:
tool input.dat -deflate output.dat
а нужно обязательно писать громоздкое:
tool -input=input.dat -method=deflate -output=output.dat

У кого-нибудь ещё идеи и предложения есть? Может я что-то забыл?

Автор: Grom PE Mar 29 2018, 16:38

Проблемы с файлами, начинающимися с "-" в винде как таковой нет, потому что маски файлов подставляются программой, а не исполняющей средой, и нет опасности, что при вызове команды "myprog *" встанет файл, превратившийся в ключ "уничтожить всё".

Лично я предпочитаю, чтоб ключи начинались с "-". Если очень надо файл "-something.txt", можно его указать программе как "?something.txt*. Проблема тут может быть в том, что в программах, обрабатывающих только один файл, авторы часто забывают сделать раскрытие масок.

А в линуксах для одиночного файла принято использовать stdin/stdout, что тоже обходит проблему с минусовым файлом.

(добавлено:)
А, можно же ещё относительно текущей директории указывать: ".\-something.txt"

Автор: -=CHE@TER=- Mar 30 2018, 16:06

QUOTE(Grom PE @ Mar 29 2018, 16:38) *
Проблемы с файлами, начинающимися с "-" в винде как таковой нет, потому что маски файлов подставляются программой, а не исполняющей средой, и нет опасности, что при вызове команды "myprog *" встанет файл, превратившийся в ключ "уничтожить всё".
Я не помню писал тут уже или нет, опять искать лень, но если ты компилируешь в GCC и не добавил библиотеку CRT_noglob.o в команду сборки, то, например, для каталога C:\Test\ с такими файлами:
something.dat
myprog.exe
readme.txt
вызов программы: "myprog.exe *" автоматически вернёт тебе не
argc = 2
argv[0] = "C:\Test\myprog.exe"
argv[1] = "*"
а вот такую штуку вместо этого:
argc = 4
argv[0] = "C:\Test\myprog.exe"
argv[1] = "something.dat"
argv[2] = "myprog.exe"
argv[3] = "readme.txt"

QUOTE(Grom PE @ Mar 29 2018, 16:38) *
Лично я предпочитаю, чтоб ключи начинались с "-". Если очень надо файл "-something.txt", можно его указать программе как "?something.txt*. Проблема тут может быть в том, что в программах, обрабатывающих только один файл, авторы часто забывают сделать раскрытие масок.
Я не хочу делать обработку маски, потому что, во-первых, тупо лень, во-вторых, если это для какой-нибудь программы типа stuns, то там это бессмысленно, потому что у тебя каждый следующий файл будет затирать выходные файлы от предыдущего. Т.е. нужно будет страдать фигнёй в отдельные каталоги записывать (например образованные от имени входного файла), либо ещё что-то придумывать. Маска больше подходит к программам, где она действительно необходима - JPGStrip например.

QUOTE(Grom PE @ Mar 29 2018, 16:38) *
А в линуксах для одиночного файла принято использовать stdin/stdout, что тоже обходит проблему с минусовым файлом.
На обработку stdin/stdout надо отдельный код писать, в отличие от разворота "*", который принудительно отключать нужно.

QUOTE(Grom PE @ Mar 29 2018, 16:38) *
(добавлено:)
А, можно же ещё относительно текущей директории указывать: ".\-something.txt"
Вот же блин, а я тоже самое сделал, просто искать лень было своё прошлое сообщение, а оно оказалось последним на предыдущей странице. Почему я про длинное имя подумал, вместо этого сам не знаю (к тому же это и не работает - короткое имя тоже будет с "-" начинаться). За 1,5 года нахрен всё забыл. Но всё равно спасибо!

Автор: Grom PE Apr 4 2018, 14:00

QUOTE(-=CHE@TER=- @ Mar 31 2018, 00:06) *
но если ты компилируешь в GCC и не добавил библиотеку CRT_noglob.o в команду сборки, то [...]
вызов программы: "myprog.exe *" автоматически вернёт тебе не argc = 2
Ну охренеть теперь, по умолчанию идти в чужой монастырь со своим уставом — это уж слишком. Впрочем, никогда не любил GCC.

QUOTE(-=CHE@TER=- @ Mar 31 2018, 00:06) *
Я не хочу делать обработку маски, потому что [...] у тебя каждый следующий файл будет затирать выходные файлы от предыдущего.
Поддержка маски ≠ поддержка нескольких входных файлов. Но если лень, то хозяин — барин.

Автор: -=CHE@TER=- Apr 6 2018, 11:34

QUOTE(Grom PE @ Apr 4 2018, 14:00) *
Ну охренеть теперь, по умолчанию идти в чужой монастырь со своим уставом — это уж слишком. Впрочем, никогда не любил GCC.
Ты чего такой злой? (*улыбается*) GCC - нативный компилятор для иксов. Я понимаю что, возможно, ты кроссплатформенностью не особо заморачиваешься, но мне в принципе было интересно обсудить возможное универсальное решение.

QUOTE(Grom PE @ Apr 4 2018, 14:00) *
Поддержка маски ≠ поддержка нескольких входных файлов. Но если лень, то хозяин — барин.
На мой взгляд поддержка нескольких входных файлов ещё более уродская, чем маска (особенно, если их много). И это всё равно не снимает вопроса о выходных файлах.

Автор: Grom PE Apr 6 2018, 14:21

QUOTE(-=CHE@TER=- @ Apr 6 2018, 19:34) *
Я понимаю что, возможно, ты кроссплатформенностью не особо заморачиваешься, но мне в принципе было интересно обсудить возможное универсальное решение.
Кроссплатформенность — это хорошо и правильно. Но в случае с GCC, как и с другими особенно юниксово-идеологическими вещами, под кроссплатформенностью почему-то понимается не подстроиться под новую исполняемую среду, а притащить пол-юникса за собой, частью чего вот такая внезапная подстановка "*" и является. И такой подход вызывает отвращение.

Автор: -=CHE@TER=- Jun 9 2018, 15:49

Мне сегодня "не баг, а фича" стандартных библиотечных сишных функций мозг выедала.

Суть:
1) Открываем файл на чтение-запись: fopen("filename.ext", "rb+")
2) Читаем что-то, меняем и тут же пишем назад:
fread(buf, sz, 1, fl); // прочитали
fseek(buf, -sz, SEEK_CUR); // вернулись назад
fwrite(buf, sz, 1, fl); // записали
3) Повторяем в цикле пару раз.
4) Получаем на выходе хрень вместо файла.

Чтобы это пофиксить, нужно либо:
а) В п.1 сразу после открытия файла делать setbuf(fl, NULL) чтоб отключить буферизацию.
б) Либо в п.2 после записи добавить, например:
fseek(fl, 0, SEEK_CUR); // сбрасываем внутренние флаги состояния

Потому что:

QUOTE
When the "r+", "w+", or "a+" access type is specified, both reading and writing are enabled (the file is said to be open for "update"). However, when you switch from reading to writing, the input operation must encounter an EOF marker. If there is no EOF, you must use an intervening call to a file positioning function. The file positioning functions are fsetpos, fseek, and rewind. When you switch from writing to reading, you must use an intervening call to either fflush or to a file positioning function.
(с) https://msdn.microsoft.com/en-us/library/yeby3zcb.aspx

В Borland Turbo C под DOS та же фигня, кстати. А я уже было думал, что баг в винде нашёл, но это, оказывается, дофига не интуитивные сишные функции. А что, в структуре FILE нельзя было поле предусмотреть для сохранения состояния последней операции и сбрасывать внутренние флаги, если операции различаются? Риторический вопрос.

Автор: -=CHE@TER=- Nov 27 2018, 12:28

Я тут просто охреневаю с происходящего. Короче, понадобилось мне скомпилять на сях одну либу, которая использовала функции из Windows DDK. Мой обычный способ для этого не канал, ибо ключ -ansi вызывал в одном из заголовочных файлов DDK кучу ошибок, так что я быстро накатал .BAT файл:
gcc -Wall -pedantic test.c
где упомянутые два ключа должны были спасти меня от всевозможных глупых ошибок, которые я мог в процессе написания сделать. Да?.. Хрен там! Как бы не так! Когда нашёл ошибку, то вытащил функцию в отдельный файл и скопилировал уже с ключами:
gcc -Wall -pedantic -ansi test.c
но предупреждение всё равно не появилось!
Скомпилировал тестовый файл обычным способом - и вот оно, пожалуйста.
Начал убирать ключи по одному, пока не выяснилось что минимальный набор ключей, для того чтобы предупреждение наконец-то появилось, должен быть таким:
gcc -Wall -Os test.c
Кто не знает или забыл:
-Wall - показывать предупреждение о любых некорректно написанных вещаях, даже самых незначительных
-pedantic - ваще-ваще показывать сообщения обо всём и придираться ко всему
-ansi - использовать стандарт C89 для компиляции (более жёсткий, чем остальные)
-Os - оптимизировать генерируемый код по размеру
Как оптимизация по размеру выкидывает предупреждение, которое должен был кидать компилятор?! Почему оно, вообще, там? Причём это предупреждение выводится на любом уровне оптимизации, кроме нулевого -O0 (без оптимизации). Заглядываем в https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html:

QUOTE
-O0
Reduce compilation time and make debugging produce the expected results. This is the default.
Понятно что по умолчанию и уменьшает время компиляции, но, блин, я же не хочу время компиляции уменьшить, я явно указал все ключи какие были, чтобы мне ошибки выводились! Я специально это указал - при чём тут оптимизация?
Можете со мной поспорить, но не инициализируемое значение возвращаемое из функции - это ошибка которую должен компилятор ловить, а не оптимизатор. И уж тем более эта ошибка обязана ловиться, если включены режимы -Wall или -pedantic - т.е. когда ты явно сказал, что тебе пофиг на скорость компиляции, ты хочешь чтобы проверялось всё.

Кому интересно, код тестовой программы с комментариями ниже.
CODE
/*

MinGW GCC 3.2

no warnings, no errors:
> gcc -Wall -ansi -pedantic test.c

only this one shows the warning:
> gcc -Wall -ansi -pedantic -Os test.c

> test.c: In function `func':
> test.c:15: warning: `b' might be used uninitialized in this function

*/

#include <stdio.h>
#include <time.h>

int func(int a) {
time_t b;
/*  b = 0;*/ /* accidentally forgotten line of code */
  if ((a >= 0) && (a <= 59)) {
    b = time(NULL);
  }
  return(b);
}

int main(void) {
  printf("%d", func(-1));
  return(0);
}

Автор: -=CHE@TER=- Jan 29 2020, 14:30

Кто знает, что будет выведено на экран в этих двух программах?

CODE
#include <stdio.h>
#include <string.h>

int main(void) {
char s[10];
int i;
  strcpy(s, "123456789");
  for (i = 0; i < strlen(s); i++) {
    printf("%c", s[i]);
    s[5] = 0;
  }
  return(0);
}

CODE
program test;
{$APPTYPE CONSOLE}
var
  s: string;
  i: integer;
begin
  s:='123456789';
  for i:=1 to Length(s) do
  begin
    Write(s[i]);
    SetLength(s, 5);
  end;
end.


Код можно переписать короче:
CODE
#include <stdio.h>

int main(void) {
int i, j;
  j = 9;
  for (i = 1; i <= j; i++) {
    printf("%c", '0' + i);
    j = 5;
  }
  return(j); /* do not optimize j = 5 */
}

CODE
program test;
{$APPTYPE CONSOLE}
var i, j: integer;
begin
  j:=9;
  for i:=1 to j do
  begin
    Write(Chr(Ord('0') + i));
    j:=5;
  end;
  Halt(j); (* remove warning about unused j:=5 *)
end.


Узнать ответ
В первом случае будет:
GCC: 12345
Delphi: 12345.789 (вместо точки там будет символ $00)
Во втором:
GCC: 12345
Delphi: 123456789
Потому что Си каждый раз вычисляет условие, а Delphi делает это только один раз в начале, поэтому любые изменения условия внутри цикла for не завершат его. Из чего следует, что если в сях условие выполнения не меняется в цикле, то нужно его посчитать заранее, чтобы оно не рассчитывалось на каждом шаге (лишние операции тормозят выполнение). По сути код прохода по строке:
for (i = 0; i < strlen(s); i++) ...
Не оптимальный, поэтому его и переделывают в такой:
for (i = 0; s[i]; i++) ...
Или такой, если порядковый номер символа не нужен:
for (d = s; *d; d++) ...
Или даже такой, если ни номер, ни сам указатель на начало не нужен:
for (; *s; s++) ...
Си достаточно гибкий язык по сравнению с Delphi, но и следить за подобными вещами там нужно внимательно.
Всё описанное выше, конечно же, справделиво только для циклов for, т.к. в while и repeat (Delphi) условие выполнение вычисляется всегда независимо от языка и компилятора.

Автор: -=CHE@TER=- Jun 23 2020, 13:36

Пацаны! Глядите, чего я в одной книжке 1999 года по Pascal нашёл (в Delphi, во всяком случае 7-ом, тоже работает):

CODE
program test;
{$APPTYPE CONSOLE}
var s: string(.4.);
begin
  s:='TEST';
  s(.1.):='W';
  writeln(s);
  readln;
end.
Кто знает, что будет выведено без компиляции?
Ну и, собственно, подсказка:
(. = [
.) = ]
Я знал, что в Pascal / Delphi, есть два способа многострочных комментариев:
{ ... } - обычный
(* ... *) - дополнительный
Второй способ пригодится когда нужно закомментарить большой кусок кода, где уже есть обычный. Хотя, это можно было бы сделать и так:
{$IFDEF NEVER} ... {$ENDIF}
Но я не знаю с какой версии в Pascal появились директивы IFDEF и ENDIF, так что, возможно, на каком-то этапе это было оправдано, особенно с учётом того, что в Pascal изначально не было строчных комментариев // как в Delphi, поэтому закомментаривать куски кода с уже какими-то комментариями внутри было проблематично.
Но вот хоть ты тресни я никак не могу понять зачем мог понадобиться второй способ указания индекса у массивов.
Наверняка причины тоже какие-нибудь исторические, но чем они обоснованы были придумать не смог. Если кто знает - напишите, ибо я понятия не имею как этот способ указания индекса гуглить.
Предположу, что, возможно, в первых версиях языка или на старых клавиатурах не было квадратных скобок, а как-то отличать аргументы функций, типа work(1), от аргументов массива, типа list(.1.), надо было.

Автор: Siberian GRemlin Jul 2 2020, 18:29

В игре выводится количество денег с разделением порядков запятыми. В ЕХЕ упоминается формат вывода

CODE
%0lld
CODE
%03lld
Как я понимаю, это оно. Есть ли формат вывода с разделением порядков пробелами?

Нужно, чтобы было 1,234,567, а стало 1 234 567.

Автор: -=CHE@TER=- Jul 2 2020, 19:03

Это слегка не так работает.
%d - вывести digit
%ld - вывести long digit (для архитектур, где размер int больше long, например int 16, а long 32)
%lld - вывести long long digit (как правило int 64)

%0lld - тоже самое что и %lld (символ один и не указан размер)
%03lld - тоже самое, что %03d (выравнять тремя нулями слева, если число меньше), но для int64

CODE
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

int main(void) {
ULARGE_INTEGER x;
  x.QuadPart = 1;
  printf("%0lld\n", x.QuadPart); // будет просто 1
  printf("%03lld\n", x.QuadPart); // будет 001
  return(0);
}

В сях нет стандартных функций для разделения тысяч, чтобы из 1234567 сделать 1,234,567 или 1 234 567. Боюсь тебе придётся вручную искать код который это делает. Попробуй поискать по работе со строками и символом 0x2C (запятая). Ставлю на то, что как раз твои строки и работают с числами - смотри где они используются.
"%03lld" нужно чтобы у тебя числа менее тысячи добивались нулями: 3007 => 3,007, а не 3,7.
"%0lld" - это остаток в левой части, в примере 3007 - это будет тройка, т.к. её добивать нулями не нужно.

Автор: Siberian GRemlin Feb 17 2021, 13:57

Нашёл. Число выводится несколькими строками, затем склеивается со вставкой запятой в одну строку и выводится на экран. Почему-то я сначала думал, что это стандартная процедура вывода, и поленился сразу в IDA поискать.

Помню, что десятичный разделитель можно было выводить и запятой, и точкой. Но после школы я этим никогда не пользовался.

Автор: -=CHE@TER=- Feb 17 2021, 18:27

QUOTE(Siberian GRemlin @ Feb 17 2021, 13:57) *
Помню, что десятичный разделитель можно было выводить и запятой, и точкой. Но после школы я этим никогда не пользовался.
В Delphi есть глобальная переменная DecimalSeparator, которой можно присвоить '.' или ',' в зависимости от того что тебе в качестве разделителя нужно. Если не ошибаюсь, это влияет на функции типа StrToFloat(), FloatToStr() и прочие. В сях, конечно, ничего подобного нет.

Автор: Siberian GRemlin Feb 26 2021, 05:30

Рано я радовался. Хоть я и заменил удачно запятую на пробел, и в игре это стало отображаться, но оказалось, что игра в основной части вылетает, так как эта переменная с запятой, видимо, используется не только там.

Кто-нибудь может глянуть код на асме функции склейки этих «%03lld» с запятой и убрать из неё прибавление запятой?

Игра Win64. Работу оплачу.

Автор: -=CHE@TER=- Feb 26 2021, 11:23

QUOTE(Siberian GRemlin @ Feb 26 2021, 05:30) *
Рано я радовался. Хоть я и заменил удачно запятую на пробел, и в игре это стало отображаться, но оказалось, что игра в основной части вылетает, так как эта переменная с запятой, видимо, используется не только там.

Кто-нибудь может глянуть код на асме функции склейки этих «%03lld» с запятой и убрать из неё прибавление запятой?

Игра Win64. Работу оплачу.
Вышли мне на почту исполняемый файл игры, я его могу в статике посмотреть. И ещё приложи к нему:
fc /b old.exe new.exe >file.txt
Чтобы было понятно что ты там и где менял (меня интересует только место где ты пробел на запятую заменил).

Автор: -=CHE@TER=- Apr 2 2022, 14:13

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

1) Программы работающие корректно только с английскими буквами в пути и именах файлов.
Вернее сказать, работают они корректно только с цифрами "0".."9", английскими буквами "A..Z" (маленькими и большими) и символом подчерк "_". Ещё можно использовать минус-тире "-", тильду "~" и много чего ещё, но не стоит, потому что, как минимум, с тире есть проблемы из-за того что некоторые не особо умные программы, сами парсят командную строку, причём криво, и видя где-то "-" считают что это начало ключа командной строки. Что до не английских букв (русских, немецких и других языков) - так у них в ANSI кодировке код символа более 127, что такие программы считают за мусорные и обрубают строку по первому такому символу.

2) Программы работающие корректно только с именами без пробелов (подмножество предыдущих).
Аналогичная предыдущей ситуация, когда кривой парсер командной строки считает пробел как разделитель аргументов. Поэтому исключайте из пути пробелы в имени каталогов и файлов.

3) Программы Windows работающие с ограничением в MAX_PATH (260) символов.
Причём, я не помню, вроде бы, MAX_PATH - это включая завершающий ноль, так что, на самом деле, есть только 259 символов. Суть такова: если путь до программы или каталога более MAX_PATH символов, то программа может либо вылететь с ошибкой, либо не запуститься вообще. Чтобы было понятно в чём проблема - большинство функций Windows работающих с файлами и каталогами, типа получения полного пути текущего каталога, принимают два аргумента: буфер и его размер в символах. Можно сделать путь длиннее MAX_PATH, но тогда нужно сперва вызывать функцию чтобы узнать сколько символов нужно, затем выделять память, ещё раз вызывать чтобы уже прочитать в буфер, а после использования этот буфер удалить, чтобы память не текла. Поэтому в 95% случаев (а то и в 99%) разработчики создают на стеке статический буфер в MAX_PATH символов и его используют. При этом некоторые функции, типа GetModuleFileName() (см. документацию в MSDN и пометки о её работе в Windows XP и более младших системах) даже не возвращают нужного размера буфера, так что их приходится вызывать увеличивая размер буфера "на глаз", пока не останется неиспользуемое место в конце.

4) Программы для DOS запущенные в Windows XP / Me / 98 - ограничение в 64 символа.
Аналогичная предыдущей ситуация. В 64 символа, вроде бы, входит также и нулевой, так что есть только 63 для пути с именем файла. Тут, правда, всё во много раз хуже из-за того, что в функциях DOS передавался только буфер, без его размера. При этом Windows пишет туда путь даже если он более 64 символов, что, как правило, приводит к разрушению стека и вылету программы с фатальной ошибкой.

5) Ограничение на имя каталога и файла для DOS в 8.3 (8 символов на имя, точка и ещё 3 на расширение).
Иными словами такой путь:
C:\Program Files\My Files\New File List.txt
Превратится во что-то типа такого для DOS приложения:
C:\PROGRA~1\MYFILE~1\NEWFIL~1.TXT
А если сделать, например, вот такой путь:
C:\PROGRAMS\MY_FILES\FILELIST.NEW
То он будет одинаковай при его указании как в DOS, так и в Windows и проблем не создаст.
А ещё обратите внимание, что в DOS используются только заглавные английские буквы в именах файлов, а также там почему-то, в отличие от Windows, дополнительно запрещены символы "[" и "]" в имени.

Большинство из описанных выше проблем решаются вот таким простым пакетным файлом "RUNSHORT.BAT":

CODE
@echo off
subst Z: .
Z:
program.exe
subst Z: /d

Виртуальный диск Z: можно заменить на любой другой, как и имя запускаемой программы "program.exe". Обратите внимание, что при создании виртуального диска (первый subst) в конце стоит точка - указатель на текущий каталог (откуда был запущен .BAT файл). Для использования нужно положить "RUNSHORT.BAT" в каталог программы рядом с "program.exe" и запустить.

И последнее - не забывайте, что русское (в общем случае не английское или с произвольными символами) имя пользователя в системе тоже может вызвать проблемы (https://www.extractor.ru/ipb/index.php?showtopic=1435&p=10391), т.к. оно входит в часть пути до временного каталога %TEMP% и %TMP%.

Автор: -=CHE@TER=- Oct 1 2022, 16:13

Обсуждали когда-то давно в этой теме код и я такой спрашиваю:

QUOTE(-=CHE@TER=- @ Jul 10 2007, 12:51) *
3) MUTEX обязательно должен называться '851137EC-3D96-4EA6-817B-30969CCF477B' или можно своё имя сунуть?..
На что мне отвечают:
QUOTE(Xplorer @ Jul 10 2007, 13:54) *
Можно и своё.
Но никто не объяснил, почему это хитровыделанное имя лучше.
Дело в том, что GUID - это всегда уникальное имя.
Если, конечно, оно не написано абы как, а создано специальной программой, по специальному алгоритму.
У кого стоит Microsoft Visual Studio 6 (1998), то там в утилитах нужная программа точно есть - называется "Create GUID":
C:\Program Files\Microsoft Visual Studio\Common\Tools\GUIDGEN.EXE
Кому лень искать и ставить, то есть аналоги в Интернете (первое что вышло в Google за запросу "create unique guid online"):
- https://www.guidgen.com/ - утверждается, что делает это также, как и Microsoft
- https://www.guidgenerator.com/
Наверное именно поэтому в том же Far Manager последних версий (в смысле, в версии 3.x точно, может и в 2.x - не проверял) для идентификации плагинов, а также других вещей (типа диалоговых окон) используются именно GUID'ы.
Кому интересно к чему приводит попытка назвать что-то простым именем: https://devblogs.microsoft.com/oldnewthing/20141001-00/?p=43943
В статье, правда, всё несколько проще, т.к. имя для CreateEvent() можно, вообще, не задавать (указать как NULL), если планируется работать только внутри одного процесса и есть другие способы (помимо имени) для передачи дескриптора между разными частями программы.

Автор: Siberian GRemlin Aug 6 2023, 11:17

Привет.
Кто-нибудь знает, в каком виде нужно пихать буфер в Zcrc32? https://github.com/ashumkin/delphi-zlib/tree/master

Автор: -=CHE@TER=- Aug 6 2023, 17:40

А с чем там проблема?

P.S. Извини, что на почту не отвечал - очень не до того было.

CODE
{ test.dpr }
uses ZLibEx, SysUtils;
{$APPTYPE CONSOLE}
var
  fl: File;
   p: pointer;
   x: longint;
   s: AnsiString;
begin
  AssignFile(fl, 'test.dpr');
  Reset(fl, 1);
  x:=FileSize(fl);
  GetMem(p, x);
  BlockRead(fl, p^, x);
  s:='test';
  WriteLn(
    'D87F7E0C = ',
     IntToHex(
       ZCrc32(0, s[1], Length(s)),
     8)
  );
  WriteLn(
    '???????? = ',
    IntToHex(
      ZCrc32(0, p^, x),
    8)
  );
  FreeMem(p, x);
  CloseFile(fl);
end.

Автор: Siberian GRemlin Aug 7 2023, 06:34

Я как всегда поторопился. Вчера хотел прогу дописать, голова уже не варила. А с утра увидел, что забыл уже ненужную строчку удалить.

Автор: -=CHE@TER=- Aug 9 2023, 09:01

Ковырял я недавно ресурсы одной игры, а они зашифрованы.
Вот код расшифровки:

CODE
mov ecx, _data; pointer to the data buffer
mov esi, _size; size of the data buffer
@uncrypt:
mov al, [ecx]
mov dl, al
not dl
xor dl, al
and dl, 55h
not al
xor dl, al
mov [ecx], dl
inc ecx
dec esi
jnz @uncrypt

Что можно переписать так (внутренняя часть цикла - расшифровка байта):
CODE
// C
uint8_t data; // BYTE data;
...
data = (((~data) ^ data) & 0x55) ^ (~data);

// Pascal
var data: byte;
...
data := (((not data) xor data) and $55) xor (not data);

И я бы так и оставил, но чем больше я смотрел на это выражение, тем больше у меня возникало ощущение, что я смотрю на какую-то фигню.
А давайте распишем логические операции (напомню, что мы работаем в пределах байта):
Операция "not data" для байта эквивалентна "data xor 0xFF", отсюда:
(not data) xor data => (data xor 0xFF) xor data => data xor 0xFF xor data
Так как "data xor data" это ноль, то получаем "0 xor 0xFF", что даёт просто 0xFF и далее:
0xFF and 0x55 - так и останется 0x55 из чего получается:
0x55 xor (not data)
Опять заменяем "not data" на "data xor 0xFF":
0x55 xor data xor 0xFF
0xFF xor 0x55 = 0xAA
Итого всё выражение:
CODE
data := (((not data) xor data) and $55) xor (not data); // Pascal

Сворачивается просто до:
CODE
data := data xor 0xAA; // Pascal

Кстати, если выражение:
CODE
data = (((~data) ^ data) & 0x55) ^ (~data); // C

Скомпилировать компилятором GCC (версия 3.2 за 2002-08-17) с оптимизацией по размеру, то он в ассемблерном коде всё и свернёт до "data ^= 0xAA".
Игра была написана на Microsoft Visual C++ 5.0 (1999) под Windows. Может быть, конечно, тот VC5 ещё не умел оптимизировать такие выражение или не был включён режим оптимизации (он замедляет компиляцию, а в те времена компьютеры были не шибко быстрые) при сборке финального исполняемого файла.

Но в любом случае, это не отменяет того факта, что тот кто писал этот код не понимал как работает битовая логика и что вся эта замудрённая дурь вырождается тупо в константу. Иными словами, можно было легко по'xor'ить ресурсы игры байтом от 0x00 до 0xFF и сохранить в отдельные файлы (например, DATA.###, где ### - номер байта, которым xor'илось), после чего просмотреть их (всего-то 256 штук) и найти расшифрованный.

Что-то похожее я прочитал в 2011 году на одном зарубежном сайте, правда про другую игру:
QUOTE
2009/06/02 / exvcpak
I kept looking at the scramble algorithm trying to figure out why it does several operations that result in constant values ... finally I concluded that whoever wrote the code is just an idiot.
© http://asmodean.reverse.net/pages/news.html

До кучи напомню про это: http://www.forum.ctpax-x.org/index.php?showtopic=273&view=findpost&p=4521 - там аж целый главный погром-мист и разработчик, ООП, абстракция на абстракции сидит и абстракцией погоняет, а основ и базы даже не языка программирования, а обычной работы с данными, не знает и не понимает.

Автор: -=CHE@TER=- Aug 14 2023, 18:28

Оказывается 16-ти битные (два байта) изображения могут быть не только в форматах:
RGB_565 (RRRRRGGG GGGBBBBB)
или:
RGBA_4444 (RRRRGGGG BBBBAAAA)
но и в совсем уж наркоманском (пришлось документацию на поддерживаемые Android форматы искать):
RGBA_5551 (RRRRRGGG GGBBBBBA)
Когда при декодировании в формате RGB_565 красный стоит как надо, но идёт слишком много синего и он очень резко обрываясь переходит в зелёный на границах, где должен быть плавный переход - знайте, что, на самом деле, это RGBA_5551.

Автор: Siberian GRemlin Aug 22 2023, 11:16

QUOTE(-=CHE@TER=- @ Aug 15 2023, 01:28) *
RGBA_5551 (RRRRRGGG GGBBBBBA)
У «Dune 2000» тоже было 15 бит на цвет и 1 бит на альфу.

Автор: -=CHE@TER=- Aug 22 2023, 15:25

Это-то понятно. Но я никогда до этого не видел, чтобы альфа была последим битом - обычно он первый.

Автор: -=CHE@TER=- Dec 16 2023, 09:35

Спросили меня как-то о том, как я пишу код.

CODE
// example 1
if (condition) {
  code();
}

// example 2
if (condition)
{
  code();
}

Я ответил, что первый вариант. Объясняю почему на конкретном примере забытой или случайно поставленной не там ";" в коде.
CODE
// example 1
if (argc != 2) {;
  return(1);
}

// example 2
if (argc != 2);
{
  return(1);
}

Первому примеру люто пофиг, а вот второй пример из-за не там оставленной ";" вырождается в:
CODE
if (argc != 2) {}
return(1);

Иными словами код условия начинает выполняться всегда независимо от значения условия.

Такая же штука и в Delphi:
CODE
Program Test;
{$APPTYPE CONSOLE}
Begin
  If (ParamCount = 1) Then; // note ";" here
  Begin
    WriteLn('two');
  End;
  WriteLn('one');
End.

Я теперь понимаю, почему в своих программах товарищ jTommy писал вот так:
CODE
if (condition) then begin
  code();
end;

В отличие от C выглядит не очень из-за того что тут слова begin-end, да ещё и с разным количеством букв, вместо фигурных скобок, зато поставленная в конце строки ";" ни на что не повлияет (можете проверить).