Форум программистов, компьютерный форум, киберфорум
Visual C++
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.86/7: Рейтинг темы: голосов - 7, средняя оценка - 4.86
176 / 2 / 1
Регистрация: 31.10.2016
Сообщений: 160

RAII: внутри функции и можно ли в ней заменить new?

12.02.2017, 16:05. Показов 1524. Ответов 15
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
По наводке Убежденный стал разбираться с RAII, но по мере чтения инфы по сабжу возникают вопросы. Например, как создавать RAII для нескольких функций, требующих закрытия\высвобождения ресурсов? Допустим, хочу проверить является ли пользователь, запускающий мое приложение, администратором, для этого вызываю OpenProcessToken для текущего процесса, тем самым после нам нужно закрыть полученный токен, далее считываю информацию о токене GetTokenInformation: здесь сперва нужно узнать размер буфера для TOKEN_GROUPS, поэтому придется использовать оператор new для выделения нужной памяти (или все же стоит использовать вектор?!); ну и в конце Sid полученный AllocateAndInitializeSid сравниваю с тем, что в группе TOKEN_GROUPS. Псевдокод:
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
BOOLEAN IsUserAdmin {
BOOLEN isAdmin = FALSE;
 
// здесь RAII для OpenProcessToken (CloseToken)
// и AllocateAndInitializeSid (FreeSid)
// как RAII объявить?
 
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)) {
   return err_code;
}
 
if (!GetTokenInformation(token, TokenGroups, NULL, 0, &buff_size &&
           ERROR_INSUFFICIENT_BUFFER != GetLastError()) {
   return err_code;
}
 
buff_size = new ...;
if (!GetTokenInformation(token, TokenGroups, token_groups, biff_size, &buff_size) {
   // высвободить память delete
   return err_code;
}
 
if (!AllocateAndInitializeSid(... &sid)) {
   return err_code;
}
 
// далее сравниваем Sid
for (int i = 0; i < token_groups->GroupsCount; i++) {
   if (EqualSid(sid, token_groups->Groups[i].Sid)) {
     isAdmin = TRUE;
     break;
   }
}
}
Можно ли обойтись без new? Или можно и для new создавать RAII?
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
12.02.2017, 16:05
Ответы с готовыми решениями:

Почему выделенная внутри функции память удаляется после возврата функции? Это можно исправить?
Вот пример функции, которая выделяет память под переменную, объявленную за её пределами: void Foo (wchar_t* test) { test =...

Можно ли сделать print внутри функции?
если да, то как? если нет, то как можно это обойти?

Можно ли внутри самого регулярного выражения использовать функции
Здравствуйте! Я бы хотела спросить, можно ли внутри самого регулярного выражения использовать функции заданные раньше? Например, функцию...

15
Ушел с форума
Эксперт С++
 Аватар для Убежденный
16481 / 7444 / 1187
Регистрация: 02.05.2013
Сообщений: 11,616
Записей в блоге: 1
14.02.2017, 15:04
Цитата Сообщение от jkadaba Посмотреть сообщение
Например, как создавать RAII для нескольких функций, требующих закрытия\высвобождения ресурсов?
Единого общепринятого подхода нет.
Я, например, предпочитаю иметь несколько маленьких классов-оберток:
auto_handle для HANDLE, auto_free для LocalFree и т.д.
Если логика работы с функцией не совсем простая, она вся заворачивается в
класс-обертку, а наружу выставляются простые и безопасные методы,
которые трудно использовать не по назначению. Если идет работа с буферами,
то всяким malloc/new/etc я предпочитаю сразу брать std::vector. И т.д.
0
176 / 2 / 1
Регистрация: 31.10.2016
Сообщений: 160
14.02.2017, 16:41  [ТС]
Убежденный, а можно конкретные примеры? То что в Вики лично мне этого мало, чтобы переварить концепцию RAII. Вот как, например, выделить память под некую структуру и если функция вернула length mismatch, как мне увеличить размер вектора?
Где почитать про автохэндл, автофри и так далее? Мне нужны корректные примеры, чтобы понять что к чему.
0
Ушел с форума
Эксперт С++
 Аватар для Убежденный
16481 / 7444 / 1187
Регистрация: 02.05.2013
Сообщений: 11,616
Записей в блоге: 1
14.02.2017, 18:07
Лучший ответ Сообщение было отмечено jkadaba как решение

Решение

Обычно я использую примерно следующий подход:
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
bool IsUserAdmin()
{
    auto_handle Token;
    
    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &Token)) {
        ThrowExc("OpenProcessToken failed, err = 0x%.8lx.", GetLastError());
    }
 
    DWORD ReqSize;
    
    if (GetTokenInformation(Token, TokenGroups, NULL, 0, &ReqSize)) {
        ThrowExc("GetTokenInformation #1 returns TRUE.");
    }
    
    DWORD const LastError = GetLastError();
    if (ERROR_INSUFFICIENT_BUFFER != LastError) {
        ThrowExc("GetTokenInformation #1 failed, err = 0x%.8lx.", LastError);
    }    
    
    DWORD const BuffSize = ReqSize;
    std::vector<byte> Buffer(BuffSize);
    
    if (!GetTokenInformation(Token, TokenGroups, &Buffer[0], BuffSize, &ReqSize)) {
        ThrowExc("GetTokenInformation #2 failed, err = 0x%.8lx.", GetLastError());
    }
    
    DWORD SidBuffSize = SECURITY_MAX_SID_SIZE;
    std::vector<byte> SidBuffer(SidBuffSize);
    
    if (!CreateWellKnownSid(WinBuiltinAdministratorsSid, NULL, &SidBuffer[0], &SidBuffSize)) {
        ThrowExc("CreateWellKnownSid failed, err = 0x%.8lx.", GetLastError());
    }
    
    TOKEN_GROUPS const * pInfo = reinterpret_cast<TOKEN_GROUPS *>(&Buffer[0]);
    DWORD const Count = pInfo->GroupCount;
    
    for (DWORD i = 0; i < Count; ++i)
    {
        if (pInfo->Groups[i].Attributes & (SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT))
        {
            if (EqualSid(pInfo->Groups[i].Sid, &SidBuffer[0]))
            {
                return true;
            }
        }
    }
    
    return false;
}
Как должно быть понятно из кода, все ресурсы, т.е. хэндл токена, а также
буферы для TOKEN_GROUPS и SID, при выходе из функции или по исключению
автоматически освобождаются. Это и есть RAII.

Класс auto_handle можно реализовать, например, так:
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
class auto_handle
{
public:
    auto_handle(HANDLE h = NULL) :
        m_h(h)
    {
    }
 
    ~auto_handle()
    {
        if ( (m_h) && (INVALID_HANDLE_VALUE != m_h) )
        {
            CloseHandle(m_h);
        }
    }
 
    operator HANDLE ()
    {
        return m_h;
    }
 
    HANDLE * operator & ()
    {
        return (&m_h);
    }
 
private:
    auto_handle(auto_handle const &);
    auto_handle & operator = (auto_handle const &);
 
private:
    HANDLE m_h;
};
Это простой и надежный подход. Хотя кому-то может показаться, что здесь
все равно слишком много низкоуровневых деталей. В большом серьезном проекте я
предпочту разработать сначала готовые классы-обертки для работы с ресурсами, а
затем использовать их. Получится что-то вроде такого:
C++
1
2
3
4
5
6
7
8
9
bool IsUserAdmin()
{
    using namespace Security;
    
    safe_handle Token = openToken(tokenType::process, rights::tokenQuery);
    groups Groups = Token.getGroups();
    sid Sid = createSid(wellKnown::builtinAdmin);
    return (Groups.isPresent(Sid));
}
P.S.
А еще проще вызвать CheckTokenMembership и не заморачиваться
0
176 / 2 / 1
Регистрация: 31.10.2016
Сообщений: 160
14.02.2017, 20:55  [ТС]
Про CheckTokenMembership в курсе, здесь речь не столько о нем, сколько попытка на каком-нибудь примере понять суть RAII. С классом автохэндл разобрался, с вектором картина прояснилась, но вот чего никак не пойму зачем порождать исключение, если апишная функция завершилась неудачно. Не проще ли для этих целей использовать __try ... __leave? Ну и все же, можно как-то запилить в функцию RAII сразу для нескольких апишинных функций?
0
Ушел с форума
Эксперт С++
 Аватар для Убежденный
16481 / 7444 / 1187
Регистрация: 02.05.2013
Сообщений: 11,616
Записей в блоге: 1
14.02.2017, 21:37
Цитата Сообщение от jkadaba Посмотреть сообщение
зачем порождать исключение, если апишная функция завершилась неудачно.
Вопрос вкуса.
Для кода, перфоманс которого не является критичным, исключения банально удобнее (IMHO).
Потому что клиентскому коду не требуется писать 150 проверок кодов ошибок.

Цитата Сообщение от jkadaba Посмотреть сообщение
Не проще ли для этих целей использовать __try ... __leave?
__try/__leave - это нестандартно, и эта конструкция имеет нежелательные побочные эффекты
(например, переход в блок __finally при возникновения деления на ноль).

Цитата Сообщение от jkadaba Посмотреть сообщение
Ну и все же, можно как-то запилить в функцию RAII сразу для нескольких апишинных функций?
Ну сделай класс, в котором бы хранились результаты вызова нескольких функций.
И в деструкторе разом все ресурсы освобождай.
0
Покинул форум
3700 / 1483 / 355
Регистрация: 07.05.2015
Сообщений: 2,903
16.02.2017, 09:52
Лучший ответ Сообщение было отмечено jkadaba как решение

Решение

Попробую телепатировать, что подразумевалось под "запилом" RAII в функцию:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
BOOL IsUserAdmin(void) {
  struct Res {
    HANDLE token;
    PSID   sid;
    
    Res() : sid(NULL), token(NULL) {}
    ~Res() {
      if (sid) FreeSid(sid);
      if (token) CloseHandle(token);
    }
  } res;
  
  BOOL status = FALSE;
  /* прочие переменные */
  
  if (!OpenProcessToken(
    GetCurrentProcess(), TOKEN_QUERY, &res.token
  )) return status;
  
  /* прочие вызовы */
}
1
176 / 2 / 1
Регистрация: 31.10.2016
Сообщений: 160
16.02.2017, 12:31  [ТС]
Я это и имел в виду. А насколько корректен будет данный подход с точки высвобождения ресурсов? Убежденный, что скажешь? Можно так?
0
Ушел с форума
Эксперт С++
 Аватар для Убежденный
16481 / 7444 / 1187
Регистрация: 02.05.2013
Сообщений: 11,616
Записей в блоге: 1
16.02.2017, 16:07
Цитата Сообщение от jkadaba Посмотреть сообщение
А насколько корректен будет данный подход с точки высвобождения ресурсов?
А какие здесь проблемы? Я проблем не вижу.
На выходе из функции sid и т.п. будут очищены.
0
176 / 2 / 1
Регистрация: 31.10.2016
Сообщений: 160
16.02.2017, 19:32  [ТС]
Спасибо, ребят!
0
176 / 2 / 1
Регистрация: 31.10.2016
Сообщений: 160
20.09.2017, 12:25  [ТС]
Убежденный, а где можно посмотреть примеры классов оберток auto_handle, auto_free и так далее? Можешь поделиться наработками и опытом? Просто тема лично меня очень интересует.
0
Ушел с форума
Эксперт С++
 Аватар для Убежденный
16481 / 7444 / 1187
Регистрация: 02.05.2013
Сообщений: 11,616
Записей в блоге: 1
20.09.2017, 13:20
К сожалению, не могу привести код именно тех классов, которыми пользуюсь я
(т.к. они часть нашего проекта с закрытыми исходниками), а хотелось бы.
Но это все очень легко слепить самому из ниток, бумаги и пластилина.


Пример с auto_handle есть выше, по аналогии пишется все остальное.
Можно вообще сделать шаблонные классы с готовыми базовыми функциями,
останется только наследоваться от них или написать один раз typedef...

На самом деле RAII в виде scope-оберток для каких-то локально используемых ресурсов
типа HANDLE - это лишь первый маленький шаг к написанию надежного и безопасного кода.
Дальше надо все "сырые" буферы заменить на vector/array/etc, вместо C-строк использовать
std::string/QString/CString/etc, поменьше работать с "сырыми" указателями (предпочитая
им указатели "умные") и так далее.
1
232 / 135 / 19
Регистрация: 10.11.2015
Сообщений: 305
20.09.2017, 16:57
Лучший ответ Сообщение было отмечено jkadaba как решение

Решение

jkadaba, я делаю так:

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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
//
// Auto.hpp
//
 
#pragma once
 
#include <Windows.h>
 
namespace Auto {
 
template <typename T, T INVALID> 
class AutoCleanup {
 
protected:
 
    AutoCleanup(T Res) : m_Res(Res) {}
 
public:  
 
    bool IsValid() const
    {
        return INVALID != m_Res;
    }
 
    operator T() const 
    { 
        ASSERT(true == IsValid());
        return m_Res; 
    }
 
    T* operator&()
    {
        ASSERT(false == IsValid());
        return &m_Res;
    }
 
    T operator=(T Res)
    { 
        ASSERT(false == IsValid());
        m_Res = Res;
        return m_Res;  
    }
 
    void Release()
    {
        ASSERT(true == IsValid());
        m_Res = INVALID;
    }
 
protected:
 
    T m_Res;
 
private:
 
    AutoCleanup(const AutoCleanup<T, INVALID>&);
    AutoCleanup<T, INVALID>& operator=(const AutoCleanup<T, INVALID>&);
};
 
//
// Simple classes
//
 
#define AUTO_CLEANUP_CLASS(FUNC, T, INVALID, NAME)          \
                                                            \
    struct NAME : public Auto::AutoCleanup<T, INVALID> {    \
                                                            \
        NAME(T Res = INVALID) : AutoCleanup(Res) {}         \
                                                            \
        ~NAME()                                             \
        {                                                   \
            if (IsValid()) {                                \
                FUNC(m_Res);                                \
            }                                               \
        }                                                   \
                                                            \
        T operator=(T Res)                                  \
        {                                                   \
            return AutoCleanup::operator=(Res);             \
        }                                                   \
    };
 
AUTO_CLEANUP_CLASS(::CloseHandle, HANDLE, NULL, AutoCloseHandle)
AUTO_CLEANUP_CLASS(::CloseHandle, HANDLE, INVALID_HANDLE_VALUE, AutoCloseFile)
AUTO_CLEANUP_CLASS(::CloseServiceHandle, SC_HANDLE, NULL, AutoCloseServiceHandle)
AUTO_CLEANUP_CLASS(::FreeLibrary, HMODULE, NULL, AutoFreeLibrary)
AUTO_CLEANUP_CLASS(::LocalFree, HLOCAL, NULL, AutoLocalFree)
AUTO_CLEANUP_CLASS(::UnmapViewOfFile, PVOID, NULL, AutoUnmapViewOfFile)
AUTO_CLEANUP_CLASS(::CloseDesktop, HDESK, NULL, AutoCloseDesktop)
AUTO_CLEANUP_CLASS(::CloseWindowStation, HWINSTA, NULL, AutoCloseWindowStation)
AUTO_CLEANUP_CLASS(::RegCloseKey, HKEY, NULL, AutoRegCloseKey)
 
//
// AutoVirtualFree
//
 
struct AutoVirtualFree : public AutoCleanup<PVOID, NULL> {
 
    AutoVirtualFree(PVOID Address = NULL, SIZE_T Size = 0, DWORD FreeType = MEM_RELEASE) 
    : 
    AutoCleanup(Address), m_Size(Size), m_FreeType(FreeType) {}
 
    ~AutoVirtualFree()
    {
        if (true == IsValid()) {
            ::VirtualFree(m_Res, m_Size, m_FreeType);
        }
    }
 
    HANDLE operator=(HANDLE Res)
    { 
        return AutoCleanup::operator=(Res);  
    }
 
private:
 
    const SIZE_T m_Size;
    const DWORD m_FreeType;
};
 
}; // namespace Auto
В Auto.hpp держу часто используемые классы. По мере надобности объявляю новые:

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
//
// ScreenCreator.cpp
//
 
    ...
 
#include "Auto.hpp"
 
#pragma comment(lib, "gdiplus.lib" )
 
using namespace Gdiplus;
using namespace Gdiplus::DllExports;
 
namespace Auto {
 
    AUTO_CLEANUP_CLASS(DeleteDC, HDC, NULL, AutoDeleteDC)
    AUTO_CLEANUP_CLASS(DeleteObject, HBITMAP, NULL, AutoDeleteObject)
    AUTO_CLEANUP_CLASS(GdipDisposeImage, GpBitmap*, NULL, AutoGdipDisposeImage)
    
    //
    // AutoReleaseDC
    //
    
    struct AutoReleaseDC : public AutoCleanup<HDC, NULL> {
 
        AutoReleaseDC(HDC hDC = NULL, HWND hWnd = NULL) 
        : 
        AutoCleanup(hDC), m_hWnd(hWnd) {}
 
        ~AutoReleaseDC()
        {
            if (true == IsValid()) {
                ::ReleaseDC(m_hWnd, m_Res);
            }
        }
 
        HDC operator=(HDC Res)
        { 
            return AutoCleanup::operator=(Res);  
        }
 
    private:
 
        const HWND m_hWnd;
    };
 
}; // namespace Auto
 
    ...
2
Ушел с форума
Эксперт С++
 Аватар для Убежденный
16481 / 7444 / 1187
Регистрация: 02.05.2013
Сообщений: 11,616
Записей в блоге: 1
20.09.2017, 17:32
jupman, симпатичненько
У меня, кстати, базовые классы тоже лежат в Auto.hpp (совпадение?).
Все хорошо, а вот операторы взятия адреса и неявные приведения к типу T я бы убрал, IMHO.
От них больше проблем, чем пользы.
1
176 / 2 / 1
Регистрация: 31.10.2016
Сообщений: 160
27.09.2017, 11:46  [ТС]
jupman, хотел бы отметить твой ответ, но почему-то кнопка с плюсом не срабатывает.
Убежденный, а как быть с аргументами командной строки. В смысле:
C++
1
2
3
int main(int argc, char *argv[]) {
  ...
}
Приводить аргументы к типу string или что? Вообще, как можно избавиться от char * и подобных ему PTCHAR и т.д.?
0
Покинул форум
3700 / 1483 / 355
Регистрация: 07.05.2015
Сообщений: 2,903
27.09.2017, 19:07
jkadaba, понимаю, что только изучаете, но что ж Вы так бестолковите? Убежденный уже не раз Вам говорил про векторы и строки. Возьмите, да запихните аргументы в вектор:
C++
1
2
3
4
int main(int argc, char * argv[]) {
   std:vector<std::string> args(argv, argv + argc);
   ...
}
И обращайтесь к аргументам как к строкам:
C++
1
2
3
...
   std::cout <<  args[0] << std::endl;
...
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
27.09.2017, 19:07
Помогаю со студенческими работами здесь

Можно ли внутри клиентской функции изменять серверную переменную?
Привет! Можно ли внутри клиентской функции изменять серверную переменную. Серверная переменная schet объявлена следующим образом: ...

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

Можно ли внутри функции f_1 создать массив размера, заданного аргументом n_1?
Ситуация простая. Язык С++. Есть функция, пусть будет f_1, которая получает аргумент n_1, натурального типа. Вопрос такой: внутри функции...

Каким образом можно во внешнем запросе выбрать все переменные, которые находятся внутри функции?
Добрый день. У меня есть пакет с функцией внутри которой производятся различные действия со многими переменными. Каким образом...

Дан указатель: double **p = 0; Выполните следующие задания (решения можно оформлять внутри функции main):
Дан указатель: double **p = 0; Выполните следующие задания (решения можно оформлять внутри функции main): * создайте конструкцию,...


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

Или воспользуйтесь поиском по форуму:
16
Ответ Создать тему
Новые блоги и статьи
Советы по крайней бережливости. Внимание, это ОЧЕНЬ длинный пост.
Programma_Boinc 28.12.2025
Советы по крайней бережливости. Внимание, это ОЧЕНЬ длинный пост. Налог на собак: https:/ / **********/ gallery/ V06K53e Финансовый отчет в Excel: https:/ / **********/ gallery/ bKBkQFf Пост отсюда. . .
Кто-нибудь знает, где можно бесплатно получить настольный компьютер или ноутбук? США.
Programma_Boinc 26.12.2025
Нашел на реддите интересную статью под названием Anyone know where to get a free Desktop or Laptop? Ниже её машинный перевод. После долгих разбирательств я наконец-то вернула себе. . .
Thinkpad X220 Tablet — это лучший бюджетный ноутбук для учёбы, точка.
Programma_Boinc 23.12.2025
Рецензия / Мнение/ Перевод Нашел на реддите интересную статью под названием The Thinkpad X220 Tablet is the best budget school laptop period . Ниже её машинный перевод. Thinkpad X220 Tablet —. . .
PhpStorm 2025.3: WSL Terminal всегда стартует в ~
and_y87 14.12.2025
PhpStorm 2025. 3: WSL Terminal всегда стартует в ~ (home), игнорируя директорию проекта Симптом: После обновления до PhpStorm 2025. 3 встроенный терминал WSL открывается в домашней директории. . .
Как объединить две одинаковые БД Access с разными данными
VikBal 11.12.2025
Помогите пожалуйста !! Как объединить 2 одинаковые БД Access с разными данными.
Новый ноутбук
volvo 07.12.2025
Всем привет. По скидке в "черную пятницу" взял себе новый ноутбук Lenovo ThinkBook 16 G7 на Амазоне: Ryzen 5 7533HS 64 Gb DDR5 1Tb NVMe 16" Full HD Display Win11 Pro
Музыка, написанная Искусственным Интеллектом
volvo 04.12.2025
Всем привет. Некоторое время назад меня заинтересовало, что уже умеет ИИ в плане написания музыки для песен, и, собственно, исполнения этих самых песен. Стихов у нас много, уже вышли 4 книги, еще 3. . .
От async/await к виртуальным потокам в Python
IndentationError 23.11.2025
Армин Ронахер поставил под сомнение async/ await. Создатель Flask заявляет: цветные функции - провал, виртуальные потоки - решение. Не threading-динозавры, а новое поколение лёгких потоков. Откат?. . .
Поиск "дружественных имён" СОМ портов
Argus19 22.11.2025
Поиск "дружественных имён" СОМ портов На странице: https:/ / norseev. ru/ 2018/ 01/ 04/ comportlist_windows/ нашёл схожую тему. Там приведён код на С++, который показывает только имена СОМ портов, типа,. . .
Сколько Государство потратило денег на меня, обеспечивая инсулином.
Programma_Boinc 20.11.2025
Сколько Государство потратило денег на меня, обеспечивая инсулином. Вот решила сделать интересный приблизительный подсчет, сколько государство потратило на меня денег на покупку инсулинов. . . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru