简体   繁体   中英

Process.Start url with hidden WindowStyle

I have a url that authenticates my credentials on a server. 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. 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..

Is there an other way to make this process invisible?

Edit: http sample

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:

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.

The entrypoint of a GUI program on Windows is the famous WinMain() function . 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. The lpCmdLine argument are the command line arguments.

nCmdShow is the important one and the subject of your question. Expected values are SW_HIDE, SW_SHOWNORMAL, SW_SHOWMAXIMIZE or SW_SHOWMINIMIZE. You can easily map them yourself to the possible ProcessWindowStyle enum values.

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. Except Hidden , hint of trouble there.


A typical C program passes the nCmdShow argument directly to the ShowWindow() function to display the main window (error checking omitted):

   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 . 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. 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. 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. Save it. Then change it to Windows Application and save.

That should get rid of any UI you are not explicitly creating yourself.

I don't think you need to start a new process to get authorization from Spoitfy. You should make a Get/Post call and get authorization token and then include it in your future API calls.

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

I am referring to Spoitfy's online help page.

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 .

Since ImplicitGrantAuth won't work with simple HTTP-Requests, you need a workaround:

Authenticate without user-action: Headless Browser

While ImplicitGrantAuth was not made for this kind of task, it's still possible but not cleanly. You will need a headless browser which you can control in your C# App, like Selenium .

Consider following code using Selenium ( related SO Question ):

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. Facebook-Login would be the same principle.

But note: OAuth was not designed for this type of use and you propably should not use such hacky workarounds in production

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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