Модераторы: Snowy, bartram, MetalFan, bems, Poseidon, Riply

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Как недопустить запуск второй копии программы, Нашел решение, чем оно плохое? 
V
    Опции темы
Демо
Дата 9.3.2006, 09:43 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1278
Регистрация: 3.11.2005

Репутация: 6
Всего: 50



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

Спасибо.


--------------------
    
PM MAIL ICQ Skype   Вверх
Girder
Дата 9.3.2006, 09:54 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Лентяй 2
***


Профиль
Группа: Участник Клуба
Сообщений: 1993
Регистрация: 12.5.2004

Репутация: 25
Всего: 155



Демо, раз не пользуемся поиском... и не видем что данная тема разжеванна и на Vingrad-е(и не однакратно!)... то какой смысл оставлять ссылку... на конкурирующий ресурс? Реклама?

Без "правильного и корректного" решения остануться только те, кто поленился сделать пару кликов... - енто раз! А второе... автор темы спрашивает не решение... а о правильности уже имеющегося решения!

PS: Тема закрыта!

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

Это сообщение отредактировал(а) Girder - 9.3.2006, 13:25


--------------------
Как слышим, так и пишим.
Истина где-то там...
PM   Вверх
Alex
Дата 9.3.2006, 13:31 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 4147
Регистрация: 25.3.2002
Где: Москва

Репутация: 6
Всего: 162



Тема действительно не однократно, обсуждалась как у нас так и на многих форумах, сказано по ней много, примеров реализации не меньше, какие-то из них перестали работать в Win NT, какие-то продолжают работать по сей день, многие из них не универсальны, срабатывают не всегда или по просту не экономичны по отношению к ресурсам с точки зрения программирования под Delphi. Хотя кажется, что тут сложного, взять и где-то запомнить уникальный идентификатор приложения и при старте проверять, то место куда мы его запоминаем, если там есть данные, то значит приложение уже запущено. На помощь в этом деле к нам приходят возможность хранения файла данных в памяти процесса.
Вот модуль, который сделает все, что нужно:
Код

unit Not2Run;

interface

uses
    Windows
  , SysUtils
  , Forms
  ;

procedure CheckOneInstance(const ApplicationName: String);
 // Проверяет что программа с идентификатором ApplicationName уже запущена.
 // Если это так, то предыдущий экземпляр "выталкивается" на поверхность и
 // выполняется Halt(1)

type
{ WIN32 Helper Classes }

{ tHandledObject }

  tHandledObject = class(tObject)
  protected
    fName  : String;
    fHandle: tHandle;
    fCreated: Boolean;
    procedure SetHandle(const aName :String; aHandle: tHandle);
    procedure ErrorCreate;
  public
    destructor Destroy; override;
    property Name: string read fName;
    property Handle: tHandle read fHandle;
    property Created: Boolean read fCreated;
  end;

{ tSharedMem }

  tSharedMem = class(tHandledObject)
  private
    fSize: Integer;
    fDataPtr: Pointer;
  public
    constructor Create(const aName: string; aSize: Integer);
    destructor Destroy; override;
    property Size: Integer read fSize;
    property DataPtr: Pointer read fDataPtr;
  end;

type
  eSharedResources = class(Exception);

implementation

{ tHandledObject }

destructor tHandledObject.Destroy;
begin
  if fHandle <> 0 then CloseHandle(fHandle);
end;

procedure tHandledObject.ErrorCreate;
begin
  raise eSharedResources.Create(Format('Ошибка создания %s(%s):'^M^J'%s',[ClassName,fName,SysErrorMessage(GetLastError)]));
end;

procedure tHandledObject.SetHandle(const aName :String; aHandle: tHandle);
begin
  fName:= aName;
  if aHandle = 0 then ErrorCreate;
  fHandle:= aHandle;
  fCreated:= GetLastError = 0;
end;

{ tSharedMem }

constructor tSharedMem.Create(const aName: string; aSize: Integer);
begin
  fSize:= aSize;
  SetHandle(aName,CreateFileMapping($FFFFFFFF, nil, PAGE_READWRITE, 0, aSize, PChar(aName)));
  fDataPtr := MapViewOfFile(fHandle, FILE_MAP_WRITE, 0, 0, aSize);
  if fDataPtr = nil then ErrorCreate;
end;

destructor TSharedMem.Destroy;
begin
  if fDataPtr <> nil then UnmapViewOfFile(fDataPtr);
  inherited Destroy;
end;

var
  FirstInstance :tSharedMem;

procedure CheckOneInstance(const ApplicationName: String);
 // Проверяет что программа с идентификатором ApplicationName уже запущена.
 // Если это так, то предыдущий экземпляр "выталкивается" на поверхность и
 // выполняется Halt(1)
var HPtr: pHandle;
begin
  FirstInstance:= tSharedMem.Create('Alex&Co_FirstInstace_'
       +AnsiUpperCase(StringReplace(ApplicationName,'\','/',[rfReplaceAll])),
                                         SizeOf(Application.Handle));
  HPtr:= pHandle(FirstInstance.DataPtr);
  if HPtr^ = 0  then  // это первый экземпляр программы в памяти?
    HPtr^:= Application.Handle  //да
  else begin  // нет, не первый. Вытащим первый на поверхность
    if IsIconic(HPtr^) then ShowWindow(HPtr^, SW_RESTORE);
    SetForegroundWindow(HPtr^);
    Halt(1); // и отваливаем
  end;
end;

initialization

finalization
  if FirstInstance <> nil then FirstInstance.Free;
end.

Пример использования:
Код

program Project1;

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

{$R *.res}

begin
  // Запущена или нет уже программа
  CheckOneInstance('Name_Program');
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.



Это сообщение отредактировал(а) Alex - 9.3.2006, 13:32


--------------------
Написать можно все - главное четко представлять, что ты хочешь получить в конце. 
PM Skype   Вверх
Демо
Дата 9.3.2006, 13:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1278
Регистрация: 3.11.2005

Репутация: 6
Всего: 50



Цитата(Alex @ 9.3.2006, 13:31 Найти цитируемый пост)
Вот модуль, который сделает все, что нужно:

код Pascal/Delphi


Не совсем всё.

В приведенном классе не реализовано выдвижение уже запущенного приложения на передний план для ОС W2000 и выше.

Код

SetForegroundWindow(HPtr^);


будет работать неправильно.
Ссылка на MSDN: http://msdn.microsoft.com/library/default....roundwindow.asp
Добавлено @ 13:58
Вот код, который выполняет восстановление приложения:

Код

function TForm1.ApplicationMessage(var Message: TMessage): Boolean;
var
  hWnd, hCurWnd, dwThreadID, dwCurThreadID: THandle;
  OldTimeOut: Cardinal;
  AResult: Boolean;
begin
   Result := False;
   if Message.Msg = RestoreOldInstance then
   begin
     Application.Restore;
     hWnd := Application.Handle;
     SystemParametersInfo(SPI_GETFOREGROUNDLOCKTIMEOUT, 0, @OldTimeOut, 0);
     SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, Pointer(0), 0);
     SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE or SWP_NOSIZE);
     hCurWnd := GetForegroundWindow;
     AResult := False;
     while not AResult do
     begin
        dwThreadID := GetCurrentThreadId;
        dwCurThreadID := GetWindowThreadProcessId(hCurWnd);
        AttachThreadInput(dwThreadID, dwCurThreadID, True);
        AResult := SetForegroundWindow(hWnd);
        AttachThreadInput(dwThreadID, dwCurThreadID, False);
     end;
     SetWindowPos(hWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE or SWP_NOSIZE);
     SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, Pointer(OldTimeOut), 0);
   end;
   inherited;
end;


(с) Rouse_

Для этого приложение, которое должно быть восстановлено, должно получить сообщение RestoreOldInstance.
Добавлено @ 14:00
Girder,
Существует ли где-либо на винграде такой код?;)
Добавлено @ 14:00
Или есть другие варианты? С удовольствием посмотрю и попробую

Это сообщение отредактировал(а) Демо - 9.3.2006, 14:02


--------------------
    
PM MAIL ICQ Skype   Вверх
Girder
Дата 9.3.2006, 14:57 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Лентяй 2
***


Профиль
Группа: Участник Клуба
Сообщений: 1993
Регистрация: 12.5.2004

Репутация: 25
Всего: 155



Цитата(Демо @ 9.3.2006, 13:47 Найти цитируемый пост)
Существует ли где-либо на винграде такой код?;)
Ссылку я тебе давал http://forum.vingrad.ru/index.php?showtopi...st&p=450839

Приспособить его... енто дело пяти секунд smile

PS: Причем дата ентого поста(на винграде)... горазда раньше... чем на исходниках. Так что делай выводы smile один уже нашел енти пять секунд smile ... шучю smile


--------------------
Как слышим, так и пишим.
Истина где-то там...
PM   Вверх
Демо
Дата 9.3.2006, 15:19 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1278
Регистрация: 3.11.2005

Репутация: 6
Всего: 50



Girder,
Поискал в форумах, в FAQ по ключевому слову SystemParametersInfo.

ничего не нашел;)

А именно изменения таймаута - один из ключевых моментов.

Цитата(Girder @ 9.3.2006, 14:57 Найти цитируемый пост)
Приспособить его... енто дело пяти секунд

Приспособить не только можно. Но и нужно. smile
Вообще, видимо придется с нуля реализовать - сделать полный пример.



--------------------
    
PM MAIL ICQ Skype   Вверх
Girder
Дата 9.3.2006, 15:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Лентяй 2
***


Профиль
Группа: Участник Клуба
Сообщений: 1993
Регистрация: 12.5.2004

Репутация: 25
Всего: 155



Цитата(Демо @ 9.3.2006, 15:19 Найти цитируемый пост)
А именно изменения таймаута - один из ключевых моментов.
Приведи пример... где без них не будет работать.


--------------------
Как слышим, так и пишим.
Истина где-то там...
PM   Вверх
Демо
Дата 9.3.2006, 15:36 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1278
Регистрация: 3.11.2005

Репутация: 6
Всего: 50



Цитата(Girder @ 9.3.2006, 15:29 Найти цитируемый пост)
Приведи пример... где без них не будет работать.


Да какой тут пример. Попробуй пару вариантов:

1. Сверни приложение, на передний план выдвинь другое. Стартуй второую копию.
2. Не сворачивая приложение все то же самое, что и в п.1.

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

В разных версиях Windows будет отличаться результат.


--------------------
    
PM MAIL ICQ Skype   Вверх
Демо
Дата 9.3.2006, 15:54 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1278
Регистрация: 3.11.2005

Репутация: 6
Всего: 50



Girder,

Для чистоты эксперимента все равно придется реализовать полный цикл :
1. Опреджеление уже запущенной копии программы.
2. Извещение запущенной копии каким-либо образом о том, что сделана попытка запуска еще копии.
3. Поднятие первой копии наверх.


--------------------
    
PM MAIL ICQ Skype   Вверх
Alex
Дата 9.3.2006, 17:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 4147
Регистрация: 25.3.2002
Где: Москва

Репутация: 6
Всего: 162



Вот, модифицировал модуль:
Код

unit Not2Run;

interface

uses
    Windows
  , SysUtils
  , Messages
  , Forms
  ;

procedure CheckOneInstance(const ApplicationName: String);
 // Проверяет что программа с идентификатором ApplicationName уже запущена.
 // Если это так, то предыдущий экземпляр "выталкивается" на поверхность и
 // выполняется Halt(1)

type
{ WIN32 Helper Classes }

{ tHandledObject }

  tHandledObject = class(tObject)
  protected
    fName  : String;
    fHandle: tHandle;
    fCreated: Boolean;
    procedure SetHandle(const aName :String; aHandle: tHandle);
    procedure ErrorCreate;
  public
    destructor Destroy; override;
    property Name: string read fName;
    property Handle: tHandle read fHandle;
    property Created: Boolean read fCreated;
  end;

{ tSharedMem }

  tSharedMem = class(tHandledObject)
  private
    fSize: Integer;
    fDataPtr: Pointer;
  public
    constructor Create(const aName: string; aSize: Integer);
    destructor Destroy; override;
    property Size: Integer read fSize;
    property DataPtr: Pointer read fDataPtr;
  end;

type
  eSharedResources = class(Exception);

implementation

{ tHandledObject }

destructor tHandledObject.Destroy;
begin
  if fHandle <> 0 then CloseHandle(fHandle);
end;

procedure tHandledObject.ErrorCreate;
begin
  raise eSharedResources.Create(Format('Ошибка создания %s(%s):'^M^J'%s',[ClassName,fName,SysErrorMessage(GetLastError)]));
end;

procedure tHandledObject.SetHandle(const aName :String; aHandle: tHandle);
begin
  fName:= aName;
  if aHandle = 0 then ErrorCreate;
  fHandle:= aHandle;
  fCreated:= GetLastError = 0;
end;

{ tSharedMem }

constructor tSharedMem.Create(const aName: string; aSize: Integer);
begin
  fSize:= aSize;
  SetHandle(aName,CreateFileMapping($FFFFFFFF, nil, PAGE_READWRITE, 0, aSize, PChar(aName)));
  fDataPtr := MapViewOfFile(fHandle, FILE_MAP_WRITE, 0, 0, aSize);
  if fDataPtr = nil then ErrorCreate;
end;

destructor TSharedMem.Destroy;
begin
  if fDataPtr <> nil then UnmapViewOfFile(fDataPtr);
  inherited Destroy;
end;

var
  FirstInstance :tSharedMem;

procedure CheckOneInstance(const ApplicationName: String);
 // Проверяет что программа с идентификатором ApplicationName уже запущена.
 // Если это так, то предыдущий экземпляр "выталкивается" на поверхность и
 // выполняется Halt(1)

  function ForceForeground(const Wnd: HWND): boolean;
  var
    ForeThreadID, NewThreadID: DWORD;
  begin
    if GetForegroundWindow <> Wnd then
    begin
      ForeThreadID:= GetWindowThreadProcessId(GetForegroundWindow, nil);
      NewThreadID:= GetWindowThreadProcessId(Wnd, nil);
      if ForeThreadID <> NewThreadID then
      begin
        AttachThreadInput(ForeThreadID, NewThreadID, True);
        Result:= SetForegroundWindow(Wnd);
        AttachThreadInput(ForeThreadID, NewThreadID, False);
        if Result then
          Result:= SetForegroundWindow(Wnd);
      end
      else
        Result:= SetForegroundWindow(Wnd);
    end
    else
      Result:= True;
  end;

var HPtr: pHandle;
begin
  FirstInstance:= tSharedMem.Create('Alex&Co_FirstInstace_'
       +AnsiUpperCase(StringReplace(ApplicationName,'\','/',[rfReplaceAll])),
                                         SizeOf(Application.Handle));
  HPtr:= pHandle(FirstInstance.DataPtr);
  if HPtr^ = 0  then  // это первый экземпляр программы в памяти?
    HPtr^:= Application.Handle  //да
  else begin  // нет, не первый. Вытащим первый на поверхность
    if IsIconic(HPtr^) then SendMessage(HPtr^, WM_SYSCOMMAND, SC_RESTORE, 0);
    ForceForeground(HPtr^);
    Halt(1); // и отваливаем
  end;
end;

initialization

finalization
  if FirstInstance <> nil then FirstInstance.Free;
end.



--------------------
Написать можно все - главное четко представлять, что ты хочешь получить в конце. 
PM Skype   Вверх
Демо
Дата 9.3.2006, 17:41 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1278
Регистрация: 3.11.2005

Репутация: 6
Всего: 50



Alex,

Вроде бы все отлично на W2000 Server!
Надо протестировать на W98, XP SP1, XP SP2 еще.



--------------------
    
PM MAIL ICQ Skype   Вверх
Alex
Дата 9.3.2006, 17:44 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 4147
Регистрация: 25.3.2002
Где: Москва

Репутация: 6
Всего: 162



Я тестировал, на SP2 и 98

Это сообщение отредактировал(а) Alex - 9.3.2006, 18:00


--------------------
Написать можно все - главное четко представлять, что ты хочешь получить в конце. 
PM Skype   Вверх
Петрович
Дата 10.3.2006, 00:40 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Участник Клуба
Сообщений: 1000
Регистрация: 2.12.2003
Где: Москва

Репутация: 15
Всего: 55



Господа.
О чем базар!
Демо, ты первый вариант Alex'а точно проверял? Или только предполагаешь что SetForegroundWindow(HPtr^) будет работать неправильно?

Я вот например таким вот способом пользуюсь уже не первый год. До сих пор проблем не было.

Более того, раз уж отсылаешь к MSDN, то будь добр, поясни тогда мне бестолковому, почему в данном случае, SetForegroundWindow(HPtr^) не должна сработать?
Только подчеркиваю, именно в данном случае, а не в том в котором его используешь ты!

Вот при твоем подходе да, тут мне ясно почему ты не получишь от SetForegroundWindow нужного тебе эффекта.

Короче, хотелось-бы получить толковые разяснения, либо забери свои слова обратно smile .



--------------------
Все знать невозможно, но хочется
PM ICQ   Вверх
Демо
Дата 10.3.2006, 01:33 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1278
Регистрация: 3.11.2005

Репутация: 6
Всего: 50



Цитата(Петрович @ 10.3.2006, 00:40 Найти цитируемый пост)
ты первый вариант Alex'а точно проверял?


Первый - это какой? Дата 9.3.2006, 13:31?

Так мне не нужно его проверять, так как с этим вариантом в свое время повозился сколько-то.
Вот последний работает однозначно.
Только еще бы на XP SP1 проверить.
Добавлено @ 01:35
Цитата(Петрович @ 10.3.2006, 00:40 Найти цитируемый пост)
почему в данном случае, SetForegroundWindow(HPtr^) не должна сработать?


Я же привел ссылку из MSDN, где описана ситуация?

Вот точная цитата из MSDN:

Windows 98, Windows 2000: The system restricts which processes can set the foreground window. A process can set the foreground window only if one of the following conditions is true:

- The process is the foreground process.
- The process was started by the foreground process.
- The process received the last input event.
- There is no foreground process.
- The foreground process is being debugged.
- The foreground is not locked (see LockSetForegroundWindow).
- The foreground lock time-out has expired (see SPI_GETFOREGROUNDLOCKTIMEOUT in SystemParametersInfo).
- Windows 2000: No menus are active.


Цитата(Петрович @ 10.3.2006, 00:40 Найти цитируемый пост)
Только подчеркиваю, именно в данном случае,


Чем данный случай отличается от общего?

Цитата(Петрович @ 10.3.2006, 00:40 Найти цитируемый пост)
Вот при твоем подходе да, тут мне ясно почему ты не получишь от SetForegroundWindow нужного тебе эффекта.


Почему?;)

Это сообщение отредактировал(а) Демо - 10.3.2006, 01:52


--------------------
    
PM MAIL ICQ Skype   Вверх
Петрович
Дата 10.3.2006, 02:33 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Участник Клуба
Сообщений: 1000
Регистрация: 2.12.2003
Где: Москва

Репутация: 15
Всего: 55



Ну вот, ты так и не удосужился разобраться.

Цитата(Демо @ 10.3.2006, 02:33 Найти цитируемый пост)
Первый - это какой? Дата 9.3.2006, 13:31?

Именно он.


Цитата(Демо @ 10.3.2006, 02:33 Найти цитируемый пост)
Так мне не нужно его проверять, так как с этим вариантом в свое время повозился сколько-то.
Вот последний работает однозначно.


Вот именно, что последний, функцианально мало чем отличается от первого. А если быть более точным, то в плане работы SetForegroundWindow(HPtr^) - вообще НИЧЕМ!.
Т.е., первый вариант работает так-же как и второй.


Цитата(Демо @ 10.3.2006, 02:33 Найти цитируемый пост)
Я же привел ссылку из MSDN, где описана ситуация?

Вот точная цитата из MSDN:

Дык читать я и сам умею.

Ты мне докажи что ни одно из преречисленных условий не выполняется для примера Alex'а !

Лично я считаю что для него выполняется условие номер 1. Поэтому, SetForegroundWindow(HPtr^) прекрастно выполнит свое дело.

Цитата(Демо @ 10.3.2006, 02:33 Найти цитируемый пост)
Чем данный случай отличается от общего?

А что в твоем понимания есть "общий" случай? Я лично вижу в данном топике два случая, твой (точнее Rose) и Alex'а. А вот они имеют одно очень существенное в данном случае отличие:
У Alex'а, вызов SetForegroundWindow делается из второй копии приложения, а в тебя из первой. Вот в этом и есть главное отличие!
Именно поэтому, у тебя не выполняется первое условие. Вот и приходится извращаться с AttachThreadInput и SystemParametersInfo, дабы играя на побочных эффектах достич выполнения третьего условия.

В общем, надо с большим вниманием читать приводимые примеры и сопоставлять их с документацией.

Это сообщение отредактировал(а) Петрович - 10.3.2006, 02:55


--------------------
Все знать невозможно, но хочется
PM ICQ   Вверх
Страницы: (3) Все 1 [2] 3 
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Delphi: WinAPI и системное программирование"
Snowybartram
MetalFanbems
PoseidonRrader
Riply

Запрещено:

1. Публиковать ссылки на вскрытые компоненты

2. Обсуждать взлом компонентов и делиться вскрытыми компонентами

  • Литературу по Delphi обсуждаем здесь
  • Действия модераторов можно обсудить здесь
  • С просьбами о написании курсовой, реферата и т.п. обращаться сюда
  • Вопросы по реализации алгоритмов рассматриваются здесь
  • 90% ответов на свои вопросы можно найти в DRKB (Delphi Russian Knowledge Base) - крупнейшем в рунете сборнике материалов по Дельфи
  • 99% ответов по WinAPI можно найти в MSDN Library, оставшиеся 1% здесь

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Snowy, bartram, MetalFan, bems, Poseidon, Rrader, Riply.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Delphi: WinAPI и системное программирование | Следующая тема »


 




[ Время генерации скрипта: 0.0977 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.