Поддерживать значение переменной между запусками программы

У меня есть простое консольное приложение на C#, которое планируется через каждые 5 минут. Каждый вызов программы требует вывода последнего запуска.

Сейчас я использую текстовый файл и сохраняю в нем результат. в следующий раз, когда он запустится, он откроет текстовый файл и узнает результаты предыдущего запуска.

Есть ли другой способ сделать это, который не требует такого текстового файла? Как поддерживать переменную сеанса и т.д.?

3 ответа

Решение

Сначала используйте решение блокировки мьютекса, чтобы приложение одновременно запускало только один экземпляр.

затем создайте сериализуемый класс для хранения состояния приложения и вспомогательный класс для загрузки и сохранения его в файл.

[XmlRoot("RegexTesterPersistantSettings")]
[Serializable]
public class State
{
    public State()
    {
        this.Pattern = string.Empty;
        this.TestString = string.Empty;
        this.Options = 0;
    }
    [XmlElement("Pattern")]
    public string Pattern{get;set;}

    [XmlElement("TestString")]
    public string TestString{get;set;}

    [XmlElement("Options")]
    public int Options { get; set; }

    public override int GetHashCode()
    {
        return this.Options.GetHashCode() ^ this.Pattern.GetHashCode() ^ this.TestString.GetHashCode();
    }

    public override bool Equals(object obj)
    {
        State anotherState = obj as State;
        if (anotherState == null)
        {
            return false;
        }

        return this.Equals(anotherState);
    }

    public bool Equals(State anotherState)
    {
        return this.GetHashCode() == anotherState.GetHashCode();
    }

    public static bool operator ==(State a, State b)
    {
        // If both are null, or both are same instance, return true.
        if (System.Object.ReferenceEquals(a, b))
        {
            return true;
        }

        // If one is null, but not both, return false.
        if (((object)a == null) || ((object)b == null))
        {
            return false;
        }
        return a.Equals(b);
    }

    public static bool operator !=(State a, State b)
    {
        return !a.Equals(b);
    }

}

public class PersistantHelper
{
    private string filename;
    private State _state;

    public PersistantHelper(string xmlFilename = "RegexTesterSettings")
    {
        string appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
        this.filename  = Path.Combine(appDataPath, xmlFilename);
    }

    private XmlSerializer _serializer;
    private XmlSerializer Serializer
    {
        get
        {
            if (this._serializer == null)
            {
                this._serializer = new XmlSerializer(typeof(State));
            }
            return this._serializer;
        }
    }

    private void SaveState(State state)
    {
        if (File.Exists(this.filename))
        {
            File.Delete(this.filename);
        }
        var stream  = new FileStream(this.filename,  FileMode.OpenOrCreate, FileAccess.Write,FileShare.None);
        this.Serializer.Serialize(stream, state);
        stream.Close();
    }

    public State State
    {
        get
        {
            if (this._state == null)
            {
                this._state = this.GetState();
            }
            return this._state;
        }
        set 
        {
            if (this.State != value)
            {
                this.SaveState(value);
            }
        }
    }

    private State dummyState = new State() { Options = 0 };
    private State GetState()
    {
        if (!File.Exists(this.filename))
        {
            return this.dummyState;
        }
        Stream stream = null;
        try
        {
            stream = new FileStream(this.filename, FileMode.Open, FileAccess.Read,FileShare.None);
            var o = this.Serializer.Deserialize(stream);
            return (State)o;
        }
        catch
        {
            return this.dummyState;
        }
        finally
        {
            if (stream != null)
            {
                stream.Close();
            }
        }

    }
}

затем загрузите и сохраните состояние из вашего приложения:

    private PersistantHelper persistantHelper;
    public frmTester()
    {
        InitializeComponent();
        this.persistantHelper = new PersistantHelper();
        .
        .
        .
    }

private void LoadPersistantData()
{
    State state = this.persistantHelper.State;
    this.txtPattern.Text = state.Pattern;
    this.txtTest.Text = state.TestString;
    foreach (Control c in this.pnlOptions.Controls)
    {
        if (c is CheckBox)
        {
            var chk = c as CheckBox;
            int tag = int.Parse(c.Tag.ToString());
            chk.Checked = (state.Options & tag) == tag;
        }
    }
}


private void SavePersistantData()
{
    this.persistantHelper.State = new State()
    {
        Options = (int)this.GetOptions(),
        Pattern = txtPattern.Text,
        TestString = txtTest.Text
    };
}       

Вы можете использовать файл настроек. Просто добавьте файл настроек в ваш проект и сохраняйте значение, которое вы хотите сохранить, каждый раз, когда программа закрывается. Когда программа запустится, проверьте значение в файле настроек.

Ну, вы должны сохранить данные где-нибудь. Консольные приложения не имеют переменных сеанса (или даже сеансов). Вы, вероятно, имеете в виду переменную среды. Если вы храните его в переменной окружения, программа должна будет работать из той же среды (то есть из того же окна консоли). В случае сбоя машины переменные среды теряются. Вы, вероятно, лучше с файлом.

Если это будет выполняться каждые 5 минут, не могли бы вы дать программе спать, пока она не запустится снова? Тогда данные будут доступны в памяти. Это все еще проблема, если машина дает сбой, поэтому вам все равно может понадобиться сохранить файл в качестве резервной копии.

Если вы не хотите, чтобы пользователи видели файл, и это не слишком много данных (хотя несколько байтов K, вероятно, будут работоспособны), как предложил @Quintin Robinson, вы можете использовать реестр. Просто убедитесь, что вы пишете в область, которая логична и где у вас есть необходимые разрешения.

Если вы не хотите, чтобы пользователи могли видеть, что находится в файле, вы можете зашифровать содержимое, чтобы сделать его нечитаемым. Если вы используете шифрование, вам потребуется ключ для дешифрования локально, чтобы опытный или решительный злоумышленник все же смог получить доступ к файлу. Может быть, лучше просто сжать данные и назвать это хорошо.

Если вы не хотите, чтобы пользователи могли легко изменять содержимое файла, вы можете сохранить контрольную сумму, хэш или HMAC с данными. Вы должны будете проверить это локально, что означает, что это может быть атаковано, но должно остановить случайного пользователя от взлома.

Конечно, вы можете объединить хранение реестра, шифрование и контрольную сумму, в зависимости от ваших потребностей и проблем.

Я подумывал упомянуть PStor и CryptProtectData/CryptUnprotectData, но потом понял, что их безопасность основана на USER и поэтому здесь не поможет.

Если машина подключена к Интернету, вы могли бы рассмотреть облачное хранилище, хотя я понятия не имею, подойдет ли это для вашего приложения.

Другие вопросы по тегам