Как я мог бы запланировать службу Windows C# для выполнения задачи ежедневно?

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

test.h :

#if !defined(TEST_DLL_H)
#define TEST_DLL_H


#if defined(_WIN32)
#  if defined(TEST_EXPORTS)
#    define TEST_API __declspec(dllexport)
#  else
#    define TEST_API __declspec(dllimport)
#  endif
#  define CALLING_CONVENTION __cdecl
#else
#  define __TEXT(X) L##X
#  define TEXT(X) __TEXT(X)
#  define TEST_API
#  define CALLING_CONVENTION
#endif


namespace TestDll {
    typedef struct Simple_ {
        wchar_t a[1024];
    } Simple;

    extern "C" {
        TEST_API int CALLING_CONVENTION simple(Simple *pSimple);
        TEST_API int CALLING_CONVENTION printStr(char *pStr);
        TEST_API int CALLING_CONVENTION wprintWstr(wchar_t *pWstr);
        TEST_API wchar_t* CALLING_CONVENTION wstr();
        TEST_API void CALLING_CONVENTION clearWstr(wchar_t *pWstr);
    }
};

#endif  // TEST_DLL_H

test.cpp :

#define TEST_EXPORTS
#include "test.h"
#if defined(_WIN32)
#  include 
#else
#  include 
#  define __FUNCTION__ "function"
#endif
#include 
//#include 

#define PRINT_MSG_0() printf("From C: - [%s] (%d) - [%s]\n", __FILE__, __LINE__, __FUNCTION__)
#define WPRINT_MSG_0() wprintf(L"From C: - [%s] (%d) - [%s]\n", TEXT(__FILE__), __LINE__, TEXT(__FUNCTION__))

#define DUMMY_TEXT_W L"Dummy text."


//using namespace std;


int TestDll::simple(Simple *pSimple) {
    //std::wcout << pSimple->a << std::endl;
    WPRINT_MSG_0();
    int ret = wprintf(L"%s", pSimple->a);
    wprintf(L"\n");
    return ret;
}


int TestDll::printStr(char *pStr) {
    PRINT_MSG_0();
    int ret = printf("%s", pStr);
    printf("\n");
    return ret;
}


int TestDll::wprintWstr(wchar_t *pWstr) {
    WPRINT_MSG_0();
    int ret = wprintf(L"%s", pWstr);
    wprintf(L"\n");
    int len = wcslen(pWstr);
    char *buf = (char*)pWstr;
    wprintf(L"Hex (%d): ", len);
    for (int i = 0; i < len * sizeof(wchar_t); i++)
        wprintf(L"%02X ", buf[i]);
    wprintf(L"\n");
    return ret;
}


wchar_t *TestDll::wstr() {
    wchar_t *ret = (wchar_t*)malloc((wcslen(DUMMY_TEXT_W) + 1) * sizeof(wchar_t));
    wcscpy(ret, DUMMY_TEXT_W);
    return ret;
}


void TestDll::clearWstr(wchar_t *pWstr) {
    free(pWstr);
}

main.cpp :

#include "test.h"
#include 
#if defined(_WIN32)
#  include 
#endif


int main() {
    char *text = "Hello, world!";
    TestDll::Simple s = { TEXT("Hello, world!") };
    int ret = simple(&s);  // ??? Compiles even if namespace not specified here !!!
    printf("\"simple\" returned %d\n", ret);
    ret = TestDll::printStr("Hello, world!");
    printf("\"printStr\" returned %d\n", ret);
    ret = TestDll::wprintWstr(s.a);
    printf("\"wprintWstr\" returned %d\n", ret);
    return 0;
}

code.py :

#!/usr/bin/env python3

import sys
import ctypes


DLL_NMAME = "./test.dll"
DUMMY_TEXT = "Hello, world!"


WCharArr1024 = ctypes.c_wchar * 1024

class SimpleStruct(ctypes.Structure):
    _fields_ = [
        ("a", WCharArr1024),
    ]


def main():

    test_dll = ctypes.CDLL(DLL_NMAME)

    simple_func = test_dll.simple
    simple_func.argtypes = [ctypes.POINTER(SimpleStruct)]
    simple_func.restype = ctypes.c_int
    stuct_obj = SimpleStruct(a=DUMMY_TEXT)

    print_str_func = test_dll.printStr
    print_str_func.argtypes = [ctypes.c_char_p]
    print_str_func.restype = ctypes.c_int

    wprint_wstr_func = test_dll.wprintWstr
    wprint_wstr_func.argtypes = [ctypes.c_wchar_p]
    wprint_wstr_func.restype = ctypes.c_int

    wstr_func = test_dll.wstr
    wstr_func.argtypes = []
    wstr_func.restype = ctypes.c_wchar_p

    clear_wstr_func = test_dll.clearWstr
    clear_wstr_func.argtypes = [ctypes.c_wchar_p]
    clear_wstr_func.restype = None

    #print("From PY: [{:s}]".format(stuct_obj.a))
    ret = simple_func(ctypes.byref(stuct_obj))
    print("\"{:s}\" returned {:d}".format(simple_func.__name__, ret))
    ret = print_str_func(DUMMY_TEXT.encode())
    print("\"{:s}\" returned {:d}".format(print_str_func.__name__, ret))
    #ret = wprint_wstr_func(ctypes.cast(DUMMY_TEXT.encode(), ctypes.c_wchar_p))
    ret = wprint_wstr_func(DUMMY_TEXT)
    print("\"{:s}\" returned {:d}".format(wprint_wstr_func.__name__, ret))
    s = wstr_func()
    print("\"{:s}\" returned \"{:s}\"".format(wstr_func.__name__, s))
    #clear_wstr_func(s)


if __name__ == "__main__":
    #print("Python {:s} on {:s}\n".format(sys.version, sys.platform))
    main()

Изменения :

  • Удалено ] Слой C ++ (чтобы исключить как можно больше переменных) и полагаться только на C
  • адаптированный код для соответствия Nix (я выполнил это на Ubtu , но я столкнулся с другими проблемами, которые я не собираюсь обсуждать)
  • Добавил больше функций (это был процесс отладки), чтобы собрать как можно больше информации [1159 ]
  • Произошли некоторые переименования, рефакторинги и другие не важные изменения
  • Во время исследования я обнаружил забавную проблему (комментарий из main.cpp ). Видимо, простая простая функция компилируется, даже если я не добавляю пространство имен, в котором она объявлена. Это не относится к другим функциям. После некоторых быстрых попыток я понял, что это из-за аргумента Simple (возможно, потому что это также часть пространства имен?). Во всяком случае, не провел слишком много времени и не докопался до этого (пока), вероятно, это Неопределенное поведение (и это работает только из-за глупой удачи) [ 1161]
  • Узкие и широкие функции смешаны, это НЕТ - НЕТ , и только для целей отладки / демонстрации

Вывод :

e:\Work\Dev\StackOverflow\q054269984>"c:\Install\x86\Microsoft\Visual Studio Community\2015\vc\vcvarsall.bat" x64

e:\Work\Dev\StackOverflow\q054269984>dir /b
code.py
main.cpp
test.cpp
test.h

e:\Work\Dev\StackOverflow\q054269984>cl /nologo /DDLL /DUNICODE /MD /EHsc test.cpp  /link /NOLOGO /DLL /OUT:test.dll
test.cpp
   Creating library test.lib and object test.exp

e:\Work\Dev\StackOverflow\q054269984>cl /nologo /DUNICODE /MD /EHsc main.cpp  /link /NOLOGO /OUT:main.exe test.lib
main.cpp

e:\Work\Dev\StackOverflow\q054269984>dir /b
code.py
main.cpp
main.exe
main.obj
test.cpp
test.dll
test.exp
test.h
test.lib
test.obj

e:\Work\Dev\StackOverflow\q054269984>main.exe
From C: - [test.cpp] (23) - [TestDll::simple]
Hello, world!
"simple" returned 13
From C: - [test.cpp] (31) - [TestDll::printStr]
Hello, world!
"printStr" returned 13
From C: - [test.cpp] (39) - [TestDll::wprintWstr]
Hello, world!
Hex (13): 48 00 65 00 6C 00 6C 00 6F 00 2C 00 20 00 77 00 6F 00 72 00 6C 00 64 00 21 00
"wprintWstr" returned 13

e:\Work\Dev\StackOverflow\q054269984>"e:\Work\Dev\VEnvs\py_064_03.06.08_test0\Scripts\python.exe" code.py
Python 3.6.8 (tags/v3.6.8:3c6b436a57, Dec 24 2018, 00:16:47) [MSC v.1916 64 bit (AMD64)] on win32

F r o m   C :   -   [ t e s t . c p p ]   ( 2 3 )   -   [ T e s t D l l : : s i m p l e ]
 H e l l o ,   w o r l d !
 "simple" returned 13
From C: - [test.cpp] (31) - [TestDll::printStr]
Hello, world!
"printStr" returned 13
F r o m   C :   -   [ t e s t . c p p ]   ( 3 9 )   -   [ T e s t D l l : : w p r i n t W s t r ]
 H e l l o ,   w o r l d !
 H e x   ( 1 3 ) :   4 8   0 0   6 5   0 0   6 C   0 0   6 C   0 0   6 F   0 0   2 C   0 0   2 0   0 0   7 7   0 0   6 F   0 0   7 2   0 0   6 C   0 0   6 4   0 0   2 1   0 0
 "wprintWstr" returned 13
"wstr" returned "Dummy text."
blockquote>
  • Кажется, что это связано с Python
  • Сами строки не перепутаны (их длина и ] wprintf возвращаемое значение верное). Это больше похоже на то, что stdout является виновником

Затем я пошел дальше:

e:\Work\Dev\StackOverflow\q054269984>for /f %f in ('dir /b "e:\Work\Dev\VEnvs\py_064*"') do ("e:\Work\Dev\VEnvs\%f\Scripts\python.exe" code.py)

e:\Work\Dev\StackOverflow\q054269984>("e:\Work\Dev\VEnvs\py_064_02.07.15_test0\Scripts\python.exe" code.py )
Python 2.7.15 (v2.7.15:ca079a3ea3, Apr 30 2018, 16:30:26) [MSC v.1500 64 bit (AMD64)] on win32

From C: - [test.cpp] (23) - [TestDll::simple]
Hello, world!
"simple" returned 13
From C: - [test.cpp] (31) - [TestDll::printStr]
Hello, world!
"printStr" returned 13
From C: - [test.cpp] (39) - [TestDll::wprintWstr]
Hello, world!
Hex (13): 48 00 65 00 6C 00 6C 00 6F 00 2C 00 20 00 77 00 6F 00 72 00 6C 00 64 00 21 00
"wprintWstr" returned 13
"wstr" returned "Dummy text."

e:\Work\Dev\StackOverflow\q054269984>("e:\Work\Dev\VEnvs\py_064_03.04.04_test0\Scripts\python.exe" code.py )
Python 3.4.4 (v3.4.4:737efcadf5a6, Dec 20 2015, 20:20:57) [MSC v.1600 64 bit (AMD64)] on win32

From C: - [test.cpp] (23) - [TestDll::simple]
Hello, world!
"simple" returned 13
From C: - [test.cpp] (31) - [TestDll::printStr]
Hello, world!
"printStr" returned 13
From C: - [test.cpp] (39) - [TestDll::wprintWstr]
Hello, world!
Hex (13): 48 00 65 00 6C 00 6C 00 6F 00 2C 00 20 00 77 00 6F 00 72 00 6C 00 64 00 21 00
"wprintWstr" returned 13
"wstr" returned "Dummy text."

e:\Work\Dev\StackOverflow\q054269984>("e:\Work\Dev\VEnvs\py_064_03.05.04_test0\Scripts\python.exe" code.py )
Python 3.5.4 (v3.5.4:3f56838, Aug  8 2017, 02:17:05) [MSC v.1900 64 bit (AMD64)] on win32

F r o m   C :   -   [ t e s t . c p p ]   ( 2 3 )   -   [ T e s t D l l : : s i m p l e ]
 H e l l o ,   w o r l d !
 "simple" returned 13
From C: - [test.cpp] (31) - [TestDll::printStr]
Hello, world!
"printStr" returned 13
F r o m   C :   -   [ t e s t . c p p ]   ( 3 9 )   -   [ T e s t D l l : : w p r i n t W s t r ]
 H e l l o ,   w o r l d !
 H e x   ( 1 3 ) :   4 8   0 0   6 5   0 0   6 C   0 0   6 C   0 0   6 F   0 0   2 C   0 0   2 0   0 0   7 7   0 0   6 F   0 0   7 2   0 0   6 C   0 0   6 4   0 0   2 1   0 0
 "wprintWstr" returned 13
"wstr" returned "Dummy text."

e:\Work\Dev\StackOverflow\q054269984>("e:\Work\Dev\VEnvs\py_064_03.06.08_test0\Scripts\python.exe" code.py )
Python 3.6.8 (tags/v3.6.8:3c6b436a57, Dec 24 2018, 00:16:47) [MSC v.1916 64 bit (AMD64)] on win32

F r o m   C :   -   [ t e s t . c p p ]   ( 2 3 )   -   [ T e s t D l l : : s i m p l e ]
 H e l l o ,   w o r l d !
 "simple" returned 13
From C: - [test.cpp] (31) - [TestDll::printStr]
Hello, world!
"printStr" returned 13
F r o m   C :   -   [ t e s t . c p p ]   ( 3 9 )   -   [ T e s t D l l : : w p r i n t W s t r ]
 H e l l o ,   w o r l d !
 H e x   ( 1 3 ) :   4 8   0 0   6 5   0 0   6 C   0 0   6 C   0 0   6 F   0 0   2 C   0 0   2 0   0 0   7 7   0 0   6 F   0 0   7 2   0 0   6 C   0 0   6 4   0 0   2 1   0 0
 "wprintWstr" returned 13
"wstr" returned "Dummy text."

e:\Work\Dev\StackOverflow\q054269984>("e:\Work\Dev\VEnvs\py_064_03.07.02_test0\Scripts\python.exe" code.py )
Python 3.7.2 (tags/v3.7.2:9a3ffc0492, Dec 23 2018, 23:09:28) [MSC v.1916 64 bit (AMD64)] on win32

F r o m   C :   -   [ t e s t . c p p ]   ( 2 3 )   -   [ T e s t D l l : : s i m p l e ]
 H e l l o ,   w o r l d !
 "simple" returned 13
From C: - [test.cpp] (31) - [TestDll::printStr]
Hello, world!
"printStr" returned 13
F r o m   C :   -   [ t e s t . c p p ]   ( 3 9 )   -   [ T e s t D l l : : w p r i n t W s t r ]
 H e l l o ,   w o r l d !
 H e x   ( 1 3 ) :   4 8   0 0   6 5   0 0   6 C   0 0   6 C   0 0   6 F   0 0   2 C   0 0   2 0   0 0   7 7   0 0   6 F   0 0   7 2   0 0   6 C   0 0   6 4   0 0   2 1   0 0
 "wprintWstr" returned 13
"wstr" returned "Dummy text."
blockquote>

Как видно, поведение воспроизводимый начиная с Python 3,5 .

Я думал, что это из-за [Python]: PEP 529 - Измените кодировку файловой системы Windows на UTF-8 , но это доступно только с версии 3.6 .

Затем я начал читать (я даже пытался сделать различие между Python 3.4 и Python 3.5 ), но без особого успеха. Некоторые статьи, которые я прочитал:

Тогда я заметил [SO]: Выведите строки юникода в консольном приложении Windows (ответ @ DuckMaestro) и начал играть с [MS.Docs]: _setmode .

Добавление:

#include 
#include 


static int set_stdout_mode(int mode) {
    fflush(stdout);
    int ret = _setmode(_fileno(stdout), mode);
    return ret;
}

и вызов его как int stdout_mode = set_stdout_mode(_O_TEXT); в test.cpp перед выводом чего-либо из C ( и C ++ : std::wcout строка без комментариев), что привело к:

e:\Work\Dev\StackOverflow\q054269984>"e:\Work\Dev\VEnvs\py_064_03.06.08_test0\Scripts\python.exe" code.py
Python 3.6.8 (tags/v3.6.8:3c6b436a57, Dec 24 2018, 00:16:47) [MSC v.1916 64 bit (AMD64)] on win32

Hello, world!
From C: - [test.cpp] (32) - [TestDll::simple]
Hello, world!
"simple" returned 13
From C: - [test.cpp] (40) - [TestDll::printStr]
Hello, world!
"printStr" returned 13
From C: - [test.cpp] (48) - [TestDll::wprintWstr]
Hello, world!
Hex (13): 48 00 65 00 6C 00 6C 00 6F 00 2C 00 20 00 77 00 6F 00 72 00 6C 00 64 00 21 00
"wprintWstr" returned 13
"wstr" returned "Dummy text."
blockquote>
  • Хотя это работает, я не знаю почему. Это может быть Неопределенное поведение
    • Печать возвращаемого значения _setmode показала, что Python 3.4 , а также [1145 ] main.exe автоматически устанавливает режим на _O_TEXT ( 0x4000 ), в то время как более новый Python версии (те, которые не работают) устанавливают значение _O_BINARY ( 0x8000 ) - что, по-видимому, , кажется, причина (может быть связана: [Python]: проблема # 16587 - Py_Initialize прерывает wprintf в Windows )
    • Попытка установить для него любую из широких связанных констант ( _O_U16TEXT , _O_U8TEXT , _O_WTEXT ) аварийно завершает работу программы при вызове printf или std::cout ( даже при восстановлении исходного режима при выполнении с широкие функции - перед узкими)
  • Попытка вывести реальные символы Unicode , работать не будет (скорее всего)
  • Вы могли бы AC достичь той же цели на стороне Python : msvcrt.setmode(sys.stdout.fileno(), 0x4000)

83
задан Jon Seigel 28 March 2010 в 21:18
поделиться

7 ответов

Я не использовал бы Поток. Сон (). Любое использование запланированная задача (поскольку другие упомянули), или настраивала таймер в Вашем сервисе, который периодически стреляет (каждые 10 минут, например) и проверяет если дата, измененная начиная с последнего выполнения:

private Timer _timer;
private DateTime _lastRun = DateTime.Now.AddDays(-1);

protected override void OnStart(string[] args)
{
    _timer = new Timer(10 * 60 * 1000); // every 10 minutes
    _timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);
    _timer.Start();
    //...
}


private void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
    // ignore the time, just compare the date
    if (_lastRun.Date < DateTime.Now.Date)
    {
        // stop the timer while we are running the cleanup task
        _timer.Stop();
        //
        // do cleanup stuff
        //
        _lastRun = DateTime.Now;
        _timer.Start();
    }
}
86
ответ дан Community 24 November 2019 в 08:47
поделиться

Выезд Quartz.NET . Можно использовать его в службе Windows. Это позволяет Вам выполнять задание на основе настроенного расписания, и это даже поддерживает простой "синтаксис" задания крона. Я имел большой успех с ним.

Вот быстрый пример его использования:

// Instantiate the Quartz.NET scheduler
var schedulerFactory = new StdSchedulerFactory();
var scheduler = schedulerFactory.GetScheduler();

// Instantiate the JobDetail object passing in the type of your
// custom job class. Your class merely needs to implement a simple
// interface with a single method called "Execute".
var job = new JobDetail("job1", "group1", typeof(MyJobClass));

// Instantiate a trigger using the basic cron syntax.
// This tells it to run at 1AM every Monday - Friday.
var trigger = new CronTrigger(
    "trigger1", "group1", "job1", "group1", "0 0 1 ? * MON-FRI");

// Add the job to the scheduler
scheduler.AddJob(job, true);
scheduler.ScheduleJob(trigger);
71
ответ дан jeremcc 24 November 2019 в 08:47
поделиться

Ежедневная задача? Кажется, что это должна просто быть запланированная задача (панель управления) - никакая потребность в сервисе здесь.

21
ответ дан Marc Gravell 24 November 2019 в 08:47
поделиться

Это должна быть практическая эксплуатация? Можно ли просто использовать созданный в запланированных задачах в панели управления окон.

15
ответ дан Ian Jacobs 24 November 2019 в 08:47
поделиться

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

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

, Если причина Вы хотите выполнить свое действие в полночь, то, что Вы ожидаете низкую рабочую нагрузку на своем компьютере, лучше заботитесь: то же предположение часто делается другими, и внезапно у Вас есть 100 действий очистки, начинающихся между 0:00 и 0:01

В этом случае, необходимо рассмотреть запуск очистки в другое время. Я обычно делаю те вещи не в час часов, а в полчаса (1:30, будучи моим персональным предпочтением)

2
ответ дан Community 24 November 2019 в 08:47
поделиться

Я предложил бы, чтобы Вы использовали таймер, но установили его для проверки каждые 45 секунд, не минута. Иначе можно столкнуться с ситуациями, где с большой нагрузкой, проверка в течение конкретной минуты пропущена, потому что между временем триггеры таймера и время код выполняет и проверяет текущее время, Вы, возможно, пропустили целевую минуту.

1
ответ дан GWLlosa 24 November 2019 в 08:47
поделиться

Путем я выполняю, это с таймером.

Выполнение таймер сервера, имейте его, проверяют Час/Минута каждые 60 секунд.

, Если это - правильный Час/Минута, затем выполняет Ваш процесс.

я на самом деле имею, это абстрагировало в базовый класс, который я называю OnceADayRunner.

Позволяют мне очистить код немного, и я отправлю его здесь.

    private void OnceADayRunnerTimer_Elapsed(object sender, ElapsedEventArgs e)
    {
        using (NDC.Push(GetType().Name))
        {
            try
            {
                log.DebugFormat("Checking if it's time to process at: {0}", e.SignalTime);
                log.DebugFormat("IsTestMode: {0}", IsTestMode);

                if ((e.SignalTime.Minute == MinuteToCheck && e.SignalTime.Hour == HourToCheck) || IsTestMode)
                {
                    log.InfoFormat("Processing at: Hour = {0} - Minute = {1}", e.SignalTime.Hour, e.SignalTime.Minute);
                    OnceADayTimer.Enabled = false;
                    OnceADayMethod();
                    OnceADayTimer.Enabled = true;

                    IsTestMode = false;
                }
                else
                {
                    log.DebugFormat("Not correct time at: Hour = {0} - Minute = {1}", e.SignalTime.Hour, e.SignalTime.Minute);
                }
            }
            catch (Exception ex)
            {
                OnceADayTimer.Enabled = true;
                log.Error(ex.ToString());
            }

            OnceADayTimer.Start();
        }
    }

говядина метода находится в e. SignalTime. Проверка минуты/Час.

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

3
ответ дан CubanX 24 November 2019 в 08:47
поделиться
Другие вопросы по тегам:

Похожие вопросы: