Не так давно потребовалось визуализировать AST-дерево. В принципе, для визуализации у Graphviz есть библиотеки для языка C++, однако не хотелось их тащить ради такой мелкой цели. В результате родился следующий несложный код:
/*! Функция для создания графа из файла. Решение WIN32-only
\param[in] firstname строка, использующаяся для формирования
имени конечного файла
\param[in] filename имя файла с графом
*/
void spawn_dot(const char * firstname,const std::string & filename)
{
//Создадим структуру для выходных параметров создания процесса
PROCESS_INFORMATION * pi=new PROCESS_INFORMATION();
STARTUPINFOA * inf=new STARTUPINFOA();
memset(pi,0,sizeof(PROCESS_INFORMATION));
memset(inf,0,sizeof(STARTUPINFOA));
//Заполним параметры запуска. Указываем то, что выходной поток дочернего
//процесса будет перенаправлен
inf->cb=sizeof(STARTUPINFOA);
inf->dwFlags=STARTF_USESTDHANDLES;
//Сформируем имя конечного файла
std::string output_filename=firstname;
output_filename+=".png";
//Здесь происходит очень интересная вещь:
//Если просто перенаправить поток выходного процесса в файл
//то есть шанс, что он туда ничего не запишет, так как не будет очищен буффер
//дескриптора выходного потока. Поэтому мы, сохраняем контроль над ним,
//перенаправляя наш выходной поток в файл и передавая его дескриптор
//в дочерний процесс.
freopen(output_filename.c_str(),"wb",stdout);
inf->hStdOutput=GetStdHandle(STD_OUTPUT_HANDLE);
//Формируем строку вызова. Нам нужно, чтобы dot вызвался
//По информации из переменной среды PATH, поэтому мы должны сформировать её полностью
//Кроме того из документации следует, что указатель должен быть изменяемым.
char * tmp=new char[271];
strcpy(tmp,"dot.exe ");
strcat(tmp,filename.c_str());
strcat(tmp," -Tpng");
//Создаем процесс
BOOL result = CreateProcessA(NULL,tmp, NULL, NULL, TRUE,
0, NULL,NULL, inf, pi);
//Если создать процесс не удалось
if (result==FALSE)
{
//Восстанавливаем изначальный выходной поток и выводим ошибку
freopen("CONOUT$","wb",stdout);
printf("Failed to spawn dot %d\n",GetLastError());
}
else
{
//Дожидаемся завершения процесса
WaitForSingleObject(pi->hProcess,INFINITE);
//Закрываем дескрипторы и восстанавливаем выходной поток.
CloseHandle(pi->hProcess);
CloseHandle(pi->hThread);
freopen("CONOUT$","wb",stdout);
}
//Очищаем память
delete pi;
delete inf;
delete tmp;
}
Комментариев нет:
Отправить комментарий