CefSharp how to rename and embed BrowserSubProcess.exe - c#

iam quite desperate here. I couldn't find any example code for this in C#.
I want to rename BrowserSubProcess.exe and i want it to embed my main exe, if possible.
I am aware of this solution;
https://github.com/cefsharp/CefSharp/issues/1149#issuecomment-225547869
Rename CefSharp.BrowserSubprocess winforms
but i couldn't implemented it. I need sample program or code to understand. I hope #amaitland will see this and helps me.

I embed the BrowserSubProcess Program.cs to my Program.cs so it is embedded now.
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
if (args.Count() < 5)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new LoginForm());
}
else
{
MyBrowserSubProcess(args);
}
}
static int MyBrowserSubProcess(string[] args)
{
Debug.WriteLine("BrowserSubprocess starting up with command line: " + String.Join("\n", args));
SubProcess.EnableHighDPISupport();
int result;
var type = args.GetArgumentValue(CefSharpArguments.SubProcessTypeArgument);
var parentProcessId = -1;
// The Crashpad Handler doesn't have any HostProcessIdArgument, so we must not try to
// parse it lest we want an ArgumentNullException.
if (type != "crashpad-handler")
{
parentProcessId = int.Parse(args.GetArgumentValue(CefSharpArguments.HostProcessIdArgument));
if (args.HasArgument(CefSharpArguments.ExitIfParentProcessClosed))
{
Task.Factory.StartNew(() => AwaitParentProcessExit(parentProcessId), TaskCreationOptions.LongRunning);
}
}
// Use our custom subProcess provides features like EvaluateJavascript
if (type == "renderer")
{
var wcfEnabled = args.HasArgument(CefSharpArguments.WcfEnabledArgument);
var subProcess = wcfEnabled ? new WcfEnabledSubProcess(parentProcessId, args) : new SubProcess(args);
using (subProcess)
{
result = subProcess.Run();
}
}
else
{
result = SubProcess.ExecuteProcess();
}
Debug.WriteLine("BrowserSubprocess shutting down.");
return result;
}
private static async void AwaitParentProcessExit(int parentProcessId)
{
try
{
var parentProcess = Process.GetProcessById(parentProcessId);
parentProcess.WaitForExit();
}
catch (Exception e)
{
//main process probably died already
Debug.WriteLine(e);
}
await Task.Delay(1000); //wait a bit before exiting
Debug.WriteLine("BrowserSubprocess shutting down forcibly.");
Environment.Exit(0);
}
}
And my BrowserSubprocessPath is my main exe.
settings.BrowserSubprocessPath = System.AppDomain.CurrentDomain.FriendlyName;

I finally managed to rename this sub process! Haven't found any solution how to do it through the CefSharp API, but found my own worked solution.
So, In your code that uses CefSharp add one setting to the Cef Settings, before Cef.Initialize()
using CefSharp;
using CefSharp.Wpf;
using System.IO;
using System.Reflection;
using System.Windows;
public App()
{
var settings = new CefSettings
{
BrowserSubprocessPath = Path.Combine(GetAppPath(), $#"runtimes\win-x64\native{ GetAppName() }.exe")
};
Cef.InitializeAsync(settings);
}
private static string GetAppPath()
{
return new FileInfo(Assembly.GetExecutingAssembly().Location).DirectoryName;
}
private static string GetAppName()
{
return Assembly.GetExecutingAssembly().GetName().Name;
}
After this go to the bin\Debug\net6.0-windows\runtimes\win-x64\native\ and rename CefSharp.BrowserSubprocess.exe to Name you want to use.
Done. Now it will use this file with custom name you need.
P.S. For the auto name set you can always use Post-Build event with command to rename the file after project built and set the name same as your assembly name. I use this approach for my needs.

Related

C# Process.Start Causing AccessViolationException Randomly

I have been tackling this issue for 3 months now.
Error I am Getting in Native Debugging:
"Exception thrown at 0x5A222FC2 (comct123.dll) in FileReader.exe:
0xC0000005: Access violation reading location 0x0000000C."
Normal Debug:
'System.AccessVioliationException' in System.Windows.Forms.dll
My setup is really simple:
public static Form_Interface Interface;
public static void Initialize()
{
Application.SetCompatibleTextRenderingDefault(false);
Interface = new Form_Interface();
Interface.Filesdgv.DataSource = File.SortableBindingList;
Application.Run(Interface);
}
Seems simple enough, right? No.
So basically I have a simple Event that simply opens the file using Process.Start() and no matter what I do it will randomly crash with 'System.AccessVioliationException' in System.Windows.Forms.dll here:
private void Filesdgv_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
Filesdgv.Invoke((Action)(() =>
{
try
{
int rowIndex = e.RowIndex;
if (rowIndex >= 0)
{
int columnIndex = e.ColumnIndex;
File file = (File)((DataGridView)sender).Rows[rowIndex].DataBoundItem;
switch (columnIndex)
{
case 0:
{
Process.Start(file.Location);
}
break;
}
}
}
catch
{
// This fking catch never works anyway.
}
}));
}
private void FileInterface_Load(object sender, EventArgs e)
{
foreach (string oCurrent in Directory.GetFiles(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), "Files To Check")))
if (oCurrent.EndsWith(".pdf", StringComparison.OrdinalIgnoreCase))
new File(oCurrent.Split('\\').Last(), oCurrent);
}
It doesn't matter if I am opening files/links or anything else, it still behaves in the same way.
The link and file location is a readonly field as well.
I have many other uses for reading row data and it never crashes, even if i spam click 10000 times, It will only crash randomly with Process.Start()
Things I tried:
Using BeginInvoke
Using Invoke
Not Using Invoke/BeginInvoke
Putting File Link into a string before reading it.
Using multiple Try Catch
Recoded on another machine... same results there aswell.
I tried using File.Open (either doesn't open the file or throws same error lmao)
Tried using [HandleProcessCorruptedStateExceptions], still won't catch the exception.
Dosen't matter if i click slow or fast still 1/30 chance it happens.
Tried Putting Task.Run(() => Process.Start()); you'd think that a thread will protect you from an exception? no still crashes...
File Class looks like this:
public class File
{
public static SortableBindingList<File> SortableBindingList = new SortableBindingList<File>(new List<File>());
public readonly string fileName;
public readonly string filePath;
public void AddRow()
{
Client.Interface.Invoke((Action)(() =>
{
lock (SortableBindingList)
if (!SortableBindingList.Contains(this))
SortableBindingList.Add(this);
}));
}
public string FileName
{
get
{
return fileName;
}
}
public string Location
{
get
{
return filePath;
}
}
public File(string fileName, string filePath)
{
this.fileName = fileName;
this.filePath = filePath;
AddRow();
}
}
Initalize() is called in static void Main(string[] args) btw.
There are no other threads running editing stuff or anything like that, the only thread running is the form thread. which waits for user input.
Solutions I am looking for:
Alternative Method to launch files/hyperlinks.
A way to avoid form crashing (try catch style)
Crashes even with static data!:
Other threads running although these were not started by me.
Task.Run(() =>
{
Thread.Sleep(100);
Process.Start("https://www.youtube.com");
});
This has fixed my issues, it seems that when trying to immediately run "process.start" during a click event, the GUI unfocusing + starting a new process the exact same moment causes an Exception. (Microsoft pls fix.)

Wait for application launch without using Thread.Sleep() using FLAUI

I am new to using FLAUI and Automation Testing and would like to use it to test my system. At the moment I am using a Thread.Sleep() to wait till the application launches to then find the Login textbox. Is there a more efficient way to do this rather than using Thread.Sleep()?
At the moment i launch the application and use Thread.sleep(10000) to wait until the applicationis fully launched and that the logIn textbox is find-able before clicking on the control to input the password to enter the application. However I understand that Thread.Sleep is the worst way to tell the system to wait especially in automated tests. Could anyone offer any other things i could test out?
It is always the best to use Retry mechanism and wait until your main window loads and controls are visible. For example, after calling Application.Launch you can retry up to 30 seconds to find main window, and txtLogin in it:
Retry.WhileException(() =>
{
using (var automation = new UIA3Automation())
{
Window mainWindow = Application.GetMainWindow(automation, TimeSpan.FromSeconds(60));
Assert.IsNotNull(Mainwindow, "Main window is not found");
TextBox loginTextBox = mainWindow.FindFirstDescendant(x => x.ByAutomationId("txtLogin")).AsTextBox();
Assert.IsNotNull(loginTextBox, "txtLogin is not found");
}
}, TimeSpan.FromSeconds(30), null, true);
The question already has good answers, but I found another way to wait for any element (including main window) using the Retry class in FlaUI.Core.Tools.Retry class
[TestFixture]
public class SmokeTests
{
private Application _theApp;
private UIA3Automation _automation;
private Window _mainWindow;
private const int BigWaitTimeout = 3000;
private const int SmallWaitTimeout = 1000;
[SetUp]
public void Setup()
{
_theApp = FlaUI.Core.Application.Launch(new ProcessStartInfo("YOUR_APPLICATION.exe", "/quickStart"));
_automation = new UIA3Automation();
_mainWindow = _theApp.GetMainWindow(_automation);
}
[TearDown]
public void Teardown()
{
_automation?.Dispose();
_theApp?.Close();
}
[Test]
public void Foo()
{
// This will wait until the element is available, or timeout passed
var examplesWrapPanel = WaitForElement(() => _mainWindow.FindFirstDescendant(cf => cf.ByAutomationId("ExamplesWrapPanel")));
// This will wait for the child element or timeout
var exampleButton = WaitForElement(() => examplesWrapPanel?.FindFirstDescendant(cf => cf.ByAutomationId("Another Automation Id")).AsButton());
// Do something with your elements
exampleButton?.WaitUntilClickable();
exampleButton?.Invoke();
}
private T WaitForElement<T>(Func<T> getter)
{
var retry = Retry.WhileNull<T>(
() => getter(),
TimeSpan.FromMilliseconds(BigWaitTimeout));
if (!retry.Success)
{
Assert.Fail("Failed to get an element within a wait timeout");
}
return retry.Result;
}
}
}
private void RunProc()
{
Process.Start("exeName");
}
public async Task StartProcessAsync()
{
var result= await Task.Run(()=>RunProc());
//optional
Task.Delay(new TimeSpan.FromSeconds(5));
}
Did you try this solution?
public static void LaunchApplication(string exePath, string arguments, bool waitForExit, bool waitForStart, int waitForStartTimeout)
{
ProcessStartInfo thisProcessInfo = new ProcessStartInfo();
thisProcessInfo.CreateNoWindow = true;
thisProcessInfo.UseShellExecute = false;
thisProcessInfo.RedirectStandardOutput = false;
thisProcessInfo.FileName = exePath;
thisProcessInfo.Arguments = arguments;
using(Process thisProcess = Process.Start(thisProcessInfo))
{
if(waitForStart)
thisProcess.WaitForInputIdle(waitForStartTimeout);
if(waitForExit)
thisProcess.WaitForExit();
}
}

Handling collection events with MongoDB C# driver (v2.0)

Playing with the new MongoDB driver (v2.0) has been quite challenging. Most of the examples you find on the web still refer to the legacy driver. The reference manual for v2.0 on the official Mongo site is "terse", to say the least.
I'm attempting to do a simple thing: detect when a collection has been changed in order to forward a C# event to my server application.
For doing so, I've found the following C# example (see below) that I'm trying to convert to the new API.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using MongoDB.Bson;
using MongoDB.Driver;
using MongoDB.Driver.Builders;
namespace TestTailableCursor {
public static class Program {
public static void Main(string[] args) {
try {
var server = MongoServer.Create("mongodb://localhost/?safe=true");
var database = server["test"];
if (database.CollectionExists("capped")) {
database.DropCollection("capped");
}
var collectionOptions = CollectionOptions.SetCapped(true).SetMaxDocuments(5).SetMaxSize(10000);
var commandResult = database.CreateCollection("capped", collectionOptions);
var collection = database["capped"];
// to test the tailable cursor manually insert documents into the test.capped collection
// while this program is running and verify that they are echoed to the console window
// see: http://www.mongodb.org/display/DOCS/Tailable+Cursors for C++ version of this loop
BsonValue lastId = BsonMinKey.Value;
while (true) {
var query = Query.GT("_id", lastId);
var cursor = collection.Find(query)
.SetFlags(QueryFlags.TailableCursor | QueryFlags.AwaitData)
.SetSortOrder("$natural");
using (var enumerator = (MongoCursorEnumerator<BsonDocument>) cursor.GetEnumerator()) {
while (true) {
if (enumerator.MoveNext()) {
var document = enumerator.Current;
lastId = document["_id"];
ProcessDocument(document);
} else {
if (enumerator.IsDead) {
break;
}
if (!enumerator.IsServerAwaitCapable) {
Thread.Sleep(TimeSpan.FromMilliseconds(100));
}
}
}
}
}
} catch (Exception ex) {
Console.WriteLine("Unhandled exception:");
Console.WriteLine(ex);
}
Console.WriteLine("Press Enter to continue");
Console.ReadLine();
}
private static void ProcessDocument(BsonDocument document)
{
Console.WriteLine(document.ToJson());
}
}
}
A few (related) questions:
Is that the right approach with the new driver?
If so, how do I set collection options (like SetCap in the example above). The new API includes something called "CollectionSettings", which seems totally
unrelated.
Is my only option to rely on the legacy driver?
Thanks for your help.
Is my only option to rely on the legacy driver?
No.
[...] how do I set collection options (like SetCap in the example above). The new API includes something called "CollectionSettings", which seems totally unrelated.
There's CreateCollectionSettings now. CollectionSettings is a setting for the driver, i.e. a way to specify default behavior per-collection. CreateCollectionOptions can be used like this:
db.CreateCollectionAsync("capped", new CreateCollectionOptions
{ Capped = true, MaxDocuments = 5, MaxSize = 10000 }).Wait();
Is that the right approach with the new driver?
I think so, tailable cursors are a feature of the database, and avoiding polling always makes sense.
I converted the gist of the code and it appears to work on my machineā„¢:
Be careful when using .Result and .Wait() in a web or UI application.
private static void ProcessDocument<T>(T document)where T : class
{
Console.WriteLine(document.ToJson());
}
static async Task Watch<T>(IMongoCollection<T> collection) where T: class
{
try {
BsonValue lastId = BsonMinKey.Value;
while (true) {
var query = Builders<T>.Filter.Gt("_id", lastId);
using (var cursor = await collection.FindAsync(query, new FindOptions<T> {
CursorType = CursorType.TailableAwait,
Sort = Builders<T>.Sort.Ascending("$natural") }))
{
while (await cursor.MoveNextAsync())
{
var batch = cursor.Current;
foreach (var document in batch)
{
lastId = document.ToBsonDocument()["_id"];
ProcessDocument(document);
}
}
}
}
}
catch (Exception ex) {
Console.WriteLine("Unhandled exception:");
Console.WriteLine(ex);
}
}

Application.Quit() doesn't quit the application

I have this simple piece of code in C# with GTK#:
Main.cs:
using System;
using Gtk;
namespace Apu{
class MainClass{
public static void Main(string[] args){
Application.Init();
new ShowForm();
Application.Run();
}
}
}
ShowForm.cs
public partial class ShowForm: Gtk.Window{
public ShowForm(): base(Gtk.WindowType.Toplevel){
MessageDialog md = new MessageDialog(
this,
DialogFlags.DestroyWithParent,
MessageType.Error,
ButtonsType.None,
"Test"
);
md.SetPosition(Gtk.WindowPosition.CenterAlways);
md.Title = "Test window";
md.AddButton("Don't stop", ResponseType.Ok);
md.AddButton("Stop", ResponseType.Cancel);
ResponseType result = (ResponseType)md.Run();
if (result.Equals(ResponseType.Cancel)) {
Console.WriteLine("Quit!");
md.DestroyEvent += delegate {
Application.Quit();
};
/*md.DeleteEvent += delegate {
Application.Quit();
};*/
}
md.Destroy();
}
}
Console outputs Quit!, but the program doesn't quit. Neither DestroyEvent nor DeleteEvent works. Can anyone explain why? This is my first app in c#, my first time using gtk#. I use monodevelop as my IDE.
EDIT
Application.Exit() gives error: Gtk.Application does not contain a definition for 'Exit'.
If you wish to close the process, try Environment.Exit(0)
Try:
md.Destroyed += delegate {
Application.Quit();
};

Is there a "first run" flag in WP7

I would like to know if there is a "first run" flag or similar in WP7. My app takes some stuff out of isolated storage so I would like to determine if this is necessary first time. I am currently using an if to check if the named storage object exists but this means I can't handle any memory loss errors in the way I would like.
I don't think there is a built in feature for this ... but I know what you mean :-) I implemented "first run" myself using iso storage in the open source khan academy for windows phone app. All I do is look in iso storage for a very small file (I just write one byte to it) ... if it's not there, it's the first time, if it is there, the app has been run more than once. Feel free to check out the source and take my implementation if you'd like :-)
private static bool hasSeenIntro;
/// <summary>Will return false only the first time a user ever runs this.
/// Everytime thereafter, a placeholder file will have been written to disk
/// and will trigger a value of true.</summary>
public static bool HasUserSeenIntro()
{
if (hasSeenIntro) return true;
using (var store = IsolatedStorageFile.GetUserStoreForApplication())
{
if (!store.FileExists(LandingBitFileName))
{
// just write a placeholder file one byte long so we know they've landed before
using (var stream = store.OpenFile(LandingBitFileName, FileMode.Create))
{
stream.Write(new byte[] { 1 }, 0, 1);
}
return false;
}
hasSeenIntro = true;
return true;
}
}
As #HenryC suggested in a comment on the accepted answer I have used IsolatedStorageSettings to implement "First Run behaviour", here is the code:
private static string FIRST_RUN_FLAG = "FIRST_RUN_FLAG";
private static IsolatedStorageSettings settings = IsolatedStorageSettings.ApplicationSettings;
public bool IsFirstRun()
{
if (!settings.Contains(FIRST_RUN_FLAG))
{
settings.Add(FIRST_RUN_FLAG, false);
return true;
}
else
{
return false;
}
}
Sometimes we need to perform some action on every update from Windows store if there is version change. Put this code in your App.xaml.cs
private static string FIRST_RUN_FLAG = "FIRST_RUN_FLAG";
private static IsolatedStorageSettings settings = IsolatedStorageSettings.ApplicationSettings;
private static string _CurrentVersion;
public static string CurrentVersion
{
get
{
if (_CurrentVersion == null)
{
var versionAttribute = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyFileVersionAttribute), true).FirstOrDefault() as AssemblyFileVersionAttribute;
if (versionAttribute != null)
{
_CurrentVersion = versionAttribute.Version;
}
else _CurrentVersion = "";
}
return _CurrentVersion;
}
}
public static void OnFirstUpdate(Action<String> action)
{
if (!settings.Contains(FIRST_RUN_FLAG))
{
settings.Add(FIRST_RUN_FLAG, CurrentVersion);
action(CurrentVersion);
}
else if (((string)settings[FIRST_RUN_FLAG]) != CurrentVersion) //It Exits But Version do not match
{
settings[FIRST_RUN_FLAG] = CurrentVersion;
action(CurrentVersion);
}
}

Categories