1 / 1 / 1
Регистрация: 12.07.2015
Сообщений: 62

Программная настройка DCOM

20.07.2015, 17:33. Показов 6497. Ответов 39
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Нужно программно настроить DCOM.Меняю в реестре (HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\O le) настройки настройки в REG_SZ,всё работает ,что делать с бинарными файлами,такими как: DefaultAccessAermissions,DefaultLaunchAe rmissions,итд.,не могу найти. В них,как я понимаю,и задаются права доступа. Поменял в самом dcomcnfg.exe вижу в реестре изменения ,но закономерности не вижу.На других форумах подобные темы поднимались но ответа там не нашёл.Буду благодарен за любую информацию как это обработать на с++.
0
Лучшие ответы (1)
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
20.07.2015, 17:33
Ответы с готовыми решениями:

Asp.net+iis+dcom=ошибка при вызове функций DCOM сервера
В роли DCOM сервера выступает собственно такая вещь, как OPC сервер( http://ru.wikipedia.org/wiki/OPC ). Поскольку компьютер, на котором...

DCOM-клиент в виде сервиса не соединяется с DCOM-сервером!?
DCOM-клиент написан как сервис (с окном). Если запускаешь как сервис на той же машине что и DCOM-сервер, все соединяется,работает. ...

Программная настройка BDLookupComboBox
Есть у меня форма с эдитами и BDLookupComboBox-ами. На этой форме я ввожу данные в эдиты и в BDLookupComboBox-ах выбираю определенные...

39
Ушел с форума
Эксперт С++
 Аватар для Убежденный
16481 / 7444 / 1187
Регистрация: 02.05.2013
Сообщений: 11,616
Записей в блоге: 1
20.07.2015, 19:10
Прежде всего расскажите, какие именно настройки вы хотите изменить.
Обычно в LaunchPermissions и AccessPermissions прописываются параметры
безопасности для запуска и подключения к COM-серверу. И да, их
вполне можно менять программно, хоть это и не документировано.
0
1 / 1 / 1
Регистрация: 12.07.2015
Сообщений: 62
20.07.2015, 19:19  [ТС]
Убежденный, dcomcnfg->Службы компонентов->Компьютеры->Мой компьютер->Свойства->Безопасность COM
Вот эти.
0
Ушел с форума
Эксперт С++
 Аватар для Убежденный
16481 / 7444 / 1187
Регистрация: 02.05.2013
Сообщений: 11,616
Записей в блоге: 1
20.07.2015, 19:41
Я делал следующим образом: сначала создавал дескриптор безопасности и добавлял
туда разрешения для нужных пользователей и групп, а затем сохранял этот дескриптор
как REG_BINARY в ключ реестра HKLM\SOFTWARE\Classes\AppId\<AppId-GUID-компонента>,
в значения LaunchPermission и AccessPermission. Разумеется, если разрешения для
LaunchPermission и AccessPermission различаются, потребуется два security-
дескриптора. В разделе CLSID COM-компонента обязательно должна быть ссылка на
его AppId, иначе разрешения действовать не будут.

Это эквивалентно ручной настройке разрешений доступа через dcomcnfg.exe,
проверял на XP-Win7, 32- и 64-битных.

В ObjBase.h для соответствующих разрешений определены такие константы:
C
1
2
3
4
5
#define COM_RIGHTS_EXECUTE 1
#define COM_RIGHTS_EXECUTE_LOCAL 2
#define COM_RIGHTS_EXECUTE_REMOTE 4
#define COM_RIGHTS_ACTIVATE_LOCAL 8
#define COM_RIGHTS_ACTIVATE_REMOTE 16
Флаг COM_RIGHTS_EXECUTE должен всегда присутствовать.

Получается примерно такая табличка для флагов доступа:
Code
1
2
3
4
5
6
7
8
9
10
11
LaunchPermission
 
    Local Launch      - 0x3
    Remote Launch     - 0x5
    Local Activation  - 0x9
    Remote Activation - 0x11
    
AccessPermission
 
    Local Access      - 0x3
    Remote Access     - 0x5
Ну а создать дескриптор безопасности не должно быть такой уж проблемой.
Можно использовать BuildSecurityDescriptor, заполнив вручную структуры
EXPLICIT_ACCESS и TRUSTEE, а можно описать разрешения на языке SDDL и
позвать ConvertStringSecurityDescriptorToSecurit yDescriptor. В обоих
случаях результат будет идентичным - вы получите self-relative SD,
который можно сериализовать в реестр.
2
1 / 1 / 1
Регистрация: 12.07.2015
Сообщений: 62
20.07.2015, 22:42  [ТС]
Убежденный, Никогда бы сам не наткнулся на такую реализацию.На msdn взял пример.
https://msdn.microsoft.com/en-... s.85).aspx
и тут почитал http://rubooks.org/book.php?bo... =149(глава 15)
у меня тут вопрос возник,по этой записи
C++
1
lRes = RegCreateKeyEx(HKEY_CURRENT_USER, _T("mykey"), 0, _T(""), 0, KEY_READ | KEY_WRITE, &sa, &hkSub, &dwDisposition);
ссылка на структуру SECURITY_ATTRIBUTES ( &sa ) это и есть дескриптор безопасности?или у меня после таких кодов ?
0
Ушел с форума
Эксперт С++
 Аватар для Убежденный
16481 / 7444 / 1187
Регистрация: 02.05.2013
Сообщений: 11,616
Записей в блоге: 1
20.07.2015, 23:10
SECURITY_ATTRIBUTES задается при создании объекта.
В этой структуре указывается дескриптор безопасности, а также то,
сможет ли хэндл объекта наследоваться в дочерние процессы.

В данном случае дескриптор безопасности устанавливается для ключа реестра
(если он создается, а не открывается).
1
1 / 1 / 1
Регистрация: 12.07.2015
Сообщений: 62
20.07.2015, 23:33  [ТС]
Убежденный, Можете ещё подсказать?
ea[0].Trustee.ptstrName = (LPTSTR) pEveryoneSID;
откуда мы берём pEveryoneSID,это стандартное значение?
тут просто не нашёл такого :https://msdn.microsoft.com/en-... s.85).aspx
И не могу понять ,куда прописываются COM_RIGHTS_EXECUTE,это какие-то поля EXPLICIT_ACCESS или TRUSTEE ?
0
Ушел с форума
Эксперт С++
 Аватар для Убежденный
16481 / 7444 / 1187
Регистрация: 02.05.2013
Сообщений: 11,616
Записей в блоге: 1
20.07.2015, 23:48
pEveryoneSID - это, по всей вероятности, SID группы "Все" (Everyone).
Предопределенные SID-ы есть здесь:

Well-known security identifiers in Windows operating systems
https://support.microsoft.com/en-us/kb/243330

Цитата Сообщение от zorro117 Посмотреть сообщение
И не могу понять ,куда прописываются COM_RIGHTS_EXECUTE,это какие-то поля EXPLICIT_ACCESS или TRUSTEE ?
В EXPLICIT_ACCESS вы указываете, сами права доступа и параметры
наследования, а в TRUSTEE перечисляются субъекты безопасности
(т.е. пользователи, группы и т.п.), для которых эти разрешения
нужно применить.

Лично я предпочитаю не связываться с "голыми" EXPLICIT_ACCESS/TRUSTEE/ACL/etc, а
сразу выразить свои намерения через SDDL-строку, сконвертить ее в дескриптор
безопасности (ConvertStringSecurityDescriptorToSecuri tyDescriptor) и уже дальше что-то делать.
Например, вот как выглядит SDDL-строка для папки, устанавливающая полный доступ
для всех с наследованием в дочерние объекты и задающая владельцем администраторов:
Code
1
O:BAG:BAD:P(A;CIOI;GA;;;WD)
Не правда ли, достаточно лаконично ?

O - Owner, т.е. владелец, в данном случае это BA, т.е. Builtin Administrators.
G - Group, первичная группа параметры те же.
Далее идет D, т.е. DACL, список контроля доступа, причем P = Protected,
т.е. защищенный от наследования от объектов верхнего уровня.
Далее, в скобках: A - Access Allowed, т.е. тип доступа - разрешающий,
CIOI - это Container Inherit + Object Inherit, означает, что дочерние
контейнерные (C) и неконтейнерные (O) объекты будут наследовать
данные разрешения. Потом идет GA, то есть, GENERIC_ALL, полная
маска доступа, ну и WD - это группа "World", т.е. "Все" (она же "Everyone").

Про SDDL-синтаксис подробнее можно почитать здесь:

Security Descriptor Definition Language
https://msdn.microsoft.com/en-... 85%29.aspx

Кстати, вместо "GA", "GR" и т.п. можно указывать "сырые" флаги
доступа в шестнадцатеричном виде. Пример SDDL для процесса:
Code
1
D:(A;;0x00100000;;;WD)
0x00100000 в данном случае соответствует флагу SYNCHRONIZE.
1
1 / 1 / 1
Регистрация: 12.07.2015
Сообщений: 62
21.07.2015, 00:10  [ТС]
Да,есть над чем поразмыслить,пойду обрабатыватьСпасибо Вам.
0
Ушел с форума
Эксперт С++
 Аватар для Убежденный
16481 / 7444 / 1187
Регистрация: 02.05.2013
Сообщений: 11,616
Записей в блоге: 1
21.07.2015, 08:11
Я рекомендую заглянуть в соответствующую главу книги "Системное
программирование в Windows" А. Побегайло, там это вещи с
безопасностью хорошо описаны. Ну и в MSDN тоже, само собой.
Больше нигде толкового описания не встречал.
1
1 / 1 / 1
Регистрация: 12.07.2015
Сообщений: 62
21.07.2015, 22:34  [ТС]
Убежденный,я записал а реестр ,но что-то не могу найти где ошибся.В настройках безопасности 3 группы сразу создаётся,причём разные для LaunchPermissions и AccessPermissions.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#include <windows.h>
#include <stdio.h>
#include <aclapi.h>
#include <tchar.h>
#include <conio.h>
#include <iostream.h>
#include <sddl.h>
 
int main(int argc, char* argv[])
{
    DWORD dwRes, dwDisposition;
    PSID pEveryoneSID = NULL, pAdminSID = NULL;
    PACL pACL = NULL;
    PSECURITY_DESCRIPTOR pSD = NULL;
    EXPLICIT_ACCESS ea;
    SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
    SID_IDENTIFIER_AUTHORITY SIDAuthNT = SECURITY_NT_AUTHORITY;
    SECURITY_ATTRIBUTES sa;
    LONG lRes;
    HKEY hkSub = NULL;
    dwDisposition=REG_CREATED_NEW_KEY;
    // Create a well-known SID for the Everyone group.
    if(!AllocateAndInitializeSid(&SIDAuthWorld, 1,
                     SECURITY_WORLD_RID,
                     0, 0, 0, 0, 0, 0, 0,
                     &pEveryoneSID)){
        _tprintf(_T("AllocateAndInitializeSid Error %u\n"), GetLastError());
        goto Cleanup;
    }
    // Initialize an EXPLICIT_ACCESS structure for an ACE.
    // The ACE will allow Everyone read access to the key.
    ZeroMemory(&ea, 1 * sizeof(EXPLICIT_ACCESS));
    ea.grfAccessPermissions = GENERIC_ALL;
    ea.grfAccessMode = SET_ACCESS;
    ea.grfInheritance= NO_INHERITANCE;
    ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
    ea.Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
    ea.Trustee.ptstrName  = (LPTSTR) pEveryoneSID;
    // Create a new ACL that contains the new ACEs.
    dwRes = SetEntriesInAcl(1, &ea, NULL, &pACL);    //(2, ea, NULL, &pACL);
    if (ERROR_SUCCESS != dwRes){
        _tprintf(_T("SetEntriesInAcl Error %u\n"), GetLastError());
        goto Cleanup;
    }
    // Initialize a security descriptor.  
    pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR,SECURITY_DESCRIPTOR_MIN_LENGTH);
    if (NULL == pSD){
        _tprintf(_T("LocalAlloc Error %u\n"), GetLastError());
        goto Cleanup;
    }
    if (!InitializeSecurityDescriptor(pSD,SECURITY_DESCRIPTOR_REVISION)){
        _tprintf(_T("InitializeSecurityDescriptor Error %u\n"),GetLastError());
        goto Cleanup; 
    } 
     // Add the ACL to the security descriptor.
    if (!SetSecurityDescriptorDacl(pSD, 
            TRUE,     // bDaclPresent flag   
            pACL,
            FALSE))   // not a default DACL 
    {  
        _tprintf(_T("SetSecurityDescriptorDacl Error %u\n"),GetLastError());
        goto Cleanup;
    }
    // Initialize a security attributes structure.
    sa.nLength = sizeof (SECURITY_ATTRIBUTES);
    sa.lpSecurityDescriptor = pSD;
    sa.bInheritHandle = FALSE;
    /*ConvertStringSecurityDescriptorToSecurityDescriptor(
                (LPCTSTR)("O:BAG:BAD:P(A;CIOI;GA;;;WD)"),
                SDDL_REVISION_1,
                &pSD,
                NULL); */
    // Use the security attributes to set the security descriptor 
    // when you create a key.
    lRes = RegCreateKeyEx(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Classes\\AppId\\{BBAA0E44-3862-490C-8E63-AC2D2D6EF733}\\"),
        0, NULL, REG_OPTION_VOLATILE,KEY_WRITE|KEY_READ , NULL, &hkSub, NULL);//AcroPDF
     if(RegSetValueEx(hkSub, _T("LaunchPermission"), 0, REG_BINARY, (BYTE*)(&sa), sizeof(sa))!=ERROR_SUCCESS)
        cout<<"Error 1\n ";
     if(RegSetValueEx(hkSub, _T("AccessPermission"), 0, REG_BINARY, (BYTE*)(&sa), sizeof(sa))!=ERROR_SUCCESS)
        cout<<"Error 2\n ";
 
    _tprintf(_T("RegCreateKeyEx result %u\n"), lRes );
 
Cleanup:
    if (pEveryoneSID)
        FreeSid(pEveryoneSID);
    if (pAdminSID) 
        FreeSid(pAdminSID);
    if (pACL) 
        LocalFree(pACL);
    if (pSD) 
        LocalFree(pSD);
    if (hkSub)
        RegCloseKey(hkSub);
    getch();
    return 0;
}
0
Ушел с форума
Эксперт С++
 Аватар для Убежденный
16481 / 7444 / 1187
Регистрация: 02.05.2013
Сообщений: 11,616
Записей в блоге: 1
21.07.2015, 22:50
Вы не то пишете в реестр.
Писать надо не SECURITY_ATTRIBUTES, а сам SECURITY_DESCRIPTOR.

И REG_OPTION_VOLATILE уберите, из-за нее ключ исчезнет при
следующей же загрузке компа.
1
1 / 1 / 1
Регистрация: 12.07.2015
Сообщений: 62
21.07.2015, 22:56  [ТС]
Убежденный, Так я понимаю?Просто у меня щас настройки создаются,но групп там нет.
C++
1
2
3
4
 if(RegSetValueEx(hkSub, _T("LaunchPermission"), 0, REG_BINARY, (BYTE*)(sa.lpSecurityDescriptor), sizeof(sa.lpSecurityDescriptor))!=ERROR_SUCCESS)
        cout<<"Error 1\n ";
     if(RegSetValueEx(hkSub, _T("AccessPermission"), 0, REG_BINARY, (BYTE*)(sa.lpSecurityDescriptor), sizeof(sa.lpSecurityDescriptor))!=ERROR_SUCCESS)
        cout<<"Error 2\n ";
0
Ушел с форума
Эксперт С++
 Аватар для Убежденный
16481 / 7444 / 1187
Регистрация: 02.05.2013
Сообщений: 11,616
Записей в блоге: 1
22.07.2015, 08:23
Уже лучше.
Только не "sizeof(sa.lpSecurityDescriptor)", что бессмысленно, т.к. sizeof любого
указателя будет всегда равен "sizeof (void *)" (4 на x86 и 8 на x64), а здесь нужна
длина security-дескриптора. Получить можно функцией GetSecurityDescriptorLength.

--------
И это еще не все. InitializeSecurityDescriptor создает дескриптор в так
называемом absolute-формате, он еще не пригоден для сохранения в реестре,
сначала нужно сконвертировать его в self-relative-формат и только
после этого сохранять. Сделать это можно функцией MakeSelfRelativeSD.

Absolute and Self-Relative Security Descriptors
https://msdn.microsoft.com/en-... 85%29.aspx
1
1 / 1 / 1
Регистрация: 12.07.2015
Сообщений: 62
22.07.2015, 20:55  [ТС]
Убежденный, Нужно ли инициализировать дескриптор ,в который выходные данные записываются?Я пробовал но это не помогло ,обращаюсь к запрещёной памяти.Выдаёт EAcccessViolation.Начинаю заниматься "вангованием"
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 PSECURITY_DESCRIPTOR pSD = NULL;
    PSECURITY_DESCRIPTOR pSD_SR = NULL;
    LPDWORD lpdwBufferLength = NULL;
pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR,SECURITY_DESCRIPTOR_MIN_LENGTH);
    if (NULL == pSD){
        _tprintf(_T("LocalAlloc Error %u\n"), GetLastError());
        goto Cleanup;
    }
    if (!InitializeSecurityDescriptor(pSD,SECURITY_DESCRIPTOR_REVISION)){
        _tprintf(_T("InitializeSecurityDescriptor Error %u\n"),GetLastError());
        goto Cleanup;
    }
     // Add the ACL to the security descriptor.
    if (!SetSecurityDescriptorDacl(pSD,TRUE,pACL,FALSE)){
        _tprintf(_T("SetSecurityDescriptorDacl Error %u\n"),GetLastError());
        goto Cleanup;
    }
 
    lpdwBufferLength = (LPDWORD)(GetSecurityDescriptorLength(pSD_SR));
    getch();
    if(MakeSelfRelativeSD(pSD,pSD_SR,lpdwBufferLength)==0){
       cout<<"Error Make SD\n ";
       goto Cleanup;
    }
0
Ушел с форума
Эксперт С++
 Аватар для Убежденный
16481 / 7444 / 1187
Регистрация: 02.05.2013
Сообщений: 11,616
Записей в блоге: 1
22.07.2015, 21:16
Лучший ответ Сообщение было отмечено zorro117 как решение

Решение

Ух, как все запущено !


MakeSelfRelativeSD: первым аргументом передается указатель на
существующий SD, вторым - указатель на буфер достаточного размера,
чтобы вместить в себя другой SD, и третим - указатель на переменную
DWORD, которая хранит размер этого буфера и в которую на выходе
будет записан итоговый размер SD. Здесь второй параметр NULL, а в
третьем вообще какая-то ерунда (GetSecurityDescriptorLength,
приведенный к указателю). Какого результата вы ждете ?
1
1 / 1 / 1
Регистрация: 12.07.2015
Сообщений: 62
23.07.2015, 19:22  [ТС]
Убежденный, Исправил,заработало!Спасибо!Каждый ответ исчерпывающий,не сразу просто врубился.
0
1 / 1 / 1
Регистрация: 12.07.2015
Сообщений: 62
25.07.2015, 18:28  [ТС]
Убежденный, а вы не подскажете где находится полный список групп ,пользователей, встроенных субъектов безопасности итд.Хочу считать.
http://windata.ru/uploads/2011/06/win043_thumb.png
0
Ушел с форума
Эксперт С++
 Аватар для Убежденный
16481 / 7444 / 1187
Регистрация: 02.05.2013
Сообщений: 11,616
Записей в блоге: 1
25.07.2015, 18:47
Так я уже приводил ссылку выше:

Well-known security identifiers in Windows operating systems
https://support.microsoft.com/en-us/kb/243330

Здесь перечислены все встроенные идентификаторы безопасности.
Только этот список не постоянный, с выходом каждой новой версии
Windows он может пополняться новыми элементами. Например, в
Windows Vista появились SID-ы для integrity levels, в Windows 8 есть
SID-ы для AppContainer ("песочницы" modern-приложений) и т.п.
1
1 / 1 / 1
Регистрация: 12.07.2015
Сообщений: 62
25.07.2015, 19:00  [ТС]
Убежденный, про эту ссылку помню,просто может где-то в реестре можно считать,что бы на конкретной машине вывести полный список.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
25.07.2015, 19:00
Помогаю со студенческими работами здесь

Программная настройка ADOConnection
Создал DataModule Разместил ADOConnection название DBConnecter, ADOQuery название SQLQuery, и дальше все по стандартной схеме ...

Программная настройка MasterSource и MasterFields
procedure TForm1.ComboBox2Change(Sender: TObject); begin if ComboBox2.ItemIndex = 0 then DBGrid2.DataSource := fDM.DSTovarMainData; ...

Программная настройка чувствительности микрофона
Добрый день. Опять я. Такой вопрос: как средствами Qt5.x настроить чувствительность микрофона, ибо качество передаваемого звука ужасное....

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

Программная настройка роутера TP-Link
Доброго времени. У меня такая проблема. delphi xe 10 + indy 10 (idhttp, coockie manager) Пишу программу для автоперенастройки роутера...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Опции темы

Новые блоги и статьи
http://iceja.net/ математические сервисы
iceja 20.01.2026
Обновила свой сайт http:/ / iceja. net/ , приделала Fast Fourier Transform экстраполяцию сигналов. Однако предсказывает далеко не каждый сигнал (см ограничения http:/ / iceja. net/ fourier/ docs ). Также. . .
http://iceja.net/ сервер решения полиномов
iceja 18.01.2026
Выкатила http:/ / iceja. net/ сервер решения полиномов (находит действительные корни полиномов методом Штурма). На сайте документация по API, но скажу прямо VPS слабенький и 200 000 полиномов. . .
Расчёт переходных процессов в цепи постоянного тока
igorrr37 16.01.2026
/ * Дана цепь постоянного тока с R, L, C, k(ключ), U, E, J. Программа составляет систему уравнений по 1 и 2 законам Кирхгофа, решает её и находит переходные токи и напряжения на элементах схемы. . . .
Восстановить юзерскрипты Greasemonkey из бэкапа браузера
damix 15.01.2026
Если восстановить из бэкапа профиль Firefox после переустановки винды, то список юзерскриптов в Greasemonkey будет пустым. Но восстановить их можно так. Для этого понадобится консольная утилита. . .
Сукцессия микоризы: основная теория в виде двух уравнений.
anaschu 11.01.2026
https:/ / rutube. ru/ video/ 7a537f578d808e67a3c6fd818a44a5c4/
WordPad для Windows 11
Jel 10.01.2026
WordPad для Windows 11 — это приложение, которое восстанавливает классический текстовый редактор WordPad в операционной системе Windows 11. После того как Microsoft исключила WordPad из. . .
Classic Notepad for Windows 11
Jel 10.01.2026
Old Classic Notepad for Windows 11 Приложение для Windows 11, позволяющее пользователям вернуть классическую версию текстового редактора «Блокнот» из Windows 10. Программа предоставляет более. . .
Почему дизайн решает?
Neotwalker 09.01.2026
В современном мире, где конкуренция за внимание потребителя достигла пика, дизайн становится мощным инструментом для успеха бренда. Это не просто красивый внешний вид продукта или сайта — это. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru