问题
I want tp build a WPF (or Windows Forms if it can solve the problem) that can display simultaneously the same web page with different credentials (it's testing tool for a web application with complex user action flow).
I have, by now, a very simple WPF app :
<Window x:Class="TestTool.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="600" Width="800">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<WebBrowser Margin="8" Source="http://theapptotest" />
<WebBrowser Margin="8" Source="http://theapptotest" Grid.Column="1" />
</Grid>
</Window>
This is not the target layout of the application, but it will allows to test if my goal is possible.
The application, as expected, display side by side the same web app. However, if I log in in the first web brower control, the second is logged in too. I guess it's because the cookies are shared for the current user.
I checked into the WebBrowser class documentation but I did not find anything useful.
How can I reach my goal?
Is it possible to "isolate" one webbrowser from the others?
Is it possible to "impersonate" a user control? Like some applications (web browser mostly) are spawning different processes, can I "spawn" processes under a different account and display it in my main window ?
Please note that I don't have to interact with the web pages. I just want to able to switch user by changing a tab of my application (one tab per user).
PS: I'm not stick to Internet Explorer engine, if a solution exists with alternatives browsers, let me know
PS: the target application use two authentication mechanisms. A user can authenticate with Windows account, or a custom Form based authentication. if required, I can use only forms authentication (a separate cookie container would do the job, if I can plug it).
[Edit] I've tried to spawn with other users credentials new instances of Internet Explorer, then attach the process main window by settings its parent to one of my windows form host. This is however, very unreliable (process often crash, can't work if any existing instance of IE is running, etc.
回答1:
Ok, I finally found a way to reach my goal, with the help of Simon Mourrier previous answer.
I've created a custom user control:
Xaml part:
<UserControl x:Class="WpfApplication1.AppIsolatedView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d" SizeChanged="UserControl_SizeChanged"
d:DesignHeight="300" d:DesignWidth="300" Loaded="UserControl_Loaded" Unloaded="UserControl_Unloaded">
<Grid>
<WindowsFormsHost Margin="12" Name="windowsFormsHost1" />
</Grid>
</UserControl>
With this code behind :
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
namespace WpfApplication1
{
public partial class AppIsolatedView : UserControl
{
public AppIsolatedView()
{
InitializeComponent();
_panel = new System.Windows.Forms.Panel();
windowsFormsHost1.Child = _panel;
}
string url = "http://theapptotest";
System.Windows.Forms.Panel _panel;
Process _process;
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
if (IsInDesignModeStatic) return; // Avoid consumer of the control to explode when in design mode
ProcessStartInfo psi = new ProcessStartInfo("iexplore.exe", "-noframemerging -private \"" + url + "\"");
_process = Process.Start(psi);
_process.WaitForInputIdle();
Thread.Sleep(2000);
SetParent(_process.MainWindowHandle, _panel.Handle);
// remove control box
int style = GetWindowLong(_process.MainWindowHandle, GWL_STYLE);
style = style & ~WS_CAPTION & ~WS_THICKFRAME;
SetWindowLong(_process.MainWindowHandle, GWL_STYLE, style);
// resize embedded application & refresh
ResizeEmbeddedApp();
}
private void UserControl_Unloaded(object sender, RoutedEventArgs e)
{
if (_process != null)
{
_process.Refresh();
_process.Close();
}
}
private void ResizeEmbeddedApp()
{
if (_process == null)
return;
SetWindowPos(_process.MainWindowHandle, IntPtr.Zero, 0, 0, (int)_panel.ClientSize.Width, (int)_panel.ClientSize.Height, SWP_NOZORDER | SWP_NOACTIVATE);
}
protected override Size MeasureOverride(Size availableSize)
{
Size size = base.MeasureOverride(availableSize);
ResizeEmbeddedApp();
return size;
}
private static bool? _isInDesignMode;
/// <summary>
/// Gets a value indicating whether the control is in design mode (running in Blend
/// or Visual Studio).
/// </summary>
public static bool IsInDesignModeStatic
{
get
{
if (!_isInDesignMode.HasValue)
{
#if SILVERLIGHT
_isInDesignMode = DesignerProperties.IsInDesignTool;
#else
var prop = DesignerProperties.IsInDesignModeProperty;
_isInDesignMode
= (bool)DependencyPropertyDescriptor
.FromProperty(prop, typeof(FrameworkElement))
.Metadata.DefaultValue;
#endif
}
return _isInDesignMode.Value;
}
}
private void UserControl_SizeChanged(object sender, SizeChangedEventArgs e)
{
ResizeEmbeddedApp();
}
#region pinvoke stuff
[DllImport("user32.dll")]
private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
[DllImport("user32.dll", SetLastError = true)]
private static extern int GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32")]
private static extern IntPtr SetParent(IntPtr hWnd, IntPtr hWndParent);
[DllImport("user32")]
private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, int uFlags);
private const int SWP_NOZORDER = 0x0004;
private const int SWP_NOACTIVATE = 0x0010;
private const int GWL_STYLE = -16;
private const int WS_CAPTION = 0x00C00000;
private const int WS_THICKFRAME = 0x00040000;
#endregion
}
}
I have to cheat a bit :
- I have to add
-noframemergingwhen firingIExplore.exein order to avoid internal IE session sharing. - I have to sleep a bit after having launch the process. Don't know exactly why, but the
MainWindowHandleproperty change after some few milliseconds (I guess iexplore has a bootstrapper process to either connect to existing process or spawn a new one, with new handle, or something like this).
I still have to extend the user control to accept input parameters (url is hardcoded), but the idea is here. I just have to concentrate on the app itself.
来源:https://stackoverflow.com/questions/11186817/create-an-application-with-multiple-view-to-the-same-web-application-but-with