Как мне хорошо играть с Cygwin?
У меня есть программа на C/C++, написанная для Windows и скомпилированная с Visual Studio. И это инструмент командной строки, который означает, что он может быть разумно запущен под Cygwin Bash, вероятно, в MinTTY, и некоторые из его пользователей сейчас делают это.
Я бы хотел изменить свою программу, чтобы она лучше играла с Cygwin.
В настоящее время моя программа использует API-интерфейсы Windows Console для вывода довольно цветного текста и управления курсором, а также для того, чтобы вообще быть "интерактивным" (или "интерактивным" для определения "интерактивный") 1980-х годов, по крайней мере, когда его вывод не передано в файл.
Чтобы моя программа играла лучше с Cygwin, я могу сделать так, чтобы она излучала ANSI \x1B[...
escape-коды вместо вызова консольных API, но настоящая проблема в том, чтобы знать, когда это сделать.
Я думаю, что есть четыре случая:
Вызывается как интерактивная консольная программа Windows. -> (Используйте консольные API.)
Вызывается как обычная консольная программа Windows, но пользователь где-то передает данные. -> (Никаких стилей или интерактивных звонков вообще.)
Вызывается как интерактивная программа Bash или в рамках MinTTY. -> (Используйте управляющие коды ANSI.)
Вызывается Bash, но пользователь где-то отправляет свой вывод. -> (Никаких стилей или интерактивных звонков вообще.)
Я могу отличить случай 1, проверяя, GetConsoleMode()
преуспевает или терпит неудачу для stdout
справиться; если это удастся, у меня определенно будет консоль Windows, и это тот случай, когда я включаю код для использования API-интерфейсов консоли.
Я хотел бы по-другому относиться к случаю 3 и использовать для него коды ANSI, но, к сожалению, случаи 2, 3 и 4 кажутся в основном неразличимыми:
-
stdout
ручка, кажется, просто непрозрачный объектFILE_TYPE_PIPE
для всех трех случаев. - Я могу использовать
OSTYPE
переменная окружения и, по крайней мере, предположить, что я нахожусь под Cygwin, который отличает случай 2 от случаев 3 и 4 - Если бы я мог связать против
cygwin1.dll
Я мог бы как-то использовать егоisatty()
, но я не могу ссылаться на это, так как многие из моих пользователей не имеют (и не будут) установлен Cygwin. (И я совсем не уверен, что это сработает, даже если это будет возможно.) - MSVC родной
_isatty()
думает, что Cygwin Bash - это труба, а не интерактивная оболочка, потому что она просто используетGetFileType()
глубоко под капотом.
Короче говоря, я не вижу очевидного способа отделить случай 3 от случая 4.
Прямо сейчас я отношусь к случаю 3 точно так же, как к случаям 2 и 4, и пользователи Cygwin (включая меня) получают дурацкое взаимодействие по телетайпу вместо дружественного интерактивного полноэкранного дисплея, и мне бы очень хотелось это исправить.
Так есть ли какой-нибудь способ, которым я могу отличить интерактивный вызов Cygwin от других вызовов, чтобы моя нативная программа Windows работала хорошо при вызове Cygwin Bash или других подобных оболочек?
(Важное примечание: есть одно решение, которое не обсуждается: программа не будет скомпилирована как нативная программа Cygwin. Это программа Windows, скомпилированная с цепочкой инструментов Microsoft и не скомпилированная с GCC. Цель состоит в том, чтобы изменить / обновить его так, чтобы он вел себя как можно лучше под Cygwin - без фактической связи с какой-либо из библиотек Cygwin. Я могу только разумно распространять один исполняемый файл для Windows, а не два.)
1 ответ
Если бы я мог связываться с cygwin1.dll, я мог бы каким-то образом использовать его isatty(), но я не могу связываться с этим, поскольку у многих из моих пользователей не установлен (и не будет) установлен Cygwin. (И я совсем не уверен, что это сработает, даже если это будет возможно.)
Хорошо, вы не можете статически связываться с этой DLL, но это не значит, что вы не можете попытаться загрузить ее динамически.
Вы могли бы попытаться LoadLibrary("cygwin1.dll")
, Если это не удается, значит, он не работает под Cygwin, и вы можете использовать встроенные функции Windows для определения типа консоли.
С другой стороны, если вы можете открыть эту библиотеку (что означает, что Cygwin доступен в текущей системе), то вы сможете выполнить динамический вызов isatty()
и используйте результат, чтобы увидеть, перенаправлен ли вывод.
Редактировать: не так просто просто загрузить Cygwin DLL и вызвать функцию, вам нужно сначала инициализировать библиотеку, как показано в этой ссылке
Выписка:
static BOOL setup_root()
{
HMODULE hCygwin = NULL;
// Load the cygwin dll into our application.
if(!load_cygwin_library(&hCygwin))
return FALSE;
// Init the cygwin environment. (Required)
if(!init_cygwin_library(hCygwin))
return FALSE;
с обоими интересными процедурами инициализации. Если init_cygwin_library
не вызывается, дальнейшие звонки могут не работать:
static BOOL load_cygwin_library(HMODULE* hCygwin)
{
if((*hCygwin = GetModuleHandleW(cyglibrary)) == NULL)
if((*hCygwin = LoadLibraryW(cyglibrary)) == NULL)
return FALSE;
return TRUE;
}
static BOOL init_cygwin_library(HMODULE hCygwin)
{
cygwin_dll_init_fn cygwin_dll_init = NULL;
if((cygwin_dll_init = (cygwin_dll_init_fn)GetProcAddress(hCygwin,"cygwin_dll_init")) == NULL) {
FreeLibrary(hCygwin);
return FALSE;
}
cygwin_dll_init();
return TRUE;
}