zl程序教程

您现在的位置是:首页 >  后端

当前栏目

C#项目--穿透Session隔离,服务调用窗体程序(详细)

c#项目服务程序 -- 详细 调用 session
2023-09-27 14:26:32 时间

系列文章


C#项目–业务单据号生成器(定义规则、自动编号、流水号)
本文链接:https://blog.csdn.net/youcheng_ge/article/details/129129787

C#项目–开始日期结束日期范围计算(上周、本周、明年、前年等)
本文链接:https://blog.csdn.net/youcheng_ge/article/details/129040663

C#项目–数据实体类使用
本文链接:https://blog.csdn.net/youcheng_ge/article/details/128816638

C#项目–单据审批流方案
本文链接:https://blog.csdn.net/youcheng_ge/article/details/128972545

C#项目–二维码标签制作及打印(完整版)
本文链接:https://blog.csdn.net/youcheng_ge/article/details/126884228

C#项目–条码管理操作手册
本文链接:https://blog.csdn.net/youcheng_ge/article/details/126589496

C#项目–WebAPI发布和IIS部署,及异常处理
本文链接:https://blog.csdn.net/youcheng_ge/article/details/126539836

C#项目–提高编程效率,代码自动生成
本文链接:https://blog.csdn.net/youcheng_ge/article/details/126890673

C#项目–提高编程效率,Excel数据导入工具
本文链接:https://blog.csdn.net/youcheng_ge/article/details/126427323

C#项目–Windows服务(Service)安装及启停方案
本文链接:https://blog.csdn.net/youcheng_ge/article/details/124053794

C#项目–穿透Session隔离,服务调用外部程序(无窗体界面解决)
本文链接:https://blog.csdn.net/youcheng_ge/article/details/124053033

C#项目–任务计划实现,使用Quartz类
本文链接:https://blog.csdn.net/youcheng_ge/article/details/123667723

C#项目–《周计划管理关于产前准备模块》解决方案20200203
本文链接:https://blog.csdn.net/youcheng_ge/article/details/122919543

C#项目–项目中,源码解析的正则表达式
本文链接:https://blog.csdn.net/youcheng_ge/article/details/118337074

C#项目–如何获取文件版本和MD5值
本文链接:https://blog.csdn.net/youcheng_ge/article/details/112513871

C#项目–如何测试网络是否连通
本文链接:https://blog.csdn.net/youcheng_ge/article/details/110137288


目录

系列文章

前言

一、面临问题

1.1 需求描述

1.2 问题原因

二、解决方案

三、软件开发(源码展示)

四、调用方法

五、运行效果

六、资源链接


前言

我能抽象出整个世界,但是我不能抽象你。 想让你成为私有常量,这样外部函数就无法访问你。 又想让你成为全局常量,这样在我的整个生命周期都可以调用你。 可惜世上没有这样的常量,我也无法定义你,因为你在我心中是那么的具体。

哈喽大家好,本专栏为【项目实战】专栏,区别于【底层库】专栏,本专栏主要介绍项目开发过程中遇到的各种问题,以及解决方案,甚至是我项目开发过程,我觉得可以使用工具实现编程的方案。

本专栏会持续更新,不断完善【项目实战】,大家有任何问题,可以私信我。本专栏之间关联性较弱(文章和文章之间依赖性较弱,没有阅读顺序可以随意阅读),如果您对本专栏感兴趣,欢迎关注,我将带你用最简洁的代码,实现最复杂的功能。

一、面临问题

1.1 需求描述

公司行政小姐姐,提了个需求要求程序开机自运行,无需人工干预。首先获得该需求,两种途径实现:①将程序放入开机启动项目录

②将程序写入服务,通过服务service控制。

最终综合考虑员工电脑权限、安全性,我拍案决定采用第二种方式,如下图所示:

于是产生了新的问题,AutoPlayerService为服务程序,它自动调用外部可执行程序(.exe),调用异常无UI界面。

打个比方说,我在每天8:00自动播放企宣视频。现在视频操作界面打不开了,只能够听见声音。【任务管理器】中可以看到该进程,正在运行,就是不显示Windows窗体界面,无法对播放器进行干预,也显示不了画面。

1.2 问题原因

在Windows XP, Windows Server 2003或者更早期的Windows操作系统中,所有的服务和应用程序都是运行在与第一个登录到控制台的用户得Session中。这个Session叫做Session 0。在Session 0 中一起运行服务和用户应用程序,由于服务是以高权限运行的,所以会造成一些安全风险。

在Windows7之后版本,所有的服务在session 0中运行, 应有程序在session1、session2....中运行,他们存在隔离,所以资源不能共享,在session 0不能够现实Windows Form界面。

二、解决方案

所以我们要解决这个问题,要 “穿透Session隔离”,具体术语的解释,可以查看微软官网,英文文档,可通过翻译软件学习一下。

三、软件开发(源码展示)

VS创建帮助类,InteropsHelper.cs,复制如下源码:

using System;
using System.Runtime.InteropServices;
using System.Security.Principal;

namespace TaskLib
{
    public class InteropsHelper
    {
        public static IntPtr WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero;

        public static void ShowMessageBox(string message, string title)
        {
            int resp = 0;
            WTSSendMessage(
                WTS_CURRENT_SERVER_HANDLE,
                WTSGetActiveConsoleSessionId(),
                title, title.Length,
                message, message.Length,
                0, 0, out resp, false);
        }

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern int WTSGetActiveConsoleSessionId();

        [DllImport("wtsapi32.dll", SetLastError = true)]
        public static extern bool WTSSendMessage(
            IntPtr hServer,
            int SessionId,
            String pTitle,
            int TitleLength,
            String pMessage,
            int MessageLength,
            int Style,
            int Timeout,
            out int pResponse,
            bool bWait);

        /// <summary>
        /// 调用进程
        /// </summary>
        /// <param name="app">应用程序名(包含扩展名)</param>
        /// <param name="path">应用程序所在目录</param>
        /// <param name="a_strParam">传入参数(第一个参数有空格)</param>
        public static void CreateProcess(string app, string path,string a_strParam)
        {
            PROCESS_INFORMATION pi = new PROCESS_INFORMATION();
            SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
            IntPtr hDupedToken = IntPtr.Zero;
            try
            {
                bool result;
                IntPtr hToken = WindowsIdentity.GetCurrent().Token;

                sa.Length = Marshal.SizeOf(sa);

                STARTUPINFO si = new STARTUPINFO();
                si.cb = Marshal.SizeOf(si);

                int dwSessionID = WTSGetActiveConsoleSessionId();
                result = WTSQueryUserToken(dwSessionID, out hToken);

                //if (!result)//此处有错误
                //{
                //    ShowMessageBox("WTSQueryUserToken failed", "AlertService Message");
                //}

                result = DuplicateTokenEx(
                      hToken,
                      GENERIC_ALL_ACCESS,
                      ref sa,
                      (int)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification,
                      (int)TOKEN_TYPE.TokenPrimary,
                      ref hDupedToken
                   );

                //if (!result)
                //{
                //    ShowMessageBox("DuplicateTokenEx failed", "AlertService Message");
                //}

                IntPtr lpEnvironment = IntPtr.Zero;
                result = CreateEnvironmentBlock(out lpEnvironment, hDupedToken, false);

                //if (!result)
                //{
                //    ShowMessageBox("CreateEnvironmentBlock failed", "AlertService Message");
                //}

                result = CreateProcessAsUser(
                                     hDupedToken,
                                     app,
                                     a_strParam,
                                     ref sa, ref sa,
                                     false, 0, IntPtr.Zero,
                                     path, ref si, ref pi);

                //if (!result)
                //{
                //    int error = Marshal.GetLastWin32Error();
                //    string message = String.Format("CreateProcessAsUser Error: {0}", error);
                //    ShowMessageBox(message, "AlertService Message");
                //}
            }
            catch (Exception)
            {
                throw;
            }
            finally
            {
                if (pi.hProcess != IntPtr.Zero)
                    CloseHandle(pi.hProcess);
                if (pi.hThread != IntPtr.Zero)
                    CloseHandle(pi.hThread);
                if (hDupedToken != IntPtr.Zero)
                    CloseHandle(hDupedToken);
            }
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct STARTUPINFO
        {
            public Int32 cb;
            public string lpReserved;
            public string lpDesktop;
            public string lpTitle;
            public Int32 dwX;
            public Int32 dwY;
            public Int32 dwXSize;
            public Int32 dwXCountChars;
            public Int32 dwYCountChars;
            public Int32 dwFillAttribute;
            public Int32 dwFlags;
            public Int16 wShowWindow;
            public Int16 cbReserved2;
            public IntPtr lpReserved2;
            public IntPtr hStdInput;
            public IntPtr hStdOutput;
            public IntPtr hStdError;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct PROCESS_INFORMATION
        {
            public IntPtr hProcess;
            public IntPtr hThread;
            public Int32 dwProcessID;
            public Int32 dwThreadID;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct SECURITY_ATTRIBUTES
        {
            public Int32 Length;
            public IntPtr lpSecurityDescriptor;
            public bool bInheritHandle;
        }

        public enum SECURITY_IMPERSONATION_LEVEL
        {
            SecurityAnonymous,
            SecurityIdentification,
            SecurityImpersonation,
            SecurityDelegation
        }

        public enum TOKEN_TYPE
        {
            TokenPrimary = 1,
            TokenImpersonation
        }

        public const int GENERIC_ALL_ACCESS = 0x10000000;

        [DllImport("kernel32.dll", SetLastError = true,
            CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern bool CloseHandle(IntPtr handle);

        [DllImport("advapi32.dll", SetLastError = true,
            CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
        public static extern bool CreateProcessAsUser(
            IntPtr hToken,//第一个hToken,是跟用户相关的访问令牌
            string lpApplicationName,//你所创建进程文件的所在路径包含文件名(最好带着扩展名)windows api 把路径转换为短路径
            string lpCommandLine,//放你给被创建进程传递的参数
            ref SECURITY_ATTRIBUTES lpProcessAttributes,
            ref SECURITY_ATTRIBUTES lpThreadAttributes,
            bool bInheritHandle,
            Int32 dwCreationFlags,
            IntPtr lpEnvrionment,
            string lpCurrentDirectory,//指的是进程运行的目录(注意不是进程文件所在的目录),为NULL时默认随父进程
            ref STARTUPINFO lpStartupInfo,
            ref PROCESS_INFORMATION lpProcessInformation);//这个是个出参。 返回被创建进程的信息,包括进程PID等

        [DllImport("advapi32.dll", SetLastError = true)]
        public static extern bool DuplicateTokenEx(
            IntPtr hExistingToken,
            Int32 dwDesiredAccess,
            ref SECURITY_ATTRIBUTES lpThreadAttributes,
            Int32 ImpersonationLevel,
            Int32 dwTokenType,
            ref IntPtr phNewToken);

        [DllImport("wtsapi32.dll", SetLastError = true)]
        public static extern bool WTSQueryUserToken(
            Int32 sessionId,
            out IntPtr Token);

        [DllImport("userenv.dll", SetLastError = true)]
        static extern bool CreateEnvironmentBlock(
            out IntPtr lpEnvironment,
            IntPtr hToken,
            bool bInherit);
    }
}

四、调用方法

 我已将该调用方法封装,你可以直接引入TaskLib.dll组件,只需一行代码搞定。是的,你没有看错就这么简单。编程是件简单的事情,不要重复作业,不要内耗,保护自己的好头发呀。

                //参数1:程序名
                //参数2:程序所在目录
                //参数3:传入参数(参数以空格分隔,参数含空以引号包含)
                InteropsHelper.CreateProcess(Const.ct_strPlayerName, l_strPath, 
                    string.Format(Const.ct_strMediaCMD1, a_strFiles, "播放:" + a_strFiles));

五、运行效果

这是我做的项目,感兴趣的我们一起交流下吧。

六、资源链接

TaskLib.dll,组件下载,减少编码类,减少秃顶几率.。

其中该组件,包含xml、文件转移、日志记录、发送邮件、SQLite操作、Datatable和list互相转换等类,visual studio 下按F12有使用说明。

链接:https://pan.baidu.com/s/1o6exHhKJv_SmAszdOuCmyQ?pwd=4v66 
提取码:4v66