【问题标题】:Process.Start url with hidden WindowStyleProcess.Start 带有隐藏 WindowStyle 的 URL
【发布时间】:2016-08-26 00:36:46
【问题描述】:

我有一个在服务器上验证我的凭据的 url。有没有办法让它不可见?简单的代码看起来就像这样:

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

    }

但是ProcessWindowStyle.Hidden 似乎并不能解决问题。如果我将UseShellExecute 设置为false,那么我会得到Win32Exception 和消息The system cannot find the file specified 该 url 是 Spotify 服务器上用于获取播放列表的身份验证,它看起来像这样 https://accounts.spotify.com/authorize/client_id=26d287105as12315e12ds56e31491889f3cd293..

还有其他方法可以使这个过程不可见吗?

编辑: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;
        }
    }

包含上述示例的整个 .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;
        }
    }
}

他们是这样称呼的:

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

带有完整可运行示例的 github url 在这里:https://github.com/JohnnyCrazy/SpotifyAPI-NET 下载它并运行 Spotify 测试以连接到 Spotify 的 web api 以重现我的示例。

【问题讨论】:

  • 浏览器不是这样工作的;你不能那样做。您可能想要发送直接 HTTP 请求(但要注意 cookie)
  • @SLaks 我想过,但是什么样的要求?我应该如何处理响应?我不必在该请求上设置 cookie 吗?我将更新一个小样本,说明我如何理解你所说的
  • 想对响应做什么?这完全取决于您。
  • @SLaks 这就是问题所在。通常我对响应什么都不做。就连接完成而言,连接上有一个打开的套接字,我可以从 Spotify 获取播放列表。我会附上整个 .cs 文件,如果你有时间并且愿意的话,你可以检查整个代码
  • 你在用什么样的项目?对于 .net 进程,您不必有任何 UI

标签: c# url process styles webclient


【解决方案1】:

Windows 上 GUI 程序的入口点是著名的WinMain() function。它看起来像这样:

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

hInstance 和 hPrevInstance 参数是遗留的,并且可以追溯到 Windows 版本

nCmdShow 是您问题的重要内容和主题。预期值为 SW_HIDE、SW_SHOWNORMAL、SW_SHOWMAXIMIZE 或 SW_SHOWMINIMIZE。您可以自己轻松地将它们映射到可能的 ProcessWindowStyle 枚举值。

它也显示在桌面快捷方式的属性中。例如:

我展开了 Run 组合框,注意与 ProcessWindowStyle 枚举值的匹配。除了Hidden,那里有麻烦的迹象。


典型的 C 程序将 nCmdShow 参数直接传递给 ShowWindow() 函数以显示主窗口(省略错误检查):

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

您会对这样的程序感到满意。然而,这并不是许多程序实际工作的方式。他们检查nCmdShow 的值并明确过滤掉SW_HIDDEN。或者他们恢复用户上次使用的窗口状态。换句话说,他们这样做:

   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);

这是出于充分的理由。恢复以前的状态是一个明显的可用性偏好,例如,许多用户会更喜欢它以这种方式用于浏览器。我愿意。

更重要的是,快捷方式配置对话框中缺少 Hidden 选项,操作系统和精心设计的 GUI 程序都有意避免造成可用性噩梦。程序开始的地方,但用户无法激活程序。隐藏窗口没有任务栏按钮。 Alt+Tab 不起作用。用户重新获得对程序的控制权的唯一方法是使用任务管理器将其终止。它也是可利用的,恶意软件可以启动程序并占领它,而用户永远不会注意到。

此类程序阻止这种情况发生的所有充分理由。对此您无能为力,程序会覆盖您的选择并拥有最终决定权。

【讨论】:

  • 我花了一些时间阅读并重新阅读此内容。怎么样 http 连接而不是构建的浏览器。有可能不是吗?我相信这只是知识的运气。我在 tcp 连接和 http 进程中为空,但我确实相信必须有一种方法可以使用 http 而不是浏览器来完成这里所做的事情。
【解决方案2】:

进入 Visual Studio 中的项目属性并将项目类型更改为类库。保存。然后将其更改为 Windows 应用程序并保存。

这应该摆脱您没有明确创建自己的任何 UI。

【讨论】:

  • 完全无关。
  • @约翰。 P. - 它回答了“如何在 Visual Studio 中制作无头应用程序”的问题。我想我想知道网络请求与此有什么关系是可以原谅的。
【解决方案3】:

我认为您无需启动新流程即可获得 Spoitfy 的授权。您应该进行 Get/Post 调用并获取授权令牌,然后将其包含在您未来的 API 调用中。

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

我指的是 Spoitfy 的在线帮助页面。

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

【讨论】:

  • 我已阅读该帮助页面数百次。仍然不知道如何执行该请求。但至少你明白我想要什么。这是一个进步!
  • @Johnny Dellinger 认为情况并非如此
  • 我想知道它是否会被使用自动浏览器阻止。
【解决方案4】:

经典的 XY 问题,你不是在寻找隐藏窗口的方法,you want to authenticate a user without user-actions involved

由于ImplicitGrantAuth 不适用于简单的 HTTP 请求,您需要一种解决方法:

无需用户操作即可进行身份验证:无头浏览器

虽然ImplicitGrantAuth 不是为此类任务而设计的,但它仍然是可能的,但并不干净。您将需要一个可以在 C# 应用程序中控制的无头浏览器,例如 Selenium

考虑使用 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();
}

这只是伪正确的代码,但您应该明白这一点。使用您可以控制、填写所有表单输入、提交和分析 URL 的无头浏览器。 Facebook-Login 的原理是一样的。

但请注意:OAuth 不是为这种类型的使用而设计的,您可能不应该在生产中使用这种 hacky 变通方法

【讨论】:

  • 不可以用任何http请求来完成吗?据我在 Spotify 开发者网站上看到的,我可以看到它是……如果不是,你能解释一下为什么吗?
  • @JohnP。 ImplicitGrantAuth 不能仅通过 HTTP 请求发出。 ClientCredentialsAuth 可以,因为它无法访问任何用户信息并且需要您的Client-Secret key。 OAuth 背后的整体意义是,用户必须专门授予您的应用程序访问他的数据的权限,然后您才能访问它。如果可以使用简单的 HTTP 请求,则必须传输用户的密码,这肯定是 Spotify 不喜欢的。
猜你喜欢
  • 2011-01-20
  • 1970-01-01
  • 2017-06-09
  • 1970-01-01
  • 1970-01-01
  • 2018-05-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多