Вопрос или проблема
Я пишу программу на CefSharp.WinForms, которая может автоматически управлять моим веб-сайтом. Она будет работать нормально, если моё приложение будет видно на экране, но если приложение будет сведено на панель задач, оно перестанет работать, в частности iframe перестанет воспроизводиться.
Существует ли способ минимизировать приложение и при этом воспроизводить iframe с помощью WinForms C#? Потому что мне нужно запустить это на сервере, и когда я выйду из удаленного подключения, оно перестанет работать.
Вот код в моей форме Form1:
using CefSharp;
using CefSharp.WinForms;
using System.Threading.Tasks;
namespace WinFormsAppElearning
{
public partial class Form1 : Form
{
private ChromiumWebBrowser browser;
private bool isLoginAttempted = false;
private string userId = "defaultUserId";
private bool isRunningInBackground = false;
public Form1(string[] args)
{
InitializeComponent();
this.Resize += new EventHandler(Form1_Resize);
}
private void Form1_Resize(object sender, EventArgs e)
{
if (this.WindowState == FormWindowState.Minimized && !isRunningInBackground)
{
browser.Focus();
isRunningInBackground = true;
Task.Run(() =>
{
while (this.WindowState == FormWindowState.Minimized)
{
CheckProgressBar();
Thread.Sleep(1000);
}
isRunningInBackground = false;
});
}
}
private void InitializeChromium()
{
if (!(Cef.IsInitialized ?? false))
{
var settings = new CefSettings
{
WindowlessRenderingEnabled = true,
PersistSessionCookies = true
};
settings.PersistSessionCookies = true;
userId = txtUsername.Text;
string cachePath = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Elearning", "Cache_" + userId);
settings.RootCachePath = cachePath;
// Уберите комментарий для отладки на удаленном устройстве
// settings.RemoteDebuggingPort = 8088;
Cef.Initialize(settings);
}
this.FormBorderStyle = FormBorderStyle.Sizable;
this.WindowState = FormWindowState.Maximized;
this.Bounds = Screen.PrimaryScreen.Bounds;
browser = new ChromiumWebBrowser("http://example.com/login")
{
Dock = DockStyle.Fill
};
this.Controls.Add(browser);
browser.IsBrowserInitializedChanged += OnBrowserInitialized;
browser.LoadingStateChanged += OnLoadingStateChanged;
}
private void OnBrowserInitialized(object sender, EventArgs e)
{
if (browser.IsBrowserInitialized)
{
// Уберите комментарий, чтобы показать инструменты разработчика
// browser.ShowDevTools();
// browser.ExecuteScriptAsync("console.log('Browser Initialized');");
}
}
private void OnLoadingStateChanged(object sender, LoadingStateChangedEventArgs e)
{
if (!e.IsLoading)
{
Invoke(new Action(() =>
{
if (browser.Address.Contains("scorm/player.php"))
{
txtUsername.Visible = false;
txtPassword.Visible = false;
btnLogin.Visible = false;
CheckProgressBar();
}
else
{
CheckForLogin();
}
}));
}
}
private void btnLogin_Click(object sender, EventArgs e)
{
string username = txtUsername.Text;
string password = txtPassword.Text;
if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password))
{
MessageBox.Show("Проверьте имя пользователя и пароль");
return;
}
InitializeChromium();
browser.Load("http://example.com/login");
}
private void CheckForLogin()
{
if (!isLoginAttempted)
{
string username = txtUsername.Text;
string password = txtPassword.Text;
if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password))
{
return;
}
// Вставьте скрипт для заполнения имени пользователя и пароля, затем отправьте форму
browser.ExecuteScriptAsync($"document.getElementById('username').value="{username}";");
browser.ExecuteScriptAsync($"document.getElementById('password').value="{password}";");
browser.ExecuteScriptAsync("document.querySelector('button[type=\"submit\"]').click();");
isLoginAttempted = true;
}
else if (!browser.Address.Contains("/login"))
{
browser.Load("http://example.com/mod/scorm/player.php?a=21¤torg=RSLBYTyDkzj5W_organization&scoid=67");
isLoginAttempted = false;
}
}
private void OnJavascriptMessageReceived(object sender, JavascriptMessageReceivedEventArgs e)
{
string currentUrl = e.Message.ToString();
MessageBox.Show(currentUrl);
this.Invoke(new Action(() =>
{
this.Text += currentUrl;
}));
}
private bool isPlayButtonClicked = false;
private async void CheckProgressBar()
{
string currentUrl = browser.Address;
if (currentUrl == "http://example.com/mod/scorm/player.php?a=21¤torg=RSLBYTyDkzj5W_organization&scoid=67")
{
if (this.InvokeRequired)
{
this.Invoke((MethodInvoker)delegate
{
btnLab.Visible = true;
});
}
else
{
btnLab.Visible = true;
}
btnLab.Text = "Текст";
}
else if (currentUrl == "http://example.com/mod/scorm/player.php?a=22¤torg=WlfhdtXvGehiY_organization&scoid=70")
{
if (this.InvokeRequired)
{
this.Invoke((MethodInvoker)delegate
{
btnLab.Visible = true;
});
}
else
{
btnLab.Visible = true;
}
btnLab.Text = "Текст";
}
else
{
if (this.InvokeRequired)
{
this.Invoke((MethodInvoker)delegate
{
btnLab.Visible = true;
});
}
else
{
btnLab.Visible = true;
}
btnLab.Text = "ТЕКСТ";
}
string script2 = @"
function checkProgressBar() {
return new Promise(function(resolve) {
let attempt = 0;
const maxAttempts = Number.MAX_SAFE_INTEGER;
let checkInterval = 1000;
function check() {
const iframe = document.getElementById('scorm_object');
if (!iframe) {
attempt++;
if (attempt >= maxAttempts) {
resolve('Не удалось найти iframe после нескольких попыток.');
return;
}
setTimeout(check, checkInterval);
return;
}
const innerDoc = iframe.contentDocument || iframe.contentWindow.document;
const progressLabel = innerDoc.querySelector('.progressbar__label');
const timeLabel = innerDoc.querySelector('.progressbar__label_type_time');
const preloader = innerDoc.querySelector('.preloader');
if (preloader && preloader.style.display !== 'none') {
resolve('Сессия истекла!');
return;
}
let totalProgresscheck = 10;
if (progressLabel) {
const progressValue = progressLabel.getAttribute('aria-label');
if (progressValue) {
const [completedProgress, totalProgress] = progressValue.trim().split("https://stackoverflow.com/");
if (timeLabel) {
const timeValue = timeLabel.getAttribute('aria-label');
if (timeValue) {
const [completedTime, totalTime] = timeValue.trim().split("https://stackoverflow.com/");
totalProgresscheck = totalTime;
console.log(completedProgress,totalProgress,completedTime,totalTime);
if (completedProgress !== totalProgress && completedTime === totalTime) {
setTimeout(1000);
const nextButton = innerDoc.querySelector('.universal-control-panel__button_next');
const playButton = innerDoc.querySelector('.universal-control-panel__button_play-pause');
console.log(playButton);
if (nextButton) {
if(playButton.getAttribute('aria-pressed') === 'false')
{
nextButton.dispatchEvent(new MouseEvent('mousedown', { bubbles: true, cancelable: true }));
nextButton.dispatchEvent(new MouseEvent('mouseup', { bubbles: true, cancelable: true }));
}
}
setTimeout(3000);
} else if (completedProgress == totalProgress && completedTime === totalTime) {
setTimeout(3000);
resolve('Следующий');
}
}
}
}
}
if (totalProgresscheck) {
let totalSeconds;
if (!isNaN(totalProgresscheck)) {
totalSeconds = parseInt(totalProgresscheck, 10);
} else {
const [minutes, seconds] = totalProgresscheck.split(':').map(Number);
totalSeconds = (minutes * 60) + seconds;
}
checkInterval = totalSeconds / 10;
console.log(""Интервал проверки (секунды):"", checkInterval);
} else {
console.warn(""Недействительное значение totalProgress:"", totalProgress);
}
attempt++;
if (attempt >= maxAttempts) {
resolve('Сессия истекла!');
return;
}
setTimeout(check, checkInterval * 1000);
}
check();
});
}
checkProgressBar();
";
if (browser != null && !browser.IsDisposed && browser.IsBrowserInitialized)
{
var resultProgress = await browser.EvaluateScriptAsync(script2);
if (resultProgress.Success)
{
if (resultProgress.Result != null)
{
string resultMessage = resultProgress.Result.ToString();
if (resultMessage.Contains("Сессия истекла!"))
{
MessageBox.Show(this, resultMessage, "Уведомление", MessageBoxButtons.OK, MessageBoxIcon.Warning);
Application.Restart();
}
else if (resultMessage.Contains("Следующий"))
{
nextSlide();
}
}
}
}
}
private void nextSlide()
{
if (browser != null && !browser.IsDisposed && browser.IsBrowserInitialized)
{
browser.ExecuteScriptAsync("document.querySelector('.universal-control-panel__button_next').click();");
}
}
}
}
Я также пытался использовать:
WindowlessRenderingEnabled = true;
но это неэффективно
Кто-нибудь, пожалуйста, помогите мне
Ответ или решение
Чтобы обеспечить активное выполнение вашего iframe внутри приложения на базе CefSharp.WinForms, даже когда приложение свернуто, необходимо провести несколько изменений в коде и принять во внимание некоторые ограничения платформы Windows и архитектуры браузера CefSharp.
1. Понимание проблемы
Когда приложение Windows Forms сворачивается, оно останавливает все действия, связанные с его визуализацией и взаимодействием. Ваша задача — обеспечить воспроизведение контента в iframe, даже когда окно приложения находится в состоянии минимизации. Приложение CefSharp, как правило, ожидает взаимодействия пользователя, которое прекращается при сворачивании окна.
2. Решение
Использование фонового процесса
Одним из возможных решений будет использование фонового потока для управления интерактивными действиями iframe, сохраняя состояние браузера CefSharp работающим в фоновом режиме. Вот основные шаги, которые вам необходимо будет выполнить:
- Создайте фоновый поток для выполнения действий, таких как продолжение взаимодействия с iframe.
- Убедитесь, что ваши действия не требуют прямого взаимодействия с пользователем, так как это может вызвать проблемы.
Обновленный код
Вот пример обновленного кода, который требует минимизации:
private async void CheckProgressBar()
{
while (this.WindowState == FormWindowState.Minimized)
{
await Task.Delay(1000); // Установите задержку, чтобы избежать излишней нагрузки на процессор
}
string currentUrl = browser.Address;
// Продолжайте выполнение кода, как было раньше
}
3. Активация фонового выполнения
Вам нужно будет убедиться, что ваш браузер может продолжать инициализацию и выполнение Javascript в фоновом режиме:
- Используйте
Task.Run()
для выполнения ваших циклов и проверок.
4. Производительность и ограничения
Учтите, что даже с фоновым потоком некоторые действия могут быть ограничены, и браузер по-прежнему не сможет реагировать на многие события. Поэтому, чтобы ваш функционал работал корректно:
- Тестируйте работу с разными сценариями: установка флагов для выхода из фона, взаимодействие с другими элементами и управление состояниями и событиями.
- Возможно, вам придется использовать указание времени ожидания в коде проверки прогресса (насколько быстро браузер может реагировать на ваши команды).
5. Заключение
Подводя итоги, для решения вашей задачи необходимо использовать фоновые потоки для управления состоянием iframe и обеспечивать, чтобы логика вашего приложения могла работать независимо от визуального состояния. Убедитесь, что выполнение всех операций синхронно с состоянием браузера, чтобы избежать конфликтов.
Если вы столкнетесь с дополнительными трудностями, рекомендуем проконсультироваться с документацией CefSharp и сообществом разработчиков, чтобы найти эффективные решения для вашей конкретной задачи.