From 22ee9426e571f408bd1fb6f3246cee8e1433d928 Mon Sep 17 00:00:00 2001 From: queinu <76971397+ZzEdovec@users.noreply.github.com> Date: Wed, 25 Jan 2023 21:57:59 +0300 Subject: [PATCH 1/9] Fix getActiveAdapter() in Lan.php --- src/bundle/windows/Lan.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bundle/windows/Lan.php b/src/bundle/windows/Lan.php index 986da62..93c624c 100644 --- a/src/bundle/windows/Lan.php +++ b/src/bundle/windows/Lan.php @@ -74,7 +74,7 @@ public static function getAdapters() : array { public static function getActiveAdapter() : lanAdapter { $adapters = self::getAdapters(); foreach ($adapters as $adapter) { - if($adapter->isActive()){ + if($adapter->isConnected()){ return $adapter; } } @@ -90,4 +90,4 @@ public static function isSupported() : bool { return sizeof(self::getAdapters()) > 0; } -} \ No newline at end of file +} From 4c8b49f3e7c08d8d55be916bc95784c5d47fe667 Mon Sep 17 00:00:00 2001 From: queinu <76971397+ZzEdovec@users.noreply.github.com> Date: Wed, 25 Jan 2023 22:41:43 +0300 Subject: [PATCH 2/9] Fix runAsAdmin in Windows.php Now it's working with .exe files --- src/bundle/windows/Windows.php | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/bundle/windows/Windows.php b/src/bundle/windows/Windows.php index 6438aaf..804b237 100644 --- a/src/bundle/windows/Windows.php +++ b/src/bundle/windows/Windows.php @@ -110,12 +110,9 @@ public static function runAsAdmin(string $file, array $args = [], string $workDi $workDir = is_null($workDir) ? CURRENT_DIRECTORY : $workDir; var_dump(['workDir' => $workDir]); - return WSH::PowerShell('Start-Process ":file" -WorkingDirectory ":dir" -Verb runAs -ArgumentList ":args"',[ - 'file' => $file, - 'dir' => $workDir, - 'args' => $argString - ]); - + return WSH::PowerShell(empty($argString) ? 'Start-Process "'.$file.'" -WorkingDirectory "'.$workDir.'" -Verb runAs' : + 'Start-Process "'.$file.'" -WorkingDirectory "'.$workDir.'" -Verb runAs -ArgumentList "'.$argString.'"'); + } /** From 33d913ca5de3e49e1fd9a32337d5820b487055d8 Mon Sep 17 00:00:00 2001 From: queinu <76971397+ZzEdovec@users.noreply.github.com> Date: Wed, 25 Jan 2023 23:17:03 +0300 Subject: [PATCH 3/9] Edit version to 2.3 --- package.php.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.php.yml b/package.php.yml index 75ec29a..b025eae 100644 --- a/package.php.yml +++ b/package.php.yml @@ -1,5 +1,5 @@ name: windows -version: 2.2 +version: 2.3 description: Пакет для взаимодействия с API Windows plugins: @@ -36,7 +36,7 @@ config: - /gradlew.** develnext-bundle: - version: 2.2 + version: 2.3 name: windows description: Плагин для взаимодействия с API Windows author: Ts.Saltan From 6b948db82c881d3bd85247b603192949297131d7 Mon Sep 17 00:00:00 2001 From: queinu <76971397+ZzEdovec@users.noreply.github.com> Date: Wed, 25 Jan 2023 23:56:45 +0300 Subject: [PATCH 4/9] Update README.md --- README.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6915049..06277a4 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,15 @@ ## Changelog ``` +--- 2.3 --- +[Fix] Windows::runAsAdmin() - Теперь нормально работает, если аргументов нет. Если их нет, для Start-Process не передается -ArgumentList +[Fix] Windows::requireAdmin() - Нормально перезапускает программу, если ей не передано аргументов и она скомпилирована в .exe +[Fix] Lan::getActiveAdapter() - Проверяет активен ли адаптер через isConnected(), а не несуществующий isActive(). Больше не вызывает исключения + +--- 2.2 --- +[Add] Windows::print() +[Add] Windows::getFileMeta() + --- 2.1.1 --- [Add] Windows::getSystemDrive() Migrate to jppm @@ -95,4 +104,4 @@ jppm add windows@git+https://github.com/TsSaltan/jphp-windows-ext ## Build bundle ``` jppm bundle:build -``` \ No newline at end of file +``` From 77a1e3e63d40d5024ab1c9fb592c5f1499c8aae2 Mon Sep 17 00:00:00 2001 From: queinu Date: Sun, 29 Jan 2023 03:26:14 +0300 Subject: [PATCH 5/9] update to 2.4 --- README.md | 5 +++++ package.php.yml | 6 +++--- .../develnext/bundle/windows/windows32.png | Bin 1873 -> 586 bytes src/bundle/windows/Startup.php | 8 ++++--- src/bundle/windows/Windows.php | 20 ++++++++++++------ 5 files changed, 26 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 06277a4..a4098d0 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,11 @@ ## Changelog ``` +--- 2.4 --- +[Change] Windows::createShortcut() - Теперь поддерживает кастомный путь до иконки и аргументы командной строки +[Change] Автором бандла поставил себя, т.к. TsSaltan явно забросил пакет +[Change] Иконка Windows 7 заменена на Windows 11 + --- 2.3 --- [Fix] Windows::runAsAdmin() - Теперь нормально работает, если аргументов нет. Если их нет, для Start-Process не передается -ArgumentList [Fix] Windows::requireAdmin() - Нормально перезапускает программу, если ей не передано аргументов и она скомпилирована в .exe diff --git a/package.php.yml b/package.php.yml index b025eae..d4be89f 100644 --- a/package.php.yml +++ b/package.php.yml @@ -1,5 +1,5 @@ name: windows -version: 2.3 +version: 2.4 description: Пакет для взаимодействия с API Windows plugins: @@ -36,10 +36,10 @@ config: - /gradlew.** develnext-bundle: - version: 2.3 + version: 2.4 name: windows description: Плагин для взаимодействия с API Windows - author: Ts.Saltan + author: queinu icon: "develnext/bundle/windows/windows32.png" group: "system" class: "develnext\\bundle\\windows\\WindowsBundle" diff --git a/src-bundle/.data/img/develnext/bundle/windows/windows32.png b/src-bundle/.data/img/develnext/bundle/windows/windows32.png index ea803e798e3169a8117cacdb0d1df26576d5aec5..29d81f6d4df5e0614225b07ffccb7adb78426bf7 100644 GIT binary patch delta 561 zcmV-10?z%>4$1_OB!3BTNLh0L01FcU01FcV0GgZ_0005}Nkl_tI>jfjD0LJEr@LAWGZh$g%H zW-Q*tyCa<5?jDNazGml+nUC+inVp#x?moFk@8WGj5I{g2sDAU-d)q+4- zc0J3l9~*-tbTaJ*PS(9JnzwNchL$05-0Tg8y>+3T40(C zXSNJ#!*sIk6bbu)LoEHUPDvGD&6}b%%BfQBaVgwM8=oTzb?S6 zdIOoM0HDbrXf9x$0$`qk|2+ksBx&P4)g~FxYJp2q&}i4{$ky?!t~@2pP7J@u1<=8B zf-M4qvMToe89V(qY^<--?sTyV3qg{=r>&Kr4ARFv3?>Ty00000NkvXXu0mjf9ijrK delta 1858 zcmV-I2fg^p1knzVB!32COGiWi{{a60|De66lK=n!32;bRa{vGUNB{r;NB~C3Yd!z~ z010+*L1zE}03ZMW03ZN|`kb8r00w$VL_t(o!|j(_Y+Oef$Nw|uoZUTp_4;nF*LNpQ zXi8~PXhTASLhG1^CwXS1t@VGf~!VJ z2ofzdxzvf%*iGYI+wtAIdv?!lW2LKd^4Ik2l&5kQIB_n z-A=!MY4)97)V9aapE0pB#Ie3QjFc_l76rzqZM>eZ;MJ!uVBx<4u>T`?>hoEA+td(c zArs6@09*rn7k^YKfRsehM{!_^{UBeVAN_C?^0ondrJaSpyAjhhb@gejvHOGMD?~~{ zE#x4l#sDSp%s4jx`o2ca*qbb~<-Gtp2Pta390Kga9G?t05UBZX~ zm2zNm5*~cVMc&m1=A+bkcL!geKo>sTo4TX3d*WF~EkcqD1`eH>2=g>F7RI7?NiF8Z z(ZD5$Xn(A=BgXFVVL+CH0aUq&iZbkRQm?0a^)E{SrWpy4(h)}|;=J>QG0WZ9brQKwBcz=ess1QvtFP2q3Kz-Y)RdFP-xWUw^{r=OPN>NdUo1W;u&kxXK5IUJw)r zP_0wHM;xLOYy=Rb>tWi99e%Js9Lx0T+qsOv@qdEHNb=zO0!veLzaIbgc+&y-4ngaG3lAO+|qjX7e_Xw$cGgAybRIBQhQl|>Y1wVBeKd@B$j8Pd0w zV3hwgDa!g0d(KFhb<(V?oZWpd@a+s@e^D#!mDjO zr?@ned;7)tx%1BspE)wOr8(NXITKpvxqoE{W`lv>G`%7TP7!ZDZx5}EYbzgWn>Mwt zyX$kOs;V2i>uOBBsm?@Gy@`g}2&xhW3?1-%W}3;CdyPb952wNl@4Puyj7IcJiR$Rb z^sr0`At9xLl=QczpcJ5NvvlF4*#G;3%STq`ysiTAWMiL_KJRKxfid>MP8R|Iz<(J~ zmC%uj$8qVsC6tTP>H9ryCooo($+Az@@iLr!TqJnT}v@$P` zJ2QpD$9`omudd8>0HH{-FJu6Y>jkc)QiO_a!?tDy9@G> zZ%XfJhkpK&FQ3?JGFc(UDl)M#Er0LnzMH<^rs2m2Uq$|=0PP$4!ttu?4ntR{RDw5? zRq;U5S9Wo1u%~BvTReZd6&lnwN$A0<>$v^-zTD?mOAFk`dbp!LGY*k=eGe>KSKCl$XUkw}r z2q5Kwln?2ZFg?o$Zszw703%ZMAdy&e_EHiQ3D~8{!)Dc7+xjfq05YMxH7+cF31v$w zPfXJrfdFmmb~c*vhP#CCp?@NL5D^Ff2w)6gjDax*Ar(kiGhsIW)`!}~VDr=~;mQu|)N!q?Ec8m3hmW87ApnR?=VQddLJ=s~K~jH zpVgj!`bGOxkmtYsmw)r<17X%st!?Se>W|c? str::replace($shortcut, "'", "\\'"), - 'target' => str::replace($target, "'", "\\'"), - 'description' => $description - ]); + public static function createShortcut($shortcut, $target, $iconpath = null, $args = null, $description = null){ + $cmd = '$Wshell = New-Object -ComObject WScript.Shell; $shortcut = $Wshell.CreateShortcut("'.$shortcut.'"); $shortcut.TargetPath = "'.$target.'";'; + if ($iconpath != null) + $cmd = $cmd.'$shortcut.IconLocation = "'.$iconpath.'";'; + if ($args != null) + $cmd = $cmd.'$shortcut.Arguments = "'.$args.'";'; + if ($description != null) + $cmd = $cmd.'$shortcut.Description = "'.$description.'";'; + $cmd = $cmd.'$shortcut.save()'; + return WSH::PowerShell($cmd); } /** From 4995a9e4487e90cefcce0bdc83b5f82f5f010130 Mon Sep 17 00:00:00 2001 From: queinu Date: Sun, 12 Mar 2023 20:35:41 +0300 Subject: [PATCH 6/9] Update Startup.php --- src/bundle/windows/Startup.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bundle/windows/Startup.php b/src/bundle/windows/Startup.php index bb9f607..6cb935c 100644 --- a/src/bundle/windows/Startup.php +++ b/src/bundle/windows/Startup.php @@ -152,7 +152,7 @@ public static function add($file, $iconpath = null, $args = null, $description = * @param string $file Путь к исполняемому файлу * @return startupItem */ - public static function find($file) : startupItem { + public static function find($file) { $list = self::getList(); foreach($list as $item){ if($item->file == $file){ From 700163cf2518ab895cd0f87bc97b7f238c42ec41 Mon Sep 17 00:00:00 2001 From: queinu Date: Tue, 19 Sep 2023 21:35:15 +0300 Subject: [PATCH 7/9] 2.5 --- README.md | 5 + package.php.yml | 4 +- .../bundle/windows/COM.php | 74 + .../bundle/windows/Lan.php | 97 ++ .../bundle/windows/Metadata.php | 74 + .../bundle/windows/Prepare.php | 125 ++ .../bundle/windows/Registry.php | 207 +++ .../bundle/windows/Startup.php | 194 +++ .../bundle/windows/Task.php | 108 ++ .../bundle/windows/Windows.php | 1495 +++++++++++++++++ .../bundle/windows/WindowsException.php | 8 + .../bundle/windows/WindowsScriptHost.php | 117 ++ .../bundle/windows/Wlan.php | 59 + .../bundle/windows/api/CSharp.php | 42 + .../bundle/windows/api/Dll.php | 50 + .../bundle/windows/result/abstractItem.php | 16 + .../bundle/windows/result/abstractResult.php | 51 + .../bundle/windows/result/comItem.php | 75 + .../bundle/windows/result/lanAdapter.php | 110 ++ .../bundle/windows/result/registryItem.php | 72 + .../bundle/windows/result/registryResult.php | 45 + .../bundle/windows/result/startupItem.php | 169 ++ .../bundle/windows/result/taskItem.php | 110 ++ .../bundle/windows/result/taskResult.php | 40 + .../bundle/windows/result/wlanInterface.php | 257 +++ .../bundle/windows/result/wshResult.php | 40 + src/bundle/windows/Lan.php | 12 +- 27 files changed, 3650 insertions(+), 6 deletions(-) create mode 100644 src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/COM.php create mode 100644 src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/Lan.php create mode 100644 src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/Metadata.php create mode 100644 src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/Prepare.php create mode 100644 src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/Registry.php create mode 100644 src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/Startup.php create mode 100644 src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/Task.php create mode 100644 src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/Windows.php create mode 100644 src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/WindowsException.php create mode 100644 src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/WindowsScriptHost.php create mode 100644 src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/Wlan.php create mode 100644 src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/api/CSharp.php create mode 100644 src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/api/Dll.php create mode 100644 src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/result/abstractItem.php create mode 100644 src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/result/abstractResult.php create mode 100644 src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/result/comItem.php create mode 100644 src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/result/lanAdapter.php create mode 100644 src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/result/registryItem.php create mode 100644 src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/result/registryResult.php create mode 100644 src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/result/startupItem.php create mode 100644 src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/result/taskItem.php create mode 100644 src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/result/taskResult.php create mode 100644 src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/result/wlanInterface.php create mode 100644 src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/result/wshResult.php diff --git a/README.md b/README.md index a4098d0..57b4ba8 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,11 @@ ## Changelog ``` +--- 2.4 --- +[Fix] Windows::getActiveAdapter() - Больше не выдаёт как активные адаптеры виртуальных локальных сетей +[Fix] Bundle для DevelNext снова корректно собирается + + --- 2.4 --- [Change] Windows::createShortcut() - Теперь поддерживает кастомный путь до иконки и аргументы командной строки [Change] Автором бандла поставил себя, т.к. TsSaltan явно забросил пакет diff --git a/package.php.yml b/package.php.yml index d4be89f..8285403 100644 --- a/package.php.yml +++ b/package.php.yml @@ -1,5 +1,5 @@ name: windows -version: 2.4 +version: 2.5 description: Пакет для взаимодействия с API Windows plugins: @@ -36,7 +36,7 @@ config: - /gradlew.** develnext-bundle: - version: 2.4 + version: 2.5 name: windows description: Плагин для взаимодействия с API Windows author: queinu diff --git a/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/COM.php b/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/COM.php new file mode 100644 index 0000000..7cae6c1 --- /dev/null +++ b/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/COM.php @@ -0,0 +1,74 @@ +readFully(); + foreach($reg as $r){ + foreach($r as $v){ + $com[$v->value] = new comItem($v->value, array_merge(self::getParams($v->value), ['path' => $v->key])); + } + } + + return $com; + } + + /** + * --RU-- + * Получить список параметров порта + * @param string $port Имя порта (от COM1 до COM255) + * @return array + */ + public static function getParams($port){ + $r = WSH::WMIC('path Win32_PnPEntity WHERE "Caption like \'%' . $port . '%\'" get'); + return isset($r[0]) ? $r[0] : []; + } + + /** + * --RU-- + * Ищет устройство по имени + * @param string $search Строка поиска + * @param array $searchFields=['Caption','Description','Name','Service'] Поля, по ктоторым осуществляется поиск + * @return comItem[] + */ + public static function searchDevice($search, $searchFields = ['Caption', 'Description', 'Name', 'Service']){ + $searchCOM = ['Caption', 'Name']; // Поля, в которых фигурирует номер COM порта + + // Формирование SQL запроса + $searchQuery = []; + foreach($searchFields as $field){ + $searchQuery[] = $field . ' like \'%' . $search . '%\''; + } + $search = implode(' OR ', $searchQuery); + $r = WSH::WMIC('path Win32_PnPEntity WHERE "'.$search.'" get'); + + $ports = []; + foreach ($r as $v) { + $string = ''; // Делаем конкатенацию полей, где может фигурировать номер COM порта + foreach ($searchCOM as $field){ + $string .= $v[$field]; + } + $regex = Regex::of('(COM[0-9]+)', Regex::CASE_INSENSITIVE + Regex::UNICODE_CASE)->with($string); + if ($regex->find()){ // Если удалось определить номер com порта + $ports[$regex->group(1)] = new comItem($regex->group(1), $v); + } + } + + return $ports; + } +} \ No newline at end of file diff --git a/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/Lan.php b/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/Lan.php new file mode 100644 index 0000000..159d4d7 --- /dev/null +++ b/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/Lan.php @@ -0,0 +1,97 @@ +useDelimiter(Regex::of('\n', Regex::MULTILINE)); + while($scanner->hasNextLine()){ + $scanner->next(); + $line = $scanner->current(); + + $regAdapter = Regex::of('Ethernet adapter ([^\n\r]+):', Regex::MULTILINE)->with($line); + if($regAdapter->find()){ + $adapterName = $regAdapter->group(1); + } elseif($adapterName != false){ + $regParam = Regex::of('\s+([^\.]+)[\.\s]+:([^\n]+)', Regex::MULTILINE)->with($line); + if($regParam->find()){ + $key = trim($regParam->group(1)); + if(isset($adapters[$adapterName][$key])) $adapterName = false; + else $adapters[$adapterName][$key] = trim($regParam->group(2)); + } + } + } + + foreach($nic as $k => $v){ + if(!isset($v['NetConnectionID'])) continue; + if(!isset($adapters[$v['NetConnectionID']])){ + $adapters[$v['NetConnectionID']] = [ + 'Description' => $v['Description'], + 'Physical Address' => $v['MACAddress'], + ]; + } + + $adapters[$v['NetConnectionID']]['Speed'] = $v['Speed'] ?? 0; + $adapters[$v['NetConnectionID']]['Manufacturer'] = $v['Manufacturer'] ?? ''; + $adapters[$v['NetConnectionID']]['Manufacturer'] = $v['Manufacturer'] ?? ''; + $adapters[$v['NetConnectionID']]['NetEnabled'] = isset($v['NetEnabled']) && $v['NetEnabled'] == 'TRUE'; + } + + foreach ($adapters as $name => $params) { + $adaptersObj[] = new lanAdapter($name, $params); + } + return $adaptersObj; + } + + /** + * Получить используемый по умолчанию адаптер + * @return lanAdapter + */ + public static function getActiveAdapter() : lanAdapter{ + $adapters = self::getAdapters(); + foreach ($adapters as $adapter) { + $device = $adapter->getDevice(); + if(str::contains($device,'Radmin') or str::contains($device,'Hamachi') or str::contains($device,'ZeroTier')) + continue; + + if($adapter->isConnected()) + return $adapter; + + } + + return null; + } + + /** + * Есть ли оборудование для работы с проводными сетями + * @return boolean + */ + public static function isSupported() : bool { + return sizeof(self::getAdapters()) > 0; + } + +} diff --git a/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/Metadata.php b/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/Metadata.php new file mode 100644 index 0000000..6e54345 --- /dev/null +++ b/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/Metadata.php @@ -0,0 +1,74 @@ +file = ($file instanceof File) ? $file : File::of($file) ; + } + + public function readData(int $items = 300){ + $title_query = ' + $objShell = New-Object -ComObject Shell.Application + $objFolder = $objShell.namespace(":path") + 0..:items | Foreach-Object { \'{0} = {1}\' -f $_, $objFolder.GetDetailsOf($null, $_) } + '; + + $data_query = ' + $objShell = New-Object -ComObject Shell.Application + $objFolder = $objShell.namespace(":path") + $objFile = $objFolder.parsename(":file") + 0..:items | Foreach-Object { \'{0} = {1}\' -f $_, $objFolder.GetDetailsOf($objFile, $_) } + '; + + $title_items = WindowsScriptHost::PowerShell($title_query, ['file' => $this->file->getName(), 'path' => dirname($this->file->getAbsolutePath()), 'items' => $items]); + $data_items = WindowsScriptHost::PowerShell($data_query, ['file' => $this->file->getName(), 'path' => dirname($this->file->getAbsolutePath()), 'items' => $items]); + + $title = $this->parse($title_items); + $data = $this->parse($data_items); + + $array = array_combine($title, $data); + foreach ($array as $k=>$v){ + if(strlen($k) == 0 || strlen($v) == 0){ + unset($array[$k]); + } + } + + return $array; + } + + + protected function parse($lines){ + $scanner = new Scanner($lines, 'UTF-8'); + $data = []; + + while ($scanner->hasNextLine()) { + $line = $scanner->nextLine(); + list($key, $value) = str::split($line, '=', 2); + $key = intval(str::trim($key)); + $value = str::trim($value); + + $data[$key] = $value; + + } + + return $data; + } +} \ No newline at end of file diff --git a/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/Prepare.php b/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/Prepare.php new file mode 100644 index 0000000..7f61e2b --- /dev/null +++ b/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/Prepare.php @@ -0,0 +1,125 @@ + 'value' + private $safeQuery; + + /** + * Обрамлять переменную кавычками + * @var boolean + */ + public $addStringQuotes = false; + + /** + * Режим управления кавычками + * 0 - ничего не делаем + * 1 - кавычки экранируются \" + * 2 - кавычки экранируются "" + * @var int + */ + public $quotesPolicy = 0; + + + /** + * Заменить отсутствующие переменные на NULL + * @var boolean + */ + public $replaceEmpty = false; + + public function __construct($query){ + $this->source = $query; + } + + /** + * @param array $bindParams [key => value] || [key => [value, type]] + */ + public function bindAll($bindParams){ + foreach($bindParams as $key => $value){ + if(is_array($value)){ + $this->bind($key, $value[0], $value[1]); + } else { + $this->bind($key, $value); + } + } + } + + public function bind($key, $value, $type = 'STRING'){ + $key = (str::sub($key, 0, 1) == ':') ? str::sub($key, 1) : $key; + $key = str::lower($key); + + switch($type){ + case self::STRING: + $value = strval($value); + $value = str::replace($value, "\\", "\\\\"); + + switch($this->quotesPolicy){ + case 1: + $value = str::replace($value, "\"", "\\\""); + break; + + case 2: + $value = str::replace($value, '"', '""'); + break; + } + + if($this->addStringQuotes){ + $value = '"' . $value . '"'; + } + + $this->vars[$key] = $value; + break; + + case self::INTEGER: + $this->vars[$key] = intval($value); + break; + + case self::FLOAT: + $this->vars[$key] = floatval($value); + break; + + case self::BOOLEAN: + $this->vars[$key] = boolval($value); + break; + } + } + + public function getQuery($bindParams = []){ + $this->bindAll($bindParams); + + $reg = Regex::of(':([\w\d_]+)', Regex::UNICODE_CASE | Regex::CASE_INSENSITIVE | Regex::MULTILINE)->with($this->source); + return $reg->replaceWithCallback(function($reg){ + $key = str::lower($reg->group(1)); + + if(isset($this->vars[$key])){ + return $this->vars[$key]; + } + + if($this->replaceEmpty === true){ + return 'NULL'; + } + else return $reg->group(0); + }); + } + + public static function Query($query, $params = []){ + return (new self($query))->getQuery($params); + } +} \ No newline at end of file diff --git a/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/Registry.php b/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/Registry.php new file mode 100644 index 0000000..fa8d4d7 --- /dev/null +++ b/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/Registry.php @@ -0,0 +1,207 @@ +path = $path; + } + + /** + * Alias __construct + * @return Registry + */ + public static function of($path){ + return new self($path); + } + + /** + * HKEY_CLASSES_ROOT + * @return Registry + */ + public static function HKCR(){ + return new self('HKEY_CLASSES_ROOT'); + } + + /** + * HKEY_CURRENT_USER + * @return Registry + */ + public static function HKCU(){ + return new self('HKEY_CURRENT_USER'); + } + + /** + * HKEY_LOCAL_MACHINE + * @return Registry + */ + public static function HKLM(){ + return new self('HKEY_LOCAL_MACHINE'); + } + + /** + * HKEY_USERS + * @return Registry + */ + public static function HKU(){ + return new self('HKEY_USERS'); + } + + /** + * HKEY_CURRENT_CONFIG + * @return Registry + */ + public static function HKCC(){ + return new self('HKEY_CURRENT_CONFIG'); + } + + /** + * --RU-- + * Полное чтение содержимого раздела (ключ, значения, подразделы) + * @param bool $recursive=false рекурсивное чтение из подразделов + * @return registryResult[] + */ + + public function readFully($recursive = false){ + $exec = $this->query('query ":path"' . ($recursive ? ' /s' : ''), ['path' => $this->path]); + return $this->parseAnswer($exec); + } + + /** + * --RU-- + * Чтение ключа + * @param string $key имя ключа + * @return registryItem + */ + public function read($key){ + $exec = $this->query('query ":path" /v ":key"', ['path' => $this->path, 'key' => $key]); + $result = $this->parseAnswer($exec); + return isset($result[0]) ? $result[0]->next() : null; + } + + /** + * --RU-- + * Добавить новый параметр в реестр + * @param string $key Имя параметра + * @param string $value Значение + * @param string $type Тип переменной (REG_SZ|REG_DWORD|REG_BINARY) + */ + public function add($key, $value, $type = 'REG_SZ'){ + return $this->query('add ":path" /v ":key" /t ":type" /d ":value" /f', [ + 'path' => $this->path, + 'key' => $key, + 'value' => $value, + 'type' => $type + ]); + } + + + /** + * --RU-- + * Создать раздел реестра + */ + public function create(){ + return $this->query('add ":path" /f', ['path' => $this->path]); + } + + /** + * --RU-- + * Удалить раздел реестра + */ + public function delete(){ + return $this->query('delete ":path" /f', ['path' => $this->path]); + } + + /** + * --RU-- + * Удалить содержимое раздела + */ + public function clear(){ + return $this->query('delete ":path" /va /f', ['path' => $this->path]); + } + + /** + * --RU-- + * Удалить ключ из реестра + * @param string $key + */ + public function deleteKey($key){ + return $this->query('delete ":path" /v ":key" /f', ['path' => $this->path, 'key' => $key]); + } + + /** + * --RU-- + * Поиск по ключам и разделам + * @param string $search + * @param bool $recursive=false Искать в подразделах + * @param bool $fullEqual=false Только полное совпадение + * @return registryResult[] + */ + public function search($search, $recursive = false, $fullEqual = false){ + $exec = $this->query('query ":path" /f ":search"' . ($fullEqual ? ' /e' : '') . ($recursive ? ' /s' : ''), ['path' => $this->path, 'search' => $search]); + return $this->parseAnswer($exec); + } + + /** + * --RU-- + * Поиск по значениям + * @param string $search + * @param bool $recursive=false Искать в подразделах + * @param bool $fullEqual=false Только полное совпадение + * @return registryResult[] + */ + public function searchValue($search, $recursive = false, $fullEqual = false){ + $exec = $this->query('query ":path" /f ":search" /d' . ($fullEqual ? ' /e' : '') . ($recursive ? ' /s' : ''), ['path' => $this->path, 'search' => $search]); + return $this->parseAnswer($exec); + } + + private function parseAnswer($answer){ + $parts = explode("\nHKEY_", $answer); + $return = []; + $reg = '\n[ ]{4}([^\n]+)[ ]{4}([^\n]+)[ ]{4}([^\n\r]*)'; + + foreach($parts as $i => $part){ + if(is_null($part)) continue; + $regex = Regex::of($reg, Regex::CASE_INSENSITIVE + Regex::MULTILINE)->with($part); + $path = str::lines($part)[0]; + $path = str::startsWith($path, 'HKEY_') ? $path : 'HKEY_' . $path; + $return[$i] = new registryResult($path); + + while ($regex->find()){ + $return[$i]->addData($regex->group(1), $regex->group(2), Str::Trim($regex->group(3))); + } + } + + + return $return; + } + + private function query(string $command, array $vars = []){ + $regPath = Windows::getSysNative('reg.exe'); + return WSH::cmd($regPath . ' ' . $command, $vars); + } + +} \ No newline at end of file diff --git a/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/Startup.php b/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/Startup.php new file mode 100644 index 0000000..6cb935c --- /dev/null +++ b/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/Startup.php @@ -0,0 +1,194 @@ +$item){ + $key = strtolower(implode('-', $item)); // Чтоб убрать повторяющиеся элементы + if(isset($return[$key])) continue; + + $return[$key] = new startupItem($item['title'], $item['command'], $item['location']); + } + + return array_values($return); + } + + /** + * Загрузка элементов из WMI + */ + private static function loadWMIC() : array{ + $list = WSH::WMIC('startup get'); + $startup = []; + + foreach($list as $v){ + $startup[] = ['title' => $v['Caption'], 'command' => $v['Command'], 'location' => self::expandRegPath($v['Location'])]; + } + + return $startup; + } + + /** + * Загрузка элементов из реестра + */ + public static function loadRegistry() : array{ + $regPaths = [ + 'HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run', + 'HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunOnce', + 'HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run', + 'HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\RunOnce', + + // If added by Group Policy + 'HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run', + 'HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run', + + //x64 + 'HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Run', + 'HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\RunOnce', + ]; + + $startup = []; + + foreach($regPaths as $path){ + try{ + $reg = Registry::of($path)->readFully(true); + foreach($reg as $r){ + foreach($r as $v){ + $startup[] = ['title' => $v->key, 'command' => $v->value, 'location' => $r->path]; + } + } + + } catch(WindowsException $e){ + } + } + + return $startup; + } + + /** + * @todo Загрузка отключенных в реестре + */ + public static function loadDisabled() : array { + $regPaths = [ + 'HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\Run', + 'HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\Run32', + 'HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\StartupFolder', + 'HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\Run', + 'HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\Run32', + 'HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\StartupFolder', + ]; + + + $startup = []; + + foreach($regPaths as $path){ + try{ + $reg = Registry::of($path)->readFully(); + foreach($reg as $r){ + foreach($r as $v){ + $startup[$r->path][] = [$v->key => $v->value]; + } + } + + } catch(WindowsException $e){ + } + } + + return $startup; + } + + private static function expandRegPath($path) : string { + $reg = [ + 'HKCR\\' => 'HKEY_CLASSES_ROOT\\', + 'HKCU\\' => 'HKEY_CURRENT_USER\\', + 'HKLM\\' => 'HKEY_LOCAL_MACHINE\\', + 'HKU\\' => 'HKEY_USERS\\', + 'HKCC\\' => 'HKEY_CURRENT_CONFIG\\', + ]; + + return str_replace(array_keys($reg), array_values($reg), $path); + } + + + /** + * --RU-- + * Добавляет программу в автозагрузку + * @param string $file Файл для автозапуска + * @param string $iconpath=null Путь до иконки + * @param string $args=null Аргументы командной строки + * @param string $description=null Описание + * @return startupItem + */ + public static function add($file, $iconpath = null, $args = null, $description = null) : startupItem { + $dir = self::getUserStartupDirectory(); + $basename = basename($file); + Windows::createShortcut($dir . '\\' . $basename . '.lnk', $file,$iconpath,$args,$description); + return self::find($file); + } + + /** + * --RU-- + * Найти запись в автозапуске по исполняемому файлу + * @param string $file Путь к исполняемому файлу + * @return startupItem + */ + public static function find($file) { + $list = self::getList(); + foreach($list as $item){ + if($item->file == $file){ + return $item; + } + } + + return false; + } + + /** + * --RU-- + * Находится ли данный файл в автозапуске + * @param string $file Путь к исполняемому файлу + * @return bool + */ + public static function isExists($file) : bool { + return self::find($file) !== false; + } + + /** + * --RU-- + * Возвращает путь к пользовательской папке автозагрузки + * @return string + */ + public static function getUserStartupDirectory() : string { + return realpath(Windows::expandEnv('%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup')); + } + + /** + * --RU-- + * Возвращает путь к папке автозагрузки для программ + * @return string + */ + public static function getCommonStartupDirectory() : string { + return realpath(Windows::expandEnv('%PROGRAMDATA%\Microsoft\Windows\Start Menu\Programs\Startup')); + } + +} \ No newline at end of file diff --git a/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/Task.php b/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/Task.php new file mode 100644 index 0000000..a2e0872 --- /dev/null +++ b/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/Task.php @@ -0,0 +1,108 @@ +length() > 0) ? $proc->first() : false ; + } + + /** + * --RU-- + * Поиск процесса по имени образа + * @param string $name + * @return \result\taskResult + */ + public static function find($name){ + $proc = self::exec('IMAGENAME eq '.$name); + return ($proc->length() > 0) ? $proc : false ; + } + + /** + * --RU-- + * Поиск процесса по заголовку окна + * @param string $title + * @return \result\taskResult + */ + public static function findByTitle($title){ + $proc = self::exec('WINDOWTITLE eq '.$title); + return ($proc->length() > 0) ? $proc : false ; + } + + /** + * --RU-- + * Существует ли процесс с таким именем образа + * @param string $name + * @return bool + */ + public static function exists($name){ + return self::find($name) !== false; + } + + /** + * --RU-- + * Существует ли процесс с таким PID + * @param int $pid + * @return bool + */ + public static function pidExists($pid){ + return self::findByPID($pid) !== false; + } + + /** + * --RU-- + * Существует ли процесс с таким заголовком окна + * @param string $title + * @return bool + */ + public static function titleExists($title){ + return self::findByTitle($title) !== false; + } + + private static function exec($filter = false){ + // cp изменит вывод на английский язык + $list = WSH::cmd('chcp 65001 | tasklist /V /FO CSV /NH' . ($filter === false ? '' : ' /FI ":filter"'), ['filter' => $filter], 'utf-8'); + return self::parseAnswer($list); + } + + private static function parseAnswer($list){ + $tasks = explode("\n", $list); + $reg = '"([^"]+)"'; + $result = new taskResult; + foreach ($tasks as $k=>$task) { + $regex = Regex::of($reg, Regex::CASE_INSENSITIVE + Regex::UNICODE_CASE)->with($task); + if ($regex->find()) { + $result->addItem($regex->all()); + } + + } + + return $result; + } +} \ No newline at end of file diff --git a/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/Windows.php b/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/Windows.php new file mode 100644 index 0000000..4231f92 --- /dev/null +++ b/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/Windows.php @@ -0,0 +1,1495 @@ +with($string); + $env = System::getEnv(); + + while ($regex->find()) { + $key = $regex->group(1); + foreach($env as $k=>$v){ + if(str::lower($k) == str::lower($key)){ + $string = str::replace($string, $regex->group(0), $v); + break; + } + } + } + + return $string; + } + + /** + * --RU-- + * Проверить, относится ли текущая система к семейству OS Windows + * @return bool + */ + public static function isWin(){ + return Str::posIgnoreCase(System::getProperty('os.name'), 'WIN') > -1; + } + + /** + * --RU-- + * Проверить, запущена ли программа от имени администратора + * @return bool + */ + public static function isAdmin(){ + try { + (new Registry('HKU\\S-1-5-19'))->readFully(); + return true; + } catch (WindowsException $e){ + return false; + } + } + + /** + * Запустить процесс от имени администратора + * @param string $file + * @param array $args + * @param string $workDir + */ + public static function runAsAdmin(string $file, array $args = [], string $workDir = null){ + if(!fs::exists($file)){ + throw new WindowsException("Invalid path '".$file."'"); + } + + foreach ($args as $k => $arg) { + if(str::contains($arg, ' ')){ + $args[$k] = '"' . $arg . '"'; + } + $args[$k] = str_replace('"', '""', $args[$k]); + } + $argString = implode(' ', $args); + var_dump(['arguments' => $argString]); + + $workDir = is_null($workDir) ? CURRENT_DIRECTORY : $workDir; + var_dump(['workDir' => $workDir]); + + return WSH::PowerShell(empty($argString) ? 'Start-Process "'.$file.'" -WorkingDirectory "'.$workDir.'" -Verb runAs' : + 'Start-Process "'.$file.'" -WorkingDirectory "'.$workDir.'" -Verb runAs -ArgumentList "'.$argString.'"'); + + } + + /** + * Перезапускает текущую программу с требованием прав администратора + */ + public static function requireAdmin(){ + global $argv; + if(self::isAdmin()) return; + + // Программа либо не собрана, либо библиотеки находятся отдельно от исполняемог файла, + // а значит узнать команду запуска и путь к исполняемому файлу невозможно + if(str::endsWith($argv[0], '/lib/jphp-core.jar')) throw new WindowsException('Cannot restart this build with administrator privileges'); + + if(str::startsWith($argv[0], '/') and str::contains($argv[0], ':')){ + $argv[0] = str::sub($argv[0], 1); + } + + switch(fs::ext($argv[0])){ + case 'exe': + $cmd = $argv[0]; + $params = array_slice($argv, 1); + break; + + case 'jar': + $cmd = JAVA_HOME . '/javaw.exe'; // javaw запускает jar без консоли + $params = array_merge(['-jar'], $argv); + break; + + default: + $cmd = self::getSystem32('cmd.exe'); + $params = array_merge(['/c'], $argv); + + } + + self::runAsAdmin($cmd, $params); + exit; + } + + /** + * Получить разрядность системы + * @return string 'x64' или 'x86' + */ + public static function getArch(){ + return isset(System::getEnv()['ProgramFiles(x86)']) ? 'x64' : 'x86'; // В 64-битных системах будет прописан путь к Program Filex (x86) + } + + /** + * Return system temp directory. + * --RU-- + * Получить путь ко временной папке + * @return string + */ + public static function getTemp(){ + return self::expandEnv('%TEMP%'); + } + + + /** + * --RU-- + * Получить список пользователей на данном ПК + * @return array + */ + public static function getUsers(){ + return WSH::WMIC('UserAccount get'); + } + + /** + * Return serial number of a drive. + * --RU-- + * Получить серийный номер носителя + * @param string $drive Буква диска + * @return string + */ + public static function getDriveSerial($drive){ + $drive = str::endsWith($drive, ':') ? $drive : $drive . ':'; + $parts = WSH::WMIC('path Win32_LogicalDiskToPartition get'); + $devices = WSH::WMIC('path Win32_PhysicalMedia get'); + + foreach($parts as $part){ + if(str::contains($part['Dependent'], '"' . $drive . '"')){ + $regex = Regex::of('DeviceID="Disk #(\d+),', Regex::CASE_INSENSITIVE + Regex::MULTILINE)->with($part['Antecedent']); + if ($regex->find()) { + $nDrive = $regex->group(1); + foreach($devices as $device){ + if(str::contains($device['Tag'], 'DRIVE' . $nDrive)){ + return str::trim($device['SerialNumber']); + } + } + } + } + } + + return null; + } + + /** + * --RU-- + * Получить список подключенных дисков и их характеристик + * @return array Двумерный массив с характеристиками каждого подключенного диска + */ + public static function getDrives(){ + return WSH::WMIC('path win32_logicaldisk get'); + } + + + /** + * Get full information of current OS. + * --RU-- + * Получить характеристики операционной системы + * @return array Массив с параметрами текущей операционной системы + */ + public static function getOS(){ + return WSH::WMIC('OS get')[0]; + } + + /** + * Get full information of current baseboard. + * --RU-- + * Получить характеристики материнской платы + * @return string + */ + public static function getMotherboard(){ + return WSH::WMIC('baseboard get')[0]; + } + + /** + * Return serial number of current mother board. + * --RU-- + * Получить серийный номер материнской платы + * @return string + */ + public static function getMotherboardSerial(){ + return WSH::WMIC('baseboard get SerialNumber')[0]['SerialNumber']; + } + + /** + * --RU-- + * Получить производителя материнской платы + * @return string + */ + public static function getMotherboardManufacturer(){ + return WSH::WMIC('baseboard get Manufacturer')[0]['Manufacturer']; + } + + /** + * --RU-- + * Получить модель материнской платы + * @return string + */ + public static function getMotherboardProduct(){ + return WSH::WMIC('baseboard get Product')[0]['Product']; + } + + /** + * --RU-- + * Получить вольтаж процессора + * @return string + */ + public static function getCpuVoltage(){ + return WSH::WMIC('CPU get CurrentVoltage')[0]['CurrentVoltage']; + } + + /** + * --RU-- + * Получить производителя процессора + * @return string + */ + public static function getCpuManufacturer(){ + return WSH::WMIC('CPU get Manufacturer')[0]['Manufacturer']; + } + + /** + * --RU-- + * Получить максимальную частоту процессора + * @return string + */ + public static function getCpuFrequency(){ + return WSH::WMIC('CPU get MaxClockSpeed')[0]['MaxClockSpeed']; + } + + /** + * --RU-- + * Получить серийный номер процессора + * @return string + */ + public static function getCpuSerial(){ + return WSH::WMIC('CPU get ProcessorId')[0]['ProcessorId']; + } + + /** + * --RU-- + * Получить модель процессора + * @return string + */ + public static function getCpuProduct(){ + return WSH::WMIC('CPU get Name')[0]['Name']; + } + + /** + * --RU-- + * Получить характеристики процессора + * @return string + */ + public static function getCPU(){ + return WSH::WMIC('CPU get')[0]; + } + + /** + * --RU-- + * Получить модель (первой) видеокарты + * @return string + */ + public static function getVideoProduct(){ + return WSH::WMIC('Path Win32_VideoController Get VideoProcessor')[0]['VideoProcessor']; + } + + /** + * --RU-- + * Получить производителя (первой) видеокарты + * @return string + */ + public static function getVideoManufacturer(){ + return WSH::WMIC('Path Win32_VideoController Get AdapterCompatibility')[0]['AdapterCompatibility']; + } + + /** + * --RU-- + * Получить память (первой) видеокарты + * @return string + */ + public static function getVideoRAM(){ + return WSH::WMIC('Path Win32_VideoController Get AdapterRAM')[0]['AdapterRAM']; + } + + /** + * --RU-- + * Получить разрешение (первой) видеокарты + * @return string + */ + public static function getVideoMode(){ + return WSH::WMIC('Path Win32_VideoController Get VideoModeDescription')[0]['VideoModeDescription']; + } + + /** + * --RU-- + * Получить характеристики всех подключенных видеокарт + * @return string + */ + public static function getVideo(){ + return WSH::WMIC('Path Win32_VideoController Get'); + } + + /** + * --RU-- + * Получить характеристики звуковых устройств + * @return string + */ + public static function getSound(){ + return WSH::WMIC('Sounddev Get'); + } + + /** + * --RU-- + * Получить характеристики устройств оперативной памяти + * @return array + */ + public static function getRAM(){ + return WSH::WMIC('path Win32_PhysicalMemory get'); + } + + /** + * --RU-- + * Получить полный объем оперативной памяти (в байтах) + * @return int + */ + public static function getTotalRAM() : int { + return intval(WSH::WMIC('path Win32_ComputerSystem get TotalPhysicalMemory')[0]['TotalPhysicalMemory']); + } + + /** + * --RU-- + * Получить объем свободной оперативной памяти (в байтах) + * @return int + */ + public static function getFreeRAM() : int { + return self::getOS()['FreePhysicalMemory'] * 1024; + } + + /** + * --RU-- + * Получить уникальный UUID системы + * @return string + */ + public static function getUUID(){ + return WSH::WMIC('path win32_computersystemproduct get')[0]['UUID']; + //return WSH::PowerShell('get-wmiobject Win32_ComputerSystemProduct | Select-Object -ExpandProperty UUID'); + } + + /** + * --RU-- + * Получить информацию о BIOS + * @return array + */ + public static function getBIOS(){ + return WSH::WMIC('path Win32_BIOS get')[0]; + } + + /** + * --RU-- + * Получить массив принтеров и их характеристики + * @return array + * @todo add Win32_PrintJob + */ + public static function getPrinter(){ + return WSH::WMIC('path Win32_Printer get'); + } + + /** + * --RU-- + * Получить ProductName системы + * @return string + */ + public static function getProductName(){ + return Registry::of('HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion')->read('ProductName')->value; + } + + /** + * Returns mac-address. + * --RU-- + * Получить MAC-адрес сетевой карты + * @return string + */ + public static function getMAC(){ + return UXApplication::getMacAddress(); + } + + /** + * Получить температуру с датчиков (желательно запускать с парвами администратора) + * @return array ([name, temp, location]) + */ + public static function getTemperature(){ + $return = []; + + // 1. Пробуем прочитать данные с HDD + try{ + $hdds = WSH::WMIC('/namespace:\\\\root\\WMI path MSStorageDriver_ATAPISmartData get'); + + // Ищем элемент со значением 194, через 5 ключей будет элемент со значением температуры + foreach($hdds as $hdd){ + $vs = explode(',', $hdd['VendorSpecific']); + $tempIndex = 0; + + foreach ($vs as $k => $v) { + if(intval($v) == 194){ + $tempIndex = $k + 5; + break; + } + } + + // Температура в цельсиях, преобразование не нужно + $temp = $vs[$tempIndex]; + $return[] = [ + 'name' => $hdd['InstanceName'], + 'temp' => intval($temp), + 'location' => 'HDD' + ]; + } + } catch (WindowsException $e){ } + + // 2. Данные с различных датчиков + try{ + $msacpi = WSH::WMIC('/namespace:\\\\root\\WMI path MSAcpi_ThermalZoneTemperature get InstanceName,CurrentTemperature'); + foreach($msacpi as $v){ + if(strpos($v['InstanceName'], '\\') > 0){ + $exp = explode('\\', $v['InstanceName']); + $name = end($exp); + } else { + $name = $v['InstanceName']; + } + + if(strpos($v['InstanceName'], 'CPUZ') !== false) $location = 'CPU'; + elseif(strpos($v['InstanceName'], 'GFXZ') !== false) $location = 'GFX'; + elseif(strpos($v['InstanceName'], 'BATZ') !== false) $location = 'Battery'; + elseif(strpos($name, 'TZ') === 0) $location = 'Chipset'; + else $location = 'None'; + + + $return[] = [ + 'temp' => $v['CurrentTemperature'] / 10 - 273, // Температура в Кельвинах * 10 + 'name' => $name, + 'location' => $location, + ]; + } + + } catch (WindowsException $e){ + // 3. Данные с датчиков материрнской карты + try{ + $temps = WSH::WMIC('/namespace:\\\\root\\cimv2 PATH Win32_PerfFormattedData_Counters_ThermalZoneInformation get Name,Temperature'); + + foreach($temps as $temp){ + if(strpos($temp['Name'], 'TZ.') > 0){ + $name = explode('.', $temp['Name'])[1]; + $location = 'Chipset'; + } + else { + $name = $temp['Name']; + $location = 'None'; + } + + $return[] = [ + 'name' => $name, + 'temp' => intval($temp['Temperature']) - 273, // Температура в Кельвинах + 'location' => $location + ]; + } + + } catch (WindowsException $e){ } + } + + return $return; + } + + /** + * Количество миллисекунд с момента запуска системы + * @var int + */ + protected static $bootupTime; + + /** + * --RU-- + * Получить время запуска системы + * @return int метка времени в миллисекундах + */ + public static function getBootUptime(){ + if(is_null(self::$bootupTime)){ + $data = explode('.', WSH::WMIC('Os Get LastBootUpTime')[0]['LastBootUpTime'])[0]; + self::$bootupTime = (new TimeFormat('yyyyMMddHHmmss'))->parse($data)->getTime(); + } + + return self::$bootupTime; + } + + /** + * --RU-- + * Получить время работы системы + * @return int миллисекунды + * @example $bootTime = Windows::getUptime(); + * $time = new Time($bootTime, TimeZone::UTC()); + * var_dump('ПК работает: ' . ($time->day() - 1) . ' дней ' . $time->hourOfDay() . ' часов ' . $time->minute() . ' минут ' . $time->second() . ' секунд'); + * // string(46) "ПК работает: 0 дней 1 часов 20 минут 36 секунд" + */ + public static function getUptime(){ + return Time::Now()->getTime() - self::getBootUptime(); + } + + /** + * --RU-- + * Получить данные о встроенной батарее + * @throws WindowsException + * @return array + */ + public static function getBatteryInfo(){ + try{ + return WSH::WMIC('Path Win32_Battery Get')[0]; + } catch (\Exception $e){ + throw new WindowsException('Battery does not support'); + } + } + + /** + * --RU-- + * Получить предположительное оставшееся время работы. + * @return int миллисекунды. В процессе зарядки АКБ функция может возвращать слишком большие значения + * @throws WindowsException + */ + public static function getBatteryTimeRemaining(){ + try{ + return intval(WSH::WMIC('Path Win32_Battery Get EstimatedRunTime')[0]['EstimatedRunTime']) * 60 * 1000; + } catch (\Exception $e){ + throw new WindowsException('Battery does not support'); + } + } + + /** + * --RU-- + * Получить процент заряда батареи + * @return int Значение от 0 до 100 + * @throws WindowsException + */ + public static function getBatteryPercent(){ + try{ + return (WSH::WMIC('Path Win32_Battery Get EstimatedChargeRemaining')[0]['EstimatedChargeRemaining'])+1; + } catch (\Exception $e){ + throw new WindowsException('Battery does not support'); + } + } + + /** + * --RU-- + * Получить напряжение батареи + * @return int милливольты + * @throws WindowsException + */ + public static function getBatteryVoltage(){ + try{ + return (int) WSH::WMIC('Path Win32_Battery Get DesignVoltage')[0]['DesignVoltage']; + } catch (\Exception $e){ + throw new WindowsException('Battery does not support'); + } + } + + /** + * --RU-- + * Находится ли батарея на зарядке + * @return bool + * @throws WindowsException + */ + public static function isBatteryCharging(){ + try{ + return ((int)WSH::WMIC('Path Win32_Battery Get BatteryStatus')[0]['BatteryStatus']) > 1; + } catch (\Exception $e){ + throw new WindowsException('Battery does not support'); + } + } + + /** + * --RU-- + * Создать symlink (ярлык) + * @param string $shortcut Расположение ярлыка + * @param string $target Ссылка на файл + * @param string $iconpath=null Путь к ico файлу + * @param string $args=null Аргументы командной строки + * @param string $description=null Описание + */ + public static function createShortcut($shortcut, $target, $iconpath = null, $args = null, $description = null){ + $cmd = '$Wshell = New-Object -ComObject WScript.Shell; $shortcut = $Wshell.CreateShortcut("'.$shortcut.'"); $shortcut.TargetPath = "'.$target.'";'; + if ($iconpath != null) + $cmd = $cmd.'$shortcut.IconLocation = "'.$iconpath.'";'; + if ($args != null) + $cmd = $cmd.'$shortcut.Arguments = "'.$args.'";'; + if ($description != null) + $cmd = $cmd.'$shortcut.Description = "'.$description.'";'; + $cmd = $cmd.'$shortcut.save()'; + return WSH::PowerShell($cmd); + } + + /** + * --RU-- + * Получить ссылку на файл lnk-ярлыка + * @param string $shortcut Расположение ярлыка + * @return string + */ + public static function getShortcutTarget($shortcut){ + return WSH::cmd('type ":lnk"|find "\\"|findstr/b "[a-z]:[\\\\]"', ['lnk' => $shortcut]); + } + + /** + * --RU-- + * Проговорить текст + * @param string $text Текст + * @deprecated + */ + public static function speak($text){ + return WSH::vbScript('CreateObject("SAPI.SpVoice").Speak(":text")', ['text' => $text]); + } + + /** + * --RU-- + * Установить уровень яркости (Windows 10 only) + * @param int $level уровень яркости от 0 до 100 + * @param int $time=1 время в миллисекундах, за которое будет изменет уровень яркости + * @throws WindowsException + */ + public static function setBrightnessLevel($level, $time = 1){ + try{ + WSH::PowerShell('(Get-WmiObject -Namespace root/WMI -Class WmiMonitorBrightnessMethods).WmiSetBrightness(:time, :level)', ['time' => 1, 'level' => $level], false); + return true; + } catch (\Exception $e){ + throw new WindowsException('Video driver does not support changing the brightness level'); + } + } + + /** + * --RU-- + * Получить уровень яркости (Windows 10 only) + * @return int уровень яркости от 0 до 100 + * @throws WindowsException + */ + public static function getBrightnessLevel(){ + try{ + return (int) WSH::PowerShell('Get-Ciminstance -Namespace root/WMI -ClassName WmiMonitorBrightness | select -ExpandProperty CurrentBrightness'); + } catch (\Exception $e){ + throw new WindowsException('Video driver does not support changing the brightness level'); + } + } + + /** + * --RU-- + * Установить уровень громкости (Windows 10 only) + * @param int $level уровень от 0 до 100 + * @throws WindowsException + */ + public static function setVolumeLevel($level){ + return self::psAudioQuery('Volume', $level/100); + } + + /** + * --RU-- + * Получить уровень громкости (Windows 10 only) + * @return int уровень от 0 до 100 + * @throws WindowsException + */ + public static function getVolumeLevel(){ + $vol = self::psAudioQuery('Volume'); + $vol = floatval(str_replace(',', '.', $vol)); + return (int)($vol * 100); + } + + + /** + * --RU-- + * Включить / выключить режим "без звука" + * @param bool $value + * @throws WindowsException + */ + public static function setMute($value){ + return self::psAudioQuery('Mute', ($value ? 1 : 0)); + } + + /** + * --RU-- + * Проверить, включен ли режим "без звука" + * @return bool + * @throws WindowsException + */ + public static function getMute(){ + return self::psAudioQuery('Mute') == 'True'; + } + + private static function psAudioQuery($key, $value = null){ + $psAudioClass = << $time]); + } + + /** + * Установить системную дату (нужны права администратора) + * @param mixed $date Строка вида dd.MM.YYYY (напирмер, "31.12.2017") или массив [31, 12, 2017] + */ + public static function setDate($date){ + $date = is_array($date) ? implode('.', $date) : $date; + if(!Regex::match('^([1-9]|[0-2][0-9]|3[0-1])\.([1-9]|0[0-9]|1[0-2])\.([1-2][0-9]{3})$', $date)){ + throw new WindowsException('Invalid date value "' . $date . '". Supported format: dd.MM.YYYY'); + } + return WSH::CMD('echo :date | date', ['date' => $date]); + } + + /** + * Извлекает и сохраняет отображаемую в проводнике иконку файла + * @param string $file Файл, откуда будет извлечена иконка + * @param string $icon Путь для сохранения иконки, поддерживаются форматы PNG, JPG, ICO, GIF + */ + public static function extractIcon(string $file, string $icon) : bool { + if(!fs::exists($file)) throw new WindowsException('File "'. $file .'" does not found'); + fs::delete($icon); + WSH::PowerShell( + '[System.Reflection.Assembly]::LoadWithPartialName(\'System.Drawing\'); '. + '[System.Drawing.Icon]::ExtractAssociatedIcon(\':file\').ToBitmap().Save(\':icon\')', + ['file' => realpath($file), 'icon' => $icon] + ); + + return fs::exists($icon); + } + + /** + * Получить системный путь, по которому расположено изображение с обоями + * @return string + */ + protected static function getWallpaperPath() : string { + $path = self::expandEnv('%AppData%\\Microsoft\\Windows\\Themes'); + foreach (File::of($path)->findFiles() as $file){ + if(str::startsWith($file->getName(), 'TranscodedWallpaper')){ + return $file->getAbsolutePath(); + } + } + + return false; + } + + /** + * Получить изображение с текущими обоями + * @return UXImage + */ + public static function getWallpaper() : UXImage { + return new UXImage(self::getWallpaperPath()); + } + + /** + * Установить обои + * @param string|UXImage $image + */ + public static function setWallpaper($image){ + /** @var UXImage $image **/ + $image = $image instanceof UXImage ? $image : new UXImage($image); + $image->save(self::getWallpaperPath(), 'jpg'); // Формат jpeg, т.к. на win7 обои кодируются в этот формат + + self::updateDesktopWallpaper(); + } + + /** + * Визуальное обновление обоев на рабочем столе + * (вместо перезапуска explorer'a) + */ + protected static function updateDesktopWallpaper(){ + $psCommand = << self::getWallpaperPath()]); + } + + /** + * Путь к системной папке windows\system32 + * + * @return string + */ + public static function getSystem32($path) : string { + return self::getSystemDrive() . ':\\Windows\\System32\\' . $path; + } + + /** + * Возвращает букву системного диска + * @return string + */ + public static function getSystemDrive() : string { + $path = $_ENV['HOMEDRIVE'] ?? $_ENV['SystemRoot'] ?? 'C'; + return str::sub($path, 0, 1); + } + + /** + * Если 32-битный процесс запущен в 64-битной системе, то он не может + * запустить 64 битный powershell, для этого монтируется виртуальная + * директория SysNative, если запустить оттуда, запущенный процесс будет 64-битный + * + * @return string + * @todo test on x86 + * @todo test on win7 + */ + public static function getSysNative($path) : string { + return fs::exists(self::getSystemDrive() . ':\\Windows\\SysNative\\' . $path) + ? (self::getSystemDrive() . ':\\Windows\\SysNative\\' . $path) + : (self::getSystemDrive() . ':\\Windows\\System32\\' . $path); + } + + /** + * Ping + * @param string $domain Домен или ip адрес + * @param int $count Количество запросов + * @param int $length Размер блока + * @return array [min => ms, max => ms, avg => ms, lost => %] + */ + public static function ping(string $domain, int $count = 1, int $length = 32) : array { + $return = [ + 'min' => false, + 'max' => false, + 'avg' => false, + 'lost' => 0, + ]; + + $answer = WSH::cmd(self::getSystem32('ping.exe') . ' :domain -n :count -l :length', [ + 'domain' => $domain, + 'count' => $count, + 'length' => $length, + ]); + + $times = Regex::of('\s([A-Z][a-z]+)[\s=]+(\d+)ms', Regex::CASE_INSENSITIVE + Regex::MULTILINE)->with($answer); + while($times->find()){ + $keys = [ + 'Minimum' => 'min', + 'Maximum' => 'max', + 'Average' => 'avg', + ]; + $k = $times->group(1); + + if(isset($keys[$k])){ + $return[$keys[$k]] = intval($times->group(2)); + } + } + + $lost = Regex::of('\((\d+)% loss\)', Regex::CASE_INSENSITIVE + Regex::MULTILINE)->with($answer); + if($lost->find()){ + $return['lost'] = intval($lost->group(1)); + } + + return $return; + } + + /** + * Проверить наличие Интернет-соединения + * @return bool + */ + public static function isInternetAvaliable() : bool { + try { + $socket = new Socket; + $socket->connect('google.com', '80', 1000); + $socket->close(); + return true; + } catch (Exception | IOException $e) { + return false; + } + } + + /** + * Получить код раскладки клавиатуры + * @return string + */ + public static function getKeyboardLayout() : string { + $c = new CSharp(' + using System; + using System.Runtime.InteropServices; + using System.Text; + public class KeyboardLayout { + [DllImport("user32.dll")] + private static extern long GetKeyboardLayoutName(StringBuilder pwszKLID); + + //each keyboard layout is defined in Windows as a hex code + public static dynamic GetLayoutCode() + { + var name = new StringBuilder(9); + GetKeyboardLayoutName(name); + + return name.ToString(); + } + } + '); + + return $c->call('KeyboardLayout', 'GetLayoutCode'); + } + + + /** + * Получить название раскладки клавиатуры + * @return string + */ + public static function getKeyboardLayoutName() : string { + $code = self::getKeyboardLayout(); + + switch($code){ + case "0000041C": + return "Albanian"; + case "00000401": + return "Arabic (101)"; + case "00010401": + return "Arabic (102)"; + case "00020401": + return "Arabic (102) Azerty"; + case "0000042B": + return "Armenian eastern"; + case "0001042B": + return "Armenian Western"; + case "0000044D": + return "Assamese - inscript"; + case "0000082C": + return "Azeri Cyrillic"; + case "0000042C": + return "Azeri Latin"; + case "0000046D": + return "Bashkir"; + case "00000423": + return "Belarusian"; + case "0000080C": + return "Belgian French"; + case "00000813": + return "Belgian (period)"; + case "0001080C": + return "Belgian (comma)"; + case "00000445": + return "Bengali"; + case "00010445": + return "Bengali - inscript (legacy)"; + case "00020445": + return "Bengali - inscript"; + case "0000201A": + return "Bosnian (cyrillic)"; + case "00030402": + return "Bulgarian"; + case "00000402": + return "Bulgarian(typewriter)"; + case "00010402": + return "Bulgarian (latin)"; + case "00020402": + return "Bulgarian (phonetic)"; + case "00040402": + return "Bulgarian (phonetic traditional)"; + case "00011009": + return "Canada Multilingual"; + case "00001009": + return "Canada French"; + case "00000C0C": + return "Canada French (legacy)"; + case "00000404": + return "Chinese (traditional) - us keyboard"; + case "00000804": + return "Chinese (simplified) -us keyboard"; + case "00000C04": + return "Chinese (traditional, hong kong s.a.r.) - us keyboard"; + case "00001004": + return "Chinese (simplified, singapore) - us keyboard"; + case "00001404": + return "Chinese (traditional, macao s.a.r.) - us keyboard"; + case "00000405": + return "Czech"; + case "00020405": + return "Czech programmers"; + case "00010405": + return "Czech (qwerty)"; + case "0000041A": + return "Croatian"; + case "00000439": + return "Deanagari - inscript"; + case "00000406": + return "Danish"; + case "00000465": + return "Divehi phonetic"; + case "00010465": + return "Divehi typewriter"; + case "00000413": + return "Dutch"; + case "00000425": + return "Estonian"; + case "00000438": + return "Faeroese"; + case "0000040B": + return "Finnish"; + case "0001083B": + return "Finnish with sami"; + case "0000040C": + return "French"; + case "00011809": + return "Gaelic"; + case "00000437": + return "Georgian"; + case "00020437": + return "Georgian (ergonomic)"; + case "00010437": + return "Georgian (qwerty)"; + case "00000407": + return "German"; + case "00010407": + return "German (ibm)"; + case "0000046F": + return "Greenlandic"; + case "00000468": + return "Hausa"; + case "0000040D": + return "Hebrew"; + case "00010439": + return "Hindi traditional"; + case "00000408": + return "Greek"; + case "00010408": + return "Greek (220)"; + case "00030408": + return "Greek (220) latin"; + case "00020408": + return "Greek (319)"; + case "00040408": + return "Greek (319) latin"; + case "00050408": + return "Greek latin"; + case "00060408": + return "Greek polyonic"; + case "00000447": + return "Gujarati"; + case "0000040E": + return "Hungarian"; + case "0001040E": + return "Hungarian 101 key"; + case "0000040F": + return "Icelandic"; + case "00000470": + return "Igbo"; + case "0000085D": + return "Inuktitut - latin"; + case "0001045D": + return "Inuktitut - naqittaut"; + case "00001809": + return "Irish"; + case "00000410": + return "Italian"; + case "00010410": + return "Italian (142)"; + case "00000411": + return "Japanese"; + case "0000044B": + return "Kannada"; + case "0000043F": + return "Kazakh"; + case "00000453": + return "Khmer"; + case "00000412": + return "Korean"; + case "00000440": + return "Kyrgyz cyrillic"; + case "00000454": + return "Lao"; + case "0000080A": + return "Latin america"; + case "00000426": + return "Latvian"; + case "00010426": + return "Latvian (qwerty)"; + case "00010427": + return "Lithuanian"; + case "00000427": + return "Lithuanian ibm"; + case "00020427": + return "Lithuanian standard"; + case "0000046E": + return "Luxembourgish"; + case "0000042F": + return "Macedonian (fyrom)"; + case "0001042F": + return "Macedonian (fyrom) - standard"; + case "0000044C": + return "Malayalam"; + case "0000043A": + return "Maltese 47-key"; + case "0001043A": + return "Maltese 48-key"; + case "0000044E": + return "Marathi"; + case "00000481": + return "Maroi"; + case "00000450": + return "Mongolian cyrillic"; + case "00000850": + return "Mongolian (mongolian script)"; + case "00000461": + return "Nepali"; + case "00000414": + return "Norwegian"; + case "0000043B": + return "Norwegian with sami"; + case "00000448": + return "Oriya"; + case "00000463": + return "Pashto (afghanistan)"; + case "00000429": + return "Persian"; + case "00000415": + return "Polish (programmers)"; + case "00010415": + return "Polish (214)"; + case "00000816": + return "Portuguese"; + case "00000416": + return "Portuguese (brazillian abnt)"; + case "00010416": + return "Portuguese (brazillian abnt2)"; + case "00000446": + return "Punjabi"; + case "00010418": + return "Romanian (standard)"; + case "00000418": + return "Romanian (legacy)"; + case "00020418": + return "Romanian (programmers)"; + case "00000419": + return "Russian"; + case "00010419": + return "Russian (typewriter)"; + case "0002083B": + return "Sami extended finland-sweden"; + case "0001043B": + return "Sami extended norway"; + case "00000C1A": + return "Serbian (cyrillic)"; + case "0000081A": + return "Serbian (latin)"; + case "0000046C": + return "Sesotho sa Leboa"; + case "00000432": + return "Setswana"; + case "0000045B": + return "Sinhala"; + case "0001045B": + return "Sinhala -Wij 9"; + case "0000041B": + return "Slovak"; + case "0001041B": + return "Slovak (qwerty)"; + case "00000424": + return "Slovenian"; + case "0001042E": + return "Sorbian extended"; + case "0002042E": + return "Sorbian standard"; + case "0000042E": + return "Sorbian standard (legacy)"; + case "0000040A": + return "Spanish"; + case "0001040A": + return "Spanish variation"; + case "0000041D": + return "Swedish"; + case "0000083B": + return "Swedish with sami"; + case "00000807": + return "Swiss german"; + case "0000100C": + return "Swiss french"; + case "0000045A": + return "Syriac"; + case "0001045A": + return "Syriac phonetic"; + case "00000428": + return "Tajik"; + case "00000449": + return "Tamil"; + case "00000444": + return "Tatar"; + case "0000044A": + return "Telugu"; + case "0000041E": + return "Thai Kedmanee"; + case "0002041E": + return "Thai Kedmanee (non-shiftlock)"; + case "0001041E": + return "Thai Pattachote"; + case "0003041E": + return "Thai Pattachote (non-shiftlock)"; + case "00000451": + return "Tibetan (prc)"; + case "0001041F": + return "Turkish F"; + case "0000041F": + return "Turkish Q"; + case "00000442": + return "Turkmen"; + case "00000422": + return "Ukrainian"; + case "00020422": + return "Ukrainian (enhanced)"; + case "00000809": + return "United Kingdom"; + case "00000452": + return "United Kingdom Extended"; + case "00000409": + return "United States"; + case "00010409": + return "United States - dvorak"; + case "00030409": + return "United States - dvorak left hand"; + case "00050409": + return "United States - dvorak right hand"; + case "00004009": + return "United States - india"; + case "00020409": + return "United States - international"; + case "00000420": + return "Urdu"; + case "00010480": + return "Uyghur"; + case "00000480": + return "Uyghur (legacy)"; + case "00000843": + return "Uzbek cyrillic"; + case "0000042A": + return "Vietnamese"; + case "00000485": + return "Yakut"; + case "0000046A": + return "Yoruba"; + case "00000488": + return "Wolof"; + + default: + return "unknown ($code)"; + } + } + + /** + * Возвращает ProductKey системы + * @return string + * @todo test on win7 + */ + public static function getProductKey() : string { + $psCommand = <<read('ReleaseId')->value; + } catch (WindowsException $e){ + $release = Registry::of('HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion')->read('CSDBuildNumber')->value; + } + + return intval($release); + } + + /** + * Возвращает номер сборки ОС + * @return int + */ + public static function getProductBuild() : int { + $build = Registry::of('HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion')->read('CurrentBuild')->value; + return intval($build); + } + + /** + * Некоторые коды кнопок + */ + const VK_VOLUME_MUTE = 0xAD; + const VK_VOLUME_DOWN = 0xAE; + const VK_VOLUME_UP = 0xAF; + const VK_MEDIA_NEXT_TRACK = 0xB0; + const VK_MEDIA_PREV_TRACK = 0xB1; + const VK_MEDIA_STOP = 0xB2; + const VK_MEDIA_PLAY_PAUSE = 0xB3; + + /** + * Имитирует нажатие на кнопку + * @link https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes + * @param int $keyCode Код кнопки + * @example Windows::pressKey(0xB3); // Press media play/pause button + */ + public static function pressKey(int $keyCode){ + $c = new CSharp(' + using System; + using System.Runtime.InteropServices; + + public class Media { + [DllImport("User32.dll",CharSet=CharSet.Unicode)] + public static extern void keybd_event(byte virtualKey, byte scanCode, uint flags, IntPtr extraInfo); + } + '); + + return $c->call('Media', 'keybd_event', [$keyCode, 0, 1, 0]); + } + + /** + * Выключить ПК + */ + public static function shutdown(){ + return WSH::cmd('shutdown /s /f /t 0'); + } + + /** + * Перезагрузить ПК + */ + public static function reboot(){ + return WSH::cmd('shutdown -r -t 0'); + } + + /** + * Отправить файл на печать. Используется принтер по умолчанию. Необходимо наличие установленного пакета офиса. + * @param string $filepath Полный путь к файлу + */ + public static function print(string $filepath){ + return WSH::PowerShell(' + $word = New-Object -ComObject Word.Application + $word.visible = $false + $word.Documents.Open(":file") > $null + $word.Application.ActiveDocument.printout() + $word.Application.ActiveDocument.Close() + $word.quit() + ', ['file' => $filepath]); + } + + public static function getFileMeta(string $filepath){ + return (new Metadata($filepath))->readData(); + } +} diff --git a/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/WindowsException.php b/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/WindowsException.php new file mode 100644 index 0000000..aeafb18 --- /dev/null +++ b/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/WindowsException.php @@ -0,0 +1,8 @@ +start(); + + $wshResult = new wshResult($process); + $wshResult->setCharset($charset); + + $output = $wshResult->getOutput(); + + if(strlen($output) == 0 and strlen($error = $wshResult->getError()) > 0){ + throw new WindowsException('WindowsScriptHost Error: ' . $error); + } + + return $output; + } + + /** + * --RU-- + * Выполнить команду + * @param string $command + * @param array $params=array() параметры для замены (в запросе можно передать именованные параметры, как в PDO) + * @param string $charset кодировка ответа (в командной строке по умолчанию cp866). utf-8 возвращает всё на английском языке + * @param string $decodeCharset кодировка, из которой будет декодироваться ответ. Некоторые команды (например ipconfig), возвращают в cp866, даже если перед ней явно указан вывод командой chcp + * @return string + * @throws WindowsException + */ + public static function cmd($command, $params = [], $charset = 'utf-8', $decodeCharset = 'auto'){ + if($charset == 'utf-8') $chcp = 65001; + else $chcp = str_replace(['cp', 'windows', '-'], '', $charset); + + $command = Windows::getSystem32('chcp.com') . ' ' . $chcp . '>nul & ' . $command; + $command = Prepare::Query($command, $params); + return self::Exec([Windows::getSystem32('cmd.exe'), '/c', $command], true, ($decodeCharset == 'auto' ? $charset : $decodeCharset)); + } + + /** + * --RU-- + * Сделать запрос к WMI + * @param string $query + * @return array + * @throws WindowsException + */ + public static function WMIC($query){ + // more убирает лишние байты возле символа переноса строки, что значительно упрощает парсинг + // cp866 не должно конфликтовать с more + $data = self::cmd(Windows::getSystem32('wbem\\wmic.exe') . ' :query /Format:List | ' . Windows::getSystem32('more.com'), ['query' => $query], 'cp866'); + + $reg = '([^\n=]+)=([^\n\r]+)'; + $regex = Regex::of($reg, Regex::CASE_INSENSITIVE + Regex::MULTILINE)->with($data); + + $return = []; + $key = 0; + while ($regex->find()) { + $k = $regex->group(1); + $v = $regex->group(2); + if (isset($return[$key][$k])) $key++; + $return[$key][$k] = $v; + } + // Не возвращает [0], т.к. может быть несколько устройств, напирмер, sounddev get + return $return; + } + + /** + * --RU-- + * Выполнить скрипт PowerShell + * @param string $query + * @param array $params параметры для замены + * @param bool $wait ожидать окончания + * @return string + * @throws WindowsException + */ + public static function PowerShell($query, $params = [], $wait = true){ + $query = "[Console]::OutputEncoding = [System.Text.Encoding]::UTF8\n" . $query; + $source = Prepare::Query($query, $params); + $command = 'Invoke-Expression ([System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String(\'' . base64_encode($source) . '\')))'; + $psPath = Windows::getSysNative('WindowsPowerShell\\v1.0\\powershell.exe'); + return self::Exec([$psPath, '-inputformat', 'none', '-command', $command], $wait, 'utf-8'); + } + + /** + * --RU-- + * Выполнить скрипт vbScript (должен располагаться в одну строку) + * @param string $query + * @param string $params + * @return string + * @throws WindowsException + * @deprecated + */ + public static function vbScript($query, $params = []){ + $command = Prepare::Query($query, $params); + + $cmd = new Prepare(Windows::getSystem32('mshta.exe') . ' vbscript:Execute(":query(window.close)")'); + $cmd->addStringQuotes = false; + $cmd->quotesPolicy = 2; + return self::cmd($cmd->getQuery(['query' => $command])); + } +} \ No newline at end of file diff --git a/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/Wlan.php b/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/Wlan.php new file mode 100644 index 0000000..bda9070 --- /dev/null +++ b/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/Wlan.php @@ -0,0 +1,59 @@ +with($cmd); + while ($regexp->find()){ + $k = trim($regexp->group(1)); + $v = trim($regexp->group(2)); + if(isset($interfaces[$i][$k]))$i++; + + $interfaces[$i][$k] = $v; + } + + foreach($interfaces as $k=>$interface){ + $interfaces[$k] = new wlanInterface($interface); + } + + return $interfaces; + } + + /** + * Есть ли оборудование для работы с беспроводными сетями + * @return boolean + */ + public static function isSupported() : bool { + return !str::contains(WSH::cmd('netsh wlan show interfaces'), 'is not running'); + } + + /** + * Получить используемый беспроводной интерфейс (идёт первый в списке интерфейсов) + * @return wlanInterface + */ + public static function getMainInterface() : wlanInterface { + return self::getInterfaces()[0]; + } + +} \ No newline at end of file diff --git a/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/api/CSharp.php b/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/api/CSharp.php new file mode 100644 index 0000000..1bd6f04 --- /dev/null +++ b/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/api/CSharp.php @@ -0,0 +1,42 @@ +source = $source; + } + + /** + * Вызов метода + * @param string $class + * @param string $method + * @param array $args + * @return string + */ + public function call(string $class, string $method, array $args = []) : string { + $argString = []; + foreach ($args as $arg) { + $argString[] = var_export($arg, true); + } + $argString = implode(", ", $argString); + + // Если использовать CSharpVersion3, там нет поддержки dynamic, CSharp по умолчанию использует последнюю доступную версию + $ps = "Add-Type -Language CSharp -TypeDefinition @\"" . $this->source . "\n\"@\n" . + "[$class]::$method($argString)"; + + return WSH::PowerShell($ps); + } +} \ No newline at end of file diff --git a/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/api/Dll.php b/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/api/Dll.php new file mode 100644 index 0000000..30991eb --- /dev/null +++ b/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/api/Dll.php @@ -0,0 +1,50 @@ +libName = $libName; + } + + public function createMethod($methodName, $argsString, $source){ + + } + + public function __call(string $method, array $args = []){ + $arguments = $args[0] ?? []; + $returnType = $args[1] ?? 'dynamic'; + $arguments = !is_array($arguments) ? [$arguments] : $arguments ; + $argString = []; + + foreach ($arguments as $i => $arg) { + $type = gettype($arg); + $type = ($type == 'integer') ? 'int' : $type ; + $argString[] = $type . ' arg' . $i; + } + + $argString = implode(", ", $argString); + $class = $this->genClassCode($method, $argString, $returnType); + $cs = new CSharp($class); + return $cs->call('DNSharp', $method, $arguments); + } + + protected function genClassCode($method, $argString, $returnType = 'dynamic'){ + return ' + using System; + using System.Runtime.InteropServices; + using System.Text; + public class DNSharp { + [DllImport("'. $this->libName . '",CharSet=CharSet.Unicode)] + public static extern '. $returnType . ' '. $method . '(' . $argString . '); + }'; + } +} \ No newline at end of file diff --git a/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/result/abstractItem.php b/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/result/abstractItem.php new file mode 100644 index 0000000..2182603 --- /dev/null +++ b/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/result/abstractItem.php @@ -0,0 +1,16 @@ +{$key}; + } + return $array; + } +} \ No newline at end of file diff --git a/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/result/abstractResult.php b/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/result/abstractResult.php new file mode 100644 index 0000000..e89d51b --- /dev/null +++ b/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/result/abstractResult.php @@ -0,0 +1,51 @@ +data[$key] = $value; + } + + public function first(){ + return array_values($this->data)[0]; + } + + public function rewind(){ + reset($this->data); + $this->next(); + } + + public function length(){ + return sizeof($this->data); + } + + public function current(){ + return current($this->data); + } + + public function key(){ + return key($this->data); + } + + public function next(){ + return next($this->data); + } + + public function valid(){ + $key = key($this->data); + return ($key !== NULL && $key !== FALSE); + } + + public function toArray(){ + return $this->data; + } +} \ No newline at end of file diff --git a/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/result/comItem.php b/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/result/comItem.php new file mode 100644 index 0000000..8043a3e --- /dev/null +++ b/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/result/comItem.php @@ -0,0 +1,75 @@ +port = $port; + $this->params = $params; + } + + /** + * Port + * --RU-- + * Порт + * @return string + */ + public function getPort(){ + return $this->port; + } + + /** + * Port params + * --RU-- + * Параметры + * @return array + */ + public function getParams(){ + return $this->params; + } + + /** + * --RU-- + * Подключиться к порту + * @param string $mode='r' + * @return php\io\MiscStream + * @throws IOException + */ + public function connect($mode = 'r'){ + return MiscStream::of($this->port); + } + + /** + * --RU-- + * Установить скорость порта (бод) + * @param int $baud + * @throws WindowsException + */ + public function setBaud($baud){ + return WSH::cmd('mode ' . $this->port . ' baud=' . $baud); + } + + public function __toString(){ + return (string) $this->port; + } + +} \ No newline at end of file diff --git a/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/result/lanAdapter.php b/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/result/lanAdapter.php new file mode 100644 index 0000000..af70062 --- /dev/null +++ b/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/result/lanAdapter.php @@ -0,0 +1,110 @@ +name = $name; + $this->params = $params; + + $this->device = $params['Description'] ?? null; + $this->ipv4 = isset($params['IPv4 Address']) ? str_replace(['%5','(Preferred)'], '', $params['IPv4 Address']) : null; + $this->ipv6 = explode('%', str_replace(['(Preferred)'], '', ($params['IPv6 Address'] ?? $params['Link-local IPv6 Address'] ?? null)))[0]; + + $this->mac = str_replace('-', ':', $params['Physical Address'] ?? null); + } + + /** + * Получить имя адаптера + */ + public function getName() : string { + return $this->name; + } + + /** + * Получить параметры адаптера + */ + public function getParams() : array { + return $this->params; + } + + /** + * Получить описание устройства + */ + public function getDevice() : string { + return $this->device; + } + + /** + * Получить mac адрес + */ + public function getMac() : string { + return $this->mac; + } + + /** + * Получить IPv4 адрес + */ + public function getIPv4() : string { + return $this->ipv4; + } + + /** + * Получить IPv6 адрес + */ + public function getIPv6() : string { + return $this->ipv6; + } + + /** + * Доступна ли сеть на данном адаптере + */ + public function isNetworkEnabled() : bool { + return $this->params['NetEnabled'] ?? false; + } + + /** + * Подключен ли сетевой кабель + */ + public function isConnected() : bool { + return str::contains(WSH::cmd('netsh interface show interface name=":adapter"', ['adapter' => $this->name]), 'Connected'); + } + + /** + * Включен ли адаптер + */ + public function isEnabled() : bool { + return str::contains(WSH::cmd('netsh interface show interface name=":adapter"', ['adapter' => $this->name]), 'Enabled'); + } + +/******************/ + /** + * Отключить адаптер (нужны права администратора) + */ + public function disable() : bool { + return strlen(WSH::cmd('netsh interface set interface name=":adapter" admin=disabled', ['adapter' => $this->name])) == 0; + } + + /** + * Включить интерфейс (нужны права администратора) + */ + public function enable() : bool { + return strlen(WSH::cmd('netsh interface set interface name=":adapter" admin=enabled', ['adapter' => $this->name])) == 0; + } +} \ No newline at end of file diff --git a/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/result/registryItem.php b/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/result/registryItem.php new file mode 100644 index 0000000..1cdc788 --- /dev/null +++ b/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/result/registryItem.php @@ -0,0 +1,72 @@ +key = $key; + $this->value = $value; + $this->type = $type; + } + + /** + * Type. + * --RU-- + * Тип + * @return string + */ + public function getType(){ + return $this->type; + } + + /** + * Key name. + * --RU-- + * Название ключа. + * @return string + */ + public function getKey(){ + return $this->key; + } + + /** + * Value. + * --RU-- + * Значение. + * @return string + */ + public function getValue(){ + return $this->value; + } + + public function __toString(){ + return (string) $this->value; + } + +} \ No newline at end of file diff --git a/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/result/registryResult.php b/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/result/registryResult.php new file mode 100644 index 0000000..f7d81af --- /dev/null +++ b/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/result/registryResult.php @@ -0,0 +1,45 @@ +path = $path; + } + + public function addData($key, $type, $value){ + $this->data[] = new registryItem($key, $type, $value); + } + + public function toArray(){ + return [$this->path => $this->data]; + } + + /** + * Get path + * --RU-- + * Получить путь + * @return string + */ + public function getPath(){ + return $this->path; + } + + /** + * Вернуть класс Registry для текущего пути + * @return Registry + */ + public function registry(){ + return new Registry($this->path); + } +} \ No newline at end of file diff --git a/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/result/startupItem.php b/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/result/startupItem.php new file mode 100644 index 0000000..bec9e47 --- /dev/null +++ b/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/result/startupItem.php @@ -0,0 +1,169 @@ +title = $title; + $this->location = $location; + $this->command = $command; + + $file = $this->getFileFromCommand($command); + $this->file = realpath(Windows::expandEnv($file)); + + if($location == 'Startup'){ + $this->shortcut = Startup::getUserStartupDirectory() . '\\' . $this->command; + $this->file = Windows::getShortcutTarget($this->shortcut); + } elseif($location == 'Common Startup'){ + $this->shortcut = Startup::getCommonStartupDirectory() . '\\' . $this->title . '.lnk'; + } elseif(str::startsWith($location, 'HK')){ + $this->shortcut = $location; + $this->location = 'Registry'; + } + + $this->forAllUsers = $this->isForAllUsers(); + } + + /** + * Автозагрузка для всех пользователей + * @return bool + */ + public function isForAllUsers(){ + return $this->location == 'Common Startup' || + ($this->location == 'Registry' && str::startsWith($this->shortcut, 'HKEY_LOCAL_MACHINE')); + + } + + private function getFileFromCommand($command){ + $file = null; + if(str::contains($command, '"') and str::sub($command, 0, 1) == '"'){ + $file = str::sub($command, 1, str::pos($command, '"', 1)); + } elseif($reg = Regex::of("\\\\([^\\s\\\\]+\\.[^\\s\\\\]+)(\\s[\\S]+)+", Regex::CASE_INSENSITIVE)->with($command) and $reg->find()){ + $file = $reg->group(1); + } else { + $file = $command; + } + + return $file; + } + + /** + * --RU-- + * Удалить объект из автозагрузки + * @return bool + */ + public function delete(){ + if($this->location == 'Registry'){ + try{ + Registry::of($this->shortcut)->deleteKey($this->title); + return true; + } catch (WindowsException $e){ + return false; + } + } else { + return fs::delete($this->shortcut); + } + } + + /** + * --RU-- + * Заголовок + * @return string + */ + public function getTitle(){ + return $this->title; + } + + /** + * --RU-- + * Команда для запуска + * @return string + */ + public function getCommand(){ + return $this->command; + } + + /** + * --RU-- + * Путь к исполняемому файлу + * @return string + */ + public function getFile(){ + return $this->file; + } + + /** + * --RU-- + * Путь к ярлыку для запуска + * @return string + */ + public function getShortcut(){ + return $this->shortcut; + } + + /** + * --RU-- + * Расположение записи для запуска + * @return string + */ + public function getLocation(){ + return $this->location; + } +} \ No newline at end of file diff --git a/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/result/taskItem.php b/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/result/taskItem.php new file mode 100644 index 0000000..5859d43 --- /dev/null +++ b/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/result/taskItem.php @@ -0,0 +1,110 @@ +name = $name; + $this->pid = $pid; + $this->sessionName = $sessionName; + $this->sessionNumber = $sessionNumber; + $this->memory = intval(str_replace([" ", ' ', 'K', 'M'], ['', '', '000', '000000'], $memory)); + $this->status = $status; + $this->user = ($user == 'N/A') ? null : $user; + + $time = explode(':', $cpuTime); + //var_dump([$cpuTime => $time]); + $this->cpuTime = ($time[0] * 60 * 60) + ($time[1] * 60) + ($time[2]); + //$this->cpuTime = [$cpuTime => $time]; + $this->title = ($title == 'N/A') ? null : $title; + } + + /** + * Завершить процесс + * @throws WindowsException + */ + public function kill(){ + return WSH::cmd('taskkill /F /PID ":process"', ['process' => $this->pid]); + } +} \ No newline at end of file diff --git a/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/result/taskResult.php b/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/result/taskResult.php new file mode 100644 index 0000000..24034a0 --- /dev/null +++ b/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/result/taskResult.php @@ -0,0 +1,40 @@ +data[] = new taskItem( + $params[0][1], + $params[1][1], + $params[2][1], + $params[3][1], + $params[4][1], + $params[5][1], + $params[6][1], + $params[7][1], + $params[8][1] + ); + } + + /** + * --RU-- + * Завершить процессы + * @throws WindowsException + */ + public function kill(){ + foreach($this->data as $k => $v){ + try{ + $v->kill(); + }catch(WindowsScriptHost $e){ + + } + unset($this->data[$k]); + } + } +} \ No newline at end of file diff --git a/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/result/wlanInterface.php b/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/result/wlanInterface.php new file mode 100644 index 0000000..75fe03b --- /dev/null +++ b/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/result/wlanInterface.php @@ -0,0 +1,257 @@ +name = $params['Name'] ?? null; + $this->description = $params['Description'] ?? null; + $this->mac = $params['Physical address'] ?? UXApplication::getMacAddress(); + } + + /** + * Получить имя интерфейса + */ + public function getName() : string { + return $this->name; + } + + /** + * Получить описание интерфейса + */ + public function getDescription() : string { + return $this->description; + } + + /** + * Получить mac-адрес + */ + public function getMac() : string { + return $this->mac; + } + + /** + * Получить текущий профиль (обычно совпадает с именем подключённой сети) + */ + public function getProfile() : string { + $params = $this->getParams(); + return $params['Profile'] ?? null ; + } + + /** + * Получить пароль текущего профиля + */ + public function getPassword() : string { + $temp = Windows::getTemp(); + + $profile = WSH::cmd('netsh wlan show profile name=":name" interface=":interface" key=clear', [ + 'name' => $this->getProfile(), + 'interface' => $this->name + ]); + + $regexp = Regex::of('\n([^:\n]+)\s+:([^\n]+)', Regex::MULTILINE)->with($profile); + while ($regexp->find()){ + $k = str::lower(trim($regexp->group(1))); + $v = trim($regexp->group(2)); + + // win7 fix: между словами key и content какой-то знак, не являющийся пробелом, поэтому определяем ключ таким образом + if(str::contains($k, 'key') && str::contains($k, 'content')) return $v; + } + return false; + } + + /** + * Перезагрузить интерфейс (нужны права администратора) + */ + public function reload(){ + $this->disable(); + $this->enable(); + } + + /** + * Отключить интерфейс (нужны права администратора) + */ + public function disable(){ + WSH::cmd('netsh interface set interface name=":interface" admin=disabled', ['interface' => $this->name]); + } + + /** + * Включить интерфейс (нужны права администратора) + */ + public function enable(){ + WSH::cmd('netsh interface set interface name=":interface" admin=enabled', ['interface' => $this->name]); + } + + /** + * Отключиться от сети + */ + public function disconnect(){ + WSH::cmd('netsh wlan disconnect interface=":interface"', ['interface' => $this->name]); + } + + /** + * Переподключиться к текущей сети + */ + public function reconnect(){ + $current = $this->getProfile(); + $this->disconnect(); + + if(!is_null($current)){ + $this->connect($current, false); + } + } + + /** + * Подключиться к сети + * @param string $ssid SSID сети + * @param mixed $password Пароль длиной минимум 8 символов, или NULL, если пароль не нужен, или false, чтоб использовать сохранённые в системе настройки + * @throws WindowsException + */ + public function connect($ssid, $password = false) : bool { + if($password !== false){ + // Сначала удалим профиль, если он существует + WSH::cmd('netsh wlan delete profile name=":ssid" interface=":interface"', ['interface' => $this->name, 'ssid' => $ssid]); + + // Создаём файл профиля с авторизационными данными + $file = $this->createConfig($ssid, $password); + $profile = WSH::cmd('netsh wlan add profile filename=":file"', ['file' => $file]); + unlink($file); + + if(str::contains($profile, 'error')){ + throw new WindowsException($profile); + } + } + + // Подключаемся к сети используя файл профиля + $connect = WSH::cmd('netsh wlan connect name=":ssid" interface=":interface"', ['interface' => $this->name, 'ssid' => $ssid]); + if(str::contains($connect, 'Reason:')){ + throw new WindowsException(explode('Reason: ', $connect)[1]); + } + + if(str::contains($connect, 'successfully')){ + $status = null; + while(!str::contains($status, 'connected')){ + $status = $this->getState(); + usleep(100); + } + + return $status == 'connected'; + } else { + return false; + } + } + + /** + * Получить состояние подключения сети + * @return string disconnected, authenticating, connected, connecting + */ + public function getState() : string { + $params = $this->getParams(); + return $params['State'] ?? 'disconnected'; + } + + /** + * Получить список параметров текущего интерфейса + */ + public function getParams() : array { + $params = []; + $data = WSH::cmd('netsh wlan show interface name=":interface"', ['interface' => $this->name]); + $regexp = Regex::of('\s*([^:]+)\s+:([^\n]+)\n', Regex::MULTILINE)->with($data); + while ($regexp->find()){ + $k = trim($regexp->group(1)); + $v = trim($regexp->group(2)); + $params[$k] = $v; + } + + return $params; + } + + /** + * Получить список обнаруженных Wi-Fi сетей + * @return array + */ + public function getNetworks() : array { + $networks = []; + $i = -1; + $cmd = WSH::cmd('netsh wlan show networks interface=":interface" mode=bssid', ['interface' => $this->name]); + $cmd = explode('currently visible.', $cmd)[1]; + + $regex = Regex::of('([^:]+):([^\n]+)\n', Regex::MULTILINE)->with($cmd); + while ($regex->find()){ + $k = trim($regex->group(1)); + $v = trim($regex->group(2)); + + if(str::startsWith($k, 'SSID')){ + $k = 'SSID'; + $i++; + } + elseif(str::startsWith($k, 'BSSID')) $k = 'BSSID'; + elseif($k == 'Signal') $v = intval(str::replace($v, '%', '')); + + $networks[$i][$k] = $v; + } + + return $networks; + } + + /** + * Генерация файла профиля (для авторизации в сети WiFi) + * @param string $ssid + * @param string $password=null + * @return string Путь к файлу + * @todo Авторизация с WEP + */ + protected function createConfig(string $ssid, string $password = null) : string { + $xml = ' + + '. $ssid .' + + + '. $ssid .' + + + ESS + manual + + '. (is_null($password) ? + ' + open + none + false + ' : + ' + WPA2PSK + AES + false + + + passPhrase + false + ' . $password . ' + + '). + ' + + + false + + '; + $tmpFile = Windows::expandEnv('%TEMP%/wlan_connect_' . str::uuid() . '.xml'); + file_put_contents($tmpFile, $xml); + return $tmpFile; + } +} \ No newline at end of file diff --git a/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/result/wshResult.php b/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/result/wshResult.php new file mode 100644 index 0000000..b268886 --- /dev/null +++ b/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/result/wshResult.php @@ -0,0 +1,40 @@ +process = $process; + } + + public function setCharset($charset){ + $this->charset = $charset; + return $this; + } + + public function getOutput(){ + return $this->decodeStream($this->process->getInput()); + } + + public function getError(){ + return $this->decodeStream($this->process->getError()); + } + + private function decodeStream($stream){ + $data = $stream->readFully(); + if($this->charset != null){ + $data = str::decode($data, $this->charset); + } + + return str::trim($data); + } +} \ No newline at end of file diff --git a/src/bundle/windows/Lan.php b/src/bundle/windows/Lan.php index 93c624c..159d4d7 100644 --- a/src/bundle/windows/Lan.php +++ b/src/bundle/windows/Lan.php @@ -71,16 +71,20 @@ public static function getAdapters() : array { * Получить используемый по умолчанию адаптер * @return lanAdapter */ - public static function getActiveAdapter() : lanAdapter { + public static function getActiveAdapter() : lanAdapter{ $adapters = self::getAdapters(); foreach ($adapters as $adapter) { - if($adapter->isConnected()){ + $device = $adapter->getDevice(); + if(str::contains($device,'Radmin') or str::contains($device,'Hamachi') or str::contains($device,'ZeroTier')) + continue; + + if($adapter->isConnected()) return $adapter; - } + } return null; - } + } /** * Есть ли оборудование для работы с проводными сетями From 21be11abf91d879e22f054b81407cc31b1e005e3 Mon Sep 17 00:00:00 2001 From: queinu <76971397+ZzEdovec@users.noreply.github.com> Date: Tue, 19 Sep 2023 21:36:11 +0300 Subject: [PATCH 8/9] Fix README --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 57b4ba8..366404e 100644 --- a/README.md +++ b/README.md @@ -8,11 +8,10 @@ ## Changelog ``` ---- 2.4 --- +--- 2.5 --- [Fix] Windows::getActiveAdapter() - Больше не выдаёт как активные адаптеры виртуальных локальных сетей [Fix] Bundle для DevelNext снова корректно собирается - --- 2.4 --- [Change] Windows::createShortcut() - Теперь поддерживает кастомный путь до иконки и аргументы командной строки [Change] Автором бандла поставил себя, т.к. TsSaltan явно забросил пакет From 094e8bece11089d0f6633eee6af3362b90662db4 Mon Sep 17 00:00:00 2001 From: queinu Date: Mon, 2 Oct 2023 00:35:38 +0300 Subject: [PATCH 9/9] 2.6 --- README.md | 5 ++++- package.php.yml | 4 ++-- .../bundle/windows/Lan.php | 7 ++++++- src/bundle/windows/Lan.php | 7 ++++++- 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 366404e..8e13801 100644 --- a/README.md +++ b/README.md @@ -8,8 +8,11 @@ ## Changelog ``` +--- 2.6 --- +[Change] Lan::getAdapters() - теперь выдаёт и Wlan адаптеры + --- 2.5 --- -[Fix] Windows::getActiveAdapter() - Больше не выдаёт как активные адаптеры виртуальных локальных сетей +[Fix] Lan::getActiveAdapter() - Больше не выдаёт как активные адаптеры виртуальных локальных сетей [Fix] Bundle для DevelNext снова корректно собирается --- 2.4 --- diff --git a/package.php.yml b/package.php.yml index 8285403..3ad12d5 100644 --- a/package.php.yml +++ b/package.php.yml @@ -1,5 +1,5 @@ name: windows -version: 2.5 +version: 2.6 description: Пакет для взаимодействия с API Windows plugins: @@ -36,7 +36,7 @@ config: - /gradlew.** develnext-bundle: - version: 2.5 + version: 2.6 name: windows description: Плагин для взаимодействия с API Windows author: queinu diff --git a/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/Lan.php b/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/Lan.php index 159d4d7..67053ee 100644 --- a/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/Lan.php +++ b/src-bundle/vendor/develnext.bundle.windows.WindowsBundle/bundle/windows/Lan.php @@ -34,7 +34,12 @@ public static function getAdapters() : array { $line = $scanner->current(); $regAdapter = Regex::of('Ethernet adapter ([^\n\r]+):', Regex::MULTILINE)->with($line); - if($regAdapter->find()){ + $isfind = $regAdapter->find(); + + if (!($isfind)){ + $regAdapter = Regex::of('Wireless LAN adapter ([^\n\r]+):', Regex::MULTILINE)->with($line); + $isfind = $regAdapter->find();} + if($isfind){ $adapterName = $regAdapter->group(1); } elseif($adapterName != false){ $regParam = Regex::of('\s+([^\.]+)[\.\s]+:([^\n]+)', Regex::MULTILINE)->with($line); diff --git a/src/bundle/windows/Lan.php b/src/bundle/windows/Lan.php index 159d4d7..67053ee 100644 --- a/src/bundle/windows/Lan.php +++ b/src/bundle/windows/Lan.php @@ -34,7 +34,12 @@ public static function getAdapters() : array { $line = $scanner->current(); $regAdapter = Regex::of('Ethernet adapter ([^\n\r]+):', Regex::MULTILINE)->with($line); - if($regAdapter->find()){ + $isfind = $regAdapter->find(); + + if (!($isfind)){ + $regAdapter = Regex::of('Wireless LAN adapter ([^\n\r]+):', Regex::MULTILINE)->with($line); + $isfind = $regAdapter->find();} + if($isfind){ $adapterName = $regAdapter->group(1); } elseif($adapterName != false){ $regParam = Regex::of('\s+([^\.]+)[\.\s]+:([^\n]+)', Regex::MULTILINE)->with($line);