// 创建交互式服务安装完成以后,启动UI的程序
BOOL LaunchApplication(LPCTSTR lpszExecute, LPCTSTR lpszCmdLine)
{
BOOL bRet = FALSE;
HANDLE hUserTokenDup = NULL;
LPVOID pEnv = NULL;
do
{
DWORD dwSessionId = 0;
if (0xFFFFFFFF == (dwSessionId = WTSGetActiveConsoleSessionId())) { PRINT(ERR, GetLastError(), TEXT("WTSGetActiveConsoleSessionId")); break; }
#define DEFAULT_WINLOGON_APPLICATION (TEXT("winlogon.exe"))
#define DEFAULT_DEFAULT_APPLICATION (TEXT("explorer.exe")) // 需要以explorer.exe进程的Token来创建Env环境块,否则以服务启动的App在打开文件路径的方面可能会遇到一些问题,比如最常见的是会访问C:\Windows\System32\config\systemprofile导致访问了错误的位置。
DWORD dwWinlogonPid = 0;
if (0xFFFFFFFF == (dwWinlogonPid = GetProcessID(DEFAULT_WINLOGON_APPLICATION, dwSessionId))) { PRINT(ERR, TEXT("Get Process ID Failed!")); break; }
if (NULL == (hUserTokenDup = GetProcessTokenDup(dwWinlogonPid))) { PRINT(ERR, TEXT("Get Process Duplicate Token Failed!")); break; }
if (!AdjustProcessTokenDup(hUserTokenDup, dwSessionId)) { PRINT(ERR, TEXT("Adjust Process Token Failed!")); break; }
HANDLE hDefaultTokenDup2 = NULL;
DWORD dwDefaultPid = 0;
if (0xFFFFFFFF == (dwDefaultPid = GetProcessID(DEFAULT_DEFAULT_APPLICATION, dwSessionId))) { PRINT(ERR, TEXT("Get Process ID Failed!")); break; }
if (NULL == (hDefaultTokenDup2 = GetProcessTokenDup(dwDefaultPid))) { PRINT(ERR, TEXT("Get Process Duplicate Token Failed!")); break; }
// Create Environment Block
DWORD dwCreateFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE;
if (CreateEnvironmentBlock(&pEnv, hDefaultTokenDup2, TRUE)) { dwCreateFlags |= CREATE_UNICODE_ENVIRONMENT; }
// Launch Process In The Client's Logon Session
TCHAR szExecute[MAX_PATH] = { 0 };
GetModuleFileName(NULL, szExecute, _countof(szExecute));
_tcsrchr(szExecute, TEXT('\\'))[1] = TEXT('\0');
StringCbCat(szExecute, _countof(szExecute) - _tcslen(szExecute), lpszExecute);
TCHAR szCmdLine[MAX_PATH] = { 0 };
if (NULL != lpszCmdLine) { StringCchPrintf(szCmdLine, _countof(szCmdLine), TEXT("%s"), lpszCmdLine); }
#define DEFAULT_DESKTOP_WINSTA0 (TEXT("winsta0\\default"))
PROCESS_INFORMATION pi = { 0 };
STARTUPINFO si = { sizeof(STARTUPINFO) };
si.lpDesktop = DEFAULT_DESKTOP_WINSTA0;
if (!CreateProcessAsUser(hUserTokenDup,
szExecute,
szCmdLine,
NULL,
NULL,
FALSE,
dwCreateFlags,
pEnv,
NULL,
&si,
&pi))
{
PRINT(ERR, GetLastError(), TEXT("CreateProcessAsUser"));
break;
}
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
// Completed
bRet = TRUE;
} while (FALSE);
if (NULL != pEnv) { DestroyEnvironmentBlock(pEnv); pEnv = NULL; }
if (NULL != hUserTokenDup) { CloseHandle(hUserTokenDup); hUserTokenDup = NULL; }
return bRet;
}
DWORD GetProcessID(LPCTSTR lpszProcessName, DWORD dwSessionId)
{
DWORD dwProcessId = 0xFFFFFFFF;
HANDLE hSnapshot = INVALID_HANDLE_VALUE;
do
{
_ASSERT(NULL != lpszProcessName);
if (NULL == lpszProcessName) { break; }
hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (INVALID_HANDLE_VALUE == hSnapshot) { PRINT(ERR, GetLastError(), TEXT("CreateToolhelp32Snapshot")); break; }
PROCESSENTRY32 pe32 = { sizeof(PROCESSENTRY32) };
if (!Process32First(hSnapshot, &pe32)) { PRINT(ERR, GetLastError(), TEXT("Process32First")); break; }
do
{
if (0 == _tcsicmp(pe32.szExeFile, lpszProcessName))
{
DWORD dwProcessSessionId = 0;
if (ProcessIdToSessionId(pe32.th32ProcessID, &dwProcessSessionId)
&& (dwSessionId == dwProcessSessionId))
{
dwProcessId = pe32.th32ProcessID;
break;
}
}
} while (Process32Next(hSnapshot, &pe32));
// Completed
} while (FALSE);
if (INVALID_HANDLE_VALUE != hSnapshot) { CloseHandle(hSnapshot); hSnapshot = INVALID_HANDLE_VALUE; }
return dwProcessId;
}
HANDLE GetProcessTokenDup(DWORD dwProcessId)
{
HANDLE hProcess = NULL;
HANDLE hToken = NULL;
HANDLE hTokenDup = NULL;
do
{
if (NULL == (hProcess = OpenProcess(MAXIMUM_ALLOWED, FALSE, dwProcessId))) { PRINT(ERR, GetLastError(), TEXT("OpenProcess")); break; }
if (!OpenProcessToken(hProcess,
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY | TOKEN_ADJUST_SESSIONID | TOKEN_READ | TOKEN_WRITE,
&hToken))
{
PRINT(ERR, GetLastError(), TEXT("OpenProcessToken"));
break;
}
if (!DuplicateTokenEx(hToken, MAXIMUM_ALLOWED, NULL, SecurityIdentification, TokenPrimary, &hTokenDup)) { PRINT(ERR, GetLastError(), TEXT("DuplicateTokenEx")); break; }
// Completed
} while (FALSE);
if (NULL != hToken) { CloseHandle(hToken); hToken = NULL; }
if (NULL != hProcess) { CloseHandle(hProcess); hProcess = NULL; }
return hTokenDup;
}
BOOL AdjustProcessTokenDup(HANDLE hTokenDup, DWORD dwSessionId)
{
BOOL bRet = FALSE;
do
{
if (NULL == hTokenDup) { break; }
// Adjust Token Privileges
if(!SetTokenInformation(hTokenDup, TokenSessionId, (LPVOID)&dwSessionId, sizeof(dwSessionId))) { PRINT(ERR, GetLastError(), TEXT("SetTokenInformation")); break; }
if (!AdjustTokenDupPrivileges(hTokenDup, SE_DEBUG_NAME)) { PRINT(ERR, TEXT("Enable SE_DEBUG_NAME Privilege Failed!")); }
if (!AdjustTokenDupPrivileges(hTokenDup, SE_TCB_NAME)) { PRINT(ERR, TEXT("Enable SE_TCB_NAME Privilege Failed!")); }
if (!AdjustTokenDupPrivileges(hTokenDup, SE_CHANGE_NOTIFY_NAME)) { PRINT(ERR, TEXT("Enable SE_CHANGE_NOTIFY_NAME Privilege Failed!")); }
if (!AdjustTokenDupPrivileges(hTokenDup, SE_INCREASE_QUOTA_NAME)) { PRINT(ERR, TEXT("Enable SE_INCREASE_QUOTA_NAME Privilege Failed!")); }
if (!AdjustTokenDupPrivileges(hTokenDup, SE_ASSIGNPRIMARYTOKEN_NAME)) { PRINT(ERR, TEXT("Enable SE_ASSIGNPRIMARYTOKEN_NAME Privilege Failed!")); }
// Completed
bRet = TRUE;
} while (FALSE);
return bRet;
}
BOOL AdjustTokenDupPrivileges(HANDLE hTokenDup, LPCTSTR lpszPrivileges)
{
BOOL bRet = FALSE;
do
{
LUID luid = { 0 };
if (!LookupPrivilegeValue(NULL, lpszPrivileges, &luid))
{
PRINT(ERR, GetLastError(), TEXT("LookupPrivilegeValue"));
break;
}
TOKEN_PRIVILEGES tp = { 0 };
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
// Adjust token privileges
if (!AdjustTokenPrivileges(hTokenDup, FALSE, &tp, sizeof(tp), NULL, NULL))
{
PRINT(ERR, GetLastError(), TEXT("AdjustTokenPrivileges"));
break;
}
// Completed
bRet = TRUE;
} while (FALSE);
return bRet;
}
void PRINT(DWORD dwLevel, LPCTSTR lpszFormat, ...)
{
do
{
if (!m_bDbgValid) { break; }
TCHAR szText[DEFAULT_PRINTF_BUFFER_LENGTH] = { 0 };
TCHAR szBuffer[DEFAULT_PRINTF_BUFFER_LENGTH] = { 0 };
// Parse Parameter
va_list args;
va_start(args, lpszFormat);
StringCchVPrintf(szBuffer, _countof(szBuffer), lpszFormat, args);
va_end(args);
// Format
FORMAT(szText, _countof(szText), szBuffer);
// Dump
DUMP(dwLevel, szText);
} while (FALSE);
}
void PRINT(DWORD dwLevel, DWORD dwErrorId, LPCTSTR lpszDescription)
{
do
{
if (!m_bDbgValid) { break; }
TCHAR szText[DEFAULT_PRINTF_BUFFER_LENGTH] = { 0 };
// Format
FORMAT(szText, _countof(szText), dwErrorId, lpszDescription);
// Dump
DUMP(dwLevel, szText);
} while (FALSE);
}
void CCommon::FORMAT(LPTSTR lpszText, size_t nLength, LPCTSTR lpszInfo)
{
_ASSERT(NULL != lpszText);
_ASSERT(nLength > 0);
_ASSERT(NULL != lpszInfo);
SYSTEMTIME st = { 0 };
GetLocalTime(&st);
StringCchPrintf(lpszText,
nLength,
TEXT("[%04d-%02d-%02d %02d:%02d:%02d.%03d] - %s\r\n"),
st.wYear,
st.wMonth,
st.wDay,
st.wHour,
st.wMinute,
st.wSecond,
st.wMilliseconds,
lpszInfo);
}
void FORMAT(LPTSTR lpszText, size_t nLength, DWORD dwErrorId, LPCTSTR lpszDescription)
{
_ASSERT(NULL != lpszText);
_ASSERT(nLength > 0);
_ASSERT(NULL != lpszDescription);
SYSTEMTIME st = { 0 };
GetLocalTime(&st);
TCHAR szBuffer[DEFAULT_PRINTF_BUFFER_LENGTH] = { 0 };
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwErrorId, LANG_NEUTRAL, (LPTSTR)szBuffer, _countof(szBuffer), NULL);
StringCchPrintf(lpszText,
nLength,
TEXT("[%04d-%02d-%02d %02d:%02d:%02d.%03d] - Call %s() Failed with Error: %d - %s\r\n"),
st.wYear,
st.wMonth,
st.wDay,
st.wHour,
st.wMinute,
st.wSecond,
st.wMilliseconds,
lpszDescription,
dwErrorId,
szBuffer);
}相关推荐
系统服务用CreateProcessAsUser实现创建界面进程
一、需求 有一个带界面的GUI程序需要一直保持运行状态,但是操作系统有时会自动更新重启,虽然已经通过注册表的方式设置为自启动,但是必须登录操纵系统才能启动,所以需要一种不登录也能让该程序运行的实现方法。 又因为这个程序是GUI程序,无法直接注册为系统服务(系统服务可以开机后不登录也在后台运行),所以考虑通过把一个控制台程序注册成系统服务,然后通过这个控制台程序调用GUI程序。下面代码是控制台程序调用GUI程序的关键逻辑。 经测试,在Win10 和 window...
在服务中用管理员权限创建一个可弹出UI的进程 (转载)
转载:http://blog.youkuaiyun.com/woshinia/article/details/7850295 转载:http://blog.youkuaiyun.com/hurryboylqs/article/details/53004970 转载:http://blog.youkuaiyun.com/fakine/article/details/8537763 HANDLE hToken = NU...
Windows系统图标定制:彻底去除快捷方式箭头与UAC盾牌
在Windows系统管理与个性化定制领域,图标渲染机制是系统界面交互的基础技术之一。系统通过图标覆盖层合成技术,在原始图标上叠加特定标识(如快捷方式箭头、UAC盾牌)来传达文件属性与安全状态。这项技术虽然提升了系统的易用性与安全性提示,但在追求视觉统一和桌面美化的场景下,这些默认覆盖层可能破坏整体设计风格。通过深入理解注册表键值与系统资源调用的原理,用户可以在不破坏系统核心功能的前提下,精准控制图标渲染流程,实现视觉元素的定制化。本文聚焦于通过注册表修改这一安全可逆的方案,详细解析如何去除快捷方式小箭头和U
windows系统服务 启动带有管理员权限的GUI进程
事情是这样的,公司的产品有个守护进程(windows Service)需要启动产品的主程序exe,让主程序它运行为管理员权限(因为主程序会加载一个插件,插件中有列出端口监听的功能,需要由端口查找到进程PID,由进程PID查找进程名或进程镜像路径,这些对于一些特殊进程例如svchost需要有管理员权限才能查到进程名和路径)。windows下的程序是不能在运行时获得管理员权限的,只能在创建进程的时候提升为管理员权限。如果是普通进程运行一个管理员权限程序,可以调用ShellExcute API。双击鼠标运行e..
Inno Setup 进阶:如何通过修改Manifest实现管理员权限自动获取
本文详细解析了Inno Setup安装包获取管理员权限的两种核心方法。针对Windows UAC机制导致的安装中断问题,重点介绍了通过修改脚本参数PrivilegesRequired的官方配置法,以及通过Resource Hacker工具修改SetupLdr.e32引导器清单的进阶技巧,帮助开发者实现安装程序的自动权限提升,提升用户体验与软件专业性。
Windows权限管理:手动添加“管理员取得所有权”右键菜单的完整指南
在Windows系统管理与开发运维中,文件权限控制是核心基础。访问控制列表(ACL)和用户账户控制(UAC)机制共同构成了系统的安全防线,通过所有者(Owner)和权限条目来管理资源访问。其技术价值在于平衡安全性与操作便利性,确保系统文件不被误改,同时允许授权用户进行必要管理。在软件部署、系统迁移、遗留文件处理等应用场景中,管理员常需快速取得文件或文件夹所有权以解决“拒绝访问”问题。本文聚焦于利用takeown和icacls命令行工具,通过直接修改Windows注册表,在右键菜单中安全、高效地集成“管理员取
大家知道如何去掉电脑桌面图标的小盾牌吗
小盾牌是Windows操作系统中文件和文件夹的安全属性的标志,它表示该文件或文件夹受到了系统的保护,对其进行修改需要管理员权限。虽然小盾牌的作用是为了保护文件不被误操作或非法访问,但在某些情况下,用户可能需要对特定文件或文件夹进行修改或删除的操作,此时小盾牌就成为了阻碍。通过本文介绍的几种方法,您可以轻松地去除小盾牌,更便捷地管理和操作自己的电脑。⑤ 在“权限”对话框中,找到自己的用户名,勾选“完全控制”或其他需要的权限。⑧ 在“输入的对象名称”栏目中输入你的用户名,然后点击“检查名称”按钮确认。
Windows安全中心空白界面修复:从服务重置到系统文件修复的完整指南
在Windows操作系统中,系统服务与核心组件是保障安全功能正常运行的基础。当关键服务状态异常或系统文件损坏时,依赖这些组件的安全应用界面可能出现加载失败,表现为空白或卡顿。从技术原理看,Windows安全中心作为UWP应用,其运行依赖于SecurityHealthService、WinDefend等后台服务,以及完整的系统文件与注册表项。一旦服务被禁用、系统映像损坏或组件注册失效,前端界面将无法获取数据。在工程实践中,通过PowerShell命令重启服务、重置应用缓存,或使用DISM和SFC工具修复系统文
Qt应用程序自启动及管理员权限
Windows开机自启动的程序很多,包括系统软件、杀毒软件、一些其他安装软件等等。自启动当然是可以禁止的,通过软件管理或者手动删除对应的注册表中的键值项即可!但是为了系统的服务或者操作上的方便,我们往往需要在开机的时候自启动一些服务。Qt程序也不例外,在发布Qt程序的时候,我们通常也需要开机自启动自己的程序,那么如何实现呢?主要采用修改注册表的方式。 代码比较简单: #define RE...
使用Windbg时遇到的几个典型问题说明
详细讲述在使用Windbg过程中遇到的几个典型问题
保姆级教程:在RV1126开发板上跑通YOLOv5口罩检测(从数据集到摄像头实时识别)
本文提供了一份详细的RV1126开发板部署YOLOv5口罩检测系统的全流程指南,涵盖数据集准备、模型训练优化、边缘部署及实时摄像头识别等关键步骤。重点解析了RV1126开发板的性能特征、YOLOv5模型训练调优技巧、RKNN模型转换量化策略,以及部署过程中的性能优化方案,帮助开发者高效实现边缘端目标检测应用。
Qt Windows应用管理员权限启动:UAC机制、清单配置与QMake实战
在Windows桌面应用开发中,用户账户控制(UAC)机制是保障系统安全的核心设计,它基于最小权限原则,默认以标准用户权限运行程序。其原理是通过可执行文件的清单资源声明权限级别,开发者可配置requestedExecutionLevel字段控制程序行为。这项技术的核心价值在于平衡安全性与功能性,使需要访问系统资源或注册表的工具软件能合法获取权限,避免运行时出现“拒绝访问”错误。典型的应用场景包括安装程序、系统配置工具、服务管理及端口监听应用等。本文聚焦于Qt框架下的具体工程实践,详细解析如何通过QMake构
深入解析高通Adreno DPU的DRM/KMS驱动架构与硬件协同设计
本文深入解析了高通Adreno DPU的DRM/KMS驱动架构与硬件协同设计,详细介绍了Adreno DPU的核心模块组成、显示接口设计以及DRM驱动架构。通过实际案例和调试技巧,帮助开发者理解硬件与软件的协同工作原理,优化显示性能并解决常见问题。
Windows 系统 OpenClaw 执行策略报错及管理员权限设置深度解析与实操指南
本文深入解析Windows系统中OpenClaw工具常见的执行策略报错和管理员权限问题,提供系统化解决方案。首先剖析PowerShell执行策略机制(Restricted/RemoteSigned等)和UAC权限控制原理,指出OpenClaw运行时的典型错误场景。针对执行策略问题,详解临时/永久修改策略、数字签名脚本等方法;针对权限问题,提供以管理员身份运行、修改快捷方式、调整UAC设置等方案。
fastjson1.2.83反序列化漏洞
【代码】fastjson1.2.83反序列化漏洞。
BOSS直聘数据采集、解析处理、分析与可视化实战
招聘和求职是一对永恒的话题,不论什么时候都有招聘或者是求职的需求,找不找得到工作可能最主要的就是匹配度是否高,待遇福利是否诱人,如今互联网的快速发展,找工作的方式早已发生了翻天覆地的变化,从以往的面对面形式的招聘简历投递和面试方式,变成了电子简历网络直接投递、远程面试录用的形式,便捷性大大提升,需求的快速增长,促生了一批互联网招聘类的企业,诸如:51job、BOSS直聘、拉勾网等等,为了能...
管理员以标准权限运行时
http://blog.youkuaiyun.com/bingghost/article/details/7592043 在XP时代,大多数用户都用一个管理员(administrator)帐号来登录Windows 利用这个账户,用户几乎能没有任何限制的访问重要的系统资源,因为该账户被授予了很高的权限,一旦用这个帐号登录了xp以及xp之前的操作系统,Windows操作系统就会创建一个安全令牌
解决开着代理情况下pip或魔搭下载失败
最近由于经常配环境导致,老是要来回切clash关掉代理,非常的不方便如下面的,魔搭模型下载失败如下面的,pip换源后下载失败。
【第36节】windows原理:权限管理和内存管理
遇到这种情况,我们可以用HeapCreate函数创建一个独立的内存堆,等使用完之后,直接用HeapDestroy函数,就能把这个内存堆里的所有内存都释放掉。而这个进程创建的线程,默认和进程的权限相同。VirtualAllocEx、ReadProcessMemory与WriteProcessMemory三个函数,可实现跨进程的内存分配、读取、写入等操作,是很多安全技术的基础函数,后续学习远程线程注入时,会详细讲解这些函数的用法。所以,在自己的进程里,是访问不了其他进程内存的,尽管不同进程的地址看上去很像。
RK3588开发板NPU加速YOLO目标检测完整实战教程
🚀 RK3588开发板NPU加速YOLO目标检测实战 本文详细介绍了如何在瑞芯微RK3588开发板上利用内置NPU加速YOLO目标检测的全流程: 1️⃣ 环境准备:配置RKNPU2 SDK和aarch64交叉编译器,搭建Ubuntu开发环境 2️⃣ 模型转换:从ONNX格式转换为专用RKNN模型,适配NPU硬件特性 3️⃣ 性能对比:NPU推理将处理时间从833ms降至50ms以下,帧率提升16倍 4️⃣ 实战应用:支持图像/视频流/摄像头实时检测,提供完整编译和运行命令 关键优势:专为边缘AI设计的6T
基于半经验模型的熔断器燃弧过程计算与分析
本文针对1200V/1250A熔断器建立电弧燃烧半经验模型,通过ANSYS计算弧前时间,结合熔体烧蚀、石英砂烧蚀等物理过程简化建模。模型引入电导率随温度变化函数,利用Matlab拟合实验数据确定参数。分析表明:熔体宽度和节距增加会提高弧压峰值并缩短过零时间;总长固定时,断口数和节距影响较小。弧压峰值需控制在额定电压3倍内,过零时间需满足安全裕度。通过Python代码复现了电弧电导率模型、能量平衡方程及弧压计算,验证了熔体结构尺寸对电弧特性的影响规律。
WIN7下权限问题分析与解答
1.WIN7下运行软件为什么会提示权限不足? 为了降低计算机系统的风险,Windows Vista引入UAC(User Access Control)机制,默认情况下,应用程序都运行在普通用户权限下。Windows 7继承了这一机制。一般应用程序通常都假设以管理员权限运行,在其它操作系统上(如:windows XP)没有UAC的限制,运行程序的权限不会降低。若以管理员账户登录后,安装软件不会
PCIe(二) —— 配置空间
好了,这一篇我们把配置空间的结构,分配和访问,都大概总结了一遍,并且还介绍了它们的头部字段和这些字段的意义,包括BAR和消息路由的原理。之后,如果有时间,我们会继续来探索PCIe的数据报文的结构和传输等等。
如何在windows开启UAC(用户账号控制)的情况下优雅的管理程序的权限申请方案(一)
问题起源: 简单的说就是在windows vista之前的windows系统,只要是管理员(比如Administrator)账户登录系统以后,该账户下的任何程序的启动权限都具有管理员权限;这个权限是很高的(仅仅比System权限低),具有这个权限的程序几乎可以“为所欲为”,试想如果一个病毒程序拥有了这个权限,那它可以做很多的“破坏”活动,这将给系统的安全造成了极大的安全隐患。 U...
VC中sqlite的使用
在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示
LinePhone3.2.7(android端最新版本) sip客户端
SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/
程序以管理员方式运行的原因分析
这是 Windows(从 Vista 系统开始引入 UAC 用户账户控制机制)最重要的安全设计之一。简单来说,系统将权限分为两个等级,绝大多数程序默认在“标准用户权限”下运行,就像在一个受保护的沙箱里,而当程序需要执行“危险”操作时,必须明确请求“管理员权限”。操作系统通过UAC弹窗询问你:“这个程序要动用万能钥匙,你确定要给它吗?” 你的回答必须是谨慎和有判断力的。:现代操作系统的“管理员权限”是一把能打开系统所有大门的。
温度PID控制PLC程序
温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值
4130



被折叠的 条评论
为什么被折叠?



