16 Вопрос: неразрешенный внешний символ __imp__fprintf и __imp____iob_func, SDL2

вопрос создан в Sat, May 23, 2015 12:00 AM

Может ли кто-нибудь объяснить, что такое

  

__ imp__fprintf

и р>

  

__ имп ____ iob_func

нерешенные внешние средства?

Потому что я получаю эти ошибки, когда пытаюсь скомпилировать:

1>SDL2main.lib(SDL_windows_main.obj) : error LNK2019: unresolved external symbol __imp__fprintf referenced in function _ShowError
1>SDL2main.lib(SDL_windows_main.obj) : error LNK2019: unresolved external symbol __imp____iob_func referenced in function _ShowError
1>E:\Documents\Visual Studio 2015\Projects\SDL2_Test\Debug\SDL2_Test.exe : fatal error LNK1120: 2 unresolved externals

Я уже могу сказать, что проблема не в неправильной связи. Я все правильно связал, но по какой-то причине он не скомпилируется.

Я пытаюсь использовать SDL2.

Я использую Visual Studio 2015 в качестве компилятора.

Я связался с SDL2.lib и SDL2main.lib в компоновщике - > Ввод - > Дополнительные зависимости и я убедился, что каталоги VC ++ верны.

    
96
  1. Не могли бы вы доказать это, показав свои настройки компоновщика, пожалуйста.
    2015-05-23 13: 01: 43Z
  2. @ πάνταῥεῖ, я установил связь с SDL2.lib и SDL2main.lib в настройках компоновщика ввода и убедился, что каталоги указывают на правильный каталог .
    2015-05-23 13: 53: 00Z
  3. 2015-05-23 15: 40: 56Z
16 ответов                              16                         

Я наконец понял, почему это происходит!

В Visual Studio 2015 stdin, stderr, stdout определяются следующим образом:

#define stdin  (__acrt_iob_func(0))
#define stdout (__acrt_iob_func(1))
#define stderr (__acrt_iob_func(2))

Но ранее они были определены как:

#define stdin  (&__iob_func()[0])
#define stdout (&__iob_func()[1])
#define stderr (&__iob_func()[2])

Так что теперь __iob_func больше не определяется, что приводит к ошибке ссылки при использовании файла .lib, скомпилированного с предыдущими версиями Visual Studio.

Чтобы решить эту проблему, попробуйте самостоятельно определить __iob_func(), который должен вернуть массив, содержащий {*stdin,*stdout,*stderr}.

Что касается других ошибок ссылок на функции stdio (в моем случае это было sprintf()), вы можете добавить legacy_stdio_definitions.lib в параметры компоновщика.

    
109
2015-06-17 14: 20: 15Z
  1. Спасибо. Как это сделать в ICU: bugs.icu-project.org/trac/ticket/11798а>
    2015-07-30 20: 47: 49Z
  2. Спасибо за отслеживание этого. IIRC проблема с {* stdin, * stdout, * stderr} может заключаться в том, что разные модули компиляции могут иметь свою «собственную» копию stdin, поэтому эти функции были вызваны напрямую.
    2015-07-30 21: 08: 30Z
  3. , который также решил для меня, просто напоминание об использовании extern "C" в объявлении /определении.
    2015-08-03 13: 14: 18Z
  4. Может кто-нибудь написать точно, как должна выглядеть функция замены? Я пробовал разные варианты, и я продолжаю получать ошибки компиляции. Спасибо.
    2015-08-07 17: 03: 50Z
  5. extern "C" { FILE __iob_func[3] = { *stdin,*stdout,*stderr }; }
    2015-11-16 22: 20: 34Z

Милану Бабушкову, ИМО, именно так должна выглядеть функция заменыкак: -)

FILE _iob[] = {*stdin, *stdout, *stderr};

extern "C" FILE * __cdecl __iob_func(void)
{
    return _iob;
}
    
54
2015-09-08 04: 33: 41Z
  1. Просто отсутствует #ifdef для MSVC и для версии MSVC < 2015
    2015-09-16 08: 09: 42Z
  2. Как отмечает МаркХ в другом ответе, который выглядит правильным, но не будет работать.
    2017-01-12 16: 34: 30Z
  3. @ paulm Я думаю, вы имеете в виду #if defined(_MSC_VER) && (_MSC_VER >= 1900).
    2017-09-01 00: 07: 56Z
  4. @ JesseChisholm, возможно, зависит от того, относится ли это также к каждой известной будущей версии MSVC или нет;)
    2017-09-02 04: 26: 38Z

У Microsoft есть специальное примечание по этому вопросу ( https: //msdn .microsoft.com /EN-US /библиотека /bb531344.aspx # BK_CRT ):

  

Семейство функций printf и scanf теперь определены как встроенные.

     

Определения всех функций printf и scanf были   встроенный в stdio.h , conio.h и другие заголовки CRT.   Это критическое изменение, которое приводит к ошибке компоновщика (LNK2019,   неразрешенный внешний символ) для любых программ, которые объявили эти   функционирует локально, не включая соответствующие заголовки CRT. Если   возможно, вы должны обновить код, чтобы включить заголовки CRT (что   есть, добавьте #include) и встроенные функции, но если вы делаете   не хотите изменять ваш код, чтобы включить эти файлы заголовков,   альтернативное решение - добавить дополнительную библиотеку в ваш компоновщик   input, legacy_stdio_definitions.lib .

     

Чтобы добавить эту библиотеку к входу компоновщика в IDE, откройте контекст   меню для узла проекта, выберите Свойства, затем в Проекте   В диалоговом окне «Свойства» выберите «Линкер» и отредактируйте вход «Линкер» для добавления   legacy_stdio_definitions.lib в список, разделенный точкой с запятой.

     

Если ваш проект связан со статическими библиотеками, которые были скомпилированы с   выпуск Visual C ++ ранее 2015 года, компоновщик может сообщить   неразрешенный внешний символ. Эти ошибки могут ссылаться на внутренние   определения stdio для _iob , _iob_func или связанных импортов для   некоторые функции stdio в виде __ imp _ * . Microsoft   рекомендует перекомпилировать все статические библиотеки с последними   версия компилятора Visual C ++ и библиотеки при обновлении   проект. Если библиотека является сторонней библиотекой, для которой   недоступно, вы должны либо запросить обновленный двоичный файл из   третье лицо или инкапсулировать использование вами этой библиотеки в отдельный   DLL, которую вы компилируете с более старой версией компилятора Visual C ++   и библиотеки.

    
36
2018-12-18 19: 16: 01Z
  1. Или #pragma comment(lib, "legacy_stdio_definitions.lib") - но это не исправляет __imp___iob_func - для этого также есть устаревшая библиотека?
    2017-03-05 13: 48: 18Z

У меня была такая же проблема в VS2015. Я решил это, скомпилировав источники SDL2 в VS2015.

  1. Перейдите на http://libsdl.org/download-2.0.php и загрузите SDL. 2 исходный код.
  2. Откройте SDL_VS2013.sln в VS2015 . Вам будет предложено преобразовать проекты. Сделай это.
  3. Скомпилируйте проект SDL2.
  4. Скомпилируйте проект SDL2main.
  5. Используйте новые сгенерированные выходные файлы SDL2main.lib, SDL2.lib и SDL2.dll в вашем проекте SDL 2 в VS2015.
27
2015-07-26 16: 36: 14Z
  1. Кстати, для сборки SDL 2.0.3 требуется установить DirectX SDK в июне 2010 года.
    2015-07-28 07: 49: 34Z
  2. Работал для меня, спасибо! Но мне нужно было только скомпилировать SDL2main и скопировать SDL2main.lib
    2015-08-04 22: 30: 18Z

Как уже было сказано выше, правильный ответ - собрать все с VS2015, но для интереса ниже приведен мой анализ проблемы.

Этот символ, по-видимому, не определен ни в одной статической библиотеке, предоставленной Microsoft как часть VS2015, что довольно странно, поскольку все остальные таковые есть. Чтобы понять почему, нам нужно посмотреть на объявление этой функции и, что более важно, как она используется.

Вот фрагмент из заголовков Visual Studio 2008:

_CRTIMP FILE * __cdecl __iob_func(void);
#define stdin (&__iob_func()[0])
#define stdout (&__iob_func()[1])
#define stderr (&__iob_func()[2])

Итак, мы можем видеть, что задача функции - возвращать начало массива объектов FILE (не дескрипторы, «FILE *» - дескриптор, FILE - базовая непрозрачная структура данных, хранящая важные сведения о состоянии) , Пользователями этой функции являются три макроса stdin, stdout и stderr, которые используются для различных вызовов в стиле fscanf, fprintf.

Теперь давайте посмотрим, как Visual Studio 2015 определяет то же самое:

_ACRTIMP_ALT FILE* __cdecl __acrt_iob_func(unsigned);
#define stdin (__acrt_iob_func(0))
#define stdout (__acrt_iob_func(1))
#define stderr (__acrt_iob_func(2))

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

Так почему они /мы не можем предоставить совместимый API? Есть два ключевых правила, которые Microsoft не может нарушить с точки зрения их первоначальной реализации через __iob_func:

  1. Должен быть массив из трех структур FILE, которые можно индексировать так же, как и раньше.
  2. Структурная схема FILE не может быть изменена.

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

Давайте посмотрим, как был определен /определен FILE.

Сначала определите ФАЙЛ VS2008:

struct _iobuf {
        char *_ptr;
        int   _cnt;
        char *_base;
        int   _flag;
        int   _file;
        int   _charbuf;
        int   _bufsiz;
        char *_tmpfname;
        };
typedef struct _iobuf FILE;

А теперь определение ФАЙЛА VS2015:

typedef struct _iobuf
{
    void* _Placeholder;
} FILE;

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

Возможные решения, упомянутые в ответах выше по этим направлениям, не будут работать (если будут вызваны) по нескольким причинам:

FILE _iob[] = {*stdin, *stdout, *stderr};

extern "C" FILE * __cdecl __iob_func(void)
{
    return _iob;
}

Массив FILE _iob будет скомпилирован с VS2015, и поэтому он будет размечен как блок структур, содержащих пустоту *. Предполагая 32-битное выравнивание, эти элементы будут на расстоянии 4 байта. Так, _iob [0] со смещением 0, _iob [1] со смещением 4 и _iob [2] со смещением 8. Вместо этого вызывающий код будет ожидать, что FILE будет намного длиннее, выровнен по 32 байта в моей системе, и поэтому он возьмет адрес возвращаемого массива и добавит 0 байтов, чтобы добраться до нулевого элемента (что нормально), но для _iob [1] он выведет, что ему нужно добавить 32 байта, а для _iob [2] выведет что ему нужно добавить 64 байта (потому что это выглядит так в заголовках VS2008). И действительно, разобранный код для VS2008 демонстрирует это.

Вторичная проблема с вышеприведенным решением заключается в том, что он копирует содержимое структуры FILE (* stdin), а не дескриптор FILE *. Таким образом, любой код VS2008 будет смотреть на структуру, отличную от VS2015. Это может сработать, если структура содержит только указатели, но это большой риск. В любом случае, первая проблема делает это неуместным.

Единственный хак, который мне удалось придумать, это тот, в котором __iob_func обходит стек вызовов, чтобы определить, какой фактический дескриптор файла они ищут (на основе смещения, добавленного к возвращаемому адресу), и возвращает вычисленное значение. такой, что дает правильный ответ. Это настолько же безумно, как и звучит, но прототип только для x86 (не x64) приведен ниже для вашего удовольствия. В моих экспериментах все работало нормально, но пробег может отличаться - не рекомендуется для производственного использования!

#include <windows.h>
#include <stdio.h>
#include <dbghelp.h>

/* #define LOG */

#if defined(_M_IX86)

#define GET_CURRENT_CONTEXT(c, contextFlags) \
  do { \
    c.ContextFlags = contextFlags; \
    __asm    call x \
    __asm x: pop eax \
    __asm    mov c.Eip, eax \
    __asm    mov c.Ebp, ebp \
    __asm    mov c.Esp, esp \
  } while(0);

#else

/* This should work for 64-bit apps, but doesn't */
#define GET_CURRENT_CONTEXT(c, contextFlags) \
  do { \
    c.ContextFlags = contextFlags; \
    RtlCaptureContext(&c); \
} while(0);

#endif

FILE * __cdecl __iob_func(void)
{
    CONTEXT c = { 0 };
    STACKFRAME64 s = { 0 };
    DWORD imageType;
    HANDLE hThread = GetCurrentThread();
    HANDLE hProcess = GetCurrentProcess();

    GET_CURRENT_CONTEXT(c, CONTEXT_FULL);

#ifdef _M_IX86
    imageType = IMAGE_FILE_MACHINE_I386;
    s.AddrPC.Offset = c.Eip;
    s.AddrPC.Mode = AddrModeFlat;
    s.AddrFrame.Offset = c.Ebp;
    s.AddrFrame.Mode = AddrModeFlat;
    s.AddrStack.Offset = c.Esp;
    s.AddrStack.Mode = AddrModeFlat;
#elif _M_X64
    imageType = IMAGE_FILE_MACHINE_AMD64;
    s.AddrPC.Offset = c.Rip;
    s.AddrPC.Mode = AddrModeFlat;
    s.AddrFrame.Offset = c.Rsp;
    s.AddrFrame.Mode = AddrModeFlat;
    s.AddrStack.Offset = c.Rsp;
    s.AddrStack.Mode = AddrModeFlat;
#elif _M_IA64
    imageType = IMAGE_FILE_MACHINE_IA64;
    s.AddrPC.Offset = c.StIIP;
    s.AddrPC.Mode = AddrModeFlat;
    s.AddrFrame.Offset = c.IntSp;
    s.AddrFrame.Mode = AddrModeFlat;
    s.AddrBStore.Offset = c.RsBSP;
    s.AddrBStore.Mode = AddrModeFlat;
    s.AddrStack.Offset = c.IntSp;
    s.AddrStack.Mode = AddrModeFlat;
#else
#error "Platform not supported!"
#endif

    if (!StackWalk64(imageType, hProcess, hThread, &s, &c, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL))
    {
#ifdef LOG
        printf("Error: 0x%08X (Address: %p)\n", GetLastError(), (LPVOID)s.AddrPC.Offset);
#endif
        return NULL;
    }

    if (s.AddrReturn.Offset == 0)
    {
        return NULL;
    }

    {
        unsigned char const * assembly = (unsigned char const *)(s.AddrReturn.Offset);
#ifdef LOG
        printf("Code bytes proceeding call to __iob_func: %p: %02X,%02X,%02X\n", assembly, *assembly, *(assembly + 1), *(assembly + 2));
#endif
        if (*assembly == 0x83 && *(assembly + 1) == 0xC0 && (*(assembly + 2) == 0x20 || *(assembly + 2) == 0x40))
        {
            if (*(assembly + 2) == 32)
            {
                return (FILE*)((unsigned char *)stdout - 32);
            }
            if (*(assembly + 2) == 64)
            {
                return (FILE*)((unsigned char *)stderr - 64);
            }

        }
        else
        {
            return stdin;
        }
    }
    return NULL;
}
    
23
2016-01-07 12: 32: 18Z
  1. Правильный ответ - это самый простой способ, как указано, чтобы обновить проект до VS2015 и затем скомпилировать.
    2017-05-04 14: 37: 49Z
  2. В моем случае мне нужно обновить многие проекты (проекты C ++ и C #) из Visual Studio 2013 для использования Visual Studio 2015 Update 3. Я хочу сохранить VC100 (Visual Studio 2010 C ++ компилятор) при создании проектов C ++, но у меня те же ошибки, что и выше. Я исправил imp _fprintf , добавив legacy_stdio_definitions.lib к компоновщику. Как я могу исправить также _imp ____ iob_func ?
    2017-07-21 17: 12: 00Z
  3. Прежде чем ответить на мой предыдущий вопрос, нормально ли, что эти ошибки возникают при использовании msbuild 14 и IntelCompiler 2016 и VC100 для компиляции проектов C ++?
    2017-07-21 17: 35: 34Z
  4. что я могу сделать для компиляции x64?
    2018-05-04 04: 09: 02Z

Я не знаю почему, но:

#ifdef main
#undef main
#endif

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

    
8
2016-05-11 02: 25: 34Z
  1. .... Хорошо .... поэтому, прежде чем пытаться это, я сказал себе, что я почему-то сомневаюсь, что это сработает, но это полностью сработало .... Можете ли вы объяснить, почему это работает ...?
    2016-10-07 03: 12: 45Z
  2. @ TrevorHart Я верю , что он не определяет "ошибочные" основные включенные ссылки SDL на буферы "undefined" и, если ваша использует ваши буферы, тогда все работает хорошо.
    2016-10-20 02: 19: 36Z
  3. Это ужасно плохая практика хаков, но это 3 строки, и это работает, и это избавило меня от необходимости зарыться в построение SDL, так что ... хорошо сделано.
    2017-08-12 01: 29: 03Z
  4. @ Cheezmeister Плохая практика и хаки часто требуются для всего. Особенно те вещи, которые им не нужны.
    2017-09-08 02: 28: 04Z

Более свежее решение этой проблемы: используйте более свежие библиотеки sdl на

" https://buildbot.libsdl.org/sdl-builds /SDL-VisualStudio /С = М;? O = D "р>

Кажется, они решили проблему, хотя это всего лишь 32-битная библиотека (я думаю).

    
7
2015-08-08 19: 43: 54Z

Ссылаться означает не работать должным образом. Копаясь в stdio.h VS2012 и VS2015 у меня сработало следующее. Увы, вам нужно решить, должен ли он работать для одного из {stdin, stdout, stderr}, но не более одного.

extern "C" FILE* __cdecl __iob_func()
{
    struct _iobuf_VS2012 { // ...\Microsoft Visual Studio 11.0\VC\include\stdio.h #56
        char *_ptr;
        int   _cnt;
        char *_base;
        int   _flag;
        int   _file;
        int   _charbuf;
        int   _bufsiz;
        char *_tmpfname; };
    // VS2015 has only FILE = struct {void*}

    int const count = sizeof(_iobuf_VS2012) / sizeof(FILE);

    //// stdout
    //return (FILE*)(&(__acrt_iob_func(1)->_Placeholder) - count);

    // stderr
    return (FILE*)(&(__acrt_iob_func(2)->_Placeholder) - 2 * count);
}
    
6
2016-02-02 13: 36: 58Z

Мой совет: не пытайтесь реализовать __iob_func.

Исправляя эти ошибки:

libpngd.v110.lib(pngrutil.obj) : error LNK2001: unresolved external symbol ___iob_func curllib.v110.lib(mprintf.obj) : error LNK2001: unresolved external symbol ___iob_func р>

Я попробовал другие ответы такОднако, в конце концов, возвращение C-массива FILE* не совпадает с массивом внутренних структур IOB Windows. @Volker прав в том, что он никогда не будет работать более чем на один из stdin, stdout или stderr.

Если библиотека на самом деле ИСПОЛЬЗУЕТ один из этих потоков, произойдет сбой . Пока ваша программа не заставляет их использовать библиотеку, вы никогда не узнаете . Например, png_default_error записывает в stderr, когда CRC не совпадает в метаданных PNG. (Обычно это не проблема, вызывающая сбой)

Вывод. Невозможно смешивать библиотеки VS2012 (Platform Toolset v110 /v110_xp) и VS2015 +, если они используют stdin, stdout и /или stderr.

Решение. Перекомпилируйте библиотеки, в которых есть неразрешенные символы __iob_func, с текущей версией VS и соответствующим набором инструментов платформы.

    
6
2018-01-02 11: 44: 35Z

Используйте предварительно скомпилированные SDL2main.lib и SDL.lib для библиотеки вашего проекта VS2015: https://buildbot.libsdl.org/sdl-builds /sdl-visualstudio/sdl-visualstudio-2225.zip р>     

1
2015-10-20 03: 43: 53Z

Мне удалось решить проблему.

Источником ошибки была эта строка кода, которую можно найти в исходном коде SDLmain.

fprintf(stderr, "%s: %s\n", title, message);

Итак, я тоже отредактировал исходный код в SDLmain этой строки:

fprintf("%s: %s\n", title, message);

А затем я собрал SDLmain, скопировал и заменил старый SDLmain.lib в моем каталоге библиотеки SDL2 на вновь созданный и отредактированный.

Затем, когда я запустил мою программу с SDL2, сообщения об ошибках не появлялись, и код работал без сбоев.

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

    
0
2015-05-23 18: 49: 48Z
  1. Ваше изменение само по себе является ошибкой и не решило бы проблему, описанную в вашем вопросе. Это просто совпадение, что вы больше не получаете ошибок компоновщика, что, вероятно, является исключительно результатом того, как вы перестроили библиотеку.
    2015-05-23 19: 02: 05Z
  2. @ RossRidge, о да, это могло бы быть именно так. Ах, хорошо.
    2015-05-23 23: 16: 58Z

Это может произойти, когда вы ссылаетесь на msvcrt.dll вместо msvcr10.dll (или аналогичного), что является хорошим планом. Потому что это освободит вас от необходимости перераспределять библиотеку времени выполнения Visual Studio внутри вашего окончательного пакета программного обеспечения.

Этот обходной путь помогает мне (в Visual Studio 2008):

#if _MSC_VER >= 1400
#undef stdin
#undef stdout
#undef stderr
extern "C" _CRTIMP extern FILE _iob[];
#define stdin   _iob
#define stdout  (_iob+1)
#define stderr  (_iob+2)
#endif

Этот фрагмент не нужен для Visual Studio 6 и его компилятора. Поэтому #ifdef.

    
0
2015-09-22 00: 50: 25Z

Чтобы внести еще большую путаницу в этот и без того насыщенный поток, я случайно натолкнулся на тот же неразрешенный внешний элемент в fprintf

main.obj : error LNK2019: unresolved external symbol __imp__fprintf referenced in function _GenerateInfoFile

Даже если в моем случае это происходило в довольно ином контексте: в Visual Studio 2005 (Visual Studio 8.0) ошибка возникала в моем собственном коде (то же самое, что я компилировал), а не в сторонней организации. р>

Случилось так, что эта ошибка была вызвана параметром /MD в моих флагах компилятора. Переключение на /MT устранило проблему. Это странно, потому что обычно статическое связывание (MT) создает больше проблем, чем динамическое (MD) ... но только вSE это служит другим, я положил это там.

    
0
2019-03-22 11: 34: 22Z

В моем случае эта ошибка возникает из-за моей попытки удалить зависимости из библиотеки времени выполнения библиотеки DLL, зависящей от версии MSVC (msvcr10.dll или около того), и /или удалить статическую библиотеку времени выполнения, чтобы удалить лишний жир из моих исполняемых файлов. р>

Поэтому я использую переключатель компоновщика /NODEFAULTLIB, мой собственный файл "msvcrt-light.lib" (Google для него, когда вам нужно) и mainCRTStartup()/WinMainCRTStartup().

Это IMHO начиная с Visual Studio 2015, поэтому я остановился на старых компиляторах.

Однако определение символа _NO_CRT_STDIO_INLINE устраняет все хлопоты, и простое приложение «Hello World» снова имеет размер 3 КБ и не зависит от необычных библиотек DLL. Протестировано в Visual Studio 2017.

    
0
2019-04-02 14: 09: 43Z

Я решаю эту проблему с помощью следующей функции. Я использую Visual Studio 2019.

FILE* __cdecl __iob_func(void)
{
    FILE _iob[] = { *stdin, *stdout, *stderr };
    return _iob;
}

, поскольку stdin вызов функции, определяемой макросом, выражение "* stdin" не может использоваться инициализатором глобального массива. Но инициализатор локального массива возможен. извини, я плохо знаю английский.

    
0
2019-05-22 19: 04: 18Z

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

Project properties --> C/C++ --> Code generation --> Runtime Library --> Multi-threaded Debug (/MTd) instead of /MDd р>     

0
2019-06-21 07: 02: 23Z
  1. Вот обсуждение этого решения: social.msdn.microsoft.com/Forums/vstudio/en-US/…
    2019-06-21 07: 03: 00Z
источник размещен Вот