Страницы

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

суббота, 27 октября 2018 г.

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

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


Ответ

Отловить событие переключения раскладки в системе не используя нативных библиотек не получится. Однако можно получать 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 байт

Для чего может понадобиться такая конструкция и почему при запросе 0 байт возвращается указатель.
char* pC = new char[0];
куда указывает указатель в данном примере?


Ответ

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

Ноль — нормальный частный случай числа. Если бы стандарт сделал массивы длиной в ноль отдельным случаем, то и в нашем коде приходилось бы обрабатывать этот случай отдельно. Зачем? Пользы от этого никакой, а только возможный вред.
(Да, отрицательные числа — не нормальный случай. У нас может быть ноль пользователей, но не может быть −1 пользователь.)

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

Получение содержимого 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
", lvi->pszText); else printf("%d
", 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)); // Пусто! }; }


Ответ

Нужно обнулять память для структуры 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
", i,j,itemText); }
VirtualFreeEx(hProcess, pItemText, 0, MEM_RELEASE); VirtualFreeEx(hProcess, lviAddress, 0, MEM_RELEASE); CloseHandle(hProcess);

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

Решил я попробовать добавить в одно своё 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
Вопрос: как иметь рекламу и не тратить так много памяти?


Ответ

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

Есть относительно критический 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 в условиях тормозов системы при старте?


Ответ

Решили проблему добавлением задачи в 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, чтобы избежать автозапуска.

Django-allauth response 302

Всем привет!
Возникает проблема при авторизации с помощью социальных сетей. Ошибка заключается в том, что при переходе по ссылке регистрации/авторизации http://MYSITE.ru/accounts/vk/login/?method=oauth2, браузер возвращает ошибку ERR_TOO_MANY_REDIRECTS. Код ошибки 302
Проверка на https://bertal.ru дает следующие результаты:
HTTP/1.1 302 Found Server: nginx Date: Thu, 05 Apr 2018 04:37:10 GMT Content-Type: text/html; charset=utf-8 Connection: keep-alive Keep-Alive: timeout=5 X-Frame-Options: SAMEORIGIN Vary: Cookie Set-Cookie: sessionid=s6ei8nyhxpx0xvl430zjtrsyimwv1l4i; expires=Thu, 19-Apr-2018 04:37:10 GMT; HttpOnly; Max-Age=1209600; Path=/ Location: https://oauth.vk.com/authorize?redirect_uri=http%3A%2F%2Fyakhshisarova.ru%2Faccounts%2Fvk%2Flogin%2Fcallback%2F&state=fUNKUiqBxJSy&response_type=code&scope=&client_id=2806261
От службы поддержки хостера получил следующий ответ:
Здравствуйте. ERR_TOO_MANY_REDIRECTS или циклическая переадресация возникает как правило из-за настроек .htaccess и обработчика ссылок в самой CMS, что посылает клиента по зацикленному кругу. Т.е после нажатия на "разрешить" происходит зацикливание, например передается какой-либо заголовок или срабатывает правило в .htaccess. Вам нужно на примере ссылки кнопки "разрешить" отследить куда перенаправляется клиент и что обрабатывает данный запрос\переход по ссылке. Судя по всему клиент переадресовывается снова на эту форму, где он уже авторизован. В данной ситуации вам сможет помочь разработчик веб-приложения.
Данная ошибка повторяется на разных устройствах и браузерах.
Настройки приложения во ВКонтакте (проблема не только с ВКонтакте, но и с Instagram):

Настройки приложения на сайте:

Настройки .htaccess
AddHandler fcgid-script .fcgi Options +FollowSymLinks RewriteEngine On RewriteBase / RewriteRule ^(media/.*)$ - [L] RewriteRule ^(static/.*)$ - [L] RewriteCond %{REQUEST_URI} !(cgi-bin/realestate.fcgi) RewriteRule ^(.*)$ cgi-bin/realestate.fcgi/$1 [L]
Настройки settings.py
import os
# Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = SECRET_KEY
# SECURITY WARNING: don't run with debug turned on in production! DEBUG = True
ALLOWED_HOSTS = ['yakhshisarova.ru', '127.0.0.1']
# Application definition
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'django.contrib.sites',
'widget_tweaks',
'allauth', 'allauth.account', 'allauth.socialaccount', 'allauth.socialaccount.providers.vk', 'allauth.socialaccount.providers.instagram' ]
AUTHENTICATION_BACKENDS = ( 'django.contrib.auth.backends.ModelBackend', 'allauth.account.auth_backends.AuthenticationBackend', )
ACCOUNT_EMAIL_REQUIRED = False
SITE_ID = 1
MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ]
ROOT_URLCONF = 'realestate.urls'
TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [ os.path.join(BASE_DIR, 'templates'), ], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ]
WSGI_APPLICATION = 'realestate.wsgi.application'
# Database # https://docs.djangoproject.com/en/2.0/ref/settings/#databases
DATABASES = { 'default': { 'HOST': HOST, 'ENGINE': 'django.db.backends.mysql', 'NAME': NAME, 'USER': USER, 'PASSWORD': PASSWORD, } }
# Password validation # https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [ { 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', }, { 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', }, { 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', }, { 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', }, ]
# Internationalization # https://docs.djangoproject.com/en/2.0/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/2.0/howto/static-files/
STATIC_URL = '/static/' STATICFILES_DIRS = [ os.path.join(BASE_DIR, 'static'), ]
LOGIN_REDIRECT_URL = 'index' LOGOUT_REDIRECT_URL = 'index' STATIC_ROOT = '/home/httpd/vhosts/yakhshisarova.ru/httpdocs/static'
В чем может быть проблема? Как происходят перенаправления внутри приложения allauth?


Ответ

Когда Вы переадресуете к ВК, то в этом запросе должна содержаться ссылка, по которой нужно перейти в случае успеха авторизации. Код по этой ссылке должен связаться с сервером ВК и запросить данные по токену, который был передан гетом в этой ссылке.
Вы адресуете юзера на http://yakhshisarova.ru/accounts/vk/login/?method=oauth2
Затем сюда https://oauth.vk.com/authorize?redirect_uri=http%3A%2F%2Fyakhshisarova.ru%2Faccounts%2Fvk%2Flogin%2Fcallback%2F&client_id=2806261&scope=&state=ZS1oaZiEzwzj&response_type=code
Обратная ссылка http://yakhshisarova.ru/accounts/vk/login/callback/ которая у вас все равно отправляет на ВК. Смотри что за код отрабатывает по данному url.(он у вас зачем-то редиректит)
Так же я не вижу у Вас кода обработки урла редиректа, смотри как это делается тут https://vivazzi.ru/it/allauth/
UPD добавить в htaccess флаг QSA к правилу Rewrite.
Также попробуйте убрать http из "Выбранные сайты"

Узнать версию удаленной OS

Есть удаленный компьютер, под какой-то версией Windows (начиная с WinXP Home и заканчивая Win10). Компьютер не входит в домен, но на нем установлен RAdmin Server.
Нужно определить версию и редакцию ОС.
Т.к. компьютеров много (~300), то нужно что-то автоматизированное.
Пробовал
(Get-WmiObject Win32_OperatingSystem -computer "Имя компа").name
но для недоменных компьютеров возвращает "Отказано в доступе (E_ACCESSDENIED)"
Может можно как-то заскриптовать работу с RAdmin?
Update
Удалось автоматизировать вход на удаленную машину через Radmin Telnet. Теперь вопрос - как получить редакцию ОС?
ver
возвращает
Microsoft Windows XP [Версия 5.1.2600]
а нужно еще редакцию Home или Professional
PowerShell на XP не установлен


Ответ

wmic os get Caption,CSDVersion /value
или (но у меня systeminfo думает ну очень долго)
systeminfo | findstr /B /C:"OS Name" /C:"OS Version"
Источник: https://www.windows-commandline.com/find-windows-os-version-from-command/

Проблемы с кодировкой в Spring 3 MVC

Здравствуйте. Пытаюсь сделать вот что: @RequestMapping(value = "/something") @ResponseBody public String helloWorld() { return "Русский текст"; } Страница принимает Примерно это: ??????? ????? На странице кодировка UTF-8. Но даже если бы она и другой была бы, то были бы в любом случае не вопросительные знаки. Посоветуйте, пожалуйста, как исправить ситуацию. Дополню вопрос: Очень странно получилось в такой ситуации: @RequestMapping(value = "/something") public ResponseEntity helloWorld() { MyCls cls = new MyCls(); cls.setStr("Русский текст");
HttpHeaders h = new HttpHeaders(); return new ResponseEntity(cls, h, HttpStatus.OK); } Сейчас сработало так, как я и ожидал: {"str":"Русский текст"} У спринга что, на тип String аллергия?


Ответ

Похоже, это известная проблема (аналогичный вопрос на SO, баг в джире), решается она несколькими способами:
так, как указал @Nofate - использовать ResponseEntity с соответствующими заголовками; добавить конвертер в hw-servlet.xml (код ниже); забабахать свой конвертер с блекдж... (как тут).
Сконфигурированный конвертер:

В ответе на SO говорится, что второй метод не сработает с mvc:annotation-driven - не знаю, у меня заработало и с ним.

UPD. Ага, с mvc:annotation-driven не заработает, если поставите конвертер после этого объявления.

“Звездочки” в input

Как сделать чтобы были звездочки в , а не шарики и квадраты? Возможно ли это сделать?


Ответ


*:
$:
.:


Немного кривоватое решение, но его можно доработать. Напильником. =)

Доказательство корректности алгоритма Куна для нахождения максимального паросочетания

Много где пишут, что его корректность очевидно следует из теоремы Бержа. Однако не понятно, почему если увеличивающая цепь существует, алгоритм её найдет?


Ответ

Если я правильно понял, идея вот в чём. Пусть и правда существует увеличивающая цепь. Поскольку мы перебираем все стартовые вершины, мы в нашем переборе попробуем начать и со стартовой вершины увеличивающей цепи (которая, как мы предположили, существует). Мы перебираем по сути все чередующиеся цепочки (в терминах из текста по ссылке @ReinRaus) из этой вершины, так что при переборе мы должны добраться и до той самой существующей цепи.

Как можно программировать на чистом си для андройд?

Как можно программировать на чистом си для андройд? С чего начать? Язык си знаю.


Ответ

С изучения ndk. Правда, насколько мне известно, так просто писать только на C не получится. Придется использовать Java прослойку. Литературы в интернете валом. Вот к примеру, первое попавшееся и второе

WPF проверка значений

Есть два изсестных мне подхода проверки вводимых данных и выдачи соотвествующего сообщения пользователю - это генерация исключения в сеттере и использование ExceptionValidationRule и использование интерфейса IDataError Вопрос в следующем: можно ли применять разные стили ErrorTemplate в зависимости от типа исключения? Приведу пример - есть класс описывающий объект, с полями A, B и C. В зависимости от занчений A и B поле C может быть либо обязательным, либо желательным. В первом случае незаполненость поля C - это критическая ошибка и нельзя давать пользователю сохранить объект, во втором же случае нужно просто уведомить пользователя. На деле же я хочу применять разные стили в зависимости от того желательное поле или обязательное для заполнения. Скажем для обязательных подсвечивать контролы красным, а для желательных - зеленым. Возможно ли это, или придется придумать другой подход, без использованяи встроенной валидации?


Ответ

В общем решение нашлось в ходе изучения структуры класса Validation. Почему я его так долго искал - не знаю, не туда смотрел видимо. У класса Validation есть коллекция Errors, которая содержит описание ошибок валидации. Дак вот каждый элемент этой коллекции имеет тип ValidationError, у которого и есть нужное нам свойство Exception. Теперь мы можем основываясь на его типе, определять шаблон. Приведу пример. Может кому пригодится. Есть простой класс: public class SomeClass { public int A { get; set; }
public int B { get; set; }
private int _c; public int C { get { return _c; } set { // Вот тут мы и будем в зависимости от состояния других полей генерировать разные исключения if (A == 0 && B == 0 && value == 0) throw new ArgumentMandatoryException("Поле является обязательным к заполнению"); if ((A != 0 || B != 0) && value == 0) throw new ArgumentDesiredException("Поле является желательным к заполнению"); _c = value; } } } Как видите в сеттере свойства C мы в зависимости от выполнения условия бросили два разных исключения. Теперь нам надо обработать это в шаблоне Validation.ErrorTemplate. Здесь нам помогут ContentControl и DataTemplate. Получим что-то такое:

Теперь в зависимости от типа исключения рамка будет либо класной, либо желтой.

Как написать драйвер клавиатуры под Mac, Win и Linux?

Есть специфическая железка самодельная, с клавиатурой. Есть желание написать драйвер, чтобы, набирая текст на этой чудо-клаве, текст появлялся в редакторе. Соответственно чтобы работала в разных ОС. К сожалению, пока никакой информации про разработку клавиатуры, драйвер клавы под ОС не нашёл. Пока не понятно, как решить вопрос с кодировкой, символами, раскладкой и как весь процесс происходит от нажатия до появления символа на экране. Прошу подсказать, что почитать, поизучать, и есть ли какие-нибудь наработки.


Ответ

Никаких драйверов не нужно. USB устройство, а именно микроконтроллер, должно уметь общаться по USB HID. Если устройство на базе AVR, посмотрите конструктор tmk_keyboard или библиотеки V-USB, LUFA и т.п. В ВК есть сообщество, где вы можете спросить, найти помощь по вопросу. Разрешите полюбопытствовать, что за устройство?

Поддержка юникода в Shiny

В процессе создания приложения при помощи пакета Shiny столкнулся с неполной поддержкой юникода (если это можно так назвать) для кириллических символов. Русский текст, содержащий букву "я", вызывает ошибку
unexpected INCOMPLETE-STRING
Если текст не содержит этой буковки, то все ок.
Файл сохраняю в юникоде, как описано в Unicode characters in Shiny apps.
Пока что пишу эскейп-код \u044f вместо буквы, но хотелось бы разобраться в причинах такого поведения и, возможно, решить эту проблему без используемого сейчас костыля.
Пример кода:
# ui.R
shinyUI(fluidPage( titlePanel("Название приложения"),
sidebarLayout( sidebarPanel( "sidebar panel"), mainPanel("main panel") ) ))
# server.R
shinyServer(function(input, output) { })
sessionInfo():
R version 3.2.0 (2015-04-16) Platform: i386-w64-mingw32/i386 (32-bit) Running under: Windows 7 (build 7601) Service Pack 1
locale: [1] LC_COLLATE=Russian_Russia.1251 LC_CTYPE=Russian_Russia.1251 [3] LC_MONETARY=Russian_Russia.1251 LC_NUMERIC=C [5] LC_TIME=Russian_Russia.1251
attached base packages: [1] stats graphics grDevices utils datasets methods base
other attached packages: [1] shiny_0.11.1 zoo_1.7-12
loaded via a namespace (and not attached): [1] R6_2.0.1 htmltools_0.2.6 tools_3.2.0 Rcpp_0.11.6 RJSONIO_1.3-0 [6] grid_3.2.0 digest_0.6.8 xtable_1.7-4 httpuv_1.3.2 mime_0.3 [11] lattice_0.20-31


Ответ

Почитал тут: http://anton-pribora.ru/articles/php/locales/
Немного поэкспериментировал.
У меня сработало следующее: один раз в текущей сессии выполнил установку хитрой локали:
Sys.setlocale("LC_ALL","Russian_Russia.20866")
Все, можно запускать Shiny-приложение с буковками "я"
R version 3.2.1 (2015-06-18) Platform: i386-w64-mingw32/i386 (32-bit) Running under: Windows 7 (build 7601) Service Pack 1
locale: [1] LC_COLLATE=Russian_Russia.20866 LC_CTYPE=Russian_Russia.20866 [3] LC_MONETARY=Russian_Russia.20866 LC_NUMERIC=C [5] LC_TIME=Russian_Russia.20866

Два ярлыка, одно приложение

Манифест:

В приложении собираюсь сделать две иконки, одна "Настройки"( в манифесте это MainActivity) и Лаунчер(Launcher). Проблема в том, что при выходе одной из иконок, через HOME-кнопку, переходя во вторую, открывается первая в том же состоянии.
Если убрать эту комбинацию "action.Main"+"category.Launcher", то не создается иконка.


Ответ

UPDATE
Я нашел ответ. В AndroidManifest.xml для нужных активити добавьте следующее в intent-filter:

А также установите lable(имя ярлыка) и icon(иконку)
android:icon="@drawable/ic_launcher" android:label="@string/app_Launcher"
Пример,

Django Solr(Filter) - Выпадающий список для объектов, как реализовать? либо адаптировать

Имеется такой список объектов, отображенных по значению атрибутов товаров.

Значения атрибутов товаров, которые связаны между собой - должны быть в форме выпадающего списка Select(option)/DropDownMenu, т.е. - к примеру вместо Size:Medium,Size:Small должно быть просто
Medium Small
То что эти значения атрибутов относятся к Size можно указать внутри Templates(label), т.е. это не так важно, главное - каким образом их можно поместить в этот Select список (Dropdown Menu).

От себя хочу добавить, что я пытался изменить значения которые указывается по умолчанию, в Templates, вместо CheckBox'ксов - я пробовал указать Select Option, но результат получился неправильным


TEMPLATES
Browse.html:
{% for field, data in facet_data.items %} {% if data.results %} {% include 'search/partials/facet.html' with name=data.name items=data.results %} {% endif %} {% endfor %}
facet.html:
{% for item in items %}

{{ item.name }} {% if item.show_count %} ({{ item.count }}) {% endif %}


Product Class - это класс всех продуктов (товаров), используемый для индексирования, фильтром. Далее идет просто уточнение тех значений, которые нужно отобразить (в моем случае это значения атрибутов)
Search_Indexes.py
class ProductIndex(indexes.SearchIndex, indexes.Indexable): product_class = indexes.MultiValueField(null=True, faceted=True)
def prepare_product_class(self, obj): attributes = obj.attribute_values.all() if len(attributes) > 0: return [product_class for product_class in attributes]


Ответ

Раз не кто не догадался как это реализовать - придется самому объяснять. Значения которые формируются в HayStack и перерабатываются в template - не приспособлены к выпадающему списку как таковому, по этому эти значения нужно перенаправить в форму, а в форме уже описать логику, для этого надо: 1.Создать специальный {{tag}} который будет получать значения от словаря, и добавить его в Template, выглядеть это будет так -
{% load choice_tags %}
В самом тэге нужно указать форму.
@register.assignment_tag() def choice_form(request,field): form_class = addToChoiceForm
И еще 1 очень важный момент, если речь идет о Django(oscar), не знаю как у кого где описано, но в Oscar есть специальный ui.js, в которым описаны функции которые передают значения из поля, когда пользователь нажал на форму выбора, выглядит это так:
o.search.initFilterWidget(); initFilterWidget: function() { // Auto-submit (hidden) search form when selecting a new sort-by option $('#id_selected_facets').on('change', function() { $(this).closest('form').submit(); }); }
selected_facets, это названия поля в вашей форме, далее в коде ее можно встретить.
Далее в Template следует передать значения от словаря.
{% for field, data in facet_data.items %} {% choice_form data field as choice_form %}
После чего следует указать форму Get, и поместить туда поле с формой, для передачи результата в адресную строку.

{% include "partials/form_fields.html" with form=choice_form %}

Далее следует итрировать объекты полученные из словаря в choice_tags, и добавить их в массив(ну либо в словарь или вообще сразу передавать в форму). Делается это как угодно я лично сделал так.
for dataResult in request['results']: items.append(dataResult)
Ну и передаем items,field в форму AddToChoiceForm
form = form_class(items,field)
Далее следует создать класс AddToChoiceForm Это стандартный класс не буду полностью описывать, обозначу лишь ключевые части которые нужно для формирования списка.
В начале Создаем массив Choices=[] , в который мы будем передавать значения из словаря. Далее итрируем объекты. Обращаемся к интересующимся нас объектам по ключу, в моем примере это имя и путь url.
for item in items: select_url = item.get('select_url') Class_name = item.get('name')
И добавляем наши объекты в массив. Тут 1 очень важный момент, чтобы избежать проблем с повторным кодированием в URL, Необходимо перекодировать все символы в простой вид для этого я использовал - родную библиотеку django purl
choices.append((unicode_unquote(select_url), Class_name))
Ну и добавляем наш массив в форму ChoiceField - выпадающий список.
self.fields['selected_facets'] = forms.ChoiceField( choices=choices,widget=forms.Select(), label=_("Variant"))
И на этом все, я не учел тут проблем с уникодом(возникающих в строке заголовка), поскольку еще сам их решаю, да и вообще сам метод не совсем красивый, и возможно в самой форме потребуется чистка, поскольку если пользователь выбирает что-то другое то что было в форме остается, и в результате пользователь получает не совсем то что хотел, но пока у меня не было времени все доделать.
Если у кого то может есть вариант по лучше буду рад услышать ! а так , решил к сожалению сам !

Настройка маппинга элементов

Есть два класса, для которые нужно сначала сохранить в некоторых DTO-объектах, которые полностью повторяют структуру этих классов, а затем снова развернуть в исходное состояние:
public class Element { public ParentItem Item { get; set; } }
public class ParentItem { public Element Parent { get; set; } }
Метод, который создает исходные данные:
static Element[] CreateElements() { var element2 = new Element(); return new[] { new Element(new ParentItem(element2)), element2, new Element() }; }
Конфигурация автомаппера и маппинг созданных элементов:
Mapper.CreateMap(); Mapper.CreateMap(); Mapper.CreateMap(); Mapper.CreateMap();
var elements = CreateElements(); var mappedElements = elements .Select(_ => Mapper.Map(_, typeof(Element), typeof(ElementDto))) .OfType() .ToArray();
Далее проверяем, сохранилась ли структура элементов после маппинга:
foreach (var element in mappedElements) { Console.WriteLine(mappedElements.Any(e => e?.Item?.Parent == element)); }
Этот код трижды выводит False. Из чего следует, что для объекта element2 из метода CreateElements было сделано две копии. Первая при копировании перечня элементов, вторая при копировании ParentItem.Parent
Как нужно настроить маппинг, чтобы в ParentItem.Parent не создавалась еще одна копия элемента?


Ответ

Как выяснилось, AutoMapper подобные штуки не умеет разруливать. Т.е. копии объектов все равно будут создаваться.
Так что подобные зависимости придется разруливать руками. В данном случае можно сделать примерно следующее. Во-первых, при маппинге не копировать всю цепочку элементов, а скопировать только Element и его Item. Остальная часть цепочки копируется с предварительными проверками на дублирование элементов.
Mapper.CreateMap() .ForMember(_ => _.Element, _ => _.Ignore());
Во-вторых, нужно запоминать элементы, которые уже были скопированы и заполнять Item вручную.
static IEnumerable MapElements(Element[] elements) { var elementToDtoMap = new Dictionary();
foreach (var element in elements) { MapElement(element, null, elementToDtoMap); }
return elementToDtoMap.Select(_ => _.Value); }
static void MapElement(Element element, ItemDto parentItem, Dictionary elementToDtoMap) { ElementDto elementDto = null; if (elementToDtoMap.TryGetValue(element, out elementDto)) return;
elementDto = Mapper.Map(element); elementToDtoMap.Add(element, elementDto);
if (parentItem != null) { parentItem.Element = elementDto; }
if (element.Item != null) { MapElement(element.Item.Element, elementDto.Item, elementToDtoMap); } }
Таким образом, можно добиться желаемого результата. Код, конечно, будет сильно зависить от конкретного случая. Но общий принцип примерно такой.

Помогите разобраться с отделением бизнес-логики от формы

Я только начинаю осваивать C#. Сейчас пытаюсь разобраться в аспектах проектирования приложения для работы с базами данных. Практически каждый раз я слышу такую фразу "бизнес-логика должна существовать отдельно от формы". Но я не совсем понимаю как этого добиться при программировании WinForms?
Посоветуйте пожалуйста исчерпывающее руководство или литературу на этот счет.
UPD:
На данный момент удалось понять, что при использовании WinForms необходимо использовать паттерн MVP. И единственный пример использования MVP для WinForm, по которому удалось построить рабочее приложение, я смог найти вот в этом топике Как начать пользоваться MVP + WinForms?. Следую изложенной в нём информации у меня получилось следующее приложение.
Можете оценить, насколько у меня получилась правильная реализация применения паттерна MVP и отделения бизнес-логики от формы?
View
using System.Linq;
namespace EFCodeFirstMVP { interface IView { void SetData(IQueryable items); } }
using System; using System.Linq; using System.Windows.Forms;
namespace EFCodeFirstMVP { public partial class Form1 : Form, IView { private readonly GoodsPresenter presenter;
public Form1() { presenter = new GoodsPresenter(this, new GoodsModel()); InitializeComponent(); }
public void SetData(IQueryable items) { dataGridView1.DataSource = items.ToList(); }
private void Form1_Load(object sender, EventArgs e) { presenter.LoadData(); } } }
Presenter
namespace EFCodeFirstMVP { class GoodsPresenter { private readonly IView view; private readonly IModel model;
public GoodsPresenter(IView view, IModel model) { this.view = view; this.model = model; }
public void LoadData() { var data = model.LoadData(); view.SetData(data); } } }
Model
using System.Linq;
namespace EFCodeFirstMVP { interface IModel { IQueryable LoadData(); } }
using System.Linq;
namespace EFCodeFirstMVP { class GoodsModel : IModel { public IQueryable LoadData() { Context context = new Context();
var items = from Items in context.Goods select Items;
return items; } } }
Data
using System.Data.Entity;
namespace EFCodeFirstMVP { class Context: DbContext { public DbSet Goods { get; set; } public DbSet GoodsList { get; set; }
public Context() { Database.SetInitializer(new CreateDatabaseIfNotExists()); } }
public class Goods { public int Id { get; set; } public string Name { get; set; } public string Description { get; set; } public string Barcode { get; set; } public int Price{ get; set; } } }


Ответ

Окей, давайте попробуем поговорить об этом вне привязки к WinForms.
Смотрите. У вас есть две различные вещи: внутреннее поведение программы, и то, как она показывает это пользователю. Представьте себе, чтобы вы пишете программное обеспечение радара. У вашей программы внутри есть список отслеживаемых самолётов. Вы принимаете информацию с датчиков, обсчитываете её, принимаете решение о том, возник новый самолёт, или ложная цель, или известный вам самолёт переместился. Всё это происходит внутри, и для этого взаимодействие с пользователем не так уж и обязательно. Это внутренняя часть программы, модель
Теперь, вам нужно донести эту информацию до оператора. В каком виде вы будете представлять информацию — в виде распечаток зелёного текста на чёрном фоне, или в виде трёхмерной голографической визуализации — не так уж важно, и модель по существу не зависит от этой части.
Поэтому вы должны писать модель так, чтобы модель ничего не знала о представлении. Это не то, чтобы строго обязательно, но это позволяет разделить программу на независимые части, и даёт лёгкость работы с ними.
Здесь ещё остаются открытыми вопросы о том, как передавать действия пользователя модели, но это отдельная тема.
Посмотрим на более приземлённый пример: работа с базой данных.
Точно так же у вас есть модель: база данных, и операции над ней, которые вы собираетесь делать. Это всё организуется в модуль, возможно, навешивается сверху синхронизация и асинхронность, на этом модель можно считать оконченной.
Представление должно просто
показывать пользователю часть модели принимать у пользователя команды, и доставлять их модели после обновления модели показывать обновлённую информацию
Обычно выделяют ещё и промежуточный уровень — бизнес-логику, контроллер, view model, которые занимаются пинанием модели, с тем чтобы представление занималось только представлением.

Не работает обработчик прерывания

Почему этот код, меняющий обработчик прерывания клавиатуры, не работает на реальном компьютере (выводит символ 'A' только 1 раз), хотя нормально работает в эмуляторах bochs и qemu? Используется компилятор gcc, архитектура x86.
asm(".code16gcc
");
typedef unsigned char uchar; typedef unsigned short ushort; typedef unsigned int uint; typedef struct __attribute__((__packed__)) FullAddr{ ushort offset; ushort seg; } FullAddr;
#define MAIN_CODE_START 0x9200
asm ( "xorw %ax, %ax
\t" "movw %ax, %ds
\t" /*По этому адресу компоновщик помещает начало секции .data*/ "movw (0x9202), %ax
\t" "movw %ax, %ds
\t" "movw %ax, %ss
\t" "movw $0xFFFB, %sp
\t" "jmp main" );
void print_char(char c){ asm volatile("int $0x10" : : "a"(0x0E00 | c), "b"(7)); }
void get_int_addr(ushort interrupt, FullAddr *addr) { asm volatile( "pushw %%ds
\t" "movw %w3, %%ds
\t" "movw (%w2), %w0
\t" "movw 2(%w2), %w1
\t" "popw %%ds" : "=c"(addr->offset), "=a"(addr->seg):"b"(interrupt*4),"a"(0) ); }
void set_int_addr(ushort interrupt, uint func){ asm volatile( "cli
\t" "pushw %%ds
\t" "movw %w2, %%ds
\t" "movw %w0, (%w1)
\t" "movw %%cs, 2(%w1)
\t" "popw %%ds
\t" "sti" : : "c"(func-MAIN_CODE_START), "b"(interrupt*4), "a"(0): ); }
void wait(uint usec) { asm volatile("int $0x15": : "a"(0x8600), "c"(usec>>16), "d"(usec&0xFFFF)); }
FullAddr addr;
void handler_func(){ print_char('A'); }
void handler(); asm( "handler:
\t" "pushal
\t" "call handler_func
\t" "popal
\t" "ljmp *addr
\t" "iret
\t" );
void main(){ get_int_addr(9, &addr); set_int_addr(9, (uint)handler); while(1){ wait(1000); } }
На всякий случай выкладываю полный код проекта , включающий образ дискеты (может, кто-нибудь запустит). Для компиляции нужно запустить файлы build.sh и build_main.sh.


Ответ

На английской версии SO подсказали, что проблема в значении сегмента %ds, которое может измениться внутри обработчика прерывания 0x15 ( функция wait) в BIOS. И в этот момент может сработать прерывание 0x9, которое я перехватываю. А в обработчике последнего, как раз используется сегмент %ds (ljmp *addr). То есть, переход будет не туда, куда нужно. Решение: каждый раз в функции handler менять %ds на своё значение, а в конце функции возвращать старое. Код:
void handler(); asm( "handler:
\t" "pushw %ds
\t" "pushal
\t" "xorw %ax, %ax
\t" "movw %ax, %ds
\t" "movw (0x9202), %ax
\t" "movw %ax, %ds
\t" "call handler_func
\t" "pushfw
\t" "lcallw *addr
\t" "popal
\t" "popw %ds
\t" "iretw
\t" );

Каким образом изменить внешний вид выпадающего списка редактора коллекции propertygrid

Имеется стандартный редактор коллекции с перегруженным базовым классом. Хотелось бы изменить внешний вид выпадающего списка, например некоторые строки выделить жирным шрифтом или другим цветом. Можно ли это сделать и как если можно.


Ответ

WinForms не дает много поля для кастомизации (или дает, но с боем). Кое-что сделать можно.
Для любого типа/свойства можно переопределять редактор с помощью атрибута Editor
public class Foo { [Editor(typeof(CustomEditor), typeof(UITypeEditor))] public string Bar { get; set; } }
Для этого нужно имплементировать UITypeEditor
public class CustomEditor : UITypeEditor { public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context) { // говорим, что это будет выпадающий список // есть вариант Modal, при котором мы можем показать форму в качестве редактора return UITypeEditorEditStyle.DropDown; }
public override object EditValue(ITypeDescriptorContext context, System.IServiceProvider provider, object value) { // context -- содержит информацию о текущем свойстве и родительском объекте
// provider -- дает сервис, с помощью которого можно отображать кастомные редакторы
// value -- содержит непосредственно редактируемый объект
IWindowsFormsEditorService service = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
var list = new ListBox() { SelectionMode = SelectionMode.One, DrawMode = DrawMode.OwnerDrawFixed }; // не забываем закрыть выпадающий список после того, как было выбрано значение list.SelectedValueChanged += (s, e) => service.CloseDropDown(); list.DrawItem += (s, e) => { // кастомизация шрифта (жирный/нежирный) e.DrawBackground(); var font = e.Index % 2 == 0 ? new Font("Arial", 9, FontStyle.Bold) : new Font("Arial", 9); e.Graphics.DrawString( list.Items[e.Index].ToString(), font, Brushes.Black, e.Bounds); e.DrawFocusRectangle(); };
// заполняем список ерундой for (int i = 1; i <= 5; i++ ) { list.Items.Add(i.ToString()); }
// устанавливаем текущее значение в качестве выбранного list.SelectedItem = value;
// показываем выпадающий список, показывать можно любой контрол service.DropDownControl(list);
// возвращаем выбранное в списке значение в качестве нового значения return list.SelectedItem; } }
Само приложение:
static class Program { [STAThread] static void Main() { Application.EnableVisualStyles(); Form form = new Form() { Controls = { new PropertyGrid() { Dock = DockStyle.Fill, SelectedObject = new Foo() } } }; Application.Run(form); } }

Еще несколько кратких примеров можно найти в статье на CodeProject

Область видимости исключения в блоке catch

Собственно, почему не функция, а только сам блок?
Проверено в Хроме, FF, IE11 и Опере 12.
function test() { var e = 10, x = 5; try { console.log(e, x); // 10 5 throw 15; } catch (e) { var x; console.log(e, x); // 15 5 e = x = 17; console.log(e, x); // 17 17 } finally { console.log(e, x); // 10 17 } } test();
Ну и ES6-версия с декомпозицией:
function test() { var e = 10, x = 5; try { console.log(e, x); // 10 5 throw {e: 15, x:3}; } catch ({e, x}) { console.log(e, x); // 15 3 e = x = 17; console.log(e, x); // 17 17 } finally { console.log(e, x); // 10 5 } } test();


Ответ

Обратимся к спецификации
Catch : catch ( CatchParameter ) Block в oldEnv сохраняется LexicalEnvironment из текущего контекста создается catchEnv как NewDeclarativeEnvironment(oldEnv) для каждого имени аргумента из BoundNames в CatchParameter в catchEnv создается MutableBinding текущим LexicalEnvironment в контексте устанавливается catchEnv в status сохраняется результат BindingInitialization если status не нормальный текущим LexicalEnvironment в контексте устанавливается oldEnv значением catch блока устанавливается значение status В B сохраняется результат выполнения Block. текущим LexicalEnvironment в контексте устанавливается oldEnv значением catch блока устанавливается значение B
Что можно отметить в данном алгоритме?
при входе в catch создается новый LexicalEnvironment значения указанные в параметрах catch связываются с новым LexicalEnvironment при выходе - всегда восстанавливается старый LexicalEnvironment
Таким образом e объявленную выше var e=10 перекрывает e в выражении catch (e), значение которой и используется внутри блока.
И так как при выходе из блока восстанавливается старый LexicalEnvironment, внутри блока finally будет видна переменная объявленная через var.
Теперь можно перейти к вопросу из комментария:
@VladimirGamalian, а разве NewDeclarativeEnvironment не означает собственный скоуп? Но у catch'а ведь его нет? Или это такая магия за счёт всплытия объявлений, что всплывает всё кроме исключения? Как-то странно получается...
В контексте выполнения присутствуют два дополнительных свойства
LexicalEnvironment - определяет Lexical Environment, в котором хранятся идентификаторы созданные в коде в текущем контексте VariableEnvironment - определяет Lexical Environment, в котором EnvironmentRecord хранит привязки созданные с помощью VariableStatements в текущем контексте
При использовании выражения var переменные добавляются в VariableEnvironment текущего контекста. Так как при заходе в catch меняется только LexicalEnvironment - то становится не важно в каком месте переменная объявлена с помощью var - она все равно будет добавлена в VariableEnvironment и будет доступна из любого места в текущем контексте выполнения.

Как убрать дубли страниц?

Привет всем!
Сейчас страница доступна и по такому адресу site.loc/page/2 и по такому site.loc/index.php?view=page&page_id=3. Движок самописанный.
.htaccess
RewriteEngine On RewriteRule ^page/(\d+)/?$ index.php?view=page&page_id=$1
Вопрос такой: как мне убрать дубликаты через .htaccess или php? Если кратко, то надо сделать редирект с site.loc/index.php?view=page&page_id=3 на site.loc/page/2


Ответ

RewriteCond %{HTTP_HOST} ^index.php$ RewriteRule ^index.php?view=(.*)&page_id=(.*)$ http://site.loc/$1/$2 [L,R=301]
Как-то так, но тут вопрос как по мне должен быть как раз в СЕО части, так как тот момент что страница доступна по нескольких адресам не является проблемой, но вот то что поисковые системы их индексируют отдельно - уже проблема. Как написал комментатор выше rel=canonical решит проблему!
К примеру движок OpenCart очень сильно грешит тем что можно к странице обратиться по ЧПУ, по полному пути с категорией и постом через ГЕТ, и напрямую по ИД записи, и при этом канонический путь очень сложно правельно настроить.

Сигналы и слоты Qt как в них ориентироваться?

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


Ответ

Какие только возможно связи делать средствами дизайнера, чтобы не забивать ими исходники. Массовое создание связей выносить в отдельный метод. В этом методе группировать создание связей по источникам. Обязательно проверять, что connect возвращает TRUE Использовать возможности C++11 для connect(), тогда проверка совместимости сигнала и слота будет происходить в момент компиляции.
Пример для 5-го пункта:
connect(sender, &Sender::valueChanged, receiver, &Receiver::updateValue );
Документация

HTML: Как сделать так, чтобы текст не выпадал из блока [дубликат]

На данный вопрос уже ответили: Почему длинное слово выходит за границы блока? 2 ответа Можно ли сделать так, что когда текст достигает края блока, он не выходит за него, а переносится на новую строку?


текст задаю в JS. И кусок CSS:
"text": "kokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokkokokokokokokkokokokokokokokokokokko .post-text { display: inline-block; width: 600px; height: auto; margin-top: -30px; margin-left: 120px; margin-bottom: -60px; padding: 5px; font: bond 12px Arial; border: 1px solid #0960bb; }


Ответ

В .post-text добавьте word-wrap: break-word;
.post-text { display: inline-block; width: 600px; height: auto; margin-top: -30px; margin-left: 120px; margin-bottom: -60px; padding: 5px; font: bond 12px Arial; border: 1px solid #0960bb; word-wrap: break-word; }

Аттрибут Obsolete

Коллеги, кто может подсказать, почему билд не пададет при использовании метода помеченного так: (Infastructure.csproj)
[Obsolete("blah blah blah", true)] public override int SaveChanges()
Метод располагается в Class Lib, вызываю в консольке. Для методов помеченых атрибутом в консольке билд ломается... Вызов такой (TestApp.csproj):
var ctx = new TestContext(); ctx.TestType.Add(new TestType { Value = "qwe" }); ctx.SaveChanges();
Переезд проекта в другую студию так же не помог.
Ссылка на Solution


Ответ

кхм... MSDN на эту тему молчит, но опытным путем удалось выяснить следующее:
Допустим имеем такую примитивную иерархию классов
class A { public virtual void foo() { } }
class B : A { [Obsolete("don't use it", true)] public override void foo() { } }
Тогда возникает Warning
Предупреждение CS0809 Член с атрибутом "obsolete" "B.foo()" переопределяет член без атрибута "obsolete" "A.foo()"
И данный атрибут просто игнорируется в дальнейшем, несмотря на явное указание генерировать ошибку.
Не претендую на истину, но имею большие основания полагать, что в данном случае студия ведет себя корректно, потому что такой прием по сути равноценен попытке понизить уровень доступа в классе наследнике, а это нарушение принципа строго расширяющего наследования в ООП. Так что либо нужно помечать устаревшим метод базового класса, что в вашем случае невозможно, либо применить другой способ изоляции, например инкапсулировать объект класса-наследника в класс-оболочку, в которой предоставить только нужные методы, но сам класс-оболочка не должен наследоваться ни от базового, ни от производного, проблемных классов.

Bash: изменить цвет prompt в зависимости от exit status последней команды

Я хочу визуализировать в консоли статус завершения последней команды.
Если последняя команда выполнилась со статусом 0, т. е. без ошибок, должен быть стандартный зеленый цвет. А если статус был не 0, то цвет должен быть красный.

Вот на примере строка после false должна быть красная. Желательно еще вывести статус завершения, если он не 0.
Как это сделать?


Ответ

Если говорим о bash, в оболочке есть встроенные переменные отвечающие за настройки строки ввода.
PROMPT_COMMAND Если установлена, значение воспринимается как команда выполняемая до выдачи основного приглашения командной строки ($PS1) PS1 Основная переменная приглашения командной строки. Значение по умолчанию \s-\v\$
Можно написать свою функцию и добавить в ~/.bashrc файл. Разбив задачку на шаги получим:
[статичный] [динамический] [станичный]
_prmtcmd() { local EXIT="$?"
local NA='\[\e[0m\]' local R='\[\e[0;31m\]' local G='\[\e[0;32m\]' local B='\[\e[1;34m\]'
# Статичный: имя пользователя и хоста PS1="${G}\u@\h${NA}
"
# Динамический: выход из последней выполненной команды if [ $EXIT != 0 ]; then PS1+="[${R}false${NA}] " else PS1+="[${G}true${NA}] " fi
# Статичный: приглашение к вводу PS1+="${B}~$ ${NA}" }
PROMPT_COMMAND=_prmtcmd
Графический пример выполнения:

Смотрите модификаторы для каждого из цветов в документации (ссылки ниже).
Ссылки
https://www.gnu.org/software/bash/manual/html_node/Variable-Index.html#Variable-Index http://misc.flogisoft.com/bash/tip_colors_and_formatting

Как чистить оперативную память сервера после выполнения php скрипта?

Проблема собственно вот в чем: мои скрипты php после отработки засоряют оперативную память сервера,но не все конечно. Системный администратор после изучения этой темы говорит что память засоряется после моих скриптов и они являются активными процессами( как я понял спящими по букве S) Я прекрасно понимаю, что вы не волшебники и не можете проинтуичить без выкладки кода, но может кто то сталкивался с подобными проблемами. Хотелось бы понять алгоритм нахождения ответа. Может он заключается в том чтобы прозвонить код на количество резервируемой под него памяти, найти самый прожорливый участок и оптимизировать его.
Даже если это будет выполнено, памяти будет жрать меньше, но причина похоже не в этом. Может необходимо найти конфигурационный файл сервера и обратить внимание на конкретные строки параметров? В зависимости от ситуации я добавлю сюда необходимую информацию
Или может добавлять какуюто строку кода в php скрипт чтобы сервер убивал соединение?
Да кстати в циклах использую
break;
Также использовал
ini_set('memory_limit', '1024M');
Но после закоменчивания этой строки изменений не было. Или может жестко поступить - найти все переменные и сделать
unset($some_param);
Откуда начинать копать? Статьи по оптимизации кода читал, применяю их на практике, но как известно в этом направлении можно далеко уйти и писать на Ассемблере
Добавлено минутой позже:
Также использую в коде
exit();
в том случае если код не имеет смысла дальше выполнять - или вместо него необходимо использовать
die();
Добавлено 5 минутами позже:
Это веб сервер. Пользователь загружает фаил эксель для парсинга и получается вышеописанное NGINX 1.6.2 PHP5-FPM без APACHE 5.6 PHP версия
Добавлено через 9 часов:
на форуме говорится о команде
strace -p
которая показывает что хочет выполнить процесс в нашем случае PID 10354. Это конечно не ответ на вопрос, но может поспособствовать его решению.
На ресурсе сказано что данный режим может быть вызван недостатком мощностей процессора(но по сути какой бы мощный процессор не был, нагрузить его на 100% можно), также сказано что такое поведение наблюдается когда в коде php есть команда
sleep($count_of_seconds);
Да - данная команда ввергает процесс в состояние Sleep, но здесь нет зависимости от количества потребляемой памяти.
Как мне объяснил системный администратор - сколько пользователей на сайте, столько будет спящих www-data, следовательно состояние sleep вполне нормально, ненормально количество удерживаемой для этого процесса памяти.
Добавлено 22,02,2017
Вот что происходит когда остаются 3 спящих процесса


Ответ

Тут оказывается не только дело в скриптах но и в настройках php-fpm а именно надо поиграться с настройками
/etc/php5/fpm/pool.d/www.conf а именно
pm.max_requests request_terminate_timeout
request_terminate_timeout устанавливает максимальное время выполнения дочернего процесса, прежде чем он будет уничтожен. Это позволяет избегать долгих запросов, если по какой-либо причине было изменено значение max_execution_time в настройках интерпретатора. Значение стоит установить исходя из логики обрабатываемых приложений, скажем 60s (1 минута).

MySQL. Как рассчитать объём таблицы?

Если я правильно понял, то на объём(размер) таблицы, который она занимает на диске компьютера, влияет тип таблицы, тип полей и т.д.
Вопрос Как рассчитать объём таблицы? Есть какие-нибудь калькуляторы для этих целей?


Ответ

Точно объем таблицы можно посчитать только в случае, если размер записи фиксирован. Для этого надо, чтобы:
Все поля записи имели фиксированный размер. char(50) всегда имеет размер 50 байт, в то время как varchar(50) имеет размер от 1 до 51 байт. Запись не содержала NULL-полей.
Размеры типов можно найти в документации по MySQL
Типы nchar, varchar, varbinary, tinyblob, tinytext, blob, text, mediumblob, mediumtext, longblob, longtext не имеют фиксированного размера. Если таблица содержит поля этих типов, точный её размер посчитать невозможно. Иногда размер можно посчитать приблизительно с высокой точностью, если понятно, какие данные будут хранится.
Все остальные типы имеют фиксированный размер. INT 4 байта, это просто. CHAR(7) 7 байт. Размер NUMERIC(12, 3) посчитать сложнее, поскольку MySQL упаковывает каждые 9 десятичных цифр в 4 байта.
Каждая запись содержит служебный заголовок, который имеет размер от 5 байт. Если NULL-поле имеет значение NULL, оно не хранится в записи, просто выставляется бит в заголовке.
После того, как размер таблицы посчитан в байтах, можно прикинуть, сколько она занимает места на диске. Для скорости MySQL хранит данные большими страницами, блоками размером 4Кбайт или 8Кбайт. Если запись с заголовком занимает 100 байт, а размер страницы 4096 байт, то на странице поместится 40 записей и ещё останется 96 пустых байт на каждой странице. 400 записей займут на диске 10 страниц или 40Кбайт.
Помимо данных, таблица содержит индексы, каждый из которых тоже занимает место. Если в индексе встречаются поля типов переменного размера, сам индекс также будет переменного размера.
Не смотря на то, что точный размер таблицы определить трудно, на практике достаточно бывает оценить порядок.
Дополнительно
Когда MySQL хранит текст, то она и цифры хранит как обычные символы. Скажем, в кодировке UTF-8 все цифры занимают один байт, а русские буквы занимают 2 байта. Чтобы считать корректно, необходимо знать, какая кодировка используется для данного поля.
Типы INT и т.д. хранят числа в двоичной системе. INT всегда требует 4 байта, где хранится целое число в интервале -231..231−1.
Тип DECIMAL хранит числа в десятичной системе, используя хитрое кодирование, чтобы упаковывать каждые 3 десятичные цифры в 10 бит (каждые 9 цифр в 4 байта).
Типы BIT(n) упаковываются вместе в достаточное количество байт. BIT(3) всегда будет занимать 1 байт, потому что меньше памяти выделить невозможно. Но два поля по BIT(3) также будут занимать 1 байт, поскольку MySQL будет хранить их вместе.

History Api запоминает только результат ajax-запроса

var counter = 1;
$('button').click(function(){ $.ajax({ url: 'index.json', method: 'GET', success: function(res){
$('body').append(JSON.stringify(res))
if (!!(window.history && history.pushState)) { history.replaceState(null, null, '?page=' + counter); }
counter++;
} }); });
На сайте вылез косяк, связанный с тем что по нажатию на кнопку происходит подгрузка данных через ajax и обновляется история браузера через history api через метод replaceState, который перезаписывает истории.
Проблема в том что если я нажимаю кнопку назад - то показывается не предыдущая страница , а результат ajax запроса. То есть в историю как будто записывается результат ajax, хотя url при этом корректный
Это в chrome, в FF отлично работает и я вижу всю страницу. Попробовал воспроизвести на отдельно html-страничке - все гуд во всех браузерах - кто нибудь сталкивался с таким?
Если меняешь GET запрос на POST, то все гуд
Как будто история запоминает этот запрос GET запрос (воспроизводиться только в chrome)


Ответ

Дело было в ajax. Он кеширует get запросы. Помог cache: false

Как посчитать значение выражения, представленного строкой, и вывести его?

Дело в том, что мне нужно решить задачи, у которых тип данных String
Например:
String bla = "5 + 10 - 5"; SystemOutPrintln(Integer.parseInt(bla));
Мне нужно получить ответ, например 10. Но, вариант выше выведет ошибку, так как символы + и - не являются целыми числами.
Как можно решить такого рода задачи, или есть другие варианты, буду признателен за дельные советы?


Ответ

Возможно несколько вариантов решения:
Написать свой интерпретатор Подключить готовый интерпретатор, например exp4j Подключить ScriptEngine, например JavaScript (вариант использования https://stackoverflow.com/a/3423360/690987) Преобразовать выражение в java-код (на лету), скомпилировать и выполнить его Сгенерировать байт-код, используя библиотеку генерации байт-кода, и выполнить его, например Byte Buddy

GitHub — как быть с недогружаемыми файлами?

Пользуюсь GitHub на Android Studio / IntelliJ IDEA. Заметил, что имена файлов в проводнике IDE имеют разные цвета — белый, красный, синий, зелёный.
Догадываюсь, что синий файл — изменённый, зелёный — созданный после последнего обновления, белый — не тронутый с последнего обновления, красный — созданный перед последним обновлением и по каким-то причинам не обновлённый. Поправьте, если я не прав.
Что точно означает расцветка файлов, не нашёл. Но ок.
Интересуют красные файлы. Сегодня подгрузил клон проекта на другой компьютер и убедился, что файлы, помеченные красным на компьютере, с которого происходят коммиты / пуши, не добавились на новый компьютер. А это уже не ок.
Подскажите, как быть в такой ситуации? Как закоммитить красные файлы? И что с ними не так?


Ответ

Статус файлов в VCS в IDEA описан в справке. Красный — конфликт после мерджа ("During the last update, file was merged with conflicts"). Вероятно, стоит разрешить конфликт.

Проблемы с json_decode

Имеется следующий json объект:
{ "ИД": "0f126618-6606-11e7-b4c8-005056c00008", "НоменклатураИД": "af0412c1-2feb-11e7-8c78-005056c00008", "Наименование": "1712 Миа", "ДатаИзменения": "0001-01-01T00:00:00", "Описание": "", "Meta_title": "", "Meta_description": "", "Meta_keyword": "", "Теги": "", "ТканьРегион": "1712 Миа", "СайтАртикул": "1712", "СайтСтиль": "Жаккардовый", "СайтАкция": "Нет", "СайтПоказыватьВМагазине": "Да" }
Функция json_decode выводит NULL при попытке вардампа Но при этом вот такой json объект она нормально декодирует:
{ "ИД": "8574a44b-2b1a-11e7-8c78-005056c00008", "Наименование": "КПБ \"Дуэт\" [Бамбук-жаккард)", "НаименованиеПолное": "КПБ \"Дуэт\" [Бамбук-жаккард)", "Артикул": "360/0034295", "ЕдиницаИзмеренияКод": "796", "ЕдиницаИзмеренияНаименование": "шт", "ВестиУчетПоХарактеристикам": true }
В чем может быть проблема?


Ответ

Вам присылают метку BOM - byte order mark. Конкретно EFBBBF характерна для UTF8.
Но json_decode не ожидает такой метки и потому считает документ невалидным. Метку можно безопасно удалять:
if (substr_compare($json, "\xEF\xBB\xBF", 0) > 0) { $json = substr($json, 3); }
Вообще от этот метки часто проблемы встречаются. Нередко стандарты кодирования (PSR стандарты PHP в том числе) требуют не использовать эту метку.

Как в PopupMenu уменьшить высоту пунктов и шрифт текста в них?

Пробовал через задание стиля так:
PopupMenu m3; if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP_MR1) { m3 = new PopupMenu(this, view, Gravity.VERTICAL_GRAVITY_MASK, 0, R.style.PopupMenuStyle); } else { m3 = new PopupMenu(this, view); }
Стиль:

Не могу найти какое свойство отвечает за высоту каждого пункта и отступ с боков.


Ответ

В теме для всего приложения попробуйте прописать следующий параметр. Но он повлияет не только на Popup
size_in_dp
Шрифт задается так же в теме приложения через параметр
sans-serif

Проблема с форматом даты при использовании tag-хелперов в ASP.NET Core 2 VS2017

Добрый день.
Есть код для ввода параметров фильтрации (делал согласно статье). Всё прекрасно работало пока не столкнулся с необходимостью делать фильтрацию по временному интервалу.

Обобщенный JSON

Сервер присылает ответы в формате
{response: {.......}}
например
{response: {code: 0}} {response: {type: "test"}}
Пытаюсь сделать парсинг при помощи библиотеки gson в таком виде
class Response { private T response; public T getResponse() { return response; } }
class CodeInfo { private int code; }
class TypeInfo { private String type; }
T parse(String str) { Response resp = gson.fromJson(str, Response.class); return resp.getResponse(); }
CodeInfo = parse("{response: {code: 0}}");
Внутренние классы довольно обширные
На конструкции Response.class компилятор меня посылает. Решается ли задача без кучи наследников Response? В принципе, могу даже сменить парсер


Ответ

Спасибо @diraria за направление на путь истинный. Итого получилось
public T parse(String str, Class AClass) { Response resp = JSON.fromJson( str, TypeToken.getParameterized(Response.class, AClass).getType() ); return resp.getResponse(); }
CodeInfo info = parse("{response: {code: 0}}", CodeInfo.class)
Если писать так
public T parse(String str) { Response resp = JSON.fromJson( str, new TypeToken>(){}.getType() ); return resp.getResponse(); }
то строка парсится, но resp.getResponse() возвращает объект класса com.google.gson.internal.LinkedTreeMap

NGINX: раздача дописываемого файла

В моем приложении есть файл, который по сути напоминает лог т.е. постоянно дописывается. Хочу использовать хедер Range для как альтернативу long polling и вебсокетам для чтения дополняющегося файла, так как на реализацию этих технологий нужно время, тем более с учетом использования С++. Насколько эффективно NGINX справится с такой задачей, возможно будут проблемы при работе с дополняющимися файлами? И какие параметры будут оптимальными для реализации этого подхода? Может быть нужно использовать directio в приложении и NGINX для оптимизации? Или стоит вообще отказаться от этой идеи?... Сервер на базе linux, файловая система ext4. Размер файла не превышает 100МБ(одновременно запись идет в один файл, потом создается новый).
PS: В файл придется писать в любом случае и разбивка на мелкие файлы не связана с реализацией этого подхода.


Ответ

Не имеет смысла изобретать велосипед так как для nginx уже есть модуль для работы с вебсокетами, который берет всю сложную работу на себя. Вам остается только передавать сообщения для доставки в nginx. Устанавливается на раз:
sudo apt install libnginx-mod-nchan
Настройка тоже ничего сложного не представляет. Может работать через Redis в конфигурации с множеством серверов.
Если вы по какой-то причине не можете использовать этот модуль, то опять же стоит изобретать очередную БД на коленке. На серьёзных нагрузках файлы на диске никогда не заменят настоящую БД, будь это MySQL или что. Хотя бы потому что типичная БД может гарантировать что горячие данные будут в оперативке, тогда как дисковый кеш вообще ничего не гарантирует. Это очень легко проверить простым бенчмарком.

Из альтернатив можно рассмотреть, например, websocketd. Эта программа, очень близкая по сути inetd, существует отдельно от nginx. Вам нужно лишь перенаправить WS соединения на неё, что очень просто.
location /mywsapp { proxy_pass http://mywsbackend; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; }
В простейшем виде, без nginx, программа, которая будет читать из файла новый строки и передавать их по WebSocket будет выглядеть так: (tail.sh)
#!/bin/bash tail -q -n0 -F /tmp/websocketdata.txt
Страница, которая читает данные, выглядит так: (index.html)


Если оба файла в одном каталоге, то запускаем:
python -m SimpleHTTPServer 8000 & ./websocketd --port=8080 ./tail.sh &
Открываем в браузере http://127.0.0.1:8000/ и в соседней консоли пишем в файл:
tee -a /tmp/websocketdata.txt
Печатаем текст и смотрим как он появляется в окне браузера в тот же миг.
Это читающей программе будут доступны всевозможные переменные как если бы она работала CGI в окружении, например QUERY_STRING и другие. Без необходимости их проверять можно сделать даже так, убрав прослойку из bash:
./websocketd --port=8080 tail -q -n0 -F /tmp/websocketdata.txt
Чтобы не усложнять программу для чтения файлов можно проверять доступ к подключению через WebSocket на стороне nginx используя директиву auth_request