На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
! Правила раздела FAQ в группе разделов С++.
1. Раздел FAQ предназначен для публикации готовых статей.
2. Здесь нельзя задавать вопросы, для этого существуют соответствующие разделы:
Чистый С++
Visual C++ / MFC / WTL / WinApi
Borland C++ Builder
COM / DCOM / ActiveX / ATL
Сопутствующие вопросы
3. Внимание, все темы и сообщения в разделе премодерируются. Любое сообщение или тема будут видны остальным участникам только после одобрения модератора.
Модераторы: B.V., Qraizer
Страницы: (2) [1] 2  все  ( Перейти к последнему сообщению )  
> Работа с ресурсами исполняемых файлов , Заготовка в Фак
    Работа с ресурсами исполняемых файлов.

    Содержание:
    • 1) Описание вопроса.
    • 2) Извлечение ресурсов.
    • 3) Загрузка ресурсов.
    • 4) Получение списка ресурсов.
    • 5) Замена ресурсов одного исполняемого файла на ресурсы другого.
    • 6) Замена ресурсов из файлов.
    • 7) Особенность работы с ресурсом Bitmap.
    • 8) Функции для работы с ресурсами.
    • 9) Ссылки.
    • 10) Примеры программ.

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

    2. Извлечение ресурсов:
    Q: Как достать файл из ресурсов другого приложения?
    A: Делается это достаточно просто, рассмотрим следующий пример:

    Данное консольное приложение работает вместе с файлом “simple.dll” (далее “Исполняемым файлом”), который на момент запуска должен находиться в той же директории. Исполняемый файл имеет ресурс IDR_JPEG1 (простой jpg файл).
    В файле resource.h:
    ExpandedWrap disabled
      #define IDR_JPEG1 1001

    В файле ресурсов simple.rc:
    ExpandedWrap disabled
      /////////////////////////////////////////////////////////////////////////////
      //
      // JPEGs
      //
      IDR_JPEGS1              JPEGs   DISCARDABLE     "Image1.jpg"

    Как добавлять ресурсы в проект я думаю, вы сможете найти сами, поэтому этого я объяснять не стану. Зная всё вышеописанное, и имея скомпилированный файл simple.dll с указанным ресурсом, мы можем извлечь его при помощи простой программы:
    ExpandedWrap disabled
      // Хидеры программы
      #include <windows.h>
      #include <iostream.h>
       
      // 1001 - число идентификатора в исполняемом файле (как и в resource.h у проекта
      // исполняемого файла simple.dll)
      #define IDR_JPEG1 1001
       
      // Ф-ция для извлечения ресурса
      bool ExtractMyJpeg()
      {
       
      // Инициализируем переменные
      HRSRC hRes = 0;
      HGLOBAL hData = 0;
      LPVOID pData;
       
      // Загружаем исполняемый файл (в данном случае dll)
      HMODULE hModule = LoadLibrary("simple.dll");
      // Если не удалось загрузить исполняемый файл, то выходим
      if(hModule == NULL) return false;
       
      // Находим ресурс в исполняемом файле, указав идентификатор и тип ресурса
      // (в примере это "JPEGs"),
      hRes = FindResource(hModule, MAKEINTRESOURCE(IDR_JPEG1), "JPEGs");
      // Если ресурс не найден, то выходим
      if(hRes == NULL)
      {
      // Освобождаем исполняемый файл
      FreeLibrary(hModule);
      return false;
      }
       
      // Получаем размер ресурса для того, чтобы сохранить его в файл
      DWORD dwSize = SizeofResource(hModule,hRes);
      // Если не смогли получить размер, то выходим
      if(dwSize == NULL)
      {
      // Освобождаем исполняемый файл
      FreeLibrary(hModule);
      return false;
      }
       
      // Загружаем ресурс
      hData = LoadResource(hModule, hRes);
      // Если не смогли загрузить, то выходим
      if(hData == NULL)
      {
      // Освобождаем исполняемый файл
      FreeLibrary(hModule);
      return false;
      }
       
      // Фиксируем ресурс в памяти и получаем указатель на первый байт ресурса
      pData = LockResource(hData);
      // Если не удалось зафиксировать ресурс, то выходим
      if(pData == NULL)
      {
      // Освобождаем исполняемый файл
      FreeLibrary(hModule);
      return false;
      }
       
      // Создаём файл, в который будем писать
      HANDLE File = CreateFile("data.jpg",GENERIC_WRITE,FILE_SHARE_WRITE,0,OPEN_ALWAYS,0,0);
      // Если не удалось создать файл, то выходим
      if(File == INVALID_HANDLE_VALUE)
      {
      // Освобождаем исполняемый файл
      FreeLibrary(hModule);
      return false;
      }
       
      // Переменная для ф-ции записи в файл
      DWORD Written=0;
       
      // Записываем весь ресурс в файл
      if(WriteFile(File,pData,dwSize,&Written,0)==NULL)
      {
      // Освобождаем исполняемый файл
      FreeLibrary(hModule);
      // Закрываем хендл файла
      CloseHandle(File);
      return false;
      }
       
      // Закрываем хендл файла
      CloseHandle(File);
      // Освобождаем исполняемый файл
      FreeLibrary(hModule);
      return true;
      }
       
      int main()
      {
      // Если не удалось извлечь ресурс, то сообщаем об ошибке
      if(!ExtractMyJpeg())
      {
      // Сообщаем об ошибке
      cout << "Error No " << GetLastError() << endl;
      }
      return 0;
      }

    В данном примере использовалась функция FindResource (подробное описание данной ф-ции доступно в (MSDN), последним параметром которой является тип ресурса (в примере это "JPEGs"), хочу отметить, что для стандартных ресурсов типы описаны в MSDN. (Типы ресурсов)

    3. Загрузка ресурсов:
    Q: Как загрузить картинку, иконку или другой ресурс из исполняемого файла?
    A: Делается это стандартными функциями, при помощи которых вы обычно работаете с собственными ресурсами.

    Для загрузки битмапа:
    ExpandedWrap disabled
      HBITMAP LoadExBitmap(int value)
      {
      // Загружаем исполняемый файл
      HMODULE hModule = LoadLibrary("simple.dll");
      // Проверка на валидность
      if (hModule == NULL) return 0;
      // Загружаем битмап стандартным способом, только в качестве
      // первого параметра передаём hModule.
      HBITMAP map = LoadBitmap(hModule, MAKEINTRESOURCE(value));
      // Возвращаем битмапу если она загружена
      if(map!=NULL) return map;
      // Освобождаем исполняемый файл
      FreeLibrary(hModule);
      return NULL;
      }

    Для иконок, String-table’ов, меню, акселераторов и других ресурсов принцип такой же, только ф-ции LoadIcon, LoadString и LoadMenu… Подробное описание использования этих ф-ций вы можете найти в MSDN:
    • LoadBitmap - Ф-ция для загрузки битмапа.
    • LoadIcon - Ф-ция для загрузки иконки.
    • LoadString - Ф-ция для загрузки элемента String-table.
    • LoadMenu - Ф-ция для загрузки меню.
      и др...

    4. Получение списка ресурсов:
    Q: Можно ли получить список ресурсов исполняемого файла? Если да, то как?
    A: Теперь следует рассмотреть случай, когда мы не знаем ни тип, ни идентификатор ресурса. Для этого нужно получить список типов и идентификаторов ресурсов в них, с чем вы и сможете в результате работать.

    Данная программа перечисляет все типы ресурсов, а потом, используя полученный тип, перечисляет все идентификаторы для исполняемого файла. Работает по такому же принципу – исполняемый файл находится в той же директории.
    ExpandedWrap disabled
      // Хидеры программы
      #include <windows.h>
      #include <iostream.h>
       
      // К этой ф-ции будет переходить управление при получении нового
      // идентификатора из списка.
      BOOL EnumNamesFunc(HMODULE hModule, LPCTSTR lpType, LPTSTR lpName, LONG lParam)
      {
      // Следует проверять идентификатор, т.к. он можен представлять
      // собой, как число, так и строку
      if((ULONG)lpName & 0xFFFF0000)
      {
      // Если это строка, то и выводим её как строку
      cout << lpName;
      }else{
      // Иначе выводим как число
      cout << (USHORT)lpName;
      }
      cout << endl;
      return TRUE;
      }
       
      // К этой ф-ции переходит управление при получении нового типа ресурсов
      BOOL EnumTypesFunc(HMODULE hModule,LPTSTR lpType,LONG lParam)
      {
      // Проверка как и в прошлой ф-ции
      if ((ULONG)lpType & 0xFFFF0000)
      {
      cout << "Type: " << lpType << endl;
      }else{
      cout << "Type: " << (USHORT)lpType << endl;
      }
      // Перечисляем идентификаторы по получению нового типа ресурсов
      EnumResourceNames(hModule, lpType, (ENUMRESNAMEPROC)EnumNamesFunc,0);
      return TRUE;
      }
       
      int main()
      {
      // Загружаем исполняемый файл
      HMODULE hModule = LoadLibrary("simple.dll");
      // Проверка на валидность
      if (hModule == NULL) return 0;
       
      // Перечисляем типы ресурсов
      EnumResourceTypes(hModule,(ENUMRESTYPEPROC)EnumTypesFunc,0);
       
      // Освобождаем исполняемый файл
      FreeLibrary(hModule);
      return 0;
      }

    Теперь, рассмотрим редактирование, добавление и удаление ресурсов…

    5. Замена ресурсов одного исполняемого файла на ресурсы другого:
    Q: При попытке перезаписать ресурс стандартными методами для работы с файлами (CreateFile, WriteFile) получил испорченный исполняемый файл. Но ведь редакторы ресурсов как-то это делают! Но вот как?!
    A: Таким способом редактировать ресурсы слишком сложно, и делается это не совсем так, потому, что сами вы не сможете узнать (а если сможете, то скорее пожалеете, что взялись за это дело ;) ), где ресурс начинается и где он заканчивается в исполняемом файле.

    Следующий код показывает, как можно заменить ресурс одного приложения на ресурс другого:
    ExpandedWrap disabled
      BOOL ReplaceIcon(WORD Number)
      {
       
      // Переменные для работы с ресурсами 2-х исполняемых файлов
      HGLOBAL hResLoad;
      HMODULE hModule;
      HRSRC hRes;
      HANDLE hUpdateRes;
      LPVOID lpResLock;
      BOOL result;
       
      // Загружаем исполняемый файл из которого будем копировать ресурс
      hModule = LoadLibrary("first.exe");
      // Если загрузить не удалось, то выходим
      if(hModule == NULL) return FALSE;
       
      // Ищем ресурс в памяти исполняемого файла
      hRes = FindResource(hModule, MAKEINTRESOURCE(1), RT_ICON);
      // Если найти ресурс не удалось, то выходим
      if(hRes == NULL)
      {
      // Освобождаем исполняемый файл
      FreeLibrary(hModule);
      return FALSE;
      }
       
      // Загружаем ресурс
      hResLoad = LoadResource(hModule, hRes);
      // Если загрузить ресурс не удалось, то выходим
      if(hResLoad == NULL)
      {
      // Освобождаем исполняемый файл
      FreeLibrary(hModule);
      return FALSE;
      }
       
      // Фиксируем ресурс
      lpResLock = LockResource(hResLoad);
      // Если не удалось зафиксировать ресурс, то выходим
      if(lpResLock==NULL)
      {
      // Освобождаем исполняемый файл
      FreeLibrary(hModule);
      return FALSE;
      }
       
      // Пытаемся начать обновлять ресурс второго файла
      hUpdateRes = BeginUpdateResource("second.exe", false);
      // Если не удалось начать обновление, то выходим
      if (hUpdateRes == NULL)
      {
      // Освобождаем исполняемый файл
      FreeLibrary(hModule);
      return FALSE;
      }
       
      // Собственно тут и происходит обновление ресурса
      result = UpdateResource(hUpdateRes,RT_ICON,MAKEINTRESOURCE(Number),
      MAKELANGID(LANG_NEUTRAL,SUBLANG_NEUTRAL),lpResLock,SizeofResource(hModule, hRes));
      // Если не удалось обновить ресурс, то выходим
      if (result == FALSE)
      {
      // Освобождаем исполняемый файл
      FreeLibrary(hModule);
      return FALSE;
      }
       
      // Завершаем обновление
      if(EndUpdateResource(hUpdateRes, FALSE) == NULL){
      // Освобождаем исполняемый файл
      FreeLibrary(hModule);
      return FALSE;
      }
       
      // Освобождаем загруженый исполняемый файл
      if(FreeLibrary(hModule) == NULL) return FALSE;
      return TRUE;
      }

    6. Замена ресурсов из файлов:
    Данный код применим не только для иконок и замены ресурсов, которые читаются из исполняемого файла. Следующий код показывает, как можно заменить ресурс – битмап, прочитав его из памяти:
    ExpandedWrap disabled
      bool ReplaceBitmap(WORD Number)
      {
       
      // Переменные для чтения и добавления ресурса
      DWORD dwResSize, dwRead;
      HANDLE File,hUpdateRes;
       
      // Открываем файл для дальнейшего чтения (файл new.bmp)
      File = CreateFile("new.bmp", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
      // Если открыть не удалось, то выходим
      if(File == INVALID_HANDLE_VALUE) return FALSE;
       
      // Сохраняем его размер исключив BITMAPFILEHEADER (для остальных ресурсов, исключать размер заголовка не нужно)
      dwResSize = GetFileSize(File, NULL)-sizeof(BITMAPFILEHEADER);
      // Если не удалось получить размер, то выходим
      if(dwResSize <= NULL)
      {
      // Закрываем хендл файла
      CloseHandle(File);
      return FALSE;
      }
       
      // Создаём массив для чтения файла
      char *pRes=new char[dwResSize];
       
      // Устанавливаем позицию чтения файла, чтобы не читать заголовок битмапа (для остальных ресурсов следует читать с начала файла и не смещать позицию чтения)
      SetFilePointer(File,sizeof(BITMAPFILEHEADER),0,0);
       
      // Читаем файл
      if(ReadFile(File, (LPVOID)pRes, dwResSize, &dwRead, NULL) == NULL)
      {
      // Закрываем хендл файла
      CloseHandle(File);
      return FALSE;
      }
       
      // Начинаем обновлять ресурсы
      hUpdateRes = BeginUpdateResource("ResourceKeeper.exe", FALSE);
      if(hUpdateRes == NULL)
      {
      // Закрываем хендл файла
      CloseHandle(File);
      return FALSE;
      }
       
      // Добавляем ресурс
      if(UpdateResource(hUpdateRes, RT_BITMAP, MAKEINTRESOURCE(Number), MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), (LPVOID)pRes, dwResSize) == NULL)
      {
      // Закрываем хендл файла
      CloseHandle(File);
      return FALSE;
      }
       
      // Завершаем обновление
      if(EndUpdateResource(hUpdateRes, FALSE) == NULL)
      {
      // Закрываем хендл файла
      CloseHandle(File);
      return FALSE;
      }
       
      // Закрываем хендл файла
      if(CloseHandle(File) == NULL) return FALSE;
      return TRUE;
      }

    7. Особенность работы с ресурсом Bitmap:
    Следует отметить, что при перезаписи битмапа из файла, следует исключить заголовок - BITMAPFILEHEADER. Также при записи ресурса в файл нужно создать этот заголовок, используя например вот эту ф-цию:
    ExpandedWrap disabled
      #include <atlimage.h>
      void StoreBitmap(HBITMAP map, char *PathName){
      CImage image;  
      image.Attach(map);  
      image.Save(PathName);
      }

    8. Функции для работы с ресурсами:
    • FindResource - Ф-ция для поиска ресурса.
    • SizeofResource - Ф-ция для получения размера ресурса.
    • LoadResource - Ф-ция для загрузки ресурса.
    • LockResource - Ф-ция для фиксирования ресурса в памяти.
    • EnumResourceTypes - Ф-ция для получения списка типов ресурсов.
    • EnumResourceNames - Ф-ция для получения списка имён ресурсов.
    • BeginUpdateResource - Ф-ция для обновления ресурсов.
    • UpdateResource - Ф-ция для замены ресурсов.
    • EndUpdateResource - Ф-ция для завершения обновления ресурсов.

    9. Ссылки:
    Ключевые слова:
    LoadLibrary, LoadLibraryEx, FindResource, SizeofResource, MAKEINTRESOURCE, LoadResource, LockResource, FreeLibrary, LoadBitmap, LoadIcon, LoadMenu, LoadString, EnumResourceNames, EnumResourceTypes, BeginUpdateResource, UpdateResource, EndUpdateResource.

    10. Примеры программ:
    В качестве примера приводятся две программы:
    Первая является "держателем" ресурсов и способна распаковать свой ресурс - битмап в файл, а вторая меняет иконку для первой, а также переписывает ресурс – битмап, читая его из файла.
    Сообщение отредактировано: SimBiOd -

    Прикреплённый файлПрикреплённый файлExample.rar (87.21 Кбайт, скачиваний: 923)
      Цитата SimBiOd, 22.04.05, 22:52:14, 692209
      ExpandedWrap disabled
        // Открываем файл для дальнейшего чтения (файл 1.bmp)
        File = CreateFile("1.bmp", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
        if(File == INVALID_HANDLE_VALUE) return FALSE;
        // Сохраняем его размер
        dwResSize = GetFileSize(File, NULL);
        if(dwResSize == NULL) return FALSE;
        // Создаём массив для чтения файла
        char *pRes=new char[dwResSize];
        // Читаем файл
        if(ReadFile(File, (LPVOID)pRes, dwResSize, &dwRead, NULL) == NULL) return FALSE;
        // Закрываем его хендл
        if(CloseHandle(File) == NULL) return FALSE;
        // Начинаем обновлять ресурсы
        hUpdateRes = BeginUpdateResource("2.exe", FALSE);
        if(hUpdateRes == NULL) return FALSE;
        // Добавляем ресурс
        if(UpdateResource(hUpdateRes, RT_BITMAP, MAKEINTRESOURCE(Number),
        MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), (LPVOID)pRes, dwResSize) == NULL) return FALSE;

      Небольшая поправка: при добавлении битмапа из файла.bmp в ресурсы надо отбрасывать BITMAPFILEHEADER, иначе получится не RT_BITMAP, а что-то другое... "Unknown bitmap format", как говорит редактор ресурсов VC++ :rolleyes:
      Рабочий пример -> Смена bmp (сообщение #604775)
        CBP, спасибо, что помогаешь, но я с этим уже разобрался, а это просто набросок, я знаю, что для записи в ресурс из файла нужно исключить BITMAPFILEHEADER, а для записи в файл дописывать. ;)

        P.S прошу не писать в эту тему пока я не закончу, пока это просто мысли в слух.
        P.P.S 2 CBP ещё раз спасибо за попытку помочь.
          HMODULE LoadLibraryEx(
          LPCTSTR lpLibFileName,
          HANDLE hFile,
          DWORD dwFlags
          );


          dwFlags
          LOAD_LIBRARY_AS_DATAFILE If this value is used, the system maps the file into the virtual address space of the calling process as if it were a data file. Nothing is done to execute or prepare to execute the mapped file. Use this flag when you want to load a DLL only to extract messages or resources from it.
          You can use the resulting module handle with any Win32 functions that operate on resources.

          DONT_RESOLVE_DLL_REFERENCES is implied.
            Цитата SimBiOd @
            P.S прошу не писать в эту тему пока я не закончу, пока это просто мысли в слух.
            Так может наоборот, лучше писАть, пока не закончил - меньше переделывать придется? :)

            У меня вот такое замечание - в кодах примеров, в нескольких местах, если после загрузки библиотеки или открытия/создания файла происходит ошибка (например, не найден ресурс или ошибка чтения/записи), то часто используется return FALSE - без всякого освобождения библиотеки или закрытия открытого хэндла файла - нехорошо это - чему мы так научим подрастающее поколение? :)
            Раз уж не хочется (это я понимаю) подробно расписывать, то лучше уж тогда в примерах ошибки вообще не обрабатывать, а просто указать, что в реальном коде это надо делать.
              Adil, я согласен, это можно и даже нужно поправить... :yes:
                SimBiOd, всё хорошо, но мне не нравятся пара-тройка вещей :(
                - красный цвет в названиях разделов
                - курсив
                - отсутствие отступов в коде
                  Господа модераторы, так что с топиком, редактировать ещё чё нужно? :huh:
                  Сообщение отредактировано: SimBiOd -
                    можно ещё в ссылки добавить заковыристые темы "Запись в StringTable" и "Проигрывание миди из ресурса (без извлечения на HDD!)" :)

                    ресурсы (сообщение #525711)
                    Воспроизведение Mid файлов из ресурса (сообщение #639315)
                      Допустим есть exeшник в котором в виде ресурса имеется нпример другой ехешник (или картинка, неважно). Можно ли, не распаковывая этот ресурс (ехе или картинку) в отдельный файл, запустить его?
                        Цитата GRIENDERS @
                        Допустим есть exeшник в котором в виде ресурса имеется нпример другой ехешник (или картинка, неважно). Можно ли, не распаковывая этот ресурс (ехе или картинку) в отдельный файл, запустить его?

                        в случае с исполняемым модулем - нельзя. а вот если картинка, то можно использовать протокол res: для обращения. например, для тестирования, можешь создать html-ку на ХР и вписать туда
                        ExpandedWrap disabled
                          <img src="res://C:\Windows\System32\user32.dll/#2/36.bmp">

                        и увидишь картинку.
                        но таким образом нельзя использовать CreateFile. Но запись типа ShellExecute( NULL, NULL, "res://C:\Windows\System32\user32.dll/#2/36.bmp", NULL, NULL, SW_SHOW ); вполне сработает.

                        более подробно смотри тут: Pluggable Protocols.
                          alexander.stoyan,спасибо.
                          Один вопрос: "Я делаю электронную книгу - на форме через элемент activex "microsoft web browser" можно просматривать HTML, которые у меня в проге в виде ресурсов. Будет ли работать прога с элементом activex "microsoft web browser" на всех виндах? "
                            Дело не в винде, а в версии IE. В твоём случае нужне IE от 4.0.. учитывая, что 4.0 по дефолту идёт с NT4, а в W2K - IE 5.0, в XP - 5.5-6.0, в Vista - 7.0, то не о чем беспокоиться ;) ... правда в 9х придётся вставлять проверки.
                              И еще одно дополнение:

                              Извлечение ресурсов dll функцией самой dll.
                              Облазил форум, и очень часто встречал темы где жалуются на ошибку 1813 при работе с ресурсами в длл.

                              Нельзя вызывать GetModuleHandle с параметром NULL - это возвратит handle на главный exe, а не на нашу dll.
                              ExpandedWrap disabled
                                HMODULE hModule = GetModuleHandle (NULL);


                              Обязательно необходимо получить HMODULE dll самой себя
                              ExpandedWrap disabled
                                HMODULE hModule = GetModuleHandle ("название_dll.dll");


                              и в дальнейшем подставлять во все функции работающие с ресурсом.
                              ExpandedWrap disabled
                                HRSRC hres = FindResource (hModule, MAKEINTRESOURCE (IDR_USER1), "USER");
                                Цитата zatomant @
                                Обязательно необходимо получить HMODULE dll самой себя

                                ExpandedWrap disabled
                                  HMODULE hModule = GetModuleHandle ("название_dll.dll");


                                учти, что ддл-ка может переименоваться.
                                а hModule приходит в DllMain
                                ExpandedWrap disabled
                                  BOOL WINAPI DllMain(
                                    HANDLE hinstDLL,  // вот его и надо использовать, и больше никакой
                                    DWORD dwReason,
                                    LPVOID lpvReserved
                                  );
                                0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                0 пользователей:


                                Рейтинг@Mail.ru
                                [ Script execution time: 0,0540 ]   [ 16 queries used ]   [ Generated: 29.03.24, 08:32 GMT ]