Call mobile detection class method in ASP.NET app_code folder - c#

Summary
I have an ASP.NET 3.5 website and a mobile detection method inside a C# class file inside my app_code folder. I want to call this method which sets a cookie, then switch my master page file if it's a mobile device.
I'm using a method i got from the comment section down in this article: http://www.codeproject.com/Articles/34422/Detecting-a-mobile-browser-in-ASP-NET
This just seemed simpler than using the 51degrees method of detection since i didn't really need a high level of detection, and i didn't want to send them to a different URL, but rather just flip to a different masterpage, and the NuGet package which makes a nice easy install doesn't work for ASP.NET 3.5.
The problem i'm at currently is with calling the method.
Here's the Code
External app_code class
public static class fooBar // test method
{
public static bool ean()
{
return true;
}
}
public static class HttpRequestExt
{
#region Private Fields
// These regular expressions retrieved from http://detectmobilebrowser.com/ "Open source mobile phone detection".
private static Regex MobileBrowsers = new Regex(#"android|avantgo|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\\/|plucker|pocket|psp|symbian|treo|up\\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino", RegexOptions.IgnoreCase | RegexOptions.Multiline);
private static Regex MobileApps = new Regex(#"1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\\-(n|u)|c55\\/|capi|ccwa|cdm\\-|cell|chtm|cldc|cmd\\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\\-s|devi|dica|dmob|do(c|p)o|ds(12|\\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\\-|_)|g1 u|g560|gene|gf\\-5|g\\-mo|go(\\.w|od)|gr(ad|un)|haie|hcit|hd\\-(m|p|t)|hei\\-|hi(pt|ta)|hp( i|ip)|hs\\-c|ht(c(\\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\\-(20|go|ma)|i230|iac( |\\-|\\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\\/)|klon|kpt |kwc\\-|kyo(c|k)|le(no|xi)|lg( g|\\/(k|l|u)|50|54|e\\-|e\\/|\\-[a-w])|libw|lynx|m1\\-w|m3ga|m50\\/|ma(te|ui|xo)|mc(01|21|ca)|m\\-cr|me(di|rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\\-2|po(ck|rt|se)|prox|psio|pt\\-g|qa\\-a|qc(07|12|21|32|60|\\-[2-7]|i\\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\\-|oo|p\\-)|sdk\\/|se(c(\\-|0|1)|47|mc|nd|ri)|sgh\\-|shar|sie(\\-|m)|sk\\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\\-|v\\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\\-|tdg\\-|tel(i|m)|tim\\-|t\\-mo|to(pl|sh)|ts(70|m\\-|m3|m5)|tx\\-9|up(\\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|xda(\\-|2|g)|yas\\-|your|zeto|zte\\-", RegexOptions.IgnoreCase | RegexOptions.Multiline);
#endregion
public const string ViewMobileSiteCookieName = "ViewMobile";
/// <summary>
/// Determines if the request emanated from a mobile-device client;
/// and stores the result in a cookie on the response.
/// </summary>
/// <param name="request"></param>
/// <param name="Response"></param>
/// <returns></returns>
///
public static bool IsMobileClient(this System.Web.HttpRequest request, System.Web.HttpRequest Response)
{
bool isMobile = false;
bool isCookieSet = false;
var viewMobileCookie = request.Cookies[ViewMobileSiteCookieName];
if (viewMobileCookie != null && bool.TryParse(viewMobileCookie.Value, out isMobile))
{
isCookieSet = true;
}
else if (request.Browser.IsMobileDevice)
{
isMobile = true;
}
else if (request.ServerVariables["HTTP_X_WAP_PROFILE"].IsNotEmpty())
{
isMobile = true;
}
else if
(
request.ServerVariables["HTTP_ACCEPT"].IsNotEmpty()
&&
(
request.ServerVariables["HTTP_ACCEPT"].ToLower().Contains("wap")
|| request.ServerVariables["HTTP_ACCEPT"].ToLower().Contains("wml+xml")
)
)
{
isMobile = true;
}
else if (request.ServerVariables["HTTP_USER_AGENT"].IsNotEmpty())
{
string userAgent = request.ServerVariables["HTTP_USER_AGENT"];
isMobile = ((MobileBrowsers.IsMatch(userAgent) || MobileApps.IsMatch(userAgent.Substring(0, 4))));
}
// Store the result as a cookie.
if (!isCookieSet)
Response.Cookies.Add(new HttpCookie(ViewMobileSiteCookieName, isMobile.ToString()));
return isMobile;
}
public static bool IsNotEmpty(this string instance)
{
return instance != null && instance.Length > 0;
}
}
My call to it)
Right now im doing it on the page, but i figure i'll do this in global.asax on session start?
sectionTitle.InnerHtml = fooBar.ean().ToString(); // test works
sectionTitle.InnerHtml = HttpRequestExt.IsMobileClient.ToString(); // compile error
Compile Error:
CS0119: 'SWIC.HttpRequestExt.IsMobileClient(System.Web.HttpRequest, System.Web.HttpRequest)' is a 'method', which is not valid in the given context
Do i have to somehow cast this to the current instance? Should i just be doing this differently all together?

You're trying to call a method like a property. Methods need parentheses:
HttpRequestExt.IsMobileClient().ToString()
^^
You'll also have to call it on the current request, not just statically, as it is an extension method (which takes a parameter). E.g.:
sectionTitle.InnerHtml = Page.Request.IsMobileClient(Page.Response).ToString();

Related

How to navigate to local file with ChromeDriver using a file path?

I am building a set of tests for an automation framework, but am running into an issue navigating to a local HTML page I have created.
Here is where I'm creating the ChromeDriver instance.
if (AllowFileAccessAcrossFiles)
{
ChromeOptions options = new ChromeOptions();
// Have tried, none, individually, as well as both.
options.AddArgument("--allow-file-access-from-files");
options.AddArgument("--enable-local-file-accesses ");
driver = new ChromeDriver(options);
}
This ChromeDriver instance is later passed into the NgWebDriver class so that I am able to use Protractor-net in my tests as well as abstract the test tools.
internal TestWebDriver(RemoteWebDriver driver, TestConfiguration configuration)
{
// ...
_driver = new NgWebDriver(driver);
// ...
}
When the framework calls into the driver to navigate to the page it passes the correct file path ("file:/// ..."), but it never makes it into the browser URL and is not navigated to. (i.e. the URL says data;)
How do I navigate to a local HTML page with a file path with ChromeDriver?
It turns out that this resolution to this issue is rooted in NgWebDriver. NgWebDriver defers to the drivers for IE, Edge, PhantomJS, Firefox, and Safari to navigate to the URL, but if it is anything else then it just runs this: this.ExecuteScript("window.name += '" + AngularDeferBootstrap + "'; window.location.href = '" + value + "';");
The JavaScript method that is being called doesn't handle just passing in a local path, it needs an http(s) string to navigate. So whether or not we can pass in a local path is up to the specific driver's implementation of the set method for the Url property.
Below is the relevant Protractor-net property.
public class NgWebDriver : IWebDriver, IWrapsDriver, IJavaScriptExecutor
{
private const string AngularDeferBootstrap = "NG_DEFER_BOOTSTRAP!";
private IWebDriver driver;
private IJavaScriptExecutor jsExecutor;
private string rootElement;
private IList<NgModule> mockModules;
// constructors and stuff
/// <summary>
/// Gets or sets the URL the browser is currently displaying.
/// </summary>
public string Url
{
get
{
this.WaitForAngular();
return this.driver.Url;
}
set
{
// Reset URL
this.driver.Url = "about:blank";
// TODO: test Android
IHasCapabilities hcDriver = this.driver as IHasCapabilities;
if (hcDriver != null &&
(hcDriver.Capabilities.BrowserName == "internet explorer" ||
hcDriver.Capabilities.BrowserName == "MicrosoftEdge" ||
hcDriver.Capabilities.BrowserName == "phantomjs" ||
hcDriver.Capabilities.BrowserName == "firefox" ||
hcDriver.Capabilities.BrowserName.ToLower() == "safari"))
{
this.ExecuteScript("window.name += '" + AngularDeferBootstrap + "';");
this.driver.Url = value;
}
else
{
this.ExecuteScript("window.name += '" + AngularDeferBootstrap + "'; window.location.href = '" + value + "';");
}
if (!this.IgnoreSynchronization)
{
try
{
// Make sure the page is an Angular page.
long? angularVersion = this.ExecuteAsyncScript(ClientSideScripts.TestForAngular) as long?;
if (angularVersion.HasValue)
{
if (angularVersion.Value == 1)
{
// At this point, Angular will pause for us, until angular.resumeBootstrap is called.
// Add default module for Angular v1
this.mockModules.Add(new Ng1BaseModule());
// Register extra modules
foreach (NgModule ngModule in this.mockModules)
{
this.ExecuteScript(ngModule.Script);
}
// Resume Angular bootstrap
this.ExecuteScript(ClientSideScripts.ResumeAngularBootstrap,
String.Join(",", this.mockModules.Select(m => m.Name).ToArray()));
}
else if (angularVersion.Value == 2)
{
if (this.mockModules.Count > 0)
{
throw new NotSupportedException("Mock modules are not supported in Angular 2");
}
}
}
}
catch (WebDriverTimeoutException wdte)
{
throw new InvalidOperationException(
String.Format("Angular could not be found on the page '{0}'", value), wdte);
}
}
}
}
Since this property assumes that an application is using Angular, when navigating with Navigate().GoToUrl() you must include, again, whether the application is using Angular via a bool.
In our case, we were not using Angular and passing that into the GoToUrl() method calls directly into the wrapped IWebDriver via INavigation. This wrapped driver handles local files correctly.
Below is the navigation class in Protractor-net:
public class NgNavigation : INavigation
{
private NgWebDriver ngDriver;
private INavigation navigation;
// irrelevant constructors and such
/// <summary>
/// Load a new web page in the current browser window.
/// </summary>
/// <param name="url">The URL to load. It is best to use a fully qualified URL</param>
/// <param name="ensureAngularApp">Ensure the page is an Angular page by throwing an exception.</param>
public void GoToUrl(string url, bool ensureAngularApp)
{
if (ensureAngularApp)
{
this.ngDriver.Url = url;
}
else
{
this.navigation.GoToUrl(url);
}
}

RouteUrl with querystring - with duplicate keys

I use this extension to create an RouteUrl with the current query-string appended to it. This works fine for query-strings which does not have the same key twice or more.
public static string CurrentQueryStringRouteUrl(this UrlHelper url, string routeName, RouteValueDictionary routeValues)
{
var context = url.RequestContext;
var combinedRouteValues = new RouteValueDictionary();
var queryString = context.HttpContext.Request.QueryString;
foreach (var key in queryString.AllKeys.Where(key => key != null))
{
combinedRouteValues[key] = queryString[key];
}
if (routeValues != null)
{
foreach (var routeValue in routeValues)
{
combinedRouteValues[routeValue.Key] = routeValue.Value;
}
}
return url.RouteUrl(routeName, combinedRouteValues);
}
When there is query-string keys of same name, e.g. ?id=1&id=2&id=3, this is converted into ?id=1,2,3 using the method above. Is there any way to avoid that? I wish to keep the original query-string as I am binding these values on a model list.
I am aware I can create a custom model-binder to bind the comma separated string to string[] (or int[] in this example) but I wish to avoid this as far of consistence goes.
You can to do this, but it is dirty hack:
string _rawstring = context.HttpContext.Request.RawUrl;
int _f;
_f = _rawstring.IndexOf('?');
string _resultString = _rawstring.SubString(_f, _rawstring.Length);
Here you can find helpful info about that problem: How to deal with more than one value per key in ASP.NET MVC 3?
I took a slightly different approach, because I really needed separate duplicated keys in my query string. I alter the key using a counter, and then after rendering the url string, I restore the original parameter names. I needed this for GridMvc's grid_filter queries, but you can adapt it for your purpose.
/// <summary>
/// Allows you to create or extend a collection of route values to use in a url action
/// </summary>
public class RouteValueBuilder
{
readonly RouteValueDictionary routeValues;
private int gridFilterCounter = 0;
public RouteValueBuilder(object existingRouteValues = null)
{
routeValues = existingRouteValues as RouteValueDictionary ?? new RouteValueDictionary(existingRouteValues);
}
public void Add(string field, object value)
{
if (field == "grid_filter" && routeValues.ContainsKey(field))
{
// Because we can't add duplicate keys and GridMvc doesn't support joined comma format for query strings,
// we briefly rename each new filter, and then the Finalise method must be called after the url
// string is rendered to restore the grid_filter names back to normal.
gridFilterCounter++;
routeValues.Add(field + gridFilterCounter, value);
}
else if (routeValues.ContainsKey(field))
{
// Since duplicate key names are not supported, the concatenated comma approach can be used
routeValues[field] += "," + value;
}
else
{
routeValues.Add(field, value);
}
}
public RouteValueDictionary Get()
{
return routeValues;
}
/// <summary>
/// Cleans up the final string url, fixing workarounds done during the building process.
/// This must be called after the final url string is rendered.
/// </summary>
public static string Finalise(string url)
{
// Restores grid_filter parameters to their correct naming. See comments on Add method.
for (var i = 0; i < 100; i++)
{
url = url.Replace("grid_filter" + i, "grid_filter");
}
return url;
}
}
Usage:
var builder = new RouteValueBuilder();
builder.Add("grid_filter", "value1");
builder.Add("grid_filter", "value2");
string url = Html.Action("Index", "Home", builder.Get());
url = RouteValueBuilder.Finalise(url);
Edit: Note that the comma concatenation approach actually doesn't work in the class because it gets encoded, but the support for duplication is the main taker from this example.

Best way to collect and store times and dates

This question might be a little ambiguous so I'll explain my goal and my previous implementation. I'm looking for some advice on how to improve my implementation.
My application needs a certain set of days and times (hours and minutes) to be used for criteria later in the program.
The days and times are variable and depend on whether a user is a member of a particular group or not.
My previous implementation was to get the name of the group that was selected and then go to the web server and download the appropriate file which contained the day and time. There was a file for each group.
The format of the text file was:
Day,Day,Day,etc..HH,HH,MM,MM
It was then read into two separate arrays with the positions hardcoded. E.g. Index 0, 1,2 where days while 3,4 where hours and 5,6 where minutes.
This method also meant that I'd need a longer array for a group that had more days than another.
Obviously this was all very inefficient and the code wasn't very reusable or extendable. I'd have to alter if a new group was introduced and it had more of less data in the text file.
Edit - due to the vagueness of the question I have included code:
This method is passed the group name in the fileName parameter of CollectQualifyingTimes. The string looked like gtstimes.txt or gtsdates.txt or gectimes.txt or gecdates.txt
internal static class DownloadQualifyingTimes
{
//TimeDate Arrays
public static readonly List<string> QDaysList = new List<string>();
public static readonly List<int> QTimesList = new List<int>();
private static StreamReader _reader;
private static string _line;
private static string _file;
/// <summary>
/// Collects the Times
/// </summary>
public static void CollectQualifyingTimes(string fileName)
{
Logger.Debug("About to download the " + fileName + " Qualifying Times and Dates");
FileDownload.DownloadOnlineFile(fileName);
OpenQualifyingFile(fileName);
}
/// <summary>
/// Open the qualifying file and read the values.
/// </summary>
/// <returns></returns>
private static void OpenQualifyingFile(string fileName)
{
try
{
_file = Path + "\\" + fileName;
using (_reader = new StreamReader(_file))
{
while ((_line = _reader.ReadLine()) != null)
{
if (fileName.Contains("Times"))
{
QTimesList.Add(Convert.ToInt16(_line));
Logger.Debug("Times " + _line);
}
else
{
QDaysList.Add(_line);
Logger.Debug("Days " + _line);
}
}
}
}
catch (WebException exception)
{
Logger.Error(exception);
}
}
}
//The method that calls the Downloading class looks like this:
/// <summary>
///
/// </summary>
/// <param name="selectedLeague"></param>
/// <returns></returns>
public static bool QualificationTimeCheck(string selectedLeague)
{
var currentUtcTime = DateTime.UtcNow;
//Day check regardless of league
if (DownloadQualifyingTimes.QDaysList.Contains(currentUtcTime.DayOfWeek.ToString()))
{
Logger.Debug("Qualifying day condition meet");
if (selectedLeague.IsOneOf("GTS", "CAT"))
{
Logger.Debug("GTS or CAT qualifying logic");
if (currentUtcTime.Hour ==
DownloadQualifyingTimes.QTimesList[0] ||
currentUtcTime.Hour ==
DownloadQualifyingTimes.QTimesList[1])
{
Logger.Debug("Qualifying hour condition meet");
if (((currentUtcTime.Minute > DownloadQualifyingTimes.QTimesList[2])
&& (currentUtcTime.Minute < DownloadQualifyingTimes.QTimesList[3])) || SessionObject.LastLapStartedMinute <= DownloadQualifyingTimes.QTimesList[3])
{
Logger.Debug("Qualifying minute condition meet");
return true;
}
I hope this illustrates the nature of my question and the problem.
Can you think of a better way to implement this process? If you need any more information regarding it please don't hesitate to ask.
Edit - Ended up implementing List as per first comment suggestion.

Script Notify for ms-appdata

I want to notify my web view from button in html file and trigger the javascript:
function notify(str) {
window.external.notify(str);
}
The event captured using wv_ScriptNotify(..., ...):
void wv_ScriptNotify(object sender, NotifyEventArgs e)
{
Color c=Colors.Red;
if (e.CallingUri.Scheme =="ms-appx-web" || e.CallingUri.Scheme == "ms-appdata")
{
if (e.Value.ToLower() == "blue") c = Colors.Blue;
else if (e.Value.ToLower() == "green") c = Colors.Green;
}
appendLog(string.Format("Response from script at '{0}': '{1}'", e.CallingUri, e.Value), c);
}
I set the html file on ms-appx-web and it running well, and I realize that the html file must be store into local folder. So I change the ms-appx-web:///.../index.html to ms-appdata:///local/.../index.html.
Already search in microsoft forum and get this. On that thread there is a solution using resolver, but I'm still confusing, how can it notify from javascript like using window.external.notify? And what kind of event in C# side that will capture the "notify" from javascript other than "ScriptNotify"?
Update
There is a solution from here, example using the resolver and it said to use ms-local-stream:// rather than using ms-appdata://local so I can still use the ScriptNotify event. But unfortunately the example using the ms-appx that means using the InstalledLocation not the LocalFolder.
Trying to googling and search in msdn site for the documentation for ms-local-stream but the only documentation is just the format of ms-local-stream without any example like this ms-local-stream://appname_KEY/folder/file.
Based from that documentation, I made some sample to try it:
public sealed class StreamUriWinRTResolver : IUriToStreamResolver
{
/// <summary>
/// The entry point for resolving a Uri to a stream.
/// </summary>
/// <param name="uri"></param>
/// <returns></returns>
public IAsyncOperation<IInputStream> UriToStreamAsync(Uri uri)
{
if (uri == null)
{
throw new Exception();
}
string path = uri.AbsolutePath;
// Because of the signature of this method, it can't use await, so we
// call into a separate helper method that can use the C# await pattern.
return getContent(path).AsAsyncOperation();
}
/// <summary>
/// Helper that maps the path to package content and resolves the Uri
/// Uses the C# await pattern to coordinate async operations
/// </summary>
private async Task<IInputStream> getContent(string path)
{
// We use a package folder as the source, but the same principle should apply
// when supplying content from other locations
try
{
// My package name is "WebViewResolver"
// The KEY is "MyTag"
string scheme = "ms-local-stream:///WebViewResolver_MyTag/local/MyFolderOnLocal" + path; // Invalid path
// string scheme = "ms-local-stream:///WebViewResolver_MyTag/MyFolderOnLocal" + path; // Invalid path
Uri localUri = new Uri(scheme);
StorageFile f = await StorageFile.GetFileFromApplicationUriAsync(localUri);
IRandomAccessStream stream = await f.OpenAsync(FileAccessMode.Read);
return stream.GetInputStreamAt(0);
}
catch (Exception) { throw new Exception("Invalid path"); }
}
}
And inside my MainPage.xaml.cs:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
// The 'Host' part of the URI for the ms-local-stream protocol needs to be a combination of the package name
// and an application-defined key, which identifies the specific resolver, in this case 'MyTag'.
Uri url = wv.BuildLocalStreamUri("MyTag", "index.html");
StreamUriWinRTResolver myResolver = new StreamUriWinRTResolver();
// Pass the resolver object to the navigate call.
wv.NavigateToLocalStreamUri(url, myResolver);
}
It always get the exception when it reach the StorageFile f = await StorageFile.GetFileFromApplicationUriAsync(localUri); line.
If anybody ever got this problem and already solved it, please advise.
After debugging it, I found something interesting, the BuildLocalStreamUri part is already make the ms-local-stream automatically.
I made some changes on the getContent method inside StreamUriWinRTResolver class:
public sealed class StreamUriWinRTResolver : IUriToStreamResolver
{
/// <summary>
/// The entry point for resolving a Uri to a stream.
/// </summary>
/// <param name="uri"></param>
/// <returns></returns>
public IAsyncOperation<IInputStream> UriToStreamAsync(Uri uri)
{
if (uri == null)
{
throw new Exception();
}
string path = uri.AbsolutePath;
// Because of the signature of this method, it can't use await, so we
// call into a separate helper method that can use the C# await pattern.
return getContent(path).AsAsyncOperation();
}
/// <summary>
/// Helper that maps the path to package content and resolves the Uri
/// Uses the C# await pattern to coordinate async operations
/// </summary>
private async Task<IInputStream> getContent(string path)
{
// We use a package folder as the source, but the same principle should apply
// when supplying content from other locations
try
{
// Don't use "ms-appdata:///" on the scheme string, because inside the path
// will contain "/local/MyFolderOnLocal/index.html"
string scheme = "ms-appdata://" + path;
Uri localUri = new Uri(scheme);
StorageFile f = await StorageFile.GetFileFromApplicationUriAsync(localUri);
IRandomAccessStream stream = await f.OpenAsync(FileAccessMode.Read);
return stream.GetInputStreamAt(0);
}
catch (Exception) { throw new Exception("Invalid path"); }
}
}
Change the file path on the MainPage.xaml.cs:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
// The 'Host' part of the URI for the ms-local-stream protocol needs to be a combination of the package name
// and an application-defined key, which identifies the specific resolver, in this case 'MyTag'.
Uri url = wv.BuildLocalStreamUri("MyTag", "/local/MyFolderOnLocal/index.html");
StreamUriWinRTResolver myResolver = new StreamUriWinRTResolver();
// Pass the resolver object to the navigate call.
wv.NavigateToLocalStreamUri(url, myResolver);
wv.ScriptNotify += wv_ScriptNotify;
}
protected override void wv_ScriptNotify(object sender, NavigationEventArgs e)
{
if (e.CallingUri.Scheme == "ms-local-stream")
{
// Do your work here...
}
}

win api and c# - desktops

Okay, so I found this class online that "creates" a second desktop which does not run anything (ie, explorer.exe is not called and so forth).
However, this newly created desktop refuses to shutdown and go back to the orignal desktop. I have no idea whats going on. So if someone can try it on their machine, it would be very helpful.
note: assume all win api headers have been declared and work.
The class that "locks" the current dekstop:
namespace Locker
{
class CLocker
{
public static int DesktopHandle; // Hold desktop handle.
public static int oldDesktopHandle;
public static int DesktopInputID; // Hold desktop input id.
public static int DesktopThreadID; // Hold desktop thread id.
static string DesktopName = "DL.Locker.Desktop"; // Hold the name of new created desktop.
static FileStream TaskMan; // Hold the file stream object to control task manager.
static string FastSwitching = string.Empty; // Hold the original value of fast switching i.e. welcome screen
static string ShutdownWithoutLogin = string.Empty; // Hold the original value of showinh the shutdown button on welcome screen.
/// <summary>
/// Enabled used to enable or disable the locker
/// </summary>
public static bool Enabled
{
set
{
SetProcessPriorityHigh(); // Set the process priority to high.
if (value) // Enable or disable the locker?
{
CreateNewDesktop(); // Creating new desktop.
StartProcess(Application.ExecutablePath); // Starting the locker form, to allow the user to enter login info.
}
else
{
DestroyDesktop(); // Destroy the desktop.
ExitProcess(0); // Exit the current process, if desktop attached with no process, default desktop will be activated.
}
}
}
public static bool NeedBootStrapping()
{
Console.WriteLine((GetDesktopName() != DesktopName).ToString());
return (GetDesktopName() != DesktopName);
}
static string GetDesktopName()
{
int DLength = 0, DHandle = GetThreadDesktop(GetCurrentThreadId());
StringBuilder DName = new StringBuilder();
GetUserObjectInformation(DHandle, UOI_NAME, DName, 0, ref DLength);
if (DLength != 0) GetUserObjectInformation(DHandle, UOI_NAME, DName, DLength, ref DLength);
Console.WriteLine(DName.ToString());
return (DName.ToString());
}
static void CreateNewDesktop()
{
DesktopThreadID = GetThreadDesktop(GetCurrentThreadId());
DesktopInputID = OpenInputDesktop(0, false, DESKTOP_SWITCHDESKTOP);
DesktopHandle = CreateDesktop(DesktopName, "", 0, 0, GENERIC_ALL, 0);
if (DesktopHandle != 0)
{
SetThreadDesktop(DesktopHandle);
SwitchDesktop(DesktopHandle);
}
}
public static void DestroyDesktop()
{
SwitchDesktop(DesktopInputID);
DesktopInputID = 0;
SetThreadDesktop(DesktopInputID);
DesktopThreadID = 0;
CloseDesktop(DesktopHandle);
DesktopHandle = 0;
}
static void StartProcess(string Path)
{
MessageBox.Show("Hello from startProcess");
DestroyDesktop();
}
static void SetProcessPriorityHigh()
{
SetThreadPriority(GetCurrentThread(), THREAD_BASE_PRIORITY_MAX);
SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
}
}
}
And the main() function:
static void Main()
{
if (CLocker.NeedBootStrapping())
CLocker.Enabled = true; // Check if we need boot strapping or not, if true then a new desktop will created.
else // Run application as usual.
{
MessageBox.Show("Hello, this is your new desktop");
CLocker.Enabled = false;
}
}
Update: This code does not compile. It comes up with about 40 red squiggly lines underneath the words saying "Does not exists in current context".
Dont pop a message box, which requires user input to clear, on a hidden desktop instance. That's usually the down fall of hosting UI code in services as well.
Also, it's a good idea to research any and all unmanaged API calls before just running the code.

Categories

Resources