简体   繁体   English

使用隐藏的WindowStyle启动Process.Start url

[英]Process.Start url with hidden WindowStyle

I have a url that authenticates my credentials on a server. 我有一个在服务器上验证我的凭据的URL。 Is there a way to make it invisible? 有没有办法让它看不见? The simple code looks exactly like this: 简单的代码看起来完全像这样:

public void DoAuth()
    {
        String uri = GetUri();
        ProcessStartInfo startInfo = new ProcessStartInfo(uri);
        startInfo.WindowStyle = ProcessWindowStyle.Hidden;
        Process.Start(startInfo);

    }

however ProcessWindowStyle.Hidden doesn't seem to do the trick. 但是ProcessWindowStyle.Hidden似乎没有做到这一点。 If I set UseShellExecute to false then I get Win32Exception with the message The system cannot find the file specified The url is a authentication on Spotify server to get the playlists and it looks something like this https://accounts.spotify.com/authorize/client_id=26d287105as12315e12ds56e31491889f3cd293.. 如果我将UseShellExecute设置为false然后我收到Win32Exception并显示消息The system cannot find the file specified url是Spotify服务器上的身份验证以获取播放列表,它看起来像这样https://accounts.spotify.com/authorize/client_id=26d287105as12315e12ds56e31491889f3cd293..

Is there an other way to make this process invisible? 还有其他方法可以使这个过程不可见吗?

Edit: http sample 编辑:http示例

public void DoAuth()
    {
        String uri = GetUri();

        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
        HttpWebResponse webResponse;
        try
        {
            webResponse = (HttpWebResponse)request.GetResponse();
            Console.WriteLine("Error code: {0}", webResponse.StatusCode);
            using (Stream data = webResponse.GetResponseStream())
            using (var reader = new StreamReader(data))
            {
                //do what here?
            }
        }
        catch (Exception e)
        {
            throw;
        }
    }

The entire .cs file containing the sample above: 包含上述示例的整个.cs文件:

using SpotifyAPI.Web.Enums;
using SpotifyAPI.Web.Models;
using System;
using System.IO;
using System.Net;
using System.Text;
using System.Threading;

namespace SpotifyAPI.Web.Auth
{
    public class ImplicitGrantAuth
    {
        public delegate void OnResponseReceived(Token token, String state);

        private SimpleHttpServer _httpServer;
        private Thread _httpThread;
        public String ClientId { get; set; }
        public String RedirectUri { get; set; }
        public String State { get; set; }
        public Scope Scope { get; set; }
        public Boolean ShowDialog { get; set; }

        public event OnResponseReceived OnResponseReceivedEvent;

        /// <summary>
        ///     Start the auth process (Make sure the internal HTTP-Server ist started)
        /// </summary>
        public void DoAuth()
        {
            String uri = GetUri();

            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
            HttpWebResponse webResponse;
            try
            {
                webResponse = (HttpWebResponse)request.GetResponse();
                Console.WriteLine("Error code: {0}", webResponse.StatusCode);
                using (Stream data = webResponse.GetResponseStream())
                using (var reader = new StreamReader(data))
                {
                    //nothing
                }
            }
            catch (Exception e)
            {
                throw;
            }


            /*ProcessStartInfo startInfo = new ProcessStartInfo(uri);
            startInfo.WindowStyle = ProcessWindowStyle.Hidden;
            Process.Start(startInfo);
            */
        }

        private String GetUri()
        {
            StringBuilder builder = new StringBuilder("https://accounts.spotify.com/authorize/?");
            builder.Append("client_id=" + ClientId);
            builder.Append("&response_type=token");
            builder.Append("&redirect_uri=" + RedirectUri);
            builder.Append("&state=" + State);
            builder.Append("&scope=" + Scope.GetStringAttribute(" "));
            builder.Append("&show_dialog=" + ShowDialog);
            return builder.ToString();
        }

        /// <summary>
        ///     Start the internal HTTP-Server
        /// </summary>
        public void StartHttpServer(int port = 80)
        {
            _httpServer = new SimpleHttpServer(port, AuthType.Implicit);
            _httpServer.OnAuth += HttpServerOnOnAuth;

            _httpThread = new Thread(_httpServer.Listen);
            _httpThread.Start();
        }

        private void HttpServerOnOnAuth(AuthEventArgs e)
        {
            OnResponseReceivedEvent?.Invoke(new Token
            {
                AccessToken = e.Code,
                TokenType = e.TokenType,
                ExpiresIn = e.ExpiresIn,
                Error = e.Error
            }, e.State);
        }

        /// <summary>
        ///     This will stop the internal HTTP-Server (Should be called after you got the Token)
        /// </summary>
        public void StopHttpServer()
        {
            _httpServer.Dispose();
            _httpServer = null;
        }
    }
}

And they are called like this: 它们被称为这样:

_auth.OnResponseReceivedEvent += _auth_OnResponseReceivedEvent;
_auth.StartHttpServer(8000);
_auth.DoAuth();

The github url with a full runnable sampe is here: https://github.com/JohnnyCrazy/SpotifyAPI-NET Download it and run the Spotify Test to connect to Spotify's web api to reproduce the sample I have. 带有完整可运行样品的github url在这里: https//github.com/JohnnyCrazy/SpotifyAPI-NET下载并运行Spotify测试以连接Spotify的web api以重现我的样本。

The entrypoint of a GUI program on Windows is the famous WinMain() function . Windows上GUI程序的入口点是着名的WinMain()函数 It looks like this: 它看起来像这样:

  int CALLBACK WinMain(
    _In_ HINSTANCE hInstance,
    _In_ HINSTANCE hPrevInstance,
    _In_ LPSTR     lpCmdLine,
    _In_ int       nCmdShow
  );

The hInstance and hPrevInstance arguments are legacy and date back to Windows versions <= 3, versions that did not have support processes yet and required an app to take care of multiple instances of a task itself. hInstance和hPrevInstance参数是遗留的,并且可以追溯到Windows版本<= 3,尚未支持进程的版本,并且需要应用程序来处理任务本身的多个实例。 The lpCmdLine argument are the command line arguments. lpCmdLine参数是命令行参数。

nCmdShow is the important one and the subject of your question. nCmdShow是您的重要问题和主题。 Expected values are SW_HIDE, SW_SHOWNORMAL, SW_SHOWMAXIMIZE or SW_SHOWMINIMIZE. 预期值为SW_HIDE,SW_SHOWNORMAL,SW_SHOWMAXIMIZE或SW_SHOWMINIMIZE。 You can easily map them yourself to the possible ProcessWindowStyle enum values. 您可以轻松地将它们映射到可能的ProcessWindowStyle枚举值。

It is also exposed in the properties of a shortcut on your desktop. 它也暴露在桌面上的快捷方式属性中。 For example: 例如:

在此输入图像描述

I expanded the Run combobox, note the match with the ProcessWindowStyle enum values. 我展开了Run组合框,注意与ProcessWindowStyle枚举值的匹配。 Except Hidden , hint of trouble there. 除了Hidden ,那里有一丝麻烦。


A typical C program passes the nCmdShow argument directly to the ShowWindow() function to display the main window (error checking omitted): 典型的C程序将nCmdShow参数直接传递给ShowWindow()函数以显示主窗口(省略错误检查):

   HWND hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
   ShowWindow(hWnd, nCmdShow);

You'd be happy with such a program. 你会对这样的节目感到满意。 It is however not the way many programs actually work. 然而,这并不是许多程序实际工作的方式。 They check the value of nCmdShow and explicitly filter out SW_HIDDEN . 他们检查 nCmdShow的值并明确过滤掉SW_HIDDEN Or they restore the window state that the user last used. 或者他们恢复用户上次使用的窗口状态。 In other words, they do this: 换句话说,他们这样做:

   HWND hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
   if (nCmdShow == SW_HIDDEN) nCmdShow = SW_SHOWNORMAL;
   if (HasPreviousWindowState) nCmdShow = PreviousWindowState;   // optional
   ShowWindow(hWnd, nCmdShow);

This is for a done for good reasons. 这是有充分理由的。 Restoring the previous state is an obvious usability preference, many users will prefer it works that way for a browser for example. 恢复以前的状态是一个明显的可用性偏好,许多用户会更喜欢它以这种方式为浏览器工作。 I do. 我做。

More to the point for the missing Hidden option in the shortcut configuration dialog, both the OS and well-designed GUI programs intentionally avoid creating a usability nightmare. 对于快捷方式配置对话框中缺少Hidden选项的更多信息,操作系统和精心设计的GUI程序都有意避免产生可用性噩梦。 Where the program gets started but there is no way for the user to activate the program. 程序启动的地方,但用户无法激活程序。 There is no taskbar button for a hidden window. 隐藏窗口没有任务栏按钮。 Alt+Tab does not work. Alt + Tab不起作用。 The only way the user can regain control over the program is to terminate it with Task Manager. 用户重新获得对程序的控制权的唯一方法是使用任务管理器终止它。 It is also exploitable, malware could start a program and commandeer it and the user never notices. 它也是可利用的,恶意软件可以启动程序并征用它并且用户从不会注意到它。

All good reasons why such a program prevents this from happening. 这样一个程序阻止这种情况发生的所有好理由。 Nothing you can do about it, the program overrides your choice and has the last say about it. 没有什么可以做的,程序会覆盖你的选择并对此有最后的发言权。

Go into the project properties in Visual Studio and change your project type to Class Library. 进入Visual Studio中的项目属性,并将项目类型更改为类库。 Save it. 保存。 Then change it to Windows Application and save. 然后将其更改为Windows应用程序并保存。

That should get rid of any UI you are not explicitly creating yourself. 这应该摆脱你没有明确创建自己的UI。

I don't think you need to start a new process to get authorization from Spoitfy. 我认为你不需要开始一个新的流程来获得Spoitfy的授权。 You should make a Get/Post call and get authorization token and then include it in your future API calls. 您应该进行Get / Post调用并获取授权令牌,然后将其包含在您将来的API调用中。

GET https://accounts.spotify.com/authorize

I am referring to Spoitfy's online help page. 我指的是Spoitfy的在线帮助页面。

https://developer.spotify.com/web-api/authorization-guide/

Classic XY-Problem, you are not searching for a way to hide the window, you want to authenticate a user without user-actions involved . 经典XY问题,您没有搜索隐藏窗口的方法, 您希望在没有涉及用户操作的情况下对用户进行身份验证

Since ImplicitGrantAuth won't work with simple HTTP-Requests, you need a workaround: 由于ImplicitGrantAuth不适用于简单的HTTP请求,因此您需要一种解决方法:

Authenticate without user-action: Headless Browser 无用户操作进行身份验证:无头浏览器

While ImplicitGrantAuth was not made for this kind of task, it's still possible but not cleanly. 虽然ImplicitGrantAuth不是为这种任务而制作的,但它仍然可能但不干净。 You will need a headless browser which you can control in your C# App, like Selenium . 你需要一个无头浏览器,你可以在你的C#App中控制它,比如Selenium

Consider following code using Selenium ( related SO Question ): 考虑使用Selenium代码( 相关SO问题 ):

void DoAuth()
{
    IWebDriver driver = new FirefoxDriver();

    driver.Navigate().GoToUrl(GetUri());
    IWebElement query = driver.FindElement(By.Name("username")); //You need to search for the correct element
    query.SendKeys("username");
    query = driver.FineElement(By.Name("password"));
    query.SendKeys("password");

    //Somehow submit the form and wait till you get the Token via URL
    //Thread.Sleep(5000);
    String urlWithToken = driver.Url;

    //Analyze it and get the token out of it

    driver.Quit();
}

This is just pseudo-correct code, but you should get the idea. 这只是伪正确的代码,但你应该明白这个想法。 Use a headless browser which you can control, fill in all form inputs, submit and analyze the URL. 使用您可以控制的无头浏览器,填写所有表单输入,提交和分析URL。 Facebook-Login would be the same principle. Facebook登录将是相同的原则。

But note: OAuth was not designed for this type of use and you propably should not use such hacky workarounds in production 但是请注意: OAuth不是为这种类型的使用而设计的,并且您可能不应该在生产中使用这种hacky变通方法

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM