1

I'm creating a C# program to assist me in testing websites I develop and am having some issues. The goal is to have the program store different accounts for my website along with their associated cookies so it can automatically log them in for testing. Right now I am using Playwright for the browser automation as well as the cookies storage (no issues with any of this). However, my issue seems to stem from the fact that FileSystemWatcher doesn't fire after I save my cookies.

My goal is to update my programs interface with the accounts I have saved each time I add a new one. To do this I am using the FileSystemWatcher to call a function that reloads all my user accounts from a file.

My function for creating the FileSystemWatcher looks like this:

private string filesPath = @"" + Path.GetDirectoryName(Application.ExecutablePath) + "\\inc";

private void CreateFileWatcher()
{
    FileSystemWatcher fsWatcher = new FileSystemWatcher(filesPath, "accounts.dat");
    fsWatcher.NotifyFilter = NotifyFilters.LastWrite;

    fsWatcher.Changed += new FileSystemEventHandler((object s, FileSystemEventArgs e) =>
    {
        // Load accounts
    });

    fsWatcher.EnableRaisingEvents = true;
}

I want to specifically watch that "accounts.dat" file so I've added it as the filter as you can see from my code.

For adding a new account my function looks like this:

private void CreateAccount() {
    Dictionary<Guid, Account> accounts;

    Stream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read);
    BinaryFormatter bf = new BinaryFormatter();

    try
    {
        accounts = (Dictionary<Guid, Account>)bf.Deserialize(stream);
        accounts.Add(account.UUID, account);
    }
    catch
    {
        accounts = new Dictionary<Guid, Account>();
        accounts.Add(account.UUID, account);
    }
    stream.Close();

    stream = new FileStream(fileName, FileMode.Truncate, FileAccess.Write);
    bf.Serialize(stream, accounts);
    stream.Close();

    // Then I call my login function that logs the account in and saves the cookies
}

My function for logging in is:

public static Task Login(Account account, string url) {
    try
    {
        using var playwright = await Playwright.CreateAsync();
        await using var browser = await playwright.Chromium.LaunchAsync(new()
        {
            Headless = false,
        });
        await using var context = await browser.NewContextAsync(new()
        {
            ViewportSize = new ViewportSize() { Height = 450, Width = 500 }
        });

        // Create page
        var page = await context.NewPageAsync();
        await page.GotoAsync(url);

        // Sign in
        await Login(page, account);

        // Save auth state
        string cookies = await context.StorageStateAsync();

        // Write cookies
        await WriteCookies(cookies, account);
    }
    catch (Exception e)
    {
        // Log to my logfile
    }
}

And lastly, my function for saving cookies is:

public static async Task WriteCookies(string cookies, Account account) {
    // Create list of accounts
    Dictionary<Guid, string> sessions;

    Stream stream = new FileStream(sessionsFile, FileMode.Open, FileAccess.Read);
    BinaryFormatter bf = new BinaryFormatter();

    try
    {
        sessions = (Dictionary<Guid, string>)bf.Deserialize(stream);
        sessions.Add(account.UUID, cookies);
    }
    catch
    {
        sessions = new Dictionary<Guid, string>();
        sessions.Add(account.UUID, cookies);
    }
    stream.Close();

    stream = new FileStream(sessionsFile, FileMode.Truncate, FileAccess.Write);
    bf.Serialize(stream, sessions);
    stream.Close();
}

As you can see from my code, each session is associated to a user account via the accounts Guid so I can easily access the correct session related to the account later. I thought the issue stemmed from the fact that my write cookies function was asynchronous so it was throwing off the FileSystemWatcher so I tried separating the sessions and accounts to fix this but have had no luck. I can't seem to figure out what I'm doing wrong but it seems like the event fires when the account is first created, then once I write the cookies it never fires again until I manually reload the form. I appreciate any help.

Also, right now I am opening the file to grab the existing data, closing it, then reopening it in Truncate mode as I want to clear the file each time so if you recommend a better way of adding new objects to my list I would love to hear them.

Thanks!

wolfy
  • 446
  • 4
  • 10
  • This might be a dumb question but have you checked to see if the file actually gets updated? Also, have you tried to look upon all file change events in FileSystemWatcher. (basically `fsWatcher.NotifyFilter = NotifyFilters.Attributes | NotifyFilters.CreationTime | NotifyFilters.FileName | NotifyFilters.LastWrite | NotifyFilters.Security | NotifyFilters.Size;`) – NavidM Dec 12 '21 at 18:20
  • @NvMat I feel like theres no such thing as a dumb question because as programmers we make the simple mistake all the time lol. But I have tried it with all the notify filters as well and had no success either. And yes I have checked the file and the correct account is properly updated each time – wolfy Dec 12 '21 at 18:24
  • My other guess would be that the FileSystemWatcher is not watching the right file so might be helpful to do just hardcode the path to check the filesystem watcher. – NavidM Dec 12 '21 at 18:28
  • So I recently tried something similar as a way to debug. I created the watcher with only the path to all the files. I then had it log out the file name each time it is triggered but it still has the same issue for some reason. – wolfy Dec 12 '21 at 18:31
  • What is the value of filesPath? Did you check that? Is it relative? If so, did you try to log out what it is when turned into an absolute path? – Bent Tranberg Dec 12 '21 at 18:33
  • The general thing is this: You should never assume that your relative paths are relative to the location of your executable, or anything else for that matter. You need to understand the difference between the current working directory, the disks' current directories, and the directory or directories of your binaries. When unit tests or (web) servers are involved, things become even more confusing. In .NET you have the tools necessary to handle all of this, so you just need to know what and where they are. – Bent Tranberg Dec 12 '21 at 18:38
  • @BentTranberg I have edited my code to show how I am grabbing my filesPath. If I'm doing this wrong I'd love to know because that would definitely be an issue! – wolfy Dec 12 '21 at 18:41
  • You are combining paths in a risky way. Do this instead: Path.Combine(Path.GetDirectoryName(Application.ExecutablePath), "inc") – Bent Tranberg Dec 12 '21 at 18:50
  • @BentTranberg Ill try that thank you! – wolfy Dec 12 '21 at 18:53

1 Answers1

2

Try creating the FileSystemWatcher as a program level variable and not as a local variable in the method and see if that sorts you out .

i.e. moved the FileSystemWatcher to a private local variable.

Vaibhav J
  • 1,316
  • 8
  • 15
  • 1
    Ya not sure why that was downvoted either, I want to give this a try as well. Are you saying you recommend I move `FileSystemWatcher fsWatcher = new FileSystemWatcher(filesPath, "accounts.dat");` to the top of my class as a private variable? – wolfy Dec 12 '21 at 19:02
  • Yes. If you create it inside a method like that, it will just vanish randomly at some point in time shortly after, as soon as the garbage collector sees appropriate. This is guaranteed to be a problem in your case. – Bent Tranberg Dec 12 '21 at 19:03
  • This was the solution, thank you all for the help! – wolfy Dec 12 '21 at 19:06