Страницы

Поиск по вопросам

воскресенье, 7 июля 2019 г.

Не выполняется рекурсия

Имеется код:
char field[3][3]; void enterX() { cout << "Enter X on vertical: "; int i; cin >> i; if (cin.good() && i < 3 && i >= 0) {
} else { cout << "Please, enter the number 0-2!"; cin.clear(); cin.ignore(INT_MAX); enterX(); }
cout << "Enter X on horizontal: "; int j; cin >> j; if (cin.good() && j < 3 && j >= 0) {
} else { cout << "Please, enter the number 0-2!"; cin.clear(); cin.ignore(INT_MAX); enterX(); } field[i][j] = 'x';
}
Задача - считывание номера элемента массива с консоли и вставке в это место символа 'x'. Однако при неверном вводе не происходит рекурсия. В чём проблема?


Ответ

Вообще-то это - cin.ignore(INT_MAX); - игнорировать все (ну ладно, INT_MAX символов) до конца файла - т.е. пока какое-то ^Z не закроет поток...
input.ignore(std::numeric_limits::max(), '
');
И вообще, я бы делал так (если уж приспичила именно рекурсия):
char field[3][3]; void enterX() { cout << "Enter X && Y: "; unsigned int i, j; cin >> i >> j; if (cin.fail() || i > 2 || j > 2) { cin.clear(); cin.ignore(INT_MAX,'
'); cout << "Wrong values!
"; enterX(); } else { field[i][j] = 'x'; } }
Update
Имея типичную оконечную рекурсию, лучше переделать ее в итерацию:
char field[3][3]; void enterX() { for(;;) { cout << "Enter X && Y: "; unsigned int i, j; cin >> i >> j; if (!(cin.fail() || i > 2 || j > 2)) { field[i][j] = 'x'; break; } cin.clear(); cin.ignore(INT_MAX,'
'); cout << "Wrong values!
"; } }

Как сгруппировать `div` по 4 штуки?

Изначально есть такая структура:

1
2
3
4
5
6
7
8
9
10
11
12

В результате должна получиться такая:
1
2
3
4
5
6
7
8
9
10
11
12

То есть необходимо добиться того, чтобы элементы группировались по 4 штуки в отдельный div с классом row-n, где n - это номер "группы" элементов.
Пытался реализовать так:
'use strict' var arr = []; var row1 = []; var row2 = []; var row3 = []; arr.push(document.getElementsByClassName('box')); var boxes = arr[0]; for (var i = 0; i < boxes.length; i++) { if(i < 4) {
row1.push(boxes[i]) } else if(i >= 4 && i < 8) {
row2.push(boxes[i]) } else {
row3.push(boxes[i]) } };
Но ничего не вышло.


Ответ

var box, raw, i = 1, z = 1;
// ключевой момент здесь. Пока внутри родителя есть элементы с классом .box (родитель // в данном случае - body, но у вас может быть любой), то выполняем тело цикла. while (box = document.querySelector("body > .box")) { // i - номер итерации. Инкрементируется в конце цикла. Если переваливает за 4, то начинаем // отсчет заново (это нужно, чтобы сгруппировать элементы по 4 штуки) if (i > 4) { i = 1; }
// если это первый заход в группе, то создаем новый div с классом raw-z if (i === 1) { // переопределяем raw в новый, свжесозданный div raw = document.createElement("div"); // с классом raw-z raw.classList.add("raw-" + z); // после чего z увеличиваем z += 1; }
// в созданный на "первом" проходе div с классом raw-z добавляем box'ы raw.appendChild(box);
// если это последний элемент из группы, то отрисовываем всю группу элементов if (i === 4) { document.body.appendChild(raw); }
// увеличиваем счетчик на 1 i += 1; }

Почему $watch не всегда срабатывает при изменении объекта?

var app = angular.module('testApp', ['ui.bootstrap', 'ui.select']); app.decorator('uiSelectMatchDirective', function($rootScope, $delegate) { var originalLinkFn = $delegate[0].link; $delegate[0].compile = function(element) { return function(scope, elm, attrs, controller) { elm.bind('click', function() { $rootScope.$broadcast('customSelect', scope.$selectMultiple.activeMatchIndex); }); originalLinkFn.apply($delegate, arguments); }; }; return $delegate; }); app.decorator('uiSelectMultipleDirective', function($delegate) { var originalLinkFn = $delegate[0].link; $delegate[0].compile = function(element) { return function(scope, elm, attrs, controller) { scope.$on('uis:select', function(event, item) { scope.$selectMultiple.activeMatchIndex = controller[0].selected.length; }); originalLinkFn.apply($delegate, arguments); }; }; return $delegate; }); app.controller('detailsController', function($scope, toolService) { $scope.assembly = { tools: [] }; $scope.$on('customSelect', function(event, value) { $scope.tool = $scope.assembly.tools[value]; console.log('from handler custom select'); }); $scope.$watch('tool', function(value) { console.log('from watch') }); $scope.onSelectTool = function(tool) { $scope.tool = $scope.assembly.tools[$scope.assembly.tools.indexOf(tool)]; }; $scope.tools = toolService.getTools(); }); app.factory('assemblyService', function() { var assemblies = [{ "Position": 1.00, "Tools": [{ "Id": 104, "Type": "Центровочное сверло D10 ∠90°", "OrderCode": "D5306100", "Vendor": "YG-1", "IsConsumables": true, "Sequance": 14, "Overhang": 40.00, "AmountCuttingEdge": 0, "AmountPlates": 0, "Durability": 200.00, }, { "Id": 53, "Type": "Термопатрон ", "OrderCode": "50 10 A63 S", "Vendor": "POKOLM", "IsConsumables": false, "Sequance": 6, "Overhang": 40.00, "AmountCuttingEdge": 0, "AmountPlates": 0, "Durability": 200.00 } ], "Runtime": 1.00, "Note": null }, { "Position": 5.00, "Tools": [{ "Id": 889, "Type": "Корпус фрезы D42 z5", "OrderCode": "5 42 367", "Vendor": "POKOLM", "IsConsumables": false, "Sequance": 14, "Overhang": 43.00, "AmountCuttingEdge": 2, "AmountPlates": 5, "Durability": 90.00 }, { "Id": 750, "Type": "Пластина r2", "OrderCode": "04 67 896 R20 M40", "Vendor": "POKOLM", "IsConsumables": true, "Sequance": 12, "Overhang": 43.00, "AmountCuttingEdge": 2, "AmountPlates": 5, "Durability": 90.00 }, { "Id": 890, "Type": "Патрон ", "OrderCode": "HSK 63-16-50", "Vendor": "KEMMLER", "IsConsumables": false, "Sequance": 6, "Overhang": 43.00, "AmountCuttingEdge": 2, "AmountPlates": 5, "Durability": 90.00 } ], "Runtime": 40.00, "Note": null }, { "Position": 9.00, "Tools": [{ "Id": 891, "Type": "Фреза D20 r1", "OrderCode": "ф20R1z4 nACRo ТП", "Vendor": "ТЕХНОПОЛИС", "IsConsumables": true, "Sequance": 14, "Overhang": 70.00, "AmountCuttingEdge": 0, "AmountPlates": 0, "Durability": 180.00 }, { "Id": 223, "Type": "Термопатрон ", "OrderCode": "60 20 A63 S", "Vendor": "POKOLM", "IsConsumables": false, "Sequance": 6, "Overhang": 70.00, "AmountCuttingEdge": 0, "AmountPlates": 0, "Durability": 180.00 } ], "Runtime": 6.00, "Note": null }, { "Position": 11.00, "Tools": [{ "Id": 1241, "Type": "Фреза D12 r2", "OrderCode": "ф12R2z4 nACRo ТП", "Vendor": "ТЕХНОПОЛИС", "IsConsumables": true, "Sequance": 14, "Overhang": 55.00, "AmountCuttingEdge": 0, "AmountPlates": 0, "Durability": 180.00 }, { "Id": 51, "Type": "Термопатрон ", "OrderCode": "50 12 A63 S", "Vendor": "POKOLM", "IsConsumables": false, "Sequance": 6, "Overhang": 55.00, "AmountCuttingEdge": 0, "AmountPlates": 0, "Durability": 180.00 } ], "Runtime": 40.00, "Note": null }, { "Position": 15.00, "Tools": [{ "Id": 547, "Type": "Сверло D5.60", "OrderCode": "DH451056", "Vendor": "YG-1", "IsConsumables": true, "Sequance": 14, "Overhang": 25.00, "AmountCuttingEdge": 0, "AmountPlates": 0, "Durability": 100.00 }, { "Id": 78, "Type": "Термопатрон ", "OrderCode": "50 06 A63 S", "Vendor": "POKOLM", "IsConsumables": false, "Sequance": 6, "Overhang": 25.00, "AmountCuttingEdge": 0, "AmountPlates": 0, "Durability": 100.00 } ], "Runtime": 3.00, "Note": null } ]; return { getAssemblies: () => { return assemblies.slice(); } }; }); app.factory('toolService', function() { var tools = [{ "Id": 104, "Type": "Центровочное сверло D10 ∠90°", "OrderCode": "D5306100", "Vendor": "YG-1", "IsConsumables": true }, { "Id": 53, "Type": "Термопатрон ", "OrderCode": "50 10 A63 S", "Vendor": "POKOLM", "IsConsumables": false }, { "Id": 889, "Type": "Корпус фрезы D42 z5", "OrderCode": "5 42 367", "Vendor": "POKOLM", "IsConsumables": false }, { "Id": 750, "Type": "Пластина r2", "OrderCode": "04 67 896 R20 M40", "Vendor": "POKOLM", "IsConsumables": true }, { "Id": 890, "Type": "Патрон ", "OrderCode": "HSK 63-16-50", "Vendor": "KEMMLER", "IsConsumables": false, }, { "Id": 891, "Type": "Фреза D20 r1", "OrderCode": "ф20R1z4 nACRo ТП", "Vendor": "ТЕХНОПОЛИС", "IsConsumables": true }, { "Id": 223, "Type": "Термопатрон ", "OrderCode": "60 20 A63 S", "Vendor": "POKOLM", "IsConsumables": false }, { "Id": 1241, "Type": "Фреза D12 r2", "OrderCode": "ф12R2z4 nACRo ТП", "Vendor": "ТЕХНОПОЛИС", "IsConsumables": true }, { "Id": 51, "Type": "Термопатрон ", "OrderCode": "50 12 A63 S", "Vendor": "POKOLM", "IsConsumables": false }, { "Id": 547, "Type": "Сверло D5.60", "OrderCode": "DH451056", "Vendor": "YG-1", "IsConsumables": true }, { "Id": 78, "Type": "Термопатрон ", "OrderCode": "50 06 A63 S", "Vendor": "POKOLM", "IsConsumables": false } ]; return { getTools: () => { return tools.slice(); } }; });

{{$item.OrderCode}}


В исходную директиву при помощи декоратора внесены незначительные изменения(создание события).
$rootScope.$broadcast('customSelect', scope.$selectMultiple.activeMatchIndex);
В контроллере обрабатываю это событие
$scope.$on('customSelect', function(event, value){ console.log('from custom select'); $scope.tool = $scope.assembly.tools[value]; });
так же в контроллере слежу за изменением tool при помощи $watch
$scope.$watch('tool', function(value){ console.log('from watch'); });
Выбираем любое кол-во инструментов в выпадающем списке, каждая добавленная позиция становится активной, применение класса btn-primary из бутстрапа.
При первом клике по одной из выбранных позиций в консоле видно что сработал обработчик события customSelect который в свою очередь меняет $scope.tool, но почему то не срабатывает обработчик из $watch, а если повторно кликнуть тот же элемент то в консоли видно что сработало оба обработчика.
Подскажите почему так происходит.


Ответ

Проблема в декораторе.
Если глянуть в исходники того же ngClick, можно отметить, что внутри обработчика jqLite вызывается $evalAsync, либо $apply
В твоем случае ты делаешь $broadcast, который, как видно из исходников не запускает $digest, следовательно нет проверки watch
Поэтому его нужно запускать самому, например с помощью .$apply()
var app = angular.module('testApp', ['ui.bootstrap', 'ui.select']); app.decorator('uiSelectMatchDirective', function($rootScope, $delegate) { var originalLinkFn = $delegate[0].link; $delegate[0].compile = function(element) { return function(scope, elm, attrs, controller) { elm.on('click', function(e) { $rootScope.$broadcast('customSelect', scope.$selectMultiple.activeMatchIndex); scope.$apply(); }); originalLinkFn.apply($delegate, arguments); }; }; return $delegate; }); app.decorator('uiSelectMultipleDirective', function($delegate) { var originalLinkFn = $delegate[0].link; $delegate[0].compile = function(element) { return function(scope, elm, attrs, controller) { scope.$on('uis:select', function(event, item) { scope.$selectMultiple.activeMatchIndex = controller[0].selected.length; }); originalLinkFn.apply($delegate, arguments); }; }; return $delegate; }); app.controller('detailsController', function($scope, toolService) { $scope.assembly = { tools: [] }; $scope.$on('customSelect', function(event, value) { $scope.tool = $scope.assembly.tools[value]; console.log('from handler custom select'); }); $scope.$watch('tool', function(value) { console.log('from watch') }); $scope.onSelectTool = function(tool) { $scope.tool = $scope.assembly.tools[$scope.assembly.tools.indexOf(tool)]; }; $scope.tools = toolService.getTools(); }); app.factory('assemblyService', function() { var assemblies = [{ "Position": 1.00, "Tools": [{ "Id": 104, "Type": "Центровочное сверло D10 ∠90°", "OrderCode": "D5306100", "Vendor": "YG-1", "IsConsumables": true, "Sequance": 14, "Overhang": 40.00, "AmountCuttingEdge": 0, "AmountPlates": 0, "Durability": 200.00, }, { "Id": 53, "Type": "Термопатрон ", "OrderCode": "50 10 A63 S", "Vendor": "POKOLM", "IsConsumables": false, "Sequance": 6, "Overhang": 40.00, "AmountCuttingEdge": 0, "AmountPlates": 0, "Durability": 200.00 } ], "Runtime": 1.00, "Note": null }, { "Position": 5.00, "Tools": [{ "Id": 889, "Type": "Корпус фрезы D42 z5", "OrderCode": "5 42 367", "Vendor": "POKOLM", "IsConsumables": false, "Sequance": 14, "Overhang": 43.00, "AmountCuttingEdge": 2, "AmountPlates": 5, "Durability": 90.00 }, { "Id": 750, "Type": "Пластина r2", "OrderCode": "04 67 896 R20 M40", "Vendor": "POKOLM", "IsConsumables": true, "Sequance": 12, "Overhang": 43.00, "AmountCuttingEdge": 2, "AmountPlates": 5, "Durability": 90.00 }, { "Id": 890, "Type": "Патрон ", "OrderCode": "HSK 63-16-50", "Vendor": "KEMMLER", "IsConsumables": false, "Sequance": 6, "Overhang": 43.00, "AmountCuttingEdge": 2, "AmountPlates": 5, "Durability": 90.00 } ], "Runtime": 40.00, "Note": null }, { "Position": 9.00, "Tools": [{ "Id": 891, "Type": "Фреза D20 r1", "OrderCode": "ф20R1z4 nACRo ТП", "Vendor": "ТЕХНОПОЛИС", "IsConsumables": true, "Sequance": 14, "Overhang": 70.00, "AmountCuttingEdge": 0, "AmountPlates": 0, "Durability": 180.00 }, { "Id": 223, "Type": "Термопатрон ", "OrderCode": "60 20 A63 S", "Vendor": "POKOLM", "IsConsumables": false, "Sequance": 6, "Overhang": 70.00, "AmountCuttingEdge": 0, "AmountPlates": 0, "Durability": 180.00 } ], "Runtime": 6.00, "Note": null }, { "Position": 11.00, "Tools": [{ "Id": 1241, "Type": "Фреза D12 r2", "OrderCode": "ф12R2z4 nACRo ТП", "Vendor": "ТЕХНОПОЛИС", "IsConsumables": true, "Sequance": 14, "Overhang": 55.00, "AmountCuttingEdge": 0, "AmountPlates": 0, "Durability": 180.00 }, { "Id": 51, "Type": "Термопатрон ", "OrderCode": "50 12 A63 S", "Vendor": "POKOLM", "IsConsumables": false, "Sequance": 6, "Overhang": 55.00, "AmountCuttingEdge": 0, "AmountPlates": 0, "Durability": 180.00 } ], "Runtime": 40.00, "Note": null }, { "Position": 15.00, "Tools": [{ "Id": 547, "Type": "Сверло D5.60", "OrderCode": "DH451056", "Vendor": "YG-1", "IsConsumables": true, "Sequance": 14, "Overhang": 25.00, "AmountCuttingEdge": 0, "AmountPlates": 0, "Durability": 100.00 }, { "Id": 78, "Type": "Термопатрон ", "OrderCode": "50 06 A63 S", "Vendor": "POKOLM", "IsConsumables": false, "Sequance": 6, "Overhang": 25.00, "AmountCuttingEdge": 0, "AmountPlates": 0, "Durability": 100.00 } ], "Runtime": 3.00, "Note": null } ]; return { getAssemblies: () => { return assemblies.slice(); } }; }); app.factory('toolService', function() { var tools = [{ "Id": 104, "Type": "Центровочное сверло D10 ∠90°", "OrderCode": "D5306100", "Vendor": "YG-1", "IsConsumables": true }, { "Id": 53, "Type": "Термопатрон ", "OrderCode": "50 10 A63 S", "Vendor": "POKOLM", "IsConsumables": false }, { "Id": 889, "Type": "Корпус фрезы D42 z5", "OrderCode": "5 42 367", "Vendor": "POKOLM", "IsConsumables": false }, { "Id": 750, "Type": "Пластина r2", "OrderCode": "04 67 896 R20 M40", "Vendor": "POKOLM", "IsConsumables": true }, { "Id": 890, "Type": "Патрон ", "OrderCode": "HSK 63-16-50", "Vendor": "KEMMLER", "IsConsumables": false, }, { "Id": 891, "Type": "Фреза D20 r1", "OrderCode": "ф20R1z4 nACRo ТП", "Vendor": "ТЕХНОПОЛИС", "IsConsumables": true }, { "Id": 223, "Type": "Термопатрон ", "OrderCode": "60 20 A63 S", "Vendor": "POKOLM", "IsConsumables": false }, { "Id": 1241, "Type": "Фреза D12 r2", "OrderCode": "ф12R2z4 nACRo ТП", "Vendor": "ТЕХНОПОЛИС", "IsConsumables": true }, { "Id": 51, "Type": "Термопатрон ", "OrderCode": "50 12 A63 S", "Vendor": "POKOLM", "IsConsumables": false }, { "Id": 547, "Type": "Сверло D5.60", "OrderCode": "DH451056", "Vendor": "YG-1", "IsConsumables": true }, { "Id": 78, "Type": "Термопатрон ", "OrderCode": "50 06 A63 S", "Vendor": "POKOLM", "IsConsumables": false } ]; return { getTools: () => { return tools.slice(); } }; });

{{$item.OrderCode}}


Почему это срабатывает при последующих переключениях: внутри элемента с классом ui-select-match есть элемент с классом ui-select-match-item - у которого прописан ng-click
Таким образом при клике, сначала вызывается клик внутреннего элемента, проходит digest цикл, затем шлется твой broadcast и на этом все заканчивается. При следующем клике при проверке watch определяет, что tools изменился с предыдущего запуска digest - и отрисовывает, а затем в обработчике твоего события ты снова меняешь реальное значение tools.

Стандарты кодирования WordPress для phpStorm под Windows

Соблюдение стандартов кодирования WordPress часто требуется при групповой работе над проектом. Кроме того, выполненный по стандартам код повышает оценку претендента в глазах потенциального работодателя.
Сами стандарты описаны в Codex, но подключение их к Code Sniffer в phpStorm вызывает определённые сложности. Подключение требует установки и настройки нескольких пакетов, инструкции для которых разбросаны по разным сайтам.
Как выглядит полная последовательность действий по установке стандартов кодирования WordPress для phpStorm под Windows?


Ответ

Анализаторы кода такие, как PHP Code Sniffer, и стандарты кодирования такие, как WordPress Coding Standards, позволяют создавать современный, легко обновляемый код, избежать множества ошибок еще на стадии написания кода, и совершенно незаменимы при командной работе над проектом. Эти средства встроены в phpStorm — мощную современную среду разработки под php.
В то же время, у многих разработчиков возникают сложности при установке анализатора кода PHP Code Sniffer и правил оформления кода WordPress Coding Standards для него в PhpStorm под Windows. Ниже сведены воедино сведения по установке, собранные из разных источников.
Процедура подключения стандартов кодирования под Windows выглядит следующим образом.
Установка PHP
Откройте страницу http://windows.php.net/download/ и выберите 32-х или 64-х битный файл в зависимости от битности архитектуры своей системы.
Еще есть варианты TS (Thread Safe) и NTS (Non Thread Safe). TS отличается тем, что позволяет PHP работать в нескольких потоках. Для простых приложений и взаимодействия с веб-сервером вполне подойдёт и NTS.
Распакуйте содержимое zip-архива, например, в c:\php
Переименуйте файл c:\php\php.ini-development в c:\php\php.ini
Найдите строку ;On windows: и раскомментируйте после нее
extension_dir = "ext"
Для работы с WordPress понадобятся несколько расширений.
Найдите секцию ; Windows Extensions и раскомментируйте строки
extension=php_mbstring.dll extension=php_mysqli.dll
Найдите строчку ;date.timezone =, раскомментируйте и задайте вашу временную зону, например:
date.timezone = 'Europe/Moscow'
Наконец, добавьте строчки для отладчика Xdebug в самый конец.
[xdebug] zend_extension=c:/php/ext/php_xdebug-2.5.5-7.1-vc14-x86_64.dll xdebug.remote_autostart=on xdebug.remote_enable=on xdebug.remote_handler="dbgp" xdebug.remote_host="localhost" xdebug.remote_port=9001 xdebug.remote_mode=req xdebug.idekey="ваш_пароль"
Сам отладчик в виде dll-файла надо скачать отсюда: https://xdebug.org/download.php и поместить в c:\php\ext. При скачивании выбрать ту же версию и разрядность PHP, что были выбраны при установке PHP.
Установка PEAR
Сохраните страницу https://pear.php.net/go-pear.phar как файл c:\php\go-pear.phar
Запустите cmd.exe и введите команды
cd c:\php php go-pear.phar
Ответьте на вопросы, приняв значения по умолчанию, нажимая Enter
Добавьте пути в системный PATH, дважды щёлкнув на созданном в процессе установки файле c:\php\PEAR_ENV.reg
Установка PHP Code Sniffer
В папке c:\php появился файл pear.bat, с помощью которого надо установить PHP Code Sniffer.
Последняя версия PHP Code Sniffer — https://pear.php.net/package/PHP_CodeSniffer/download
В cmd.exeвыполните команду, указанную на странице с последней версией PHP Code Sniffer. На данный момент это:
pear install PHP_CodeSniffer-3.1.1
В папке c:\php появились новые файлы, в том числе c:\php\phpcs.bat
Установка Composer
Для дальнейшей установки понадобится Composer
Загрузите файл https://getcomposer.org/Composer-Setup.exe в папку c:\php.
Запустите файл Composer-Setup.exe и проведите установку по умолчанию.
Для активизации путей Composer надо перезапустить окно командной строки.
Composer добавлен, теперь вы можете его использовать для установки других пакетов.
Установка PHP Code Sniffer в PhpStorm
Откройте в PhpStorm окно установок проекта (Ctrl+Alt+S) и выберите Languages&Frameworks → PHP
Установите PHP language level (7.1)
Нажмите на многоточие справа от CLI Interpreter
В окне CLI Interpreters выберите файл PHP executable (C:\php\php.exe) и Debugger extension (C:\php\ext\php_xdebug-2.5.5-7.1-vc14-x86_64.dll).

Нажмите OK
В этом же окне установок выберите Languages&Frameworks → PHP → Code Sniffer
Задайте к нему путь C:\php\phpcs.bat

Нажмите OK
В этом же окне установок выберите Editor → Inspections → PHP → PHP Code Sniffer validation и требуемый Coding standard. На картинке ниже выбран PEAR.

Нажмите ОК
Сейчас PHP Code Sniffer установлен, можно пользоваться командой меню Code-Inspect Code
Установка WordPress Coding Standards
В папке c:\php запустите команду (вот и пригодился Composer)
composer create-project wp-coding-standards/wpcs --no-dev
Появилась папка c:\php\wpcs
Добавление WordPress Coding Standards в PhpStorm
В окне установок выберите Languages&Frameworks → PHP → Code Sniffer и задайте к нему путь C:\php\wpcs\vendor\bin\phpcs.bat
Можете нажать кнопку Validate рядом для проверки.
Обратите внимание — вы подключили теперь другой Code Sniffer, из папки c:\php\wpcs
Нажмите ОК
В этом же окне установок выберите Editor → Inspections → PHP → PHP Code Sniffer validation и требуемый Coding standard. Если в списке стандартов не видите WordPress, нажмите на кнопку обновления справа (кольцевые стрелочки).

Советую использовать WordPress Extra — самый строгий стандарт кодирования, за исключением VIP. Но VIP предназначен для специальных установок WordPress, и на обычных сайтах невозможно удовлетворить его требованиям. Подробнее здесь: https://github.com/WordPress-Coding-Standards/WordPress-Coding-Standards
На этом процесс установки закончен, и теперь вы можете создавать профессиональный код для своих сайтов на WordPress.

Ansible как запустить скрип от другого пользователя, если плейбук начат root?

Доброй ночи,
Составляю роль для установки Rust.
Весь плейбук запущен под become_user = root
Игра фейлится после выполнения последнего таска, где нужно запустить скачаный скрипт от пользователя который был до become_user:
iwi.yml
---
- hosts: localhost become: True become_method: su become_user: root roles: - iwi-sys-upgrade-install - iwi-lang-rust
iwi-lang-rast/tasks/main.yml
---
- name: "download rustup-init.sh from https://www.rustup.rs" get_url: url: https://sh.rustup.rs dest: /tmp/rustup-init.sh mode: 0755
- name: "install Rust with /tmp/rustup-init.sh, run by {{ ansible_user_id }}" become: yes become_user: {{ ansible_user_id }} shell: '/tmp/rustup-init.sh -y'
Почемуто выдает синтаксическую ошибку:
ERROR! Syntax Error while loading YAML.
The error appears to have been in '/home/Sharlatan/Projects/prj/iwi/Ansible/roles/iwi-lang-rust/tasks/main.yml': line 11, column 17, but may be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
become: yes become_user: {{ ansible_user_id }} ^ here


Ответ

Кавычки добавьте:
become_user: "{{ ansible_user_id }}"
Подробности в доках. Без " Ansible думает, что { - это словарик.

Вывод нечетных чисел в Java

Есть метод, который выводит нечетные числа от 1 до 100
public String printUnpared() { for (d = 1; d <= 100; d++) { if (d % 2 != 0) { unpared[d] = d; }
} String stringUnpared = Arrays.toString(unpared); return stringUnpared;
И получаю вывод
[0, 1, 0, 3, 0, 5, 0, 7, 0, 9, 0, 11, 0, 13, 0, 15, 0, 17, 0, 19, 0, 21, 0, 23, 0, 25, 0, 27, 0, 29, 0, 31, 0, 33, 0, 35, 0, 37, 0, 39, 0, 41, 0, 43, 0, 45, 0, 47, 0, 49, 0, 51, 0, 53, 0, 55, 0, 57, 0, 59, 0, 61, 0, 63, 0, 65, 0, 67, 0, 69, 0, 71, 0, 73, 0, 75, 0, 77, 0, 79, 0, 81, 0, 83, 0, 85, 0, 87, 0, 89, 0, 91, 0, 93, 0, 95, 0, 97, 0, 99]
Как вывести нечетные числа без нулей не используя ArrayList? Как начать массив с 1, а не с 0? Нужно вернуть через return, выводить через System.out.print нельзя.


Ответ

Можно использовать ArrayList:
public String printUnpared() { List unpared = new ArrayList(); for (d = 1; d <= 100; d+=2) { unpaired.add(d); } String stringUnpared = Arrays.toString(unpared.toArray()); return stringUnpared; }
Либо рассчитывать нечётное число отдельно от индекса:
for (i = 0, d = 1; d <= 100; i++, d+=2) { unpaired[i] = d; }

Разделить данные из select

Есть множественный select с группами:

При передаче в обработчик, получаем подобный массив:
"place":["metro1","metro2","raion1"]
Вопрос: как, если, конечно, возможно, разбить передаваемые данные на вложенные массивы? Чтобы получилось вроде:
"place":{"metro":["metro1", "metro2"],"district":["district1"]}
Если невозможно, то какие способы есть для разделения подобных данных (на ум приходят только префиксы для value)?


Ответ

Если прям так сильно нужно, можно попробовать сделать так, не самый оптимальный вариант конечно:
В примере используется Jquery
HTML


Jquery
$('#placeID').change(function() { var arr = {}; $('select :selected').each(function(){
$('optgroup ').each(function() { arr[$(this).data('name')] = $(this).find('option:selected').map(function() { return $(this).val(); }).get(); }); console.log( JSON.stringify(arr) ); //Вывод для понимания $("#select_array").val( JSON.stringify(arr) ); });
php
print_r(json_decode( $_GET['select_array'] ,true)); //parse ?>
Результат:

Callback метода класса из другого класса без использования static, или с использованием только одного static объекта в С++ 0х14

Всем доброго времени суток, столкнулся с такой проблемой.
Есть чисто абстрактный класс UART который имеет следующий набор виртуальных методов :
virtual int8_t initialize(char* port_name, int baud_rate, Parity parity, int dataBits, StopBits stop_bits) = 0; virtual int8_t EDataReceived(void(*delegate)(char* data, const int16_t & length)) = 0; virtual void transmitDataToUart(char* data, const int16_t & length) = 0;
и есть 5 различных платформ на которых есть своя реализация класса, но все они унаследованы от класса UART, например class UART_Linux: UART. Это нужно для того что бы единожды написанный драйвер под микросхему SIM666 или SIM777, принимая в качестве параметра конкретный объект наследника UART мог бы работать на всех пяти платформах без изменений в коде. Когда на аппаратный uart приходят данные то класс UART должен делать колбэк вызов и передавать принятые данные с помощью функции, которая регистрируется в методе
virtual int8_t EDataReceived(void(*delegate)(char* data, const int16_t & length)).
Эта регистрация происходит внутри конструктора классов SIM666 и SIM777, например:
Sim666::Sim666(Uart& in_uart){ in_uart.EDataReceived(ReceiveDataFromUART); }
сам класс UART_Linux: UART не должен не чего знать о классе SIM666 т. к. это однажды написанный универсальный класс а SIM666 будет написан допустим спустя 5 лет после этого. Проблема заключается в том что метод ReceiveDataFromUART который является частью SIM666 должен обязательно быть статическим. Статическим его сделать не получается т. к. внутри этого метода должны использоваться не статик поля и свойства класса (т. е. конкретные экземпляры объекта).
Что бы решить мою проблему можно решить хотя бы один из трёх вопросов:
1) Реализовать возможность вызова внутри UART_Linux не статик стороннего метода.
2) Сделать метод Sim666.ReceiveDataFromUART статическим но реализовать в нём возможность обращения к не статическим полям класса (т. е. на уровне объекта).
3) Можно создать статический объект класса SIM666, тогда если регистрировать метода происходит снаружи класса SIM666 то всё работает, но внутри метода
void getPeriphery(const sd::Uart& in_uart) { in_uart.EDataReceived(ReceiveDataFromUART); };
компилятор не понимает что объект сейчас статический и данный вызов является корректным. Можно ли как то сообщить об этом компилятору? Что то типа как привести ReceiveDataFromUART к статику?
Стандарт языка С++ 0х14 , компиляторы и платформы как вы понимаете различны. Из стандартных библиотек можно использовать только стандартную библиотеку С.


Ответ

Если в Вашем случае использовать std::function, то это может покрыть все необходимые варианты использования
#include #include
typedef std::function Callback;
class MyClass { public: MyClass(Callback _cb): //регистрация колбэк функции в конструкторе m_callback(_cb) {
}
void invokeSomeFunc() { //много каких-то действий //... m_callback(0, 1); }
private: std::function m_callback; };
void func1(char* data, const int & length) { std::cout << __FUNCSIG__ << std::endl; }
class AnotherClass { public: static void staticFunc(char* data, const int & length) { std::cout << __FUNCSIG__ << std::endl; } };
class AnotherClass2 { public: void notStaticFunc(char* data, const int & length) { std::cout << __FUNCSIG__ << std::endl; } };
int main(int argc, char *argv[]) { //вариант использования с свободной функцией { MyClass someObj(func1); someObj.invokeSomeFunc(); }
//вариант использования с статической функцией класса { MyClass someObj(&AnotherClass::staticFunc); someObj.invokeSomeFunc(); }
//вариант использования с не статической функцией класса //тут нужно понимать, что объект someAnotherObj не должен быть разрушен на момент // вызова колбек функции, если invokeSomeFunc - асинхронная, то можно создать объект в куче. { AnotherClass2 someAnotherObj;
auto fn = [&someAnotherObj](char* data, const int & length) { someAnotherObj.notStaticFunc(data, length); };
MyClass someObj(fn); someObj.invokeSomeFunc(); }
{ AnotherClass2 *someAnotherObj = new AnotherClass2();
auto fn = [someAnotherObj](char* data, const int & length) { someAnotherObj->notStaticFunc(data, length); delete someAnotherObj; };
MyClass someObj(fn); someObj.invokeSomeFunc(); }
return 0; }
Если не хочется использовать std::function, можно в колбэк функцию добавить еще один параметр типа void* для пользовательских данных:
#include #include
typedef void(*Callback)(char* data, const int & length, void *_userData);
class MyClass { public: MyClass(Callback _cb, void *_userData = 0): m_callback(_cb), m_userData(_userData) {
}
void invokeSomeFunc() { //много каких-то действий //... m_callback(0, 1, m_userData); }
private: Callback m_callback; void *m_userData; };
void func1(char* data, const int & length, void *userData) { std::cout << __FUNCSIG__ << std::endl; }
class AnotherClass { public: static void staticFunc(char* data, const int & length, void *userData) { std::cout << __FUNCSIG__ << std::endl; } };
class AnotherClass2 { public: void notStaticFunc(char* data, const int & length) { std::cout << __FUNCSIG__ << std::endl; } };
void funcToCallObjectMethod(char* data, const int & length, void *userData) { AnotherClass2 *obj = static_cast(userData); obj->notStaticFunc(data, length); delete obj; }
int main(int argc, char *argv[]) { //вариант использования с свободной функцией { MyClass someObj(func1); someObj.invokeSomeFunc(); }
//вариант использования с статической функцией класса { MyClass someObj(&AnotherClass::staticFunc); someObj.invokeSomeFunc(); }
//вариант использования с не статической функцией класса //тут нужно понимать, что объект someAnotherObj не должен быть разрушен на момент // вызова колбек функции, если invokeSomeFunc - асинхронная, то можно создать объект в куче. { AnotherClass2 someAnotherObj;
auto fn = [](char* data, const int & length, void *userData) { AnotherClass2 *obj = static_cast(userData); obj->notStaticFunc(data, length); };
MyClass someObj(fn, &someAnotherObj); someObj.invokeSomeFunc(); }
{ AnotherClass2 *someAnotherObj = new AnotherClass2();
auto fn = [](char* data, const int & length, void *userData) { AnotherClass2 *obj = static_cast(userData); obj->notStaticFunc(data, length); delete obj; };
MyClass someObj(fn, someAnotherObj); someObj.invokeSomeFunc(); }
{ AnotherClass2 *someAnotherObj = new AnotherClass2();
MyClass someObj(funcToCallObjectMethod, someAnotherObj); someObj.invokeSomeFunc(); }
return 0; }
Как третий вариант, можно использовать реализацию паттерна Observer, пример уже приводить не буду.
IMHO, первый вариант самый удобный и экономит время в разработке.

Как объединить 2 Dictionary в один новый?

Добрый день. Возник дурацкий вопрос: Есть 2 Dictionary. Нужно получить третий, который будет включать в себя все пары "ключ-значение" из первого и второго. Точно известно, что совпадения ключей в словарях нет.
Пробовал так - не получается.
Markers = new Dictionary(CreateCommonMarkers(vm)); Markers.Concat(UniqueMarkers); return Markers;
CreateCommonMarkers - создается правильно UniqueMarkers - создается правильно Markers получает первый словарь правильно. А вот Concat не срабатывает. На выходе - дубликат словаря, создаваемого CreateCommonMarkers(vm) Соответственно вопрос: что я сделал не так?
P.S. сделать через foreach я могу, но может есть менее извращенный способ.


Ответ

Если уверены, что совпадений ключей нет, то можно попробовать так:
var dict1 = Enumerable.Range(0,10).ToDictionary(x=>x); var dict2 = Enumerable.Range(15,10).ToDictionary(x=>x);
Для объединения словарей можно использвать методы Union или Concat. Разница между ними только в том, что Union удаляет доубликаты, что в данном контексте не требуется. Для маленьких словарей нет разницы, какой из методов использовать, для больших, как мне кажется, Concat будет работать быстрей.
var combined_version_1 = dict1.Union(dict2).ToDictionary(x=>x.Key, x=>x.Value); var combined_version_2 = dict1.Concat(dict2).ToDictionary(x=>x.Key, x=>x.Value);
Как видно, на входе получается третий словарь, который можно вернуть как результат.

Как сделать синхронным запрос к vk api в цикле через async/await или promise?

Есть следующий код:
for(let i = 1; i <= length; i++){ VK.Api.call('groups.getById', {group_id: groups[i].gid, fields: 'description'}, (output) => { showVisual(groups[i]); console.log(output); }) }
Проблема в том, что цикл for уже завершается, когда мы получаем ответ в колбеке. Как сделать запрос синхронным?


Ответ

Предполагая, что Ваша проблема в некорректном значении i в callback, предлагаю замкнуть значение этой переменной:
for(let i = 1; i <= length; i++){ VK.Api.call('groups.getById', {group_id: groups[i].gid, fields: 'description'}, ((i) => (output) => { showVisual(groups[i]); console.log(output); })(i) ); }
Или, можно добавить в замыкание значение group:
for(let i = 1; i <= length; i++){ VK.Api.call( 'groups.getById', {group_id: groups[i].gid, fields: 'description'}, ((group) => (output) => { showVisual(group); console.log(output); } )(groups[i]) ); }
С Promise можно реализовать так:
function getGroupDescription(group) { return new Promise(resolve => VK.Api.call(..., resolve)); }
for (let i = 1; i <= length; ++i) { getGroupDescription(groups[i]).then(output => console.log(output)); }
Проблема async/await в его каскадном распространении - чтобы его использовать, клиент должен завернуть свой код в async функцию:
function getGroupDescription(group) { return new Promise(resolve => VK.Api.call(..., resolve)); }
async function foo () { // ... for (let i = 1; i <= length; ++i) { groups[i].description = await getGroupDescription(groups[i]); } } foo();

Рабочий пример:
function timedTwice(val, time = 1000) { return new Promise(res => setTimeout(() => res(2*val), time)); } timedTwice(5).then(r => console.log(r)); async function foo() { for (let i = 0; i < 8; ++i) { let result = await timedTwice(i, 100); console.log(i, result); } } foo();

Как записать данные в файл с консоли

Мне надо создать метод, который будет записывать все содержимое введенное пользователем с консоли в файл по заданному пути, не перезаписывая содержимое файла. И запись в файл должна производиться после ввода команды wr в отдельной строке. Метод я сделал, но в результате его работы, срабатывает сразу первый cath. Когда дебажу программу показывает - процесс не может получить доступ к файлу, так как этот файл занят другим процессом.
public static void writeToFileFromConsole(String path){
InputStreamReader reader = new InputStreamReader(System.in); BufferedReader br = new BufferedReader(reader);
FileWriter writer = null; BufferedWriter bufferedWriter = null;
System.out.println("Enter file content to write in the file:");
try { String command = "wr"; String line; line = br.readLine(); if ((line = br.readLine()).equals(command)) { writer = new FileWriter(path, true); bufferedWriter = new BufferedWriter(writer);
bufferedWriter.append("
"); bufferedWriter.append(line); } }catch (IOException e){ System.err.println("Can't write to file with path " + path); } finally { try { reader.close(); br.close(); if (writer != null){ writer.close(); } if (bufferedWriter != null){ bufferedWriter.close(); } }catch (IOException e){ System.err.println("File with path " + path + " not found" ); } } }
Подскажите в чём моя ошибка ? И как исправить, чтобы в файл записывались данные введенные в консоли.


Ответ

Проверил ваш код, ошибок никаких не возникло, кроме исключения сообщавшего, что файла не существует.
Переделал, чтобы его не возникало:
private static void writeToFileFromConsole(String path) { BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
System.out.println("Enter file content to write in the file:");
try (OutputStream output = Files .newOutputStream( Paths.get(path), StandardOpenOption.APPEND, StandardOpenOption.CREATE); BufferedOutputStream outputStream = new BufferedOutputStream(output)) {
String line; while (!"rw".equalsIgnoreCase(line = reader.readLine())) { outputStream.write(System.lineSeparator().getBytes()); outputStream.write(line.getBytes()); } } catch (IOException e) { e.printStackTrace(); } }

Получение ближайших элементов в массиве

Дан двумерный массив:
static int[][] multi = new int[][] { {2, 0, 4, 1241, 424, 1, 12323}, {1, 3, 5, 7}, {321, 320, 32, 41241, -11, -12, -13, -66, -688} };
Нужно создать метод, который принимает индекс определенного элемента и int range - количество элементов, находящихся рядом с элементом. Метод должен вернут массив. Например:
nearby(0, 2, 2) вернет 2, 0, 1241, 424
Я сделал что-то вроде:
public static int[] nearby(int x, int y, int range) { int index = 1; ArrayList leftSize = new ArrayList(); ArrayList rightSize = new ArrayList(); while (index != range + 1) { leftSize.add(multi[x][y - index]); rightSize.add(multi[x][y + index]); index += 1; } leftSize.addAll(rightSize); int[] stockArr = new int[leftSize.size()]; return stockArr; }
Но не уверен, что это лучшее решение. Какой алгоритм использовать, чтобы работать напрямую с массивом, не создавая ArrayList?


Ответ

Если опустить проверки границ, то получается так:
public static int[] nearby(int x, int y, int range) { int[] result = new int[range * 2]; System.arraycopy(multi[x], y - range, result, 0, range); System.arraycopy(multi[x], y + 1, result, range, range); return result; }
Результат:
[2, 0, 1241, 424]

Если при выходе за границы массива дело должно кончаться не ArrayIndexOutOfBoundsException, а возвратом только допустимых элементов (вплоть до пустого массива), то проще сделать через список:
public static int[] nearby(int x, int y, int range) { if (x < 0 || x >= multi.length) return new int[0]; List result = new ArrayList<>(); for (int i = Math.max(y - range, 0); i <= y + range && i < multi[x].length; i++) { if (i != y) { result.add(multi[x][i]); } } return result.stream().mapToInt(e -> e).toArray(); }
Альтернативным вариантом, с использованием только массивов, тут будет предварительный расчет границ копируемых участков (leftFirst, leftLast, rightFirst, rightLast), но восприниматься это будет тяжелее:
public static int[] nearby(int x, int y, int range) { if (x < 0 || x >= multi.length || y - range >= multi[x].length || y + range < 0) return new int[0]; int leftFirst = Math.max(y - range, 0); int leftLast = Math.min(y - 1, multi[x].length - 1); int leftLength = leftLast - leftFirst + 1; int rightFirst = Math.min(y + 1, multi[x].length); int rightLast = Math.min(y + range, multi[x].length - 1); int rightLength = rightLast - rightFirst + 1; int[] result = new int[leftLength + rightLength]; System.arraycopy(multi[x], leftFirst, result, 0, leftLength); System.arraycopy(multi[x], rightFirst, result, leftLength, rightLength); return result; }

inconsistent java and sql object types

Есть типы в БД:
CREATE OR REPLACE TYPE my_type IS OBJECT ( id VARCHAR2(20), name VARCHAR2(40), phone NUMBER );
Пытаюсь создать обьекты с использованием этих типов:
.... Object[] myArray = new Object[3]; Object[] struct = new Object[values.size()];
int arrayIndex = 0; for (User user : values) { myArray[0] = user.id().toString(); myArray[1] = user.getName().toString(); myArray[2] = user.getPhone();
struct[arrayIndex++] = con.createStruct("my_type",myArray); }
На строке с созданием структуры con.createStruct выпадает ошибка:
java.sql.SQLException: Inconsistent java and sql object types, for classes implementing ORAData or OracleData, respective factory classes ORADataFactory and OracleDataFactory should be registered in typeMap.
В чем может быть дело?


Ответ

Это помогло мне.
for (User user : values) { struct[arrayIndex++] = con.createStruct( "my_type", new Object[]{ user.id().toString(); user.getName().toString(); user.getPhone(); }); }

Как конвертировать Bitmap в HBITMAP без потери прозрачности?

Здравствуйте! Мне нужно загрузить из файла/ресурса png рисунок с альфа пикселями и вывести его на контекст. С загрузкой хорошо справляется GDI+, но вот с выводом изображения Graphics::DrawImage() всё плохо, поэтому я решил использовать GDI (StretchBlt и т.д.). Но в StretchBlt Bitmap не запихнёшь, нужен HBITMAP. У Bitmap есть метод GetHBITMAP(Color, HBITMAP*), и в нём-то и проблема: он, почему-то конвертирует с потерей прозрачности. Я пробовал передавать Color::Black и Color::Transparent, но ничего не помогает. Как тогда получить HBITMAP с прозрачностью?
ОБНОВЛЕНО
Я понял в чём была ошибка и она глупая. Ниже вы можете увидить мой код, звёздочками я выделил то чего не было. То есть я рисовал изображение на второй буффер, на котором ничего не было, и удивлялся почему-это, альфа пиксели чёрные. Теперь я додумался что нужно сначала нарисовать на буфер фон, а потом уже рисовать изображение. Но опять проблема: Рисую несколько картинок, вместо картинки 1920/1080(мой экран) просто чёрное(у картинки нет альфа пикселей), картинки других размеров, 100/100 например, в произвольных расположениях рисуются, но с чёрным прямоугольником в левом верхнем углу(у картинки нет альфа пикселей). Если закрасить главный фон(перед выводом изображения 1920/1080, то фон таким же и будет(картинка вообще никак не выводится, получается?), у маленьких картинок цвет прямоугольника такой же становится(походу это прозрачность(но альфа пикселей,ещё раз, там нет).
Я так понимаю, второй раз использовать SelectObject нельзя, да? В общем, подскажите в чём тут проблема?
class Imagee { HDC hdc; HBITMAP bm;
Imagee(HDC hdc, HBITMAP bm, another args) { this->hdc=CreateCompatibleDC(hdc); this->bm=bm; SelectObject(this->hdc,this->bm); }
void draw(int hdcc, int x,int y, int cx, int cy) { **StretchBlt(this->hdc,0,0,cx,cy,hdcc,x,y,cx,cy,SRCCOPY); SelectObject(this->hdc,this->bm); **
StretchBlt(this->hdcc,x,y,cx,cy,hdc,0,0,cx,cy,SRCCOPY); };
};
Imagee *image;
void render() { for(;;) {
//здесь я загружая через гди+ битмам, конвертирую его в хбитмап, записывая в перменную HBITMAP hbm
if(image==0) image=new Imagee(hdc_mem, hbm, x, y и т.д.);
image->draw(hdc_mem, x, y и т.д.);
StretchBlt(hdc_main,0,0,1920,1080,hdc_mem,0,0,1920,1080,SRCCOPY); //hdc_main главный дс, hdc_mem буфер
} }


Ответ

Отвечая на вопрос:
_Check_return_ ::HBITMAP LoadPng(_In_z_ const ::LPWSTR psz_file_path) { ::HBITMAP bitmap_handle{}; if(psz_file_path) { const ::BOOL embedded_color_management{TRUE}; ::Gdiplus::Bitmap bitmap{psz_file_path, embedded_color_management}; if(::Gdiplus::Status::Ok == bitmap.GetLastStatus()) { const ::Gdiplus::Color background{0, 0, 0, 0}; if(::Gdiplus::Status::Ok != bitmap.GetHBITMAP(background, &bitmap_handle)) { bitmap_handle = NULL; } } } return(bitmap_handle); }
Как ни странно, но Color::Transparent это не то же самое, что 0, 0, 0, 0. Еще стоит отметить что полученная картинка будет уже с предварительно умноженным (premultiplied) альфа каналом, т.е. пригодная для рисования посредством GdiAlphaBlend и прочих функций.

Отображение нескольких подсказок при нажатии на блок

Здравствуйте. Необходимо реализовать следующий функционал: пользователь нажимает на блок (в примере ниже это li) и над другими блоками появляется текст из атрибута title. При чем текст должен появится над всем указанными блоками и исчезнуть по повторному нажатию на кнопку (или на li). Пробовал сделать вот так
function showTittles() { var buttShow = document.getElementById("buttShow"); var titlesContainer = document.querySelectorAll("#servicelist1, #servicelist2, #servicelist3"); var buttonsArr = document.getElementById("buttonsArr"); var listItem = buttonsArr.querySelectorAll("#redBlc, #greenBlc, #blueBlc"); var showingTooltip; var titles = []; for (var i = 0; i < titlesContainer.length; i++) { titles.push(titlesContainer[i].getAttribute('title')); } listItem.forEach (function (i) { listItem[i].addEventListener("click", function() { switch (i) { case i === 0: addTitle(0); break; case i === 1: addTitle(1); break; case i === 2: addTitle(2); break; default: consol.log("жми!"); break; } }) }) } function addTitle(elem) { var target = titlesContainer[elem]; var tooltipElem = document.createElement('div'); tooltipElem.className = 'tooltip-title'; tooltipElem.innerHTML = titles[elem]; document.body.appendChild(tooltipElem); var coords = target.getBoundingClientRect(); var left = coords.left + (target.offsetWidth - tooltipElem.offsetWidth) / 2; if (left < 0) left = 0; // не вылезать за левую границу окна var top = coords.top - tooltipElem.offsetHeight - 5; if (top < 0) { // не вылезать за верхнюю границу окна top = coords.top + target.offsetHeight + 5; } tooltipElem.style.left = left + 'px'; tooltipElem.style.top = top + 'px'; showingTooltip = tooltipElem } showTittles(); .servicelist { display: inline-block; width: 100px; height: 100px; position: relative; } #servicelist1 { border: 1px solid #f00; } #servicelist2 { border: 1px solid #0f0; } #servicelist3 { border: 1px solid #00f; } .tooltip-title { position: fixed; padding: 10px 20px; border: 1px solid #b3c9ce; border-radius: 4px; text-align: center; font: italic 14px/1.3 arial, sans-serif; color: #333; background: #fff; box-shadow: 3px 3px 3px rgba(0, 0, 0, .3); }

  • Показать красный тултип
  • Показать зеленый тултип
  • Показать голубой тултип

Некоторый код брал из этого примера
Сейчас он ругается на addEventListener и я не знаю как сделать так, чтобы по нажатию на отдельный li появлялся личный title над каждым блоком.
P.S в проекте уже есть jquery и bootstrap, так что если легче будет с ними, то подойдет решение с применением этих инструментов, спасибо :)


Ответ

Не совсем понял что вам нужно. Написал как понял. Уточните/напишите если что не так.
В примере используется jquery 2.1.1
(function($) { $('li').click(function(){ var id = $(this).attr('id'); $(document).find('.servicelist').html(''); $(document).find('.servicelist').html($(document).find('[data-id="'+id+'"]').attr('title')); }); })( jQuery ); /* Удалить CSS */ body { display: flex; flex-wrap: wrap; } .servicelist { margin: 5px; } ul#buttonsArr { width: 100%; } /* конец CSS */ .servicelist { display: inline-block; width: 100px; height: 100px; position: relative; } #servicelist1 { border: 1px solid #f00; } #servicelist2 { border: 1px solid #0f0; } #servicelist3 { border: 1px solid #00f; } .tooltip-title { position: fixed; padding: 10px 20px; border: 1px solid #b3c9ce; border-radius: 4px; text-align: center; font: italic 14px/1.3 arial, sans-serif; color: #333; background: #fff; box-shadow: 3px 3px 3px rgba(0, 0, 0, .3); }

  • Показать красный тултип
  • Показать зеленый тултип
  • Показать голубой тултип

Положить в HashMap одинаково параметризованные дженерики

Предположим, есть следующая иерархия классов:
static interface A { void roll(); }
static class B implements A { @Override public void roll() { System.out.println("B"); } }
static class C implements A { @Override public void roll() { System.out.println("C"); } }
Требуется положить в HashMap следующее: map.put(Class, List), где E - класс, наследующийся от A, причем, нужно запретить класть разноименно параметризованные классы. Пример: Должно компилироваться:
map.put(Class,List); map.put(Class,List);
Ошибка компиляции:
map.put(Class, List);
Как это можно реализовать, если вообще такое возможно?


Ответ

Если я правильно понял вопрос, то нужно сделать так:
private static Map, List> getMap() { return new HashMap<>(); }
И использовать этот метод следующим образом:
Map, List> map = getMap(); map.put(B.class, new ArrayList<>()); map.put(C.class, new ArrayList<>()); //<- ошибка компиляции
UPDATE
Боюсь, то что вы хотите, нельзя сделать без создания дополнительного класса, который реализовывал бы данное ограничение.
Например так:
private static class CustomMap { private final Map, List> map = new HashMap<>();
@SuppressWarnings("unchecked") public

List

get(P key) { return (List

) map.get(key); }
@SuppressWarnings("unchecked") public

List

put(Class

key, List

value) { return (List

) map.put(key, value); } }
Тогда,
CustomMapmap = new CustomMap<>() ;
map.put(C.class, new ArrayList()); map.put(B.class, new ArrayList()); map.put(C.class, new ArrayList()); //<-- ошибка компиляции
Но в данном решении есть существенные недостатки, одно из них это то, что CustomMap не реализовывает интерфейс Map

C# CefSharp.OffScreen как его использовать, как получить html с уже выполненным JS

Здесь есть несколько статей по CefSharp, но как правило примеры кода скудные, и не раскрываю как использовать библиотеку . То что здесь нашёл выкидывает исключения, в общем прошу помочь - привести пример . Как без загрузки фрейма получить html текст с исполненным javascript. Скрипты должны выполнятся все, как в браузере , а не отдельно указанный в Task . Привожу пример с отображением в форме для CefSharp.WinForms , мне нужна аналогия кода для CefSharp.OffScreen 57 . DotNet 4.5.2
public partial class FormMain : Form { private ChromiumWebBrowser browser; private CefSettings settings;
public FormMain() { InitializeComponent(); InitializeWebBrowser(); } private void InitializeWebBrowser() { settings = new CefSettings();
settings.CefCommandLineArgs.Add("proxy-server", "37.75.9.131:8080"); settings.UserAgent = "Mozila 5.0";
Cef.Initialize(settings);
browser = new ChromiumWebBrowser(string.Empty) { Dock = DockStyle.Fill };
Controls.Add(browser); } private void btnLoad_Click(object sender, EventArgs e) { browser.Load("https://google.com"); } private async void btnSource_Click(object sender, EventArgs e) { var doc = browser.GetMainFrame(); string text = await doc.GetTextAsync(); } }
Таким образом я задал Proxy и UserAgent получил исходный код с обработанным JS спарсил текст страницы без разметки.
Html можно получить сл. образом : заменить string text = await doc.GetTextAsync(); на string html = await doc.GetSourceAsync();


Ответ

Пришёл к такому решению для CefSharp.OffScreen
private void btnGetText_Click(object sender, EventArgs e) { LoadFullText("https://google.com"); }
private async void LoadFullText(string url) { CefSettings settings = new CefSettings(); settings.CefCommandLineArgs.Add("proxy-server", "37.75.9.131:8080"); settings.UserAgent = "Mozila 5.0"; Cef.Initialize(settings); using (var chromium = new ChromiumWebBrowser(string.Empty)) { Thread.Sleep(1000); await LoadPageAsync(chromium, url); string text = await chromium.GetTextAsync(); } }
public static Task LoadPageAsync(IWebBrowser browser, string address = null) { var tcs = new TaskCompletionSource(); EventHandler handler = null; handler = (sender, args) => { if (!args.IsLoading) { browser.LoadingStateChanged -= handler; tcs.TrySetResultAsync(true); } }; browser.LoadingStateChanged += handler; if (!string.IsNullOrEmpty(address)) { browser.Load(address); } return tcs.Task; }
Для проверки работы просто вставил Thread.Sleep(1000); здесь нужно доработать.

Как сравнить отдельно объекты двух массивов

Есть 2 массива с именами и ценами некоторых предметов (x и ItemPrice). Для того чтобы у меня были отдельно имена двух массивов, сделал для обоих такие циклы:
for (i = 0; i < x.length; i++){ names = x[i]; name = names.custom_market_name; }
for (i = 0; i< ItemPrice.length; i++){ namess = ItemPrice[i]; namex = namess.market_name; }
Теперь мне их нужно отдельно последовательно сравнить(имена одного с именами другого массива). Я пытался сделать это такой функцией:
function OnGoPressed(){ var ln1 = namex.length, ln2 = name.length; for (var i = 0; i < ln1; ++i){ cache = namex[i]; for (var j = 0; j < ln2; ++j){ if (cache == name[j]){ console.log('найдено совпадение: ' + cache); break; } } } }
но результата 0.


Ответ

Ваши переменные name и namex - это не массивы, а имена последних элементов в соответствующих массивах.
var namesX = x.map(function(item){ return item.custom_market_name; }); var namesIP = ItemPrice.map(function(item){ return item.market_name; });
for (var i = 0; i < namesX.length; i++) { if (namesIP.indexOf(namesX[i]) != -1) console.log('найдено совпадение: ' + namesX[i]); }

Доступ к локальным переменным из анонимного класса Java

Всем добрый день, стало интересно, как работает доступ к локальным переменным из анонимного класса. Допустим у меня такой код
public void addListener() { String s = "Hello World"; this.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { System.out.println(s); } }); }
И пусть этот метод будет определён в классе унаследованном от JButton. Вот мы при нажатии на эту кнопку всегда будет видеть сообщение Hello World! Но мне интересно, как он будет ссылаться на этот Hello World!. Есть предположение, что у него есть некая невидимая ссылка на тот же на объект, что ссылается s т.е. на Hello World!, но я не уверен в своём предположении, так что прошу у вас ответа на данный вопрос, спасибо.


Ответ

Все просто, анонимные классы компилируются в отдельные классы с названием <название родительского класса>$<индекс>.class. Если посмотреть на его содержимое, в частности, для примера который был в вопросе, то увидим следующее:
class ru.izebit.Main$1 implements java.awt.event.ActionListener { final java.lang.String val$s; final ru.izebit.Main this$0; ru.izebit.Main$1(ru.izebit.Main, java.lang.String); public void actionPerformed(java.awt.event.ActionEvent); }
ru.izebit.Main - это название класса, в котором я объявил анонимный класс. val$s - ссылка на строковую переменную со значением "Hello World" this$0 - ссылка на объект класса, в котором был объявлен анонимный класс, т.к. из анонимного класса есть возможность получать доступ к полям объекта, а это происходит именно через эту переменную

Авторизация на сервере через POST HTTP-запрос с Android

Пишется приложение на Android для сервера WildFly. Со стороны сервера вход под своим логином и паролем на сайт со страницы авторизации выглядит так:
@WebServlet(name = "LoginServlet", urlPatterns = "/login.do") public class LoginServlet extends HttpServlet{ SecurityManager sm = new SecurityManager();
@Override protected void doPost(HttpServletRequest request, HttpServletResponse response) { String email = request.getParameter("email").toLowerCase(); String pass = request.getParameter("pass");
if (sm.checkUser(email, pass)){ request.getSession().setAttribute("user", email); response.sendRedirect(request.getContextPath() + "/testpage"); } else { response.sendRedirect(request.getContextPath()); } } }
Также есть WebFilter, не пускающий на /testpageпользователя, если у сессии нет атрибута user. Это достигается следующим методом в классе LogiFilter
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws ServletException, IOException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; HttpSession session = request.getSession(false); boolean loggedIn = session != null && session.getAttribute("user") != null;
if (loggedIn) { chain.doFilter(request, response); } else { response.sendRedirect(request.getContextPath()); } }
При попытке осуществления авторизации через браузер сервер адекватно отрабатывает согласно имеющейся в БД паре log | pass. В зависимости от выданного атрибута user на дочерней странице /testpage отображается соответствующая авторизованному пользователю информация.
Тем не менее, при попытке авторизации с приложения Android несмотря на отправку методом POST верной (не верной в том числе) пары log | pass атрибут user не закрепляется, из-за чего происходит редирект со страницы /testpage на страницу авторизации. Код осуществления POST-запроса:
private class PostTask extends AsyncTask { @Override protected String doInBackground(String... data) { final String USER_AGENT = "Mozilla/5.0";
try { StringBuilder tokenUri = new StringBuilder("email="); tokenUri.append(URLEncoder.encode("email", "UTF-8")); tokenUri.append("&pass="); tokenUri.append(URLEncoder.encode("password", "UTF-8"));

String url = "http://xxx.xxx.xxx.xxx:xxxxx/sendmsg/login.do"; URL obj = new URL(url); //HttpURLConnection.setFollowRedirects(false); HttpURLConnection con = (HttpURLConnection) obj.openConnection();
con.setRequestMethod("POST"); con.setRequestProperty("User-Agent", USER_AGENT); con.setRequestProperty("Accept-Language", "UTF-8"); //con.setInstanceFollowRedirects(false);
con.setDoOutput(true); OutputStreamWriter outputStreamWriter = new OutputStreamWriter(con.getOutputStream()); outputStreamWriter.write(tokenUri.toString()); outputStreamWriter.flush();
int responseCode = con.getResponseCode(); System.out.println("
Sending 'POST' request to URL : " + url); System.out.println("Post parameters : " + tokenUri); System.out.println("Response Code : " + responseCode); System.out.println("Response Body : " + con.getResponseMessage());
BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream())); String inputLine; StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) { response.append(inputLine); }; in.close();
System.out.println(response.toString());
}catch (Exception e){e.printStackTrace();} return null; }
@Override protected void onPostExecute(String s) { //new GetTask().execute(); super.onPostExecute(s); } }
При использовании параметра HttpURLConnection.setFollowRedirects(false); редирект не производится, а response code становится равным 307 (Temporary Redirect). При попытке получить GET-запросом /testpage приходит response code 302 (Found). При включении редиректа в любом случае приходит страница авторизации.
Не исключено, что требуется ручное присвоение сессии атрибутов или использование cookie.


Ответ

Да, вы верно подметили, HttpURLConnection сам сессиями и куками не занимается, ему их надо каждый раз выставлять.
Как пример, можете посмотреть образец кода на enSO: https://stackoverflow.com/a/16171708/5479247

Как работать с символами в const char*

Есть переменная которая объявлена как const char* str_value. Нужно получить самое короткое слово в ней. Например, при выводе в цикле printf("%s = ", str_value[i]); программа завершается, а при сравнении с " " ругается на char*. Как это нужно исправить? Завершение даже при printf("%s = ", str_value[1]);
При выводе printf("%d = ", str_value[1]); выходит число. Это код символа? Почему?


Ответ

Примерно так должна выглядеть ваша функция:
void findShortestWord(const char* in, char* out) { int len = strlen(in);
int beginWord = 0; int lengthWord = 0;
for (int i = 0; i < len; i++) { while ( (in[i] == ' ') && ( in[i] != '\0') ) i++; int begin = i; while ( (in[i] != ' ') && (in[i] != '\0') ) i++; int end = i; char tmp[256]; for(int j = begin, k=0; j < end; j++, k++) tmp[k] = in[j]; tmp[end - begin] = '\0';
int currentLen = end - begin; if (currentLen > lengthWord) { lengthWord = currentLen; beginWord = begin; } }
for (int i = beginWord, k = 0; i < beginWord + lengthWord; i++, k++) out[k] = in[i];
out[lengthWord] = '\0'; }

Упрощения триггера WPF

Добрый утро! Каким образом можно упростить данный триггер и какие замечания будут по данному коду?
...

Как динамически задать курсор из строки?

Хочу передавать в процедуру строку с запросом для курсора. Пробую сделать это так:
DECLARE TYPE t_client_cursor IS REF CURSOR RETURN CLIENTS%ROWTYPE; v_client_cursor t_client_cursor; v_client CLIENTS%ROWTYPE; v_query_text VARCHAR2(100) := 'SELECT * FROM CLIENTS C WHERE ROWNUM < 4 ORDER BY C.CLNT_ID'; BEGIN OPEN v_client_cursor FOR exec (v_query_text); .... END; /
Ругается
ORA-06550: Трока 8, столбец 6; PLS-00455: курсор 'V_CLIENT_CURSOR' нельзя использовать в команде OPEN dynamic SQL ORA-06550: Строка 8, столбец 1: PL/SQL: Statement ignored
Но как-то ведь должно быть возможным определить курсор из строки?


Ответ

Я так понимаю, вам нужно что-то вроде этого:
declare v_client_cursor sys_refcursor; v_query_text VARCHAR2(100) := 'SELECT * FROM CLIENTS C WHERE ROWNUM < 4 ORDER BY C.CLNT_ID'; begin open v_client_cursor for v_query_text;
...
close v_client_cursor; end; /

Постоянное соединение с базой MySQL

Из-за повторных соединений с базой возникает существенная нагрузка на сервер MySQL, на эти операции приходится 64% от общей нагрузки, неизбежно приходиться смотреть в сторону постоянного подключения, подскажите как лучше это сделать? PDO или Myqli? Сейчас сайт сверстан на PDO. Читал в интернете, что при постоянном соединений не рекомендуется PDO. С радостью приму также дополнительные советы.
Вот статистика нагрузки на базу:


Ответ

Сначала, ссылка, просто прочитайте всё там - минут 10, зато многие вопросы сами собой отпадут.
Не понимаю, откуда взялась эта дурацкая легенда о том, что нужно обязательно "закрывать" соединение? Может так и было в php версии 4 и ниже, на устаревшем ныне расширении mysql, но в PDO совершенно точно это не так. Вот просто реально, все кто про это пишет - зайдите и еще раз внимательно прочитайте
При успешном подключении к базе данных в скрипт будет возвращен созданный PDO объект. Соединение остается активным на протяжении всего времени жизни объекта. Чтобы закрыть соединение, необходимо уничтожить объект путем удаления всех ссылок на него (этого можно добиться, присваивая NULL всем переменным, указывающим на объект). Если не сделать этого явно, PHP автоматически закроет соединение по окончании работы скрипта.
Как правильно пишут товарищи в комментариях - по-хорошему, за выполнение скрипта соединение должно создаваться один лишь раз. Как вы это реализуете - без разницы: можно использовать глобальную переменную, можно использовать статическую переменную функции, можно использовать статический метод класса (со статической переменной класса). Все кто пишут просто "статические методы - плохо", это, конечно, гениально, повторять за большинством, но я реально не вижу ни одной разумной причины, по которой, например, для каждого объекта люди почему-то создают новое соединение. И уж совершенно точно не нужно на каждый запрос открывать отдельное соединение. Раз уж у вас есть PDO - используйте его возможности на максимум:
а. используйте подготовленные запросы и никогда не вклеивайте переменные вручную в строку запроса.
б. где нужно именно выполнить несколько запросов в цикле - делаете PDO::prepare перед циклом, записываете в переменную, далее в цикле уже выполняете PDOStatement::execute и PDOStatement::fetchAll. Но всё же в большинстве случаев рекомендую подумать - а нельзя ли все необходимые данные вытянуть, всё же, одним запросом.
в. ознакомьтесь с константами PDO::FETCH_*. чаще всего необходимы:
PDO::FETCH_ASSOC - почти всегда (рекомендуется его вообще прописать в опциях соединения, как режим извлечения по-умолчанию) PDO::FETCH_COLUMN - когда нужно получить массив, в котором собраны все значения лишь по одному из столбцов в бд PDO::FETCH_KEY_PAIR - ключ - первое поле выборки, значение - второе поле выборки (например массив [ id_клиента => имя_клиента ]). PDO::FETCH_UNIQUE | PDO::FETCH_ASSOC - когда нужно получить массив, ключом которого станет первое поле из выборки, а значением - массив данных. PDO::FETCH_GROUP | PDO::FETCH_ASSOC - вообще чудеснейшая комбинация, по первому полю выборка группируется и раскладывается на отдельные "подмассивы".
P.S. ну и, не относится напрямую к PDO, не забывайте про ключи и предподготовку статистических данных (судя по тому, что у Вас всего три апдейта за сутки - это должно сократить время выборок в разы).

Как программно получить список всех свойств устройства в Device Manager'e?

Привет всем!
Возникла идея написать приложение на подобии USBDeview (приложение, отображающее все подключенные на данный момент USB девайсы). Хочу также реализовать функционал включения/выключения USB устройства по расписанию (USBDeview тоже так умеет). Все манипуляции провожу через запросы к WMI . Столкнулся с рядом проблем:
При подключении нового USB устройства появляются два девайса - USB Mass Storage Device и Disk Drive. Какое из включать/выключать, чтобы USB устройство правильно отключилось? Запрос "select * from Win32_PnpEntity" возвращает довольно скудный набор свойств (например отсутствуют нужные мне параметры, такие как Matching device Id, Class, Is connected и т.д. - все эти параметры есть в Device Manager'e во вкладке Details). Отсюда вытекает вопрос. Как отобразить ВСЕ свойства, которые есть в Device Manager'e? Небольшой пример кода на C#:
class Program { static void Main(string[] args) { ManagementObjectCollection collection; using (var searcher = new ManagementObjectSearcher(@"Select * From Win32_PnpEntity")) collection = searcher.Get();
foreach (var device in collection) { Console.WriteLine("==================================="); foreach (var prop in device.Properties) Console.WriteLine("\t{0} : {1}", prop.Name, prop.Value); Console.WriteLine("==================================="); }
collection.Dispose();
Console.Read(); }
При подключении флешки появляется два новых устройства:
Disk Drive:
USB Mass Storage Device:
Мне же нужно получить список этих свойств:
Подскажите, пожалуйста, в каком направлении мне идти?


Ответ

При подключении нового USB устройства появляются два девайса - USB Mass Storage Device и Disk Drive. Какое из включать/выключать, чтобы USB устройство правильно отключилось?
Я полагаю, USB Mass Storage Device, т.к. Disk Drive является для него дочерним устройством, и отключается автоматически
Как отобразить ВСЕ свойства, которые есть в Device Manager'e?
Не знаю насчет "все", но довольно много свойств можно получить с помощью Setup API. Функция на C++ для вывода всех свойств по заданному ID устройства:
#pragma comment(lib,"Setupapi.lib")
#include #include #include #include #include #include
void ErrorMes(LPTSTR lpszFunction) { // Retrieve the system error message for the last-error code
LPVOID lpMsgBuf; LPVOID lpDisplayBuf; DWORD dw = GetLastError();
FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL );
// Display the error message and exit the process
lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT, (lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR)); wprintf(L"%s failed with error %d: %s", lpszFunction, dw, lpMsgBuf);
LocalFree(lpMsgBuf); LocalFree(lpDisplayBuf);
}
/*Вывод информации об устройстве с указанным InstanceID*/ BOOL PrintDevice(wchar_t* id) { unsigned index; HDEVINFO hDevInfo; SP_DEVINFO_DATA DeviceInfoData; TCHAR id_upper[1024]=L""; TCHAR buf[1024]=L""; DEVPROPTYPE dpt=0; DEVPROPKEY arr[100]; DWORD count=0;
for(int i=0;i // List all connected devices hDevInfo = SetupDiGetClassDevs(NULL, NULL, NULL, DIGCF_PRESENT | DIGCF_ALLCLASSES); for (index = 0; ; index++) { DeviceInfoData.cbSize = sizeof(DeviceInfoData); if (!SetupDiEnumDeviceInfo(hDevInfo, index, &DeviceInfoData)) { return FALSE; // no match }
BOOL res=SetupDiGetDeviceProperty(hDevInfo,&DeviceInfoData, &DEVPKEY_Device_InstanceId,&dpt,(PBYTE)buf,1000,NULL,0); if(res==FALSE)continue;
if(wcscmp(buf,id_upper)==0){ //устройство найдено res=SetupDiGetDeviceProperty(hDevInfo,&DeviceInfoData, &DEVPKEY_Device_DeviceDesc,&dpt,(PBYTE)buf,1000,NULL,0);
//вывод названия устройства if(res==FALSE)ErrorMes(L"SetupDiGetDeviceProperty"); else wprintf(L"* %s's properties: *

",buf);
//получение свойств res=SetupDiGetDevicePropertyKeys(hDevInfo,&DeviceInfoData,arr,100,&count,0); if(res==FALSE)ErrorMes(L"SetupDiGetDevicePropertyKeys");
for(int i=0;i if(res==FALSE){ErrorMes(L"SetupDiGetDeviceProperty");continue;}
//вывод DEVPROPKEY wprintf(L"{%08lX-%04hX-%04hX-%02hhX%02hhX-%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX; %3d}: ", arr[i].fmtid.Data1, arr[i].fmtid.Data2, arr[i].fmtid.Data3, arr[i].fmtid.Data4[0], arr[i].fmtid.Data4[1], arr[i].fmtid.Data4[2], arr[i].fmtid.Data4[3], arr[i].fmtid.Data4[4], arr[i].fmtid.Data4[5], arr[i].fmtid.Data4[6], arr[i].fmtid.Data4[7], arr[i].pid);
//вывод типа и значения свойства switch (dpt) { case DEVPROP_TYPE_STRING:wprintf(L"String \t%s
",buf); break; case DEVPROP_TYPE_STRING_LIST:wprintf(L"Strings \tFirst line: %s
",buf);break; case DEVPROP_TYPE_BOOLEAN:wprintf(L"Bool \t%d
",(bool)*((LPBYTE)(&buf)));break; case DEVPROP_TYPE_UINT16:wprintf(L"%Uint16 \t%d
",*((LPWORD)(&buf)));break; case DEVPROP_TYPE_UINT32:wprintf(L"Uint \t%d
",*((LPUINT)(&buf)));break; case DEVPROP_TYPE_GUID: wprintf(L"GUID \t{%08lX-%04hX-%04hX-%02hhX%02hhX-%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX}
", (*((GUID*)(&buf))).Data1, (*((GUID*)(&buf))).Data2, (*((GUID*)(&buf))).Data3, (*((GUID*)(&buf))).Data4[0], (*((GUID*)(&buf))).Data4[1], (*((GUID*)(&buf))).Data4[2], (*((GUID*)(&buf))).Data4[3], (*((GUID*)(&buf))).Data4[4], (*((GUID*)(&buf))).Data4[5], (*((GUID*)(&buf))).Data4[6], (*((GUID*)(&buf))).Data4[7]);
break; case DEVPROP_TYPE_BINARY:wprintf(L"(Binary data)
");break;
default:wprintf(L"Other \tType: 0x%x
",(int)dpt); break; }
}
SetupDiDestroyDeviceInfoList(hDevInfo); return TRUE; }//end if
}//end for
SetupDiDestroyDeviceInfoList(hDevInfo); return FALSE;//устройство не найдено }
int main() { setlocale(LC_ALL,"Russian");
PrintDevice(L"USB\\VID_046E&PID_5505\\5&15C311E1&0&6");
system("PAUSE"); return 0; }
Пример вывода:
* Составное USB устройство's properties: *
{A45C254E-DF1C-4EFD-8020-67D146A850E0; 2}: String Составное USB устройство {A45C254E-DF1C-4EFD-8020-67D146A850E0; 3}: Strings First line: USB\VID_046E&PID_5505&REV_0100 {A45C254E-DF1C-4EFD-8020-67D146A850E0; 4}: Strings First line: USB\DevClass_00&SubClass_00&Prot_00 {A45C254E-DF1C-4EFD-8020-67D146A850E0; 6}: String usbccgp {A45C254E-DF1C-4EFD-8020-67D146A850E0; 9}: String USB {A45C254E-DF1C-4EFD-8020-67D146A850E0; 10}: GUID {36FC9E60-C465-11CF-8056-444553540000} {A45C254E-DF1C-4EFD-8020-67D146A850E0; 11}: String {36fc9e60-c465-11cf-8056-444553540000}\0009 {A45C254E-DF1C-4EFD-8020-67D146A850E0; 12}: Uint 0 {A45C254E-DF1C-4EFD-8020-67D146A850E0; 13}: String (Стандартный USB хост-контроллер) {A45C254E-DF1C-4EFD-8020-67D146A850E0; 15}: String Port_#0006.Hub_#0003 {A45C254E-DF1C-4EFD-8020-67D146A850E0; 16}: String \Device\USBPDO-6 {A45C254E-DF1C-4EFD-8020-67D146A850E0; 17}: Uint 132 {A45C254E-DF1C-4EFD-8020-67D146A850E0; 21}: GUID {9D7DEBBC-C85D-11D1-9EB4-006008C3A19A} {A45C254E-DF1C-4EFD-8020-67D146A850E0; 22}: Uint 15 {A45C254E-DF1C-4EFD-8020-67D146A850E0; 23}: Uint 0 {A45C254E-DF1C-4EFD-8020-67D146A850E0; 24}: String USB {A45C254E-DF1C-4EFD-8020-67D146A850E0; 30}: Uint 6 {A45C254E-DF1C-4EFD-8020-67D146A850E0; 32}: (Binary data) {A45C254E-DF1C-4EFD-8020-67D146A850E0; 33}: Uint 3 {A45C254E-DF1C-4EFD-8020-67D146A850E0; 34}: Uint 3 {A45C254E-DF1C-4EFD-8020-67D146A850E0; 36}: Uint 0 {A45C254E-DF1C-4EFD-8020-67D146A850E0; 38}: GUID {23447943-B752-11E7-B8FD-806E6F6E6963} {B725F130-47EF-101A-A5F1-02608C9EEBAC; 10}: String Составное USB устройство {78C34FC8-104A-4ACA-9EA4-524D52996E57; 256}: String USB\VID_046E&PID_5505\5&15C311E1&0&6 {4340A6C5-93FA-4706-972C-7B648008A5A7; 2}: Uint 25182218 {4340A6C5-93FA-4706-972C-7B648008A5A7; 3}: Uint 0 {4340A6C5-93FA-4706-972C-7B648008A5A7; 7}: Strings First line: USB\VID_046E&PID_5505&MI_00\6&157c6e38&0&0000 {4340A6C5-93FA-4706-972C-7B648008A5A7; 8}: String USB\ROOT_HUB30\4&1ed8c4ae&0&0 {4340A6C5-93FA-4706-972C-7B648008A5A7; 9}: Strings First line: USB\VID_046E&PID_5505&MI_00\6&157c6e38&0&0000 {4340A6C5-93FA-4706-972C-7B648008A5A7; 10}: Strings First line: USB\VID_09DA&PID_000A\5&15c311e1&0&3 {AFD97640-86A3-4210-B67C-289C41AABE55; 2}: Bool 0 {8C7ED206-3F8A-4827-B3AB-AE9E1FAEFC6C; 2}: GUID {23447943-B752-11E7-B8FD-806E6F6E6963} {540B947E-8B40-45BC-A8A2-6A0B894CBDA2; 5}: Bool 1 {540B947E-8B40-45BC-A8A2-6A0B894CBDA2; 6}: Bool 0 {83DA6326-97A6-4088-9453-A1923F573B29; 15}: Bool 1 {540B947E-8B40-45BC-A8A2-6A0B894CBDA2; 16}: Bool 0 {540B947E-8B40-45BC-A8A2-6A0B894CBDA2; 8}: Uint 2026182162 {8C7ED206-3F8A-4827-B3AB-AE9E1FAEFC6C; 4}: Bool 0 {540B947E-8B40-45BC-A8A2-6A0B894CBDA2; 14}: Strings First line: \Driver\usbccgp {3464F7A4-2444-40B1-980A-E0903CB6D912; 10}: Uint 3 {540B947E-8B40-45BC-A8A2-6A0B894CBDA2; 4}: String USB Multimedia Keyboard {540B947E-8B40-45BC-A8A2-6A0B894CBDA2; 7}: String usb.inf:USB\COMPOSITE,Composite.Dev.NT {540B947E-8B40-45BC-A8A2-6A0B894CBDA2; 9}: (Binary data) {540B947E-8B40-45BC-A8A2-6A0B894CBDA2; 10}: String \_SB.PCI0.XHC.RHUB.HS06 {80497100-8C73-48B9-AAD9-CE387E19C56E; 6}: Uint 0 {80497100-8C73-48B9-AAD9-CE387E19C56E; 7}: Other Type: 0x9 {83DA6326-97A6-4088-9453-A1923F573B29; 3}: String usb.inf:392c3d53461eda3a:Composite.Dev.NT:10.0.15063.674:USB\COMPOSITE {83DA6326-97A6-4088-9453-A1923F573B29; 10}: String USB\ROOT_HUB30\4&1ed8c4ae&0&0 {83DA6326-97A6-4088-9453-A1923F573B29; 100}: Other Type: 0x10 {83DA6326-97A6-4088-9453-A1923F573B29; 101}: Other Type: 0x10 {83DA6326-97A6-4088-9453-A1923F573B29; 102}: Other Type: 0x10 {A45C254E-DF1C-4EFD-8020-67D146A850E0; 37}: Strings First line: PCIROOT(0)#PCI(1400)#USBROOT(0)#USB(6) {A8B865DD-2E3D-4094-AD97-E593A70C75D6; 2}: Other Type: 0x10 {A8B865DD-2E3D-4094-AD97-E593A70C75D6; 3}: String 10.0.15063.674 {A8B865DD-2E3D-4094-AD97-E593A70C75D6; 4}: String USB Composite Device {A8B865DD-2E3D-4094-AD97-E593A70C75D6; 5}: String usb.inf {A8B865DD-2E3D-4094-AD97-E593A70C75D6; 6}: String Composite.Dev.NT {A8B865DD-2E3D-4094-AD97-E593A70C75D6; 8}: String USB\COMPOSITE {A8B865DD-2E3D-4094-AD97-E593A70C75D6; 9}: String Microsoft {A8B865DD-2E3D-4094-AD97-E593A70C75D6; 14}: Uint 16719875 Для продолжения нажмите любую клавишу . . .
Свойства определяются по значениям DEVPROPKEY. Чтобы понять, что это за свойство, нужно сравнить его с константами из Devpkey.h (например, Matching Device ID - это DEVPKEY_Device_MatchingDeviceId).