Страницы

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

понедельник, 16 декабря 2019 г.

Как работает aligned_storage() С++11

#cpp #cpp11


Прочитал про aligned_storage() на cppreference, но так и не смог понять. Объясните,
пожалуйста, попроще.
    


Ответы

Ответ 1



Давайте разберём указанную вами статью по абзацам. Provides the member typedef type, which is a PODType... Класс-обёртка std::aligned_storage является примитивным типом, то есть данные хранятся в нём непрерывной кучей байт (как структура в Си), без C++-заморочек. Благодаря этому его можно спокойно копировать хоть через memcpy. ... suitable for use as uninitialized storage... Так как обёртка является примитивным типом, то у неё нет ни конструктора, ни деструктора. Соответственно, у оборачиваемого типа конструктор тоже вызван не будет. Как результат, мы можнем оборачивать только примитивные типы (увы). ... for any object whose size is at most Len and whose alignment requirement is a divisor of Align. Оборачиваемый тип должен 1) полностью помещаться в обёртку и 2) его допустимое выравнивание (кратность адреса в оперативной памяти) должно быть делителем Align. Возьмём следующий пример. Пусть Align = 16. Его делители — 1, 2, 4, 8 и 16. Это означает, что мы можем поместить в этот контейнер: char/uint8_t/int8_t (выравнивание — 1 байт), uint16_t/int16_t (выравнивание — 2 байта), uint32_t/int32_t (выравнивание — 4 байта), uint64_t/int64_t (выравнивание — 8 байт), либо SSE вектор, представленный в виде uint8_t[16] (ожидаемое выравнивание — 16 байт). Зачем было введено выравнивание? Процессор обменивается с ОЗУ не байтами, а блоками фиксированного размера 2n при неком целом n. Если переменная выровнена, то она может быть передана за наименее возможное количество пересылок. Невыровненные же данные могут пересечь границу блока и потребовать на одну пересылку больше. В придачу, некоторые операции (команды быстрой выровненной загрузки в SSE, например), вообще выбрасывают прерывание процессора при отсутствии выравнивания на определённую границу (для SSE — 16 байт). Стоит отметить, что выравнивание равно размеру только для примитивных типов. У составных же типов (структур и объединений) выравнивание равно таковому у наибольшего поля. Это гарантирует, что наиболее длинное поле будет выровнено по своему размеру. Ну а так как размеры примитивных типов равны степени двойки (то есть выравнивание больших типов кратно выравниванию типов меньших), то и все остальные поля будут выровнены. The default value of Align is the most stringent (the largest) alignment requirement for any object whose size is at most Len. Align можно и не указывать. Тогда компилятор будет выравнивать так, как он бы выравнивал близжайший (но не превышающий) по размерам тип. То есть будто мы написали std::aligned_storage, где T — некий встроенный (пусть и не существующий) тип с нужным нам размером. If the default value is not used, Align must be the value of alignof(T) for some type T, or the behavior is undefined. Третий фрагмент текста перечислял типы, допустимые для размещения в конкретной специализации обёртки с конкретным размером и выравниванием. Этот же абзац налагает ограничение на конкретный выбранный тип: Align должен иметь типичное для данного типа выравнивание. То есть мы не можем разместить uint16_t с Align=6, полагая, что 6 и 3 — допустимые значения выравнивания, — это провоцирует неопределённое поведение (выше уже был приведён пример про выравнивание для SSE). Но данный фрагмент также запрещает задание Align, кратного настоящему выравниванию. К примеру, мы не можем разместить uint16_t с выравниванием в 4, 8, 12 и т. д. байт. И да, мы не можем выравнивать SSE-вектор, если у нас нет готового, 16-байтного типа. Ведь тогда мы вынуждены использовать char[16], чьё родное выравнивание — 1 байт (т. к. тип char). Нам же нужно 16 — здравствуй, неопределённое поведение? А будь у нас готовый тип — компилятор сам его выровняет по собственному размеру (который совпадает с требуемым выравниванием), и std::aligned_storage нам как бы и не нужен. The behavior is undefined if Len == 0. Обёртка должна иметь ненулевую длину (иначе в неё ничего не поместится).

Golang загрузка больших файлов Google Drive

#golang #fileupload #google_drive


Есть задача загружать большие файлы (txt,docx,csv,xls) на Google Drive через API
средствами с немедленной конвертацией в форматы Google языка Go, при реализации нашей
программы выгрузки мы столкнулись проблемой загрузки файлов более 5МБ, так как для
загрузки файлов методом:


  Simple upload: uploadType=media


всё да вольно тривиально. Но мы не смогли пока придумать как с помощью библиотеки
https://github.com/google/google-api-go-client сделать загрузку методом:


  Resumable upload: uploadType=resumable


Так как нам необходимо выгружать таблиц по 500MB(и в плоть до 10GB) и более.

Суть: нужен рабочий пример на Golang реализации функции загрузки больших (>500MB,
но также поддерживающий и любой меньший размер) файлов на Google Drive через API.

Кусок существующего кода отвечающий за загрузку файла:

showProgress := func(current, total int64) {
    fmt.Printf("Uploaded at %s, %s/%s\r", getRate(current), Comma(current), Comma(total))
}

r, err := d.Files.Insert(f).ResumableMedia(context.Background(), input, inputInfo.Size(),
mimeType).Convert(true).ProgressUpdater(showProgress).Do()
if err != nil {
    fmt.Printf("An error occurred: %v\n", err)
    return nil, err
}


Результат:

An error occurred: googleapi: Error 400: Bad Request, badRequest

    


Ответы

Ответ 1



Наши желания по загрузке данных в Google таблицы не могут быть реализованы в коде по причине того что на данный счёт есть целый ряд ограничений, и наш объём данных не может быть с конвертирован в форматы Google. Мы сможем использовать Google Drive только для хранения оригинальных файлов. Какие файлы можно хранить на Google Диске Размеры файлов Ниже перечислены ограничения на размеры документов, таблиц и презентаций, которые можно хранить на Google Диске. Документы. Максимум 1 020 000 символов. Размер текстовых документов, преобразуемых в формат Google, не может превышать 50 МБ. Таблицы. Максимум 2 миллиона ячеек для файлов, созданных в Google Таблицах или преобразуемых в этот формат. Презентации. Максимальный размер файлов, преобразуемых в формат Google, составляет 100 МБ. Другие файлы. Максимум 5 ТБ. Поддерживаемые типы файлов Ниже перечислены форматы файлов, которые можно открыть на Google Диске. Обычные файлы: архивы (ZIP, RAR, TAR, GZIP); аудиофайлы (MP3, MPEG, WAV, OGG); изображения (JPEG, PNG, GIF, BMP); файлы разметки/кода (CSS, HTML,PHP, C, CPP, H, HPP, JS); текстовые файлы (TXT); видео (WebM, MPEG4, 3GPP, MOV, AVI, MPEGPS, WMV, FLV, OGG); Файлы Adobe: Autodesk AutoCad (DXF); Illustrator (AI); Photoshop (PSD); документы переносимого формата (PDF); Postscript (EPS, PS); масштабируемые векторные рисунки (SVG); изображения TIFF (рекомендуется при использовании режима RGB); TrueType (TTF). Файлы Microsoft: Excel (XLS и XLSX); PowerPoint (PPT и PPTX); Word (DOC и DOCX); XML Paper Specification (XPS). Оригинал -> Cправка - Диск

Установка последней версии python на debian

#python #debian


Мне нужно установить последнюю версию Python на Debian. Уже изменил репозитории в
sources.list на тестовые и обновился, но все равно не последняя версия Python. Обновлять
всю систему с нестабильных или экспериментальных репозиториев не хочу.  

Можно ли как-то из них установить только Python, либо установить из архива с официального
сайта?
    


Ответы

Ответ 1



Нет пакета - можно собрать из исходников. Рассмотрим глобальную установку с нуля (без обсуждения таких утилит как pyenv), для гольной Debian 8. Вам потребуется sudo: su apt-get install sudo Потребуется обновить список репозиториев с пакетами. Пример списка для версии, отличной от 8 можно взять отсюда. Нужно открыть файл /etc/apt/sources.list в любом текстовом редакторе (sudo vi /etc/apt/sources.list) и добавить для jessie: deb http://httpredir.debian.org/debian jessie main deb-src http://httpredir.debian.org/debian jessie main deb http://httpredir.debian.org/debian jessie-updates main deb-src http://httpredir.debian.org/debian jessie-updates main deb http://security.debian.org/ jessie/updates main deb-src http://security.debian.org/ jessie/updates main Возможно, в этом же файле нужно будет закомментировать (#) строку (если она есть) с deb:cdrom [...], чтобы пакеты не искались на диске. Затем sudo apt-get update - обновит список пакетов. Потребуется компилятор для C из пакета GNU Compiler Collection: gcc и make. Оба пакета есть в build-essential sudo apt-get install build-essential Еще есть необязательные, но важные зависимости: zlib и ssl sudo apt-get install openssl sudo apt-get install zlib1g-dev Без zlib не получится работать с zip архивами, без ssl не получиться открыть https:// адреса (и это как минимум). Последняя зависимость - checkinstall - sudo apt-get install checkinstall. Затем выбираем любую папку и в ней будет собираться Python 3.6. Для другой версии, необходимо будет поменять ссылку и имя файла на соответствующую версию. Пояснения по configure --enable-optimizations есть в README. wget https://www.python.org/ftp/python/3.6.0/Python-3.6.0.tgz tar xvf Python-3.6.0.tgz cd Python-3.6.0 ./configure --enable-optimizations make -j4 make test sudo checkinstall -D --pkgname=python3_6 make altinstall Аргумент -j4 разрешит параллельную компиляцию на 4 ядрах - можно указать любое доступное системе количество и это значительно ускорит сборку. checkinstall вместо копирования в папки напрямую создаст .deb пакет и затем установит его. Основное преимущество - потом его (пакет) очень легко удалить. В противном случае нужно знать что и куда было установлено, чтобы удалить все вручную. Во время выполнения последней команды вам будет предложено настроить пакет - шаг можно пропустить и оставить все значения по-умолчанию. Аргумент pkgname не должен конфликтовать с существующими пакетами. altinstall параметр не перезапишет версию python3 по-умолчанию (системные и не только утилиты могут ее использовать), а создаст только pythonX.X бинарник. Пакет был установлен автоматически, удалить его можно командой dpkg -r python3-6 Установленный интерпретатор можно запустить командой python3.6

Ответ 2



Делал всё, как описано в инструкции выше. Устанавливается долго, но работает. Ставил на Debian 9, список репозиториев для неё такой: deb http://deb.debian.org/debian/ stretch main deb-src http://deb.debian.org/debian/ stretch main deb http://deb.debian.org/debian/ stretch-updates main deb-src http://deb.debian.org/debian/ stretch-updates main deb http://security.debian.org/debian-security/ stretch/updates main deb-src http://security.debian.org/debian-security/ stretch/updates main Всплыл один момент: по ходу установки везде мелькал python 3.6. После установки пытался вызвать - не работает. Методом тыка удалось определить, что встал python3.5.3. Так что если после установки у Вас та же проблема, то пробуйте запустить (естественно, из папки с установленным python): python5.3

Авторизация пользователя на php/mysql/session

#php #mysql #сессия


Делаю авторизацию, возникла такая проблема. При нажатии на 
Logout  ,  происходит редирект на login.php, т.е. на форму, но если в соседней
вкладке зайти на home.php, то редирект не происходит и пользователь как бы остаётся
в системе. Т.е. не происходит разрушение сессии.

Вот код php:

login.php

 location.href='home.php'
";   
  }

  else
  {
echo "";
}

}

?>


home.php



Untitled Document

    

Welcome

Logout logout.php


Ответы

Ответ 1



Ну, я уже не помню всех тонкостей с сессиями, но в моем видении это так: при логине создаешь некий ключ в сессии, например: //login.php if (mysql_num_rows($query) != 0){ $row = mysql_fetch_assoc($query) $user_login = $row['username']; $_SESSION['user_login'] = $user_login;//присваиваем нашему ключу значение //В нашем случае, логин юзера if(isset($_GET['requested_url']){ header('Location: '.urldecode($_GET['requested_url'])); }//Если был задан запрашиваемый адрес, то автоматически перенаправляем его туда. } Потом, когда к тебе на страницу заходят, первым делом вызываешь скрипт, который проверяет сессию: //somepage.php if(!isset($_SESSION['user_login']){ header('Location: login.php?requested_url='.urlencode($_SERVER[REQUEST_URI]));//В идеале, передавая и адрес страницы, на которую изначально хотел зайти пользователь, чтобы потом сразу перенаправить его туда. } require_once('content.php'); //logout.php session_unset(); header('Location: login.php'); Сразу, просьба исключить комментарии про голый mysql вместо PDO и иже с ним, ответ ориентируется на достижение других целей.

Ответ 2



Я использую сессии и авторизацию через $_SERVER, но может вам пригодится следующее: session_unset (); session_destroy (); $connection->close(); unset($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW'], $_SERVER); header('HTTP/1.0 401 Unauthorized');

Ответ 3



Попробуй использовать unset($_SESSION['login_user']); session_destroy() не желательно использовать Используй проверки if(isset($_SESSION['login_user'])) ...

Как асинхронно работать с 2мя сокетами?

#python #python_3x #async_await #asyncio


В питоне хочу сделать не блокирующую работу с 2мя сокетами. Для упрощения примера
представим Proxy. 

Пытаюсь на asincio реализовать.

Клиент_1 устанавливает соединение с сервером - получаю reader и writer. Сервер устанавливает
новое соединение с внутренним процессом через файловый сокет (в данном проекте, но
может понадобится и другие типы соединения).

Процесс может создавать запросы клиенту, а клиент к процессу.

Очень давно на втором питоне делал несколько тредов которые общались через multiprocessing.Queue.
Эти схемы надо бы исключить потому как передача большого количества данных займет много
оперативной памяти.

С клиента читаю 4 байта заголовка, и по размеру тело сообщения. Тело обрабатывается
и отправляется к процессу.

С процесса читаю по мере поступления отдельные, их также надо перепаковать.

Запускаю читалки в циклах. Та что с клиента отрабатывает одно сообщение, а вторая
продолжает дергать процесс. Такое чувство что второй блокирует первый и вставляет awaitы
в LIFO, не давая первому читать сокет клиента.
    


Ответы

Ответ 1



Давайте попробуем реализовать асинхронность по классике =) Асинхронность в отличии от синхронности заключается в том, что для каждого соединения мы должны создать контекст: некую структуру данных, которая будет хранить состояние соединения, информацию о том, что сейчас происходит на этом соединении. Действия же с соединением (чтение, запись и т.д.) происходят только тогда, когда соединение к этому готово. Поэтому, ни одно действие не вызывает ожидания. Ожидания в таком дизайне становятся недопустимы, так как все обработчики будут ждать одного. Когда-то давно мы в универе на сетевом программировании постигли дзен и обрели знание о методе select. Метод select предназначен для опроса блокирующих потоковых классов: стандартный ввод (stdin), запуск приложения с ожидание результата (popen), получение данных с сокета (socket). В Python она тоже есть и выглядит следующим образом: readst,writest,errorst = select(rlist, wlist, xlist[ , timeout]) Вместо того, чтобы запускать отдельный поток для каждого прокси-соединения, программа "крутится" в цикле событий и ждет активности на каждом из соединений, перемещая данные всякий раз, когда соединение будет готово. Этот подход особенно подходит для программ, которые делают большое количество операций ввода-вывода. #!/usr/bin/python3 import select import socket import sys import os import fcntl import logging USAGE = "usage: python selectproxy.py proxyhost:proxyport desthost:destport" class ProxyConnection(object): # определим максимальный размер буфера MAX_BUFFER_SIZE = 1024 # ProxyConnection - класс, который пересылает данные между клиентским и целевым сокетом def __init__(self,proxyserver,listensock,servaddr): self.proxyserver = proxyserver self.servaddr = servaddr # адрес сервера # открываем сокеты self.clisock, self.cliaddr = listensock.accept() # ждем подключения клиента self.clisock.setblocking(0) self.servsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # сокет сервера self.servsock.setblocking(0) # буферы для данных, полученных из сокета self.buffers = { self.clisock:bytes(), self.servsock:bytes() } self.connected = False # подключен ли серверный сокет? # регистрируем сокеты и разрешать операции чтения self.proxyserver.registerSocket(self.clisock,self) self.proxyserver.registerSocket(self.servsock,self) self.proxyserver.activateRead(self.clisock) self.proxyserver.activateRead(self.servsock) # метод, возвращающий сокет на «другом конце» соединения def other(self,socket): if socket == self.clisock: return self.servsock else: return self.clisock # подключаемся к серверу def connect(self): # мы должны использовать метод connect_ex, потому что соединение является асинхронным и не будет выполнено немедленно self.servsock.connect_ex(self.servaddr) # метод, читающий данные из сокета def readfrom(self,s): if s == self.servsock and not self.connected: self.proxyserver.connection_count += 1 logging.getLogger("selectproxy") \ .info("соединение с установлено %s, количество текущих соединений %d"%(str(self.cliaddr),self.proxyserver.connection_count)) self.connected = True return # читаем из сокета capacity = ProxyConnection.MAX_BUFFER_SIZE - len(self.buffers[s]) try: data = s.recv(capacity) except Exception as ex: data = b"" # если чтение не удалось, закрываем сокет (это происходит, когда клиент или сервер закрывает соединение) if len(data) == 0: self.close() return self.buffers[s] += data self.proxyserver.activateWrite(self.other(s)) # перестаем читать, если буфер полон capacity -= len(data) if capacity <= 0: self.proxyserver.deactivateRead(s) # метод, который пишет в сокет def writeto(self,s): # получаем буфер, содержащий данные для чтения buf = self.buffers[self.other(s)] try: # пишем его в сокет written = s.send(buf) except Exception as ex: self.close() return # удаляем записанные данные из буфера buf = buf[written:] self.buffers[self.other(s)] = buf if len(buf) == 0: self.proxyserver.deactivateWrite(s) # разрешаем чтение, если все записали if written: self.proxyserver.activateRead(self.other(s)) # метод, закрывающий соединение def close(self): for sock in [self.clisock,self.servsock]: if sock: self.proxyserver.deactivateRead(sock) self.proxyserver.deactivateWrite(sock) sock.close() self.proxyserver.unregisterSocket(sock,self) self.proxyserver.connection_count -= 1 logging.getLogger("selectproxy") \ .info("соединение с %s разорвано, количество текущих соединений %d"%(self.cliaddr,self.proxyserver.connection_count)) class ProxyServer(object): def __init__(self,host,port,serverhost,serverport): self.address = (host,port) self.server = (serverhost,serverport) self.listensock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.listensock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.listensock.bind(self.address) self.listensock.listen(5) self.connections = {} # словарь сокет <-> ProxyConnection self.readsockets = [] self.writesockets = [] self.allsockets = [self.listensock] self.connection_count = 0 # количество активных соединений def run(self): loop = 0 while True: # Блокировать до тех пор, пока в одном из сокетов не будет активности, тайм-аут каждые 60 секунд по умолчанию r, w, e = select.select( [self.listensock]+self.readsockets, self.writesockets, self.allsockets, 60) loop += 1 # обрабатываем любые чтения for s in r: if s is self.listensock: self.open() else: if s in self.connections: self.connections[s].readfrom(s) # обрабатываем запись for s in w: if s in self.connections: self.connections[s].writeto(s) # обрабатываем ошибки for s in e: if s in self.connections: self.connections[s].close() self.sock.close() self.sock = None def activateRead(self,sock): if not sock in self.readsockets: self.readsockets.append(sock) def deactivateRead(self,sock): if sock in self.readsockets: self.readsockets.remove(sock) def activateWrite(self,sock): if not sock in self.writesockets: self.writesockets.append(sock) def deactivateWrite(self,sock): if sock in self.writesockets: self.writesockets.remove(sock) def registerSocket(self,sock,conn): self.connections[sock] = conn self.allsockets.append(sock) def unregisterSocket(self,sock,conn): del self.connections[sock] self.allsockets.remove(sock) def open(self): conn = ProxyConnection(self,self.listensock,self.server) conn.connect() if __name__ == '__main__': try: proxy = sys.argv[1].split(":") dest = sys.argv[2].split(":") proxyhost = proxy[0] proxyport = int(proxy[1]) serverhost = dest[0] serverport = int(dest[1]) except: print(USAGE) sys.exit(-1) logger = logging.getLogger('selectproxy') logger.setLevel(logging.INFO) hdlr = logging.StreamHandler() hdlr.setLevel(logging.INFO) hdlr.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')) logger.addHandler(hdlr) server = ProxyServer(proxyhost,proxyport,serverhost,serverport) server.run()

Прокрутка меню в Corona SDK

#lua #menu #coronasdk


Создаю прокрутку для меню в Lua CORONA SDK.

Исходник файла menu.lua

local composer = require( "composer" )
local widget = require( "widget" )

local scene = composer.newScene()

local function onButtonRelease( event )
composer.gotoScene( event.target.id:lower(), { effect="fade", time=300 } )
--composer.recycleOnSceneChange = true
end

function scene:create( event )

local sceneGroup = self.view

local sceneTitle = display.newText( sceneGroup, "Выберете уровень", display.contentCenterX,
10, composer.getVariable( "appFont" ), 20 )

-- Создание массива из кнопок меню
local menuButtons = { "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
"13", "14", "15", "16", "17", "18"  }

-- Создание цикла для кнопок меню
local rowNum = 0
for i = 1,#menuButtons do

    rowNum = rowNum+1
    local button = widget.newButton(
        {
            label = menuButtons[i],
            id = menuButtons[i],
            shape = "circle",
            radius = 20,
            font = composer.getVariable( "appFont" ), 
            fontSize = 16,
            fillColor = { default={ 0.12,0.32,0.52,1 } ,over={ 0.132,0.352,0.572,1
} }, -- цвет кнопки и нажатия на кнопку
            labelColor = { default={ 1,1,1,1 }, over={ 1,1,1,1 } }, -- цвет шрифта
на кнопках
            onRelease = onButtonRelease
        })

   mod = math.fmod(i, 2) -- определение кратности

    if (i*mod >= 1 ) then -- если кратно 1
        button.x = display.contentCenterX -50
        elseif (i*mod == 0) then  -- если кратно 0 
            button.x = display.contentCenterX + 50 
        end     
    button.y = 65 + ((rowNum-1)*35) -- растояние по y между кнопками

    sceneGroup:insert( button ) -- обновление сцены после нажатия на кнопку
end

end

scene:addEventListener( "create", scene ) -- слушатель на создание сцены


--обработка касаний
function scene:touch(e)
-- body
if(e.phase == "began") then
        print("начало a");
    elseif (e.phase == "moved") then
        scene.x = e.x;
        scene.y = e.y;
        print("двигаю a");
    elseif(e.phase == "ended") then
        print("отпустил a");
end
end

 scene:addEventListener("touch", scene);

return scene


Для наглядности прикрепляю изображение



По обработке касаний, пробовал как со сценой, так и с меню, и с массивом кнопок.

Подскажите, пожалуйста, решение моей проблемы.
    


Ответы

Ответ 1



Для выполнения поставленной цели, нужно создать scrollView и переместить в функцию код на создания кнопок в цикле из function scene:create в function showSlidingMenu и слушать события каждый раз в Runtime. Для реализации прокрутки меню сразу при переходе в меню, функция вызывается в scene:create local composer = require( "composer" ) local widget = require( "widget" ) local scrollView local scene = composer.newScene() local function onButtonRelease( event ) composer.gotoScene( event.target.id:lower(), { effect="fade", time=300 } ) end function scene:create( event ) sceneGroup = self.view composer.recycleOnSceneChange = true; local sceneTitle = display.newText( sceneGroup, "Выберете уровень", display.contentCenterX, 10, composer.getVariable( "appFont" ), 20 ) end local function ButtonListener( event ) if ( event.phase == "moved" ) then local dx = math.abs( event.x - event.xStart ) if ( dx > 5 ) then scrollView:takeFocus( event ) end elseif ( event.phase == "ended" ) then print( "клик по объекту" ) timer.performWithDelay( 10, function() scrollView:removeSelf(); scrollView = nil; Runtime:removeEventListener("touch",showSlidingMenu) end ) end return true end function showSlidingMenu( event ) if ( "ended" == event.phase ) then scrollView = widget.newScrollView { width = _W, height = _H, scrollWidth = width, scrollHeight = height, horizontalScrollDisabled = true } scrollView.x = display.contentCenterX scrollView.y = display.contentCenterY local menuButtons = { "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18" } rowNum = 0 for i = 1,#menuButtons do rowNum = rowNum+1 local button = widget.newButton( { label = menuButtons[i], id = menuButtons[i], shape = "circle", radius = 20, font = composer.getVariable( "appFont" ), fontSize = 16, fillColor = { default={ 0.12,0.32,0.52,1 } ,over={ 0.132,0.352,0.572,1 } }, -- цвет кнопки и нажатия на кнопку labelColor = { default={ 1,1,1,1 }, over={ 1,1,1,1 } }, -- цвет шрифта на кнопках onRelease = onButtonRelease }) mod = math.fmod(i, 2) -- определение кратности if (i*mod >= 1 ) then -- если кратно 1 button.x = display.contentCenterX -50 -- смещение относительно центра влево elseif (i*mod == 0) then -- если кратно 0 button.x = display.contentCenterX + 50 -- смещение относительно центра впрово end button.y = 30 + ((rowNum-1)*35) -- координаты первой кнопки и растояние по y между кнопками scrollView:insert( button ) sceneGroup:insert( scrollView ) scene:addEventListener( "touch", ButtonListener ) end return true end end -- hide() function scene:hide( event ) local sceneGroup = self.view local phase = event.phase if ( phase == "will" ) then -- Код здесь работает, когда сцена находится на экране (но собирается уйти с экрана) print ("ухожу с экрана"); elseif ( phase == "did" ) then -- Код здесь запускается сразу после того, как сцена полностью выходит за пределы экрана print ("ушел за пределы экрана"); end end -- destroy() function scene:destroy( event ) local sceneGroup = self.view showSlidingMenu = nil; end scene:addEventListener( "create", scene ) -- слушатель на создание сцены scene:addEventListener( "destroy", scene ) scene:addEventListener( "hide", scene ) Runtime:addEventListener("touch",showSlidingMenu) return scene Если тема создания меню с помощью Corona SDK интересна, то буду ее развивать

Как TeamViewer “обходит” UAC на Win7?

#java #c_sharp #net #windows #uac


Не все знают, но в TeamViewer есть возможность удаленного подклдючения под админом.
Если при запуске TeamViewer тех. специалист выберет авторизацию через учетную запись
Windows, введет логин и пароль администратора для удаленного компьютера, то на стороне
сотрудника появится запрос на подтверждение прав приложения. Самое удобное в этом подходе
то, что сотрудник не вводит пароль администратора и не знает его, а только нажимает
Да. Далее TeamViewer на стороне сотрудника перезапускается и работает уже с правами
администратора.

Мне нужно реализовать нечто подобное, есть много компьютеров с одинаковыми паролями,
нужно программно их удаленно модифицировать. Пока получилось только через psexec получить
доступ администратора, но работает это только с выключенным UAC, а выключить его я,
естественно, не могу (UAC мне этого не позволит).

Вариант с запросом разрешения без ввода пароля меня тоже бы устроил, но не понятно
как именно разработчики TeamViewer так сделали.
    


Ответы

Ответ 1



Т.е. я так понимаю здесь есть 2 стороны: сотрудник и администратор. Администратор желает, чтобы TV сотрудника работал от имени админа того компьютера, а сотрудник как бы не прочь. В чём сложность тогда - я не вижу. Логин и пароль администраторской учётки просто передаются на удалённый компьютер, где TV запрашивает у сотрудника разрешение. Далее TV сам себя запустит от имени администратора (ведь программу можно запустить от имени пользователя зная логин и пароль https://msdn.microsoft.com/en-us/library/windows/desktop/ms682431(v=vs.85).aspx). Вот и всё...

Ответ 2



Чтобы добиться такого же результата как в случае с TeamViewer, можно воспользоваться двумя утилитам: miniRunAs.exe Login Password Elevate.exe "C:\MyApp.exe" miniRunAs - Запуск от имени другого пользователя с паролем из консоли. http://www.source-code.biz/snippets/c/1.htm Elevate - Вывод запроса на повышение прав. Если Elevate.exe уже запущена под админом, то пароль не требуется, если нет - запрашивается еще и пароль. http://www.winability.com/elevate/

Как сделать грамотное сохранение

#unity3d


У меня в игре должно быть сохранение. Я знаю, что есть такая штука как playerprefs,
но она позволяет хранить только переменные. У меня генерация деревьев и прочего через
скрипт, и при каждом запуске сцены они разные. Неужели, мне нужно будет все объекты
сохранять через массивы, чтобы записать положение всех объектов на сцене? Если есть
какой нибудь другой путь для сохранения, скажите пожалуйста.
    


Ответы

Ответ 1



В Unity есть не только PlayerPrefs, но еще BinarySerialization - который как раз годится для хранения более сложных объектов чем PlayerPrefs, и также есть JSON Serialization - который использует класс JSONUtility и позволяет хранить данные JSON формата. В вашем случае я бы сохранял все в JSON файл.

Модифицированный Q-Learning

#математика #нейронные_сети #машинное_обучение


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

На этой картинке мы имеем алгоритм для online обучения нейронной сети, а конкретно
модифицированный Q-Learning.



Как мне рассчитывать изменение весов в нейронной сети, если:


Нейронная сеть имеет 3 и больше слоев (как я полагаю, эта формула предназначена только
для одного вектора весов) 
Мы не используем для каждого отдельного action собственную нейронную сеть.

    


Ответы

Ответ 1



Также недавно начал изучать этот вопрос и столкнулся с подобной проблемой. Скорее всего, если ваша сеть имеет 3 и более слоев, то вы обучаете ее обратным распространением. Тогда имеет смысл сделать последний слой нейронов равным количеству возможных действий, а ошибку брать как (нормализованное значение Q - значение на выходе нейрона).

Ответ 2



Для большей информативности нужно знать каким образом планируется построить многослойную сеть.(Или Так как на картинке предлагается использовать функцию полезности для корректировки весов последнего слоя нейросети.(п4), необходимо знать структуру слоев. Допустим если вы хотите использовать функцию полезности на сети с 3 скрытыми слоями, где каждый нейрон соединен с каждым нейроном следующего слоя. Вероятно проще всего использовать обратное распространение, оно является обучением с учителем и в качестве ответа использовать значение Q из матрицы пар состояние дейтвие. Тогда вы сможете расcчитать ошибку. Например: (для простоты значение на выходе нейрона последнего слоя назовем "OUT"(та самая функция активации сигмоида) ошибка = Q-OUT значение весов меняем по формуле: w(n)=nnEOUT+aw(n-1) где: w(n)-измененные значения весов, w(n-1) текущие nn-коэффициент скорости обучения E = производная сигмыошибку {OUT*(1-OUT)(Q-OUT)} a- значение импульса в пределах от 0 до 1

Настройка CI/CD для публикации приложения в Azure

#visual_studio #aspnet #azure #непрерывная_интеграция #devops


Хочу использовать Visual Studio Team Services для сборки и публикации своего ASP.NET
приложения в Azure Web Application. Раньше публиковал с помощью Web Deploy в Visual
Studio, и там мой профиль публикации содержал следующие данные:


Строка подключение к базе SQL Azure
Параметры для включения аутентификации в Azure AD


Теперь же, когда публикация происходит с помощью CI/CD конвейера, эти параметры не
подставляются и приложение публикуется в облако со строкой подключения и параметрами,
которые были на локальной машине. 

Вопрос: есть ли способ внести в Build Definition эти настройки? Импортировать профиль
публикации при развертывании или еще какие-либо способы корректно развернуть приложение
в облаке?
    


Ответы

Ответ 1



Если вы публикуете приложение именно как Azure Web Application, то вам не нужно править конфиги на стадии билда. Вынесите настройки в web.config, в стандартные секции ConnectionStrings и AppSettings. Они должны быть там по умолчанию, но вдруг вы храните из где-то в другом месте. В портале Azure, в секции Application settings для своего приложения - задайте реальные значения для своего приложения. Значения из Application settings применяются поверх того, что вписано в web.config, так что вам вообще ничего не придется заменять в процессе сборки и публикации релиза. Официальная документация по настройкам: Configure web apps in Azure App Service. App settings: For .NET apps, these settings are injected into your .NET configuration AppSettings at runtime, overriding existing settings. Connection strings: For .NET apps, these connection strings are injected into your .NET configuration connectionStrings settings at runtime, overriding existing entries where the key equals the linked database name. Кстати, Azure Web Sites умеют автопубликацию из GIT / VSTS, со встроенной поддержкой основных типов студийных проектов, так что может быть вам вообще не нужны билды в VSTS :)

Ожидание в асинхронности

#c_sharp #async_await #tpl


Есть абстрактный пример:

async void Do() 
{
    ...
    await DownloadSomething();
    // какой-то другой код, который выполнится позже 
    ...
}

void FuncMain() 
{
    Do();
    //какой-то код
} 


Когда начинается "долгая"  операция DownloadSomething, управление передаётся в FuncMain,
а после, когда загрузка закончится, продолжается код после DownloadSomething. 
Вопрос: где удерживается await DownloadSomething? Или удерживается в каком-то потоке
из пула? 
    


Ответы

Ответ 1



Смотря, что стоит в DownloadSomething(). Если это IO то поток IO. который фиксирован и независим от приложения. DB, SQL, Download и т.д. в ту же копилку. В момент передачи идут сохрание информации текущей конфигурации потока и как его возвращать. Текущий поток высвобождается и по окончанию происходит обратный вызов из пула нового/исходного потока. (ConfigureAwait(true/false)).

Ответ 2



Чтобы вопрос не оставался без ответа: Дело в том, что async-метод не является методом в обычном понимании этого слова. С точки зрения внешнего кода, его выполнение заканчивается практически сразу (с первым await'ом, который ожидает неокончившийся Task*). На время ожидания метод не выполняется нигде. Это не буддистский коан, а реальная подробность имплементации async/await. По существу await выполняется так: код просто подписывает на окончание выполнения Task'а метод специального скрытого объекта, и завершает выполнение. При окончании работы Task'а метод получает управление, и при помощи довольно простых трюков (наподобие goto в середину кода) возобновляет выполнение кода async-метода. Таким образом, во время await'а метод не выполняется ни в каком потоке. *или tasklike

Менеджер приложений против Центра приложений

#linux #ubuntu #ubuntu_software


В Ubuntu 16.04 по умолчанию запускается Менеджер приложений (он же "Ubuntu Software")
вместо Центра приложений (он же "Ubuntu Software Center"), как было в 14.04.

Оказывается, они показывают пользователю разные приложения:
Центр приложений знает gksu, а Менеджер приложений — нет!


Это так и задумано, что Менеджер приложений (который "Ubuntu Software" из 16.04)
фильтрует приложения?
Может, его надо как-то настроить?

Upd: До меня внезапно дошло, что Менеджер приложений дает в меню доступ к Программы
и обновления. Это те же Программы и обновления, которые доступны из Параметры системы
и из Центра приложений --> Правка --> Источники приложений....

Получается, что обе программы используют те же репозитории.
Так почему отличаются результаты?

Upd2. Еще картинка: для Spyder3 Менеджер приложений дает оценку 3 звездочки от 3
голосовавших, несмотря на то, что по его же данным, 3 человека поставили ему 5 звездочек. 
    


Ответы

Ответ 1



Центр приложений — старая GUI-программа для управления установленным ПО, использовалась в Ubuntu до 16.04. Позже было решено отказаться от её дальнейшей разработки в пользу Менеджера приложений из проекта Gnome. В Ubuntu поставляется весьма модифицированная версия, благо внутренняя архитектура этой программы заточена под это. Сама она написана на Си и работает довольно шустро (в отличие от предыдущего центра приложений на Python), имеет развитую систему плагинов. Вместе с тем, эти программы кардинально различаются своим подходом. Менеджер приложений Gnome (и Ubuntu) в качестве источника информации о ПО использует AppStream — БД с метаданными о компонентах программ в экосистеме СПО. Apt регулярно загружает её с зеркала обновлений по команде apt update или автоматически. В дистрибутивах основанных на Debian используется формат DEP-11. По некоторым причинам в настоящий момент в этой базе в основном представлена информация о программах, обладающих графическим пользовательским интерфейсом. А из-за того, что по большей части эта база собирается автоматизированным инструментом appstream-generator на основе .desktop файлов, справедлива импликация: "Если пакет с программой устанавливает .desktop файл со значком для меню, это приложение можно будет найти в AppStream". Но на самом деле, в менеджере приложений всё равно применяется фильтр — он отображает в списке только программы с GUI, то есть те, что отмечены type=desktop-application. Предполагается, что если вам нужна консольная программа, гораздо проще поставить её напрямую с помощью Apt. Выводы Менеджер приложений использует AppStream — независящий от дистрибутива список приложений в мире Linux и СПО. Тем не менее, пакеты ставятся из своих обычных репозиториев, которые использует Apt. Центр приложений использует инфраструктуру архива Debian или Ubuntu напрямую, не прибегая к помощи AppStream или DEP-11. Полезности Можно получить доступ к базе данных AppStream, хранящейся на компьютере, из командной строки с помощью утилиты appstreamcli из пакета appstream. Сами списки в формате DEP-11 хранятся в каталоге /var/lib/app-info/yaml. Впрочем, пользовательских обзоров вы там не найдёте (там только описания). Оценки программ загружаются с сервера reviews.ubuntu.com раз в три месяца, сразу все имеющиеся, комментарии — при открытии страницы с приложением. За это отвечает плагин /usr/lib/gs-plugins-9/libgs_plugin_ubuntu-reviews.so. По поводу gksu: у меня подключен репозиторий Debian unstable, а gksu.desktop перечислен в yaml оттуда, поэтому Менеджер приложений находит его в поиске. Тем не менее, стоит воздержаться от использования этой утилиты — она признана устаревшей и в будущем будет удалена.

Как узнать, что блокирует прокрутку страницы колёсиком?

#javascript #html #jquery #css


На сайте не работает прокрутка страницы колёсиком мыши в Google Chrome 61 на Windows
10. В других браузерах всё работает нормально. Я запускал собственный jquery-скрипт
для проверки выполнения события, но событие выполнялось, только если мышь была наведена
на полосу прокрутки, тогда же и прокручивалась страница. Разворошив все скрипты, я
нашёл в скрипте RD Parallax следующий код: 

$(window).bind('scroll', function () {
                parallax.move();
            });


В отладчике до выполнения этого кода страница прокручивалась, а после - переставала.
Сейчас я грешу на него, но чтобы простое назначение обработчика события вешало всю
прокрутку? Выглядит странно.

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


Ответы

Ответ 1



Для версии хрома Google Chrome 61.x.x window scroll работать будет так: $('html, body').bind('scroll', function () { parallax.move(); }); Так оно будет работать уже на всех браузерах. Для той версии хрома $(window).bind('scroll' и $('body').bind('scroll' не работают.

Где имеет смысл использовать Optional?

#java_8 #optional


В следующей статье про Optional такое резюме: 


  не загрязняйте свой код использованием везде Optional, используйте
  их осторожно, где это имеет смысл (don't pollute your code with
  Optional everywhere, use them carefully where it makes sense).


А где имеет смысл использовать Optional? Есть ли чёткое разделение (или хотя бы какие-нибудь
рекомендации), когда лучше использовать if-else, а когда Optional?
    


Ответы

Ответ 1



Прежде всего, все разговоры об Optional - это холивар. Однако есть ряд следствий: Правильное использование Optional упрощает контракт в приложении, когда разработчик библиотеки явно показывает, какие поля могут быть null. А разработчик конечного приложения явно видит, где необходимо сделать проверки, а где можно обойтись без них. Тут важны ключевые слова правильное использование и явно видит. В реальности сама переменная типа Optional<*> может быть null'ом. Работа с Optional ударяет по производительности, так как может потребовать выделение в куче дополнительных объектов - самих классов Optional, лябд. Заодно увеличивается вероятность боксинга примитивных значений. При работе с Optional'ами увеличивается объем кода. Из всего этого есть важные следствия: Вопросы об Optional ближе к вопросам о Code Review, о Style Guide. При правильном подходе код будет более чистый, при неправильном - всё станет только хуже. Для JVM класс Optional не оказывает положительного эффекта, однако может оказать отрицательный. Если вам надежность важнее скорости (например, нагрузка не такая большая, а данные серьезные), то Optional может помочь сделать код лучше. И еще раз - это холивар, четкого и однозначного ответа нет.

Как заблокировать вызовы JS из Console?

#javascript #console


Насмотревшись на Facebok, решил блокировать вызов JS функций из консоли:



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

 console = new Object();


Тем самым заблокировал использования любых log-ов.

Вопрос состоит в следующем:
Можно ли как то отслеживать ввод команд в консоль, либо заблокировать совсем?
    


Ответы

Ответ 1



Ранее это было возможно только в обозревателях сети, основанных на Chrome. Но сейчас разработчики Chrome эту возможность отключили. Об этом сообщил инженер по безопасности в Facebook (на английском). В своём ответе он вкратце описал как он это делал. Также один из пользователей сообщил там о том как это было ранее сделано на Facebook (на английском). Подробное описание. Тем не менее существует возможность просто напугать пользователя, выводя цветные записи в console. К примеру так: console.log('%cОстановитесь!\nВыполнив действия в console ваша учётная запись окажется в чёрном списке!', 'color:#f00;font:bold 26px "Courier New";background-color:#000;padding:16px;padding:50px 500px');

Можно ли как-то отображать динамически изменяющуюся раскладку на Java?

#java #клавиатура


Добрый день, я хочу сделать отображение активного языка раскладки клавиатуры в своём
приложении, просто потому что в полноэкранном режиме не удобно её контролить каждый
раз выходя из него. Если знаете хорошее решение поделитесь пожалуйста)
    


Ответы

Ответ 1



Отловить событие переключения раскладки в системе не используя нативных библиотек не получится. Однако можно получать Locale из InputContext нужного вам компонента или окна. Периодически опрашивая контекст вы можете обновлять отображения внутри вашего приложения. Пример на swing: import javax.swing.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; public class Main { private static void createAndShowGUI() { final JFrame frame = new JFrame(""); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); final JLabel label = new JLabel(frame.getInputContext().getLocale().toString()); frame.getContentPane().add(label); Timer timer = new Timer(2000, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { label.setText(frame.getInputContext().getLocale().toString()); } }); timer.start(); frame.pack(); frame.setVisible(true); } public static void main(String[] args) { javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { createAndShowGUI(); } }); } }

оператор new возвращает указатель на 0 байт

#cpp


Для чего может понадобиться такая конструкция и почему при запросе 0 байт возвращается
указатель.

char* pC = new char[0];


куда указывает указатель в данном примере?
    


Ответы

Ответ 1



Представьте себе, что вы пишете собственный класс-контейнер наподобие vector. Реальные данные у вас будут храниться в массиве. Для размера данных N вам нужно capacity (размер массива) не менее N. Если не разрешить массивы нулевого размера, то вам придётся в вашем коде кодировать случай пустого, только сконструированного контейнера отдельно. Другой пример, пускай в вашем коде нужен массив из N пользователей, где N приходит от пользователя. Запрещать 0 пользователей, и кодировать этот случай по-другому было бы глупо и чревато ошибками. Ну или вы разбираете HTML-документ, и возвращаете все найденные ссылки. Если их ноль, какого размера выделять под них массив? Ноль — нормальный частный случай числа. Если бы стандарт сделал массивы длиной в ноль отдельным случаем, то и в нашем коде приходилось бы обрабатывать этот случай отдельно. Зачем? Пользы от этого никакой, а только возможный вред. (Да, отрицательные числа — не нормальный случай. У нас может быть ноль пользователей, но не может быть −1 пользователь.) Ваш код возвращает какой-то ненулевой указатель. Сколько именно байт реально аллоцируется — подробности имплементации стандартной библиотеки вашего компилятора. В любом случае вы не имеете права разыменовывать полученный указатель.

В каком виде дизайнер должен передавать верстальщику изображения SVG

#вёрстка #svg


Никак не могу найти ответы на некоторые нюансы касаемо svg. Кто должен создавать
svg? Допустим есть макет, который необходимо сверстать... Логотипы и иконки не предоставлены
дизайнером в svg. Или если есть, то код svg с ошибками. Что должен делать верстальщик
в таком случае?

а) Жаловаться дизайнеру/заказчику?
b) вставлять png/иконочные шрифты?
c) писать сам svg?

Кто пишет код svg от руки, а не рисует? Входит ли это все в обязанности верстальщика/фронтендера
(a не дизайнера, например)? В полном ли объеме.   

Вот пример кода полученного от дизайнера сделанного в фотошопе 




    
        
    
    
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
    




    


Ответы

Ответ 1



Вопрос на самом деле хороший, актуальный, но надо немного под другим углом его задать: В каком виде дизайнер должен передавать верстальщику изображения SVG. Какие требования по содержанию кода SVG должны быть предъявлены дизайнеру. Ведь изображение можно рисовать совершенно по разному и многие проблемы верстальщика идут от исходного кода, порою просто невозможно реализовать поставленную задачу. Например: иконка нарисована лепкой, а не одиночными линиями, поэтому невозможно будет сделать анимацию. Растровое изображение в векторном формате обернуто в SVG. Поэтому очень затруднительно стилизовать его стилями CSS. Только с применением фильтров. см. Задать цвет картинке svg в файле со стилями Изображение в векторном редакторе нормально открывается, а в приложении не попадает в поле видимости. Изображение нарисовано в фотошопе и сохранено плагином в SVG. Код на выходе просто ужасный. Пример Невозможно изменить стили иконки с помощью обычных правил css. fill, stroke и т.д. К примеру,- стремление дизайнера к оригинальности порой доходит до абсурда. Вместо использования стандартных иконок меньше по весу 1к, типа галочки, он создаёт свой "шедевр" в растровом редакторе и сохраняет в формате base64. Пример Конечно, чтобы грамотно предъявить эти требования дизайнеру, необходимо верстальщику освоить начальные знания по SVG самому.

Как с помощью selenium(java) найти push уведомление браузера и кликнуть по нему?

#java #selenium


Как с помощью selenium(java) дождаться push уведомление браузера и когда оно появится
кликнуть по нему ? Использую драйвер chrome и selenium 3. 
вот пример сообщения 



    


Ответы

Ответ 1



Если я не ошибаюсь, то каждая нотификация может работать по-другому. Решение 1. В консоли браузера добавляем JS код, который будет печатать нам URL нотификации. Мы собираем эти данные и сами решаем какой именно URL нам нужно. // это JS if ('Notification' in window) { messaging.onMessage(function(event) { console.log('url is: ', event.notification.click_action); }); } // Это Java String js = "if ('Notification' in window) {\n" + " messaging.onMessage(function(event) {\n" + " console.log('url is: ', event.notification.click_action);\n" + " });\n" + "}"; ((JavascriptExecutor) driver).executeScript(js); Далее собираем логи с консоли, после появления нотификации и обрабатываем. Логи можно получить следующим образом: getDriver().manage().logs().get(LogType.BROWSER).getAll(); Напоминаю, что каждый вызов возвращает только НОВЫЕ логи. Также нужно включить поддержку логов при старте браузера LoggingPreferences logging = new LoggingPreferences(); logging.enable(LogType.BROWSER, Level.ALL); capabilities.setCapability(CapabilityType.LOGGING_PREFS, logging); Решение 2. В консоли браузера добавляем JS код, который будет автоматически переходить по ссылке нотификации. Минус подхода в том, что если будет много нотификации за раз, то перейдет только по одному URL-у. if ('Notification' in window) { messaging.onMessage(function(event) { console.log('url is: ', event.notification.click_action); window.location.href = event.notification.click_action; }); } Полезные ссылки: раз, два протестировать можно тут открыть сайт. Открыть DevTools. в консоль(вкладка Console) выполнить первый или второй JS скрипт. нажать кнопку SEND первый скрипт напечатает в консоле "url is: www...", второй скрипт дополнительно перейдет по этому URL

Получение содержимого ListView другого окна

#cpp #listview


Задача состоит в получении содержимого элемента ListView другого окна. Однако, данный
код приводит к критическому завершению работы процесса - владельца окна. Как исправить это?

#include "stdafx.h"
#include "windows.h"
#include "Commctrl.h"

int main() {

    HWND hWnd = FindWindow(NULL, L"");
    HWND hSysListView32 = FindWindowEx(hWnd, NULL, L"SysListView32", NULL);

    unsigned long pid;
    GetWindowThreadProcessId(hSysListView32, &pid);
    HANDLE hProc = OpenProcess(
        PROCESS_VM_OPERATION | PROCESS_VM_READ |
        PROCESS_VM_WRITE | PROCESS_QUERY_INFORMATION,
        FALSE,
        pid);

    LVITEM* lvi = new LVITEM;
    lvi->iSubItem = 0;
    lvi->cchTextMax = 256;

    LVITEM* lviAddr = (LVITEM*)VirtualAllocEx(hProc, NULL, sizeof LVITEM, MEM_COMMIT,
PAGE_READWRITE);
    WriteProcessMemory(hProc, lviAddr, lvi, sizeof LVITEM, NULL);

    if (SendMessage(hSysListView32, LVM_GETITEMTEXT, 0, (LPARAM)lviAddr) > 0)
        printf("%s\n", lvi->pszText);
    else
        printf("%d\n", GetLastError());

    system("pause");
    return 0;
}




Представляю рабочий код:

HWND hSysListView32 = FindWindowEx(hWnd, NULL, L"SysListView32", NULL);
HWND hSysHeader32 = FindWindowEx(hSysListView32, NULL, L"SysHeader32", NULL);

int itemsCount = SendMessage(hSysListView32, LVM_GETITEMCOUNT, 0, 0);
int colCount = SendMessage(hSysHeader32, HDM_GETITEMCOUNT, 0, 0);

DWORD dwProcessId;
GetWindowThreadProcessId(hSysListView32, &dwProcessId);
HANDLE hProcess = OpenProcess(
    PROCESS_VM_READ | PROCESS_VM_WRITE |
    PROCESS_VM_OPERATION,
    FALSE,
    dwProcessId);

if (!hProcess) return -1;

LVITEM* lviAddress = (LVITEM*)VirtualAllocEx(hProcess, NULL, sizeof LVITEM, MEM_COMMIT,
PAGE_READWRITE);
LPTSTR pItemText = (LPTSTR)VirtualAllocEx(hProcess, NULL, sizeof TCHAR * 512, MEM_COMMIT,
PAGE_READWRITE);

for (int i = 0; i < itemsCount; i++)
{
    TCHAR itemText[512] = { 0 };

    LVITEM lvItem = { 0 };
    lvItem.mask = LVIF_TEXT;
    lvItem.iSubItem = 0;
    lvItem.iItem = i;
    lvItem.pszText = pItemText;
    lvItem.cchTextMax = 512;

    WriteProcessMemory(hProcess, lviAddress, &lvItem, sizeof(LVITEM), NULL);

    int nSymbolsCount = SendMessage(hSysListView32, LVM_GETITEMTEXT, i, (LPARAM)lviAddress);
    if (nSymbolsCount > 0)
        ReadProcessMemory(hProcess, pItemText, itemText, sizeof TCHAR * nSymbolsCount,
NULL);

    wcout << itemText << endl;
}

VirtualFreeEx(hProcess, pItemText, 0, MEM_RELEASE);
VirtualFreeEx(hProcess, lviAddress, 0, MEM_RELEASE);
CloseHandle(hProcess);


Однако, на этот раз проблема заключается в том, что не удаётся получить текст каждого
элемента каждого столбца. В SysListView32 программы, с которой я работаю, 10 столбцов,
но получить информацию о содержимом их элементов получается только для 0-2 столбцов.
Дальше - неполная...

Как это исправить? Хотя если делать перебор не в цикле, а в ручную (ну самому i задавать),
любой item любого column выдаёт. Странно...



Никто не запрещал использовать hotkeys. Это вариант решения показался мне жизнеспособным:

HWND hWnd = FindWindow(NULL, L"");

DWORD idAttach = GetWindowThreadProcessId(GetForegroundWindow(), 0);
DWORD idAttachTo = GetWindowThreadProcessId(hWnd, 0);
AttachThreadInput(idAttach, idAttachTo, TRUE);
SetFocus(hWnd);

INPUT input = { 0 };
input.type = INPUT_KEYBOARD;
input.ki.wScan = 0;
input.ki.time = 0;
input.ki.dwExtraInfo = 0;

// CONTROL key down.
input.ki.wVk = VK_CONTROL;
input.ki.dwFlags = 0;
SendInput(1, &input, sizeof INPUT);

// "A" key down.
input.ki.wVk = 'A';
input.ki.dwFlags = 0;
SendInput(1, &input, sizeof INPUT);

// "A" key up.
input.ki.wVk = 'A';
input.ki.dwFlags = KEYEVENTF_KEYUP;
SendInput(1, &input, sizeof INPUT);

// CONTROL key up.
input.ki.wVk = VK_CONTROL;
input.ki.dwFlags = KEYEVENTF_KEYUP;
SendInput(1, &input, sizeof INPUT);

// CONTROL key down.
input.ki.wVk = VK_CONTROL;
input.ki.dwFlags = 0;
SendInput(1, &input, sizeof INPUT);

// "C" key down.
input.ki.wVk = 'C';
input.ki.dwFlags = 0;
SendInput(1, &input, sizeof INPUT);

// "C" key up.
input.ki.wVk = 'C';
input.ki.dwFlags = KEYEVENTF_KEYUP;
SendInput(1, &input, sizeof INPUT);

// CONTROL key up.
input.ki.wVk = VK_CONTROL;
input.ki.dwFlags = KEYEVENTF_KEYUP;
SendInput(1, &input, sizeof INPUT);

AttachThreadInput(idAttach, idAttachTo, FALSE);
SetFocus(NULL);


Но куда без очередных проблем... Проект на шарпе. Возникли трудности с переводом
(не работает - пустой буфер обмена):

        const ushort VK_CONTROL = 0x11;
        const uint KEYEVENTF_KEYUP = 0x0002;

        enum INPUT_TYPE : uint {
            INPUT_MOUSE = 0,
            INPUT_KEYBOARD = 1,
            INPUT_HARDWARE = 2
        }

        [StructLayout(LayoutKind.Sequential)]
        struct KEYBDINPUT {
            public ushort wVk;
            public ushort wScan;
            public uint dwFlags;
            public uint time;
            public IntPtr dwExtraInfo;
        }

        [StructLayout(LayoutKind.Explicit, Size = 28)]
        struct INPUT {
            [FieldOffset(0)] public INPUT_TYPE type;
            [FieldOffset(4)] public KEYBDINPUT ki;
        }

        [DllImport("user32.dll", CallingConvention = CallingConvention.StdCall, CharSet
= CharSet.Auto)]
        static extern IntPtr FindWindow(
            [In, Optional, MarshalAs(UnmanagedType.LPTStr)] string lpszClass,
            [In, Optional, MarshalAs(UnmanagedType.LPTStr)] string lpszWindow);

        [DllImport("user32.dll", CallingConvention = CallingConvention.StdCall, CharSet
= CharSet.Auto)]
        static extern IntPtr GetMessageExtraInfo();

        [DllImport("user32.dll", CallingConvention = CallingConvention.StdCall, CharSet
= CharSet.Auto)]
        static extern uint SendInput(
            [In] uint nInputs,
            [In, MarshalAs(UnmanagedType.LPArray)] INPUT[] pInputs,
            [In] int cbSize);

        [DllImport("user32.dll", CallingConvention = CallingConvention.StdCall, CharSet
= CharSet.Auto)]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool AttachThreadInput(
            [In] uint idAttach,
            [In] uint idAttachTo,
            [In] bool fAttach);

        [DllImport("user32.dll", CallingConvention = CallingConvention.StdCall, CharSet
= CharSet.Auto)]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool SetFocus([In, Optional] IntPtr hWnd);

        static INPUT FormateKbdInputStruct(ushort vkCode, bool down) {
            return new INPUT() {
                type = INPUT_TYPE.INPUT_KEYBOARD,
                ki = new KEYBDINPUT() {
                    wScan = 0,
                    time = 0,
                    dwExtraInfo = GetMessageExtraInfo(),
                    wVk = vkCode,
                    dwFlags = down ? 0 : KEYEVENTF_KEYUP
                }
            };
        }

        public MainForm() {

            InitializeComponent();

            Shown += delegate {
                IntPtr hWnd = FindWindow(null, "");

                uint idAttach = GetWindowThreadProcessId(GetForegroundWindow(), 0);
                uint idAttachTo = GetWindowThreadProcessId(hWnd, 0);
                if (!AttachThreadInput(idAttach, idAttachTo, true)) return;

                INPUT[] inputs = new INPUT[] { FormateKbdInputStruct(VK_CONTROL, true) };
                SendInput(1, inputs, Marshal.SizeOf(typeof(INPUT)));
                inputs[0] = FormateKbdInputStruct('A', true);
                SendInput(1, inputs, Marshal.SizeOf(typeof(INPUT)));
                inputs[0] = FormateKbdInputStruct('A', false);
                SendInput(1, inputs, Marshal.SizeOf(typeof(INPUT)));
                inputs[0] = FormateKbdInputStruct('C', true);
                SendInput(1, inputs, Marshal.SizeOf(typeof(INPUT)));
                inputs[0] = FormateKbdInputStruct('C', false);
                SendInput(1, inputs, Marshal.SizeOf(typeof(INPUT)));
                inputs[0] = FormateKbdInputStruct(VK_CONTROL, false);
                SendInput(1, inputs, Marshal.SizeOf(typeof(INPUT)));

                SetFocus(hWnd);
                SetFocus(IntPtr.Zero);
                AttachThreadInput(idAttach, idAttachTo, false);

                Debug.WriteLine(Clipboard.GetData(DataFormats.UnicodeText)); // Пусто!
           };
        }

    


Ответы

Ответ 1



Нужно обнулять память для структуры LVITEM и массива символов перед каждым вызовом WriteProcessMemory / SendMessage. Данный код позволяет получить текст для всех столбцов ListView: HWND hSysListView32 = FindWindowEx(hWnd, NULL, L"SysListView32", NULL); DWORD dwProcessId; GetWindowThreadProcessId(hSysListView32, &dwProcessId); HANDLE hProcess = OpenProcess( PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION, FALSE, dwProcessId); if (!hProcess) return -1; LVITEM* lviAddress = (LVITEM*)VirtualAllocEx(hProcess, NULL, sizeof LVITEM, MEM_COMMIT, PAGE_READWRITE); LPTSTR pItemText = (LPTSTR)VirtualAllocEx(hProcess, NULL, sizeof TCHAR * 512, MEM_COMMIT, PAGE_READWRITE); TCHAR itemText[512] = {0}; LVITEM lvItem = { 0 }; for (int i = 0; i < COUNT_ITEMS; i++) for(int j=0; j < COUNT_SUBITEMS; j++) { memset(&itemText,0,sizeof(itemText)); //обнуление памяти memset(&lvItem,0,sizeof(LVITEM)); lvItem.mask = LVIF_TEXT; lvItem.iSubItem = j; lvItem.iItem = i; lvItem.pszText = pItemText; lvItem.cchTextMax = 512; WriteProcessMemory(hProcess, lviAddress, &lvItem, sizeof(LVITEM), NULL); int nSymbolsCount = SendMessage(hSysListView32, LVM_GETITEMTEXT, i, (LPARAM)lviAddress); if (nSymbolsCount > 0) ReadProcessMemory(hProcess, pItemText, itemText, sizeof TCHAR * nSymbolsCount, NULL); wprintf(L"Item %d, subitem %d: %s\n", i,j,itemText); } VirtualFreeEx(hProcess, pItemText, 0, MEM_RELEASE); VirtualFreeEx(hProcess, lviAddress, 0, MEM_RELEASE); CloseHandle(hProcess);

Реклама съедает память устройства

#java #android #admob


Решил я попробовать добавить в одно своё Activity рекламу. Выбор упал на знаменитый
AdMob. Всё сделал, реклама работает. 

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

AdView MyBanner = (AdView) findViewById(R.id.MyBanner_id);

MobileAds.initialize(getApplicationContext(), "ca-app-pub-XXX~YYY");

AdRequest adRequest = new AdRequest.Builder()
                .addTestDevice("ZZZ")
                .build();
MyBanner.loadAd(adRequest);


Profiler говорит, что без этих строк MEMORY 35mb , со строками 80mb

Вопрос: как иметь рекламу и не тратить так много памяти?
    


Ответы

Ответ 1



AdMob использует WebView для загрузки объявлений. Это довольно ресурсоёмкий объект, который использует собственные библиотеки и подвержен сбоям. У вас нет никакого контроля над тем, как работает Admob SDK. Кроме того, использование памяти, вероятно, будет варьироваться в зависимости от типа объявления: тексты HTML и баннеры с изображениями и т.д. Так же не стоит забывать про то, что нужно управлять жизненным циклом объявлений @Override protected void onPostResume() { super.onPostResume(); mAdView.resume(); } @Override protected void onPause() { super.onPause(); mAdView.pause(); } @Override protected void onDestroy() { super.onDestroy(); mAdView.destroy(); } Это должно помочь избежать утечек памяти. С объёмом используемой памяти остаётся только смириться. Можете почитать про AdMob подробнее перейдя по ссылке.

Гарантированный запуск Windows Service

#c_sharp #net #windows_service


Есть относительно критический Windows Service, который должне гарантированно запускаться
в случае перезагрузки системы.

Проблема в том, что система после перезагрузки (точнее, пересоздания VM) достаточно
сильно тормозит, и старт сервиса падает с ошибкой:


  The ServiceName service failed to start due to the following error: 
  The service did not respond to the start or control request in a timely fashion.


Cудя по логам, код в OnStart у сервиса не вызывается, так что запросить дополнительное
время вызовом ServiceBase.RequestAdditionalTime не получается.

Сервис падает, даже если OnStart пустой. Судя по всему, инициализация CLR просто
не вписывается в стандартный таймаут.

При этом сервис нормально и быстро стартует на уже загрузившейся системе.

Настройки Recovery в свойствах сервиса не помогают - они применяются только в случае,
если сервис упал уже после успешного запуска.

Есть ли простой/стандартный/удобный способ гарантировать старт windows service в
условиях тормозов системы при старте?
    


Ответы

Ответ 1



Решили проблему добавлением задачи в Task Scheduler, которая периодически проверяет состояние сервиса и стартует его, если он еще не запущен. Наверное, можно было бы обойтись net start servicename, но прикрутили чуть более длинный скрипт: @ECHO OFF SET ServiceName=%~1 SC QUERYEX "%ServiceName%" | FIND "STATE" | FIND /v "RUNNING" > NUL && ( ECHO %ServiceName% is not running ECHO START %ServiceName% NET START "%ServiceName%" > NUL || ( ECHO "%ServiceName%" wont start EXIT /B 1 ) ECHO "%ServiceName%" is started EXIT /B 0 ) || ( ECHO "%ServiceName%" is running EXIT /B 0 ) Скрипт для создания таска через Octopus: $ErrorActionPreference = "Stop"; Set-StrictMode -Version "Latest"; # use http://msdn.microsoft.com/en-us/library/windows/desktop/bb736357(v=vs.85).aspx for API reference Function Create-ScheduledTask($TaskName,$RunAsUser,$RunAsPassword,$TaskRun,$Arguments,$Schedule,$StartTime,$StartDate,$RunWithElevatedPermissions,$Days,$Interval,$Duration) { # SCHTASKS /Create [/S system [/U username [/P [password]]]] # [/RU username [/RP password]] /SC schedule [/MO modifier] [/D day] # [/M months] [/I idletime] /TN taskname /TR taskrun [/ST starttime] # [/RI interval] [ {/ET endtime | /DU duration} [/K] [/XML xmlfile] [/V1]] # [/SD startdate] [/ED enddate] [/IT | /NP] [/Z] [/F] [/HRESULT] [/?] # note - /RL and /DELAY appear in the "Parameter list" for "SCHTASKS /Create /?" but not in the syntax above $argumentList = @(); $argumentList += @( "/Create" ); $argumentList += @( "/RU", $RunAsUser ); if( -not (StringIsNullOrWhiteSpace($RunAsPassword))) { $argumentList += @( "/RP", $RunAsPassword ); } $argumentList += @( "/SC", $Schedule ); if( -not (StringIsNullOrWhiteSpace($Interval)) ) { switch -Regex ($Schedule) { "MINUTE|HOURLY|ONLOGON|ONIDLE" { $argumentList += @( "/MO", $Interval ); } "WEEKLY|MONTHLY" { $argumentList += @( "/RI", $Interval ); } "ONCE|ONSTART|ONEVENT" { # we don't currently support providing an XPATH query string throw new-object System.NotImplementedException("Unsupported schedule option '$Schedule'."); } } } if( -not (StringIsNullOrWhiteSpace($Days)) -And $Schedule -Ne "DAILY" ) { if($Schedule -ne "WEEKDAYS") { $argumentList += @( "/D", $Days ); } else { $argumentList += @( "/D", "MON,TUE,WED,THU,FRI" ); } } $argumentList += @( "/TN", "`"$TaskName`"" ); if( $Arguments ) { $argumentList += @( "/TR", "`"'$TaskRun' $Arguments`"" ); } else { $argumentList += @( "/TR", "`"'$TaskRun'`"" ); } if( -not (StringIsNullOrWhiteSpace($StartTime)) ) { $argumentList += @( "/ST", $StartTime ); } if( -not (StringIsNullOrWhiteSpace($Duration)) ) { $argumentList += @( "/DU", $Duration ); } if( -not (StringIsNullOrWhiteSpace($StartDate)) ) { $argumentList += @( "/SD", $StartDate ); } $argumentList += @( "/F" ); if( $RunWithElevatedPermissions ) { $argumentList += @( "/RL", "HIGHEST" ); } Invoke-CommandLine -FilePath "$($env:SystemRoot)\System32\schtasks.exe" ` -ArgumentList $argumentList; } Function Delete-ScheduledTask($TaskName) { # SCHTASKS /Delete [/S system [/U username [/P [password]]]] # /TN taskname [/F] [/HRESULT] [/?] Invoke-CommandLine -FilePath "$($env:SystemRoot)\System32\schtasks.exe" ` -ArgumentList @( "/Delete", "/S", "localhost", "/TN", "`"$TaskName`"", "/F" ); } Function Stop-ScheduledTask($TaskName) { # SCHTASKS /End [/S system [/U username [/P [password]]]] # /TN taskname [/HRESULT] [/?] Invoke-CommandLine -FilePath "$($env:SystemRoot)\System32\schtasks.exe" ` -ArgumentList @( "/End", "/S", "localhost", "/TN", "`"$TaskName`"" ); } Function Start-ScheduledTask($TaskName) { # SCHTASKS /Run [/S system [/U username [/P [password]]]] [/I] # /TN taskname [/HRESULT] [/?] Invoke-CommandLine -FilePath "$($env:SystemRoot)\System32\schtasks.exe" ` -ArgumentList @( "/Run", "/S", "localhost", "/TN", "`"$TaskName`"" ); } Function Enable-ScheduledTask($TaskName) { # SCHTASKS /Change [/S system [/U username [/P [password]]]] /TN taskname # { [/RU runasuser] [/RP runaspassword] [/TR taskrun] [/ST starttime] # [/RI interval] [ {/ET endtime | /DU duration} [/K] ] # [/SD startdate] [/ED enddate] [/ENABLE | /DISABLE] [/IT] [/Z] } # [/HRESULT] [/?] Invoke-CommandLine -FilePath "$($env:SystemRoot)\System32\schtasks.exe" ` -ArgumentList @( "/Change", "/S", "localhost", "/TN", "`"$TaskName`"", "/ENABLE" ); } Function ScheduledTask-Exists($taskName) { $schedule = new-object -com Schedule.Service $schedule.connect() $tasks = $schedule.getfolder("\").gettasks(0) foreach ($task in ($tasks | select Name)) { #echo "TASK: $($task.name)" if($task.Name -eq $taskName) { #write-output "$task already exists" return $true } } return $false } Function StringIsNullOrWhitespace([string] $string) { if ($string -ne $null) { $string = $string.Trim() } return [string]::IsNullOrEmpty($string) } function Invoke-CommandLine { param ( [Parameter(Mandatory=$true)] [string] $FilePath, [Parameter(Mandatory=$false)] [string[]] $ArgumentList = @( ), [Parameter(Mandatory=$false)] [string[]] $SuccessCodes = @( 0 ) ) write-host ($FilePath + " " + ($ArgumentList -join " ")); $process = Start-Process -FilePath $FilePath -ArgumentList $ArgumentList -Wait -NoNewWindow -PassThru; if( $SuccessCodes -notcontains $process.ExitCode ) { throw new-object System.InvalidOperationException("process terminated with exit code '$($process.ExitCode)'."); } } function Invoke-OctopusStep { param ( [Parameter(Mandatory=$true)] [hashtable] $OctopusParameters ) $taskName = $OctopusParameters['TaskName'] $runAsUser = $OctopusParameters['RunAsUser'] $runAsPassword = $OctopusParameters['RunAsPassword'] $command = $OctopusParameters['Command'] $arguments = $OctopusParameters['Arguments'] $schedule = $OctopusParameters['Schedule'] $startTime = $OctopusParameters['StartTime'] $startDate = $OctopusParameters['StartDate'] if( $OctopusParameters.ContainsKey("RunWithElevatedPermissions") ) { $runWithElevatedPermissions = [boolean]::Parse($OctopusParameters['RunWithElevatedPermissions']) } else { $runWithElevatedPermissions = $false; } $days = $OctopusParameters['Days'] $interval = $OctopusParameters['Interval'] $duration = $OctopusParameters['Duration'] if((ScheduledTask-Exists($taskName))){ Write-Output "$taskName already exists, Tearing down..." Write-Output "Stopping $taskName..." Stop-ScheduledTask($taskName) Write-Output "Successfully Stopped $taskName" Write-Output "Deleting $taskName..." Delete-ScheduledTask($taskName) Write-Output "Successfully Deleted $taskName" } Write-Output "Creating Scheduled Task - $taskName" Create-ScheduledTask $taskName $runAsUser $runAsPassword $command $arguments $schedule $startTime $startDate $runWithElevatedPermissions $days $interval $duration Write-Output "Successfully Created $taskName" Enable-ScheduledTask($taskName) Write-Output "$taskName enabled" } # only execute the step if it's called from octopus deploy, # and skip it if we're runnning inside a Pester test if( Test-Path -Path "Variable:OctopusParameters" ) { $ParamsForRunning = @{ TaskName = 'Service WatchDog' RunAsUser= 'System' Command = $OctopusParameters["Octopus.Action[Deploy Worker].Output.Package.InstallationDirectoryPath"]+"\check-service.cmd" Arguments = 'SomeWorkerServiceName' Schedule = 'MINUTE' Interval = 20 } Invoke-OctopusStep -OctopusParameters $ParamsForRunning; } Не через Octopus - специфические вещи можно заменить на прямой вызов. Из неудобств - при умышленной остановке сервиса приходится менять ему Startup Type на Disabled, чтобы избежать автозапуска.

Получение частоты звука с микрофона

#c_sharp #аудио #фурье #fft #naudio


Есть задача получать частоту звука с микрофона для дальнейших преобразований на C#.
Подобное уже делал на Python с numpy, но тут как то не клеится...

private void write(byte[] angles, int byte_len)
{
    Complex[] _fftBuffer = new Complex[byte_len];
    var _m = (int)Math.Log(byte_len, 2.0);

    for (var n=0; n < byte_len; n++)
    {
        var r = angles[n];
        var i = 0;
        _fftBuffer[n].X = (float)(r * FastFourierTransform.HammingWindow(n, byte_len));
        _fftBuffer[n].Y = i;

    }
    FastFourierTransform.FFT(true, _m, _fftBuffer);
    float[] fft_x = new float[_fftBuffer.Length];
    for (var i=0; i<_fftBuffer.Length; i++)
    {
        fft_x[i] = Math.Abs(_fftBuffer[i].X);
    }
    int i_peak = fft_x.ToList().IndexOf(fft_x.Max());
    for (var i = 0; i < _fftBuffer.Length; i++)
    {
        fft_x[i] = (float)Math.Log(Math.Abs(_fftBuffer[i].X));
    }
    ///var i_peak = fft_x.Max();
    var i_interp = parabolic(fft_x, i_peak);

    float freq = byte_len * i_interp / angles.Length;
    Console.WriteLine("Debug stop");

    ///this.port.Write(this.first_command, 0, 8);
    ///this.port.Write(this.get_comand(0.0f, 0.0f), 0, 32);


}
private float parabolic(float[] f, int peak)
{
    var xv = 0.5f * (f[peak-1] - f[peak+1])/(f[peak-1]-2*f[peak] + f[peak+1]) + peak;

    return xv;
}


Это что на С# сделал. Сделано так, ибо так же было на Python.
В конечном итоге работает, но неправильно - частота получается одна и та же (+- пара
герц), но по идее там должны быть абсолютно другие частоты - передаем данные с помощью
звука.

Помогите разобраться, что в коде может быть не так?

UPD

Вот что получилось в итоге. Спасибо товарищу @MSDN.WhiteKnight - натолкнул на правильные
мысли.
Плюс использовался проект 
вот отсюда

выкладываю только код, который несет смысл по вытаскиванию частот звука с микрофона(МОНО).
Можно переделать на стерео - не особо сложно будет

public partial class MainWindow : Window
{
    static double Fs = 48000; // Частота дискретизации !В данной программе ТОЛЬКО
целые числа
    static double T = 1.0 / Fs; // Шаг дискретизации
    static int N; //Длина сигнала (точек)
    static double Fn = Fs / 2;// Частота Найквиста
    WaveIn waveIn;

    public MainWindow()
    {
        InitializeComponent();
    }

    void waveIn_DataAvailable(object sender, WaveInEventArgs e)
    {

        //данные из буфера распределяем в массив чтобы в нем они были в формате ?PCM?
        byte[] buffer = e.Buffer;
        N = buffer.Length;
        int bytesRecorded = e.BytesRecorded;
        Complex[] sig = new Complex[bytesRecorded / 2];
        for (int i = 0, j = 0; i < e.BytesRecorded; i += 2, j++)
        {
            short sample = (short)((buffer[i + 1] << 8) | buffer[i + 0]);
            sig[j] = sample / 32768f;
        }

        Fourier.Forward(sig, FourierOptions.Matlab);
        // обнуляем спектр на небольших частотах (там постоянная составляющая и вообще
много помех)
        for (int i = 0; i < 35 * sig.Length / Fn; i++)
        {
            sig[i] = 0;
        }

        write(sig);

    }
    //Окончание записи
    private void waveIn_RecordingStopped(object sender, EventArgs e)
    {
        waveIn.Dispose();
        waveIn = null;
    }

    private void start_button_Click(object sender, RoutedEventArgs e)
    {
        this.waveIn = new WaveIn();
        this.waveIn.DeviceNumber = 0;
        this.waveIn.DataAvailable += this.waveIn_DataAvailable;
        this.waveIn.RecordingStopped += this.waveIn_RecordingStopped;
        this.waveIn.WaveFormat = new WaveFormat((int)Fs, 1);
        this.waveIn.StartRecording();
        Start_button.IsEnabled = false;
        Stop_button.IsEnabled = true;
        this.log_box("старт записи");
    }


    private void stop_Button_Click(object sender, RoutedEventArgs e)
    {
        this.stop_recording();
    }

    private void stop_recording()
    {
        this.waveIn.StopRecording();
        Start_button.IsEnabled = true;
        Stop_button.IsEnabled = false;
        this.log_box("конец записи");
    }


    private void log_box(string message)
    {
        Log_Box.AppendText("\n" + message);
        Log_Box.ScrollToEnd();
    }

    private void write(Complex[] signal)
    {
        PointPairList list1 = new PointPairList();
        int max_index = 0;
        double freq = 0;
        double K = signal.Length / 2;
        for (int i = 0; i < K; i++)
        {
            list1.Add(i * Fn / K, Complex.Abs(signal[i]) / N * 2);
        }

        foreach (ZedGraph.PointPair i in list1)
        {
            if (i.Y > list1[max_index].Y)
            {
                max_index = list1.IndexOf(i);
            }
        }
        freq = list1[max_index].X;

        string s = freq.ToString();
        log_box(s);


    }

}


что использовалось...
NAudio - для получения потока звука с микрофона

MathNET - Фурье

ZedGraph - как переходник для работы с сигналом после преобразования Фурье - его
можно убрать, но в моем случае был удобен.
    


Ответы

Ответ 1



Ваш код будет работать нормально, только если на вход подать данные определенного формата: моно, 1 байт на сэмпл, определенная частота дискретизации и т.п. Кроме того, он не учитывает несколько деталей: из результата БПФ нужно отбросить первое значение ("постоянная составляющая") и вторую половину значений (которая не несет полезной информации); количество сэмплов должно быть в степени 2. Лучше написать код, который может корректно обрабатывать разные форматы, для этого возьмем за основу класс SampleAggregator из примера на Github: using System; using System.Collections.Generic; using System.Text; using System.Diagnostics; using NAudio.Dsp; namespace WindowsFormsTest1 { public class SampleAggregator { // volume public event EventHandler MaximumCalculated; private float maxValue; private float minValue; public int NotificationCount { get; set; } public Complex[] FftBuffer { get { return this.fftBuffer; } } int count; // FFT public event EventHandler FftCalculated; public bool PerformFFT { get; set; } private Complex[] fftBuffer; private FftEventArgs fftArgs; private int fftPos; private int fftLength; private int m; public SampleAggregator(int fftLength = 1024) { if (!IsPowerOfTwo(fftLength)) { throw new ArgumentException("FFT Length must be a power of two"); } this.m = (int)Math.Log(fftLength, 2.0); this.fftLength = fftLength; this.fftBuffer = new Complex[fftLength]; this.fftArgs = new FftEventArgs(fftBuffer); } bool IsPowerOfTwo(int x) { return (x & (x - 1)) == 0; } public void Reset() { count = 0; maxValue = minValue = 0; } public void Add(float value) { if (PerformFFT) { fftBuffer[fftPos].X = (float)(value * FastFourierTransform.HammingWindow(fftPos, fftBuffer.Length)); fftBuffer[fftPos].Y = 0; fftPos++; if (fftPos >= fftBuffer.Length) { fftPos = 0; // 1024 = 2^10 FastFourierTransform.FFT(true, m, fftBuffer); if(FftCalculated != null) FftCalculated(this, fftArgs); } } maxValue = Math.Max(maxValue, value); minValue = Math.Min(minValue, value); count++; if (count >= NotificationCount && NotificationCount > 0) { if (MaximumCalculated != null) { MaximumCalculated(this, new MaxSampleEventArgs(minValue, maxValue)); } Reset(); } } } public class MaxSampleEventArgs : EventArgs { [DebuggerStepThrough] public MaxSampleEventArgs(float minValue, float maxValue) { this.MaxSample = maxValue; this.MinSample = minValue; } public float MaxSample { get; private set; } public float MinSample { get; private set; } } public class FftEventArgs : EventArgs { [DebuggerStepThrough] public FftEventArgs(Complex[] result) { this.Result = result; } public Complex[] Result { get; private set; } } } Тогда для определения частоты порции из первых 1024 сэмплов Wav-файла можно использовать вот такой код: using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Text; using System.Windows.Forms; using NAudio; using NAudio.Wave; using NAudio.Wave.SampleProviders; namespace WindowsFormsTest1 { public partial class Form1 : Form { private float parabolic(float[] f, int peak) { if (peak == 0) return f[0]; var xv = 0.5f * (f[peak - 1] - f[peak + 1]) / (f[peak - 1] - 2 * f[peak] + f[peak + 1]) + peak; return xv; } public Form1() { InitializeComponent(); } void PrintFrequency(float[] samples, int n_samples, WaveFormat fmt) { textBox1.Text = ""; for (int i = 0; i < fmt.Channels; i++) { SampleAggregator aggregator = new SampleAggregator(n_samples); aggregator.PerformFFT = true; int j; float f; //выделяем данные одного канала for (j = 0; j < n_samples; j++) { int index = (j * fmt.Channels) + i; f = samples[index]; aggregator.Add(f); } float[] fft_x = new float[aggregator.FftBuffer.Length / 2]; //только первая половина БПФ имеет смысл for (j = 0; j < fft_x.Length; j++) { float real = aggregator.FftBuffer[j].X; float imag = aggregator.FftBuffer[j].Y; fft_x[j] = (float)Math.Sqrt(real * real + imag * imag); //получаем амплитуду } fft_x[0] = 0.0f;//избавляемся от постоянной составляющей int i_peak = fft_x.ToList().IndexOf(fft_x.Max()); for (j = 0; j < fft_x.Length; j++) { fft_x[j] = (float)Math.Log(Math.Abs(aggregator.FftBuffer[j].X)); } var i_interp = parabolic(fft_x, i_peak); float freq = fmt.SampleRate * i_interp / (float)n_samples; textBox1.Text += ("Channel " + i.ToString() + ": " + freq.ToString() + " Hz" + Environment.NewLine); } } private void button1_Click(object sender, EventArgs e) { WaveStream readerStream = new WaveFileReader("c:\\Test\\sound_01.wav"); WaveStream pcmStream; WaveStream stream; //создаем поток в PCM-формате if (readerStream.WaveFormat.Encoding != WaveFormatEncoding.Pcm) { pcmStream = WaveFormatConversionStream.CreatePcmStream(readerStream); stream = new BlockAlignReductionStream(pcmStream); } else { pcmStream = readerStream; stream = readerStream; } float[] samples; const int N_SAMPLES = 1024; //количество сэмплов для спектрального анализа ISampleProvider prov; using(stream) using(readerStream) using (pcmStream) { prov = stream.ToSampleProvider(); samples = new float[N_SAMPLES * prov.WaveFormat.Channels]; int res = prov.Read(samples, 0, N_SAMPLES * prov.WaveFormat.Channels); if (res < N_SAMPLES * prov.WaveFormat.Channels) throw new Exception("Not enough data"); } PrintFrequency(samples,N_SAMPLES,prov.WaveFormat); } } }

Как реализовать анимацию по клику SVG фигур в EDGE и IE?

#javascript #css #svg #анимация #microsoft_edge



  Данный вопрос уже можно сказать не актуален  Microsoft Edge-Chromium
  Insider


 


  Чтобы не возникало лишних вопросов, и не было предложений, которые не
  решают  вопроса. Изменил примеры на оригинал.


Коллеги, я знаю два способы анимации...


  Оба варианта работают во всех доступных мне браузерах, не проверял только Safari


Первый - это с использованием функции elem.beginElement();



var wrapper_svg_1 = document.getElementById("wrapper_svg_1"),
  close = document.getElementById('close'),
  open = document.getElementById("open");

let flag = true;

wrapper_svg_1.addEventListener('click', function() {
  if (flag == true) {
    close.beginElement();
    flag = false;
  } else {
    open.beginElement();
    flag = true;
  }
});
* {
  margin: 0;
  padding: 0;
}

html,
body {
  width: 100vw;
  height: 100vh;
  background: #272727;
  font-size: 20px;
}

#wrapper {
  width: 100vw;
  height: 100vh;
  background: transparent;
}
Вариант второй, можно реализовать при помощи смены class-A элементу let wrapper = document.getElementById("wrapper"), iconActive = document.getElementById("icon-active"); wrapper.addEventListener('click', function() { iconActive.classList.toggle('icon-active'); }); * { margin: 0; padding: 0; } html, body { width: 100vw; height: 100vh; background: #272727; font-size: 20px; } #wrapper { width: 100vw; height: 100vh; background: transparent; }
В первом примере Microsoft Edge не поддерживает функцию elem.beginElement(); Вопрос, есть ли ее аналог для Microsoft Edge && IE? А во-втором варианте, главное то, что class элементу left добавляется и удаляется, но не работает. Кто подскажет, что делать? Главным образом,надо решить проблему с EDGE... IE - не обязательно, но для общей информации и можно привести пример решение для этого браузера от - "САТАНЫ" Данный вопрос на StackOverflow


Ответы

Ответ 1



Для решения данной проблемы нужно использовать polyfill( FakeSmile не подходит, так как не работает в Microsoft Edge ( исправьте, если не прав ) ). Вот данный polyfill — svg-animation. Подключаем скрипты, добавляем анимацию и тестируем в браузерах, которые не поддерживают SMIL. Пример кода: var rect= document.getElementById('rectangle'); rect.animate([{ width: '200' }, { width: '0' }], { duration: 2000, iterations: 10 });