TCL高管的程序运行正常
Program does work correctly from TCL exec
我编写/借用了以下C++程序,该程序根据窗口名称对窗口进行屏幕截图。
当我通过Windows命令提示符运行该程序时,它工作正常。但是,当我用exec
命令调用TCL脚本中的程序时,它会使wish86应用程序崩溃。
为什么程序通过命令行工作,而不使用exec
命令?
示例:screenshot.exe calc.bmp "Calculator"
#include <windows.h>
#include <gdiplus.h>
#include <stdio.h>
#pragma comment(lib, "user32.lib")
#pragma comment(lib, "gdi32.lib")
#pragma comment(lib, "gdiplus.lib")
using namespace Gdiplus;
// From http://msdn.microsoft.com/en-us/library/ms533843%28VS.85%29.aspx
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
UINT num = 0; // number of image encoders
UINT size = 0; // size of the image encoder array in bytes
ImageCodecInfo* pImageCodecInfo = NULL;
GetImageEncodersSize(&num, &size);
if(size == 0)
return -1; // Failure
pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
if(pImageCodecInfo == NULL)
return -1; // Failure
GetImageEncoders(num, size, pImageCodecInfo);
for(UINT j = 0; j < num; ++j) {
if( wcscmp(pImageCodecInfo[j].MimeType, format) == 0 ) {
*pClsid = pImageCodecInfo[j].Clsid;
free(pImageCodecInfo);
return j; // Success
}
}
free(pImageCodecInfo);
return -1; // Failure
}
int wmain(int argc, wchar_t** argv)
{
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
HDC desktopdc;
HDC mydc;
HBITMAP mybmp;
desktopdc = GetDC(NULL);
if(desktopdc == NULL) {
return -1;
}
// If three arguments were passed, capture the specified window
if(argc == 3) {
RECT rc;
// Convert wchar_t[] to char[]
char title[512] = {0};
wcstombs(title, argv[2], wcslen(argv[2]));
HWND hwnd = FindWindow(NULL, title); //the window can't be min
if(hwnd == NULL) {
return -1;
}
GetWindowRect(hwnd, &rc);
mydc = CreateCompatibleDC(desktopdc);
mybmp = CreateCompatibleBitmap(desktopdc, rc.right - rc.left, rc.bottom - rc.top);
SelectObject(mydc,mybmp);
//Print to memory hdc
PrintWindow(hwnd, mydc, PW_CLIENTONLY);
} else {
// Capture the entire screen
int width = GetSystemMetrics(SM_CXVIRTUALSCREEN);
int height = GetSystemMetrics(SM_CYVIRTUALSCREEN);
mydc = CreateCompatibleDC(desktopdc);
mybmp = CreateCompatibleBitmap(desktopdc, width, height);
HBITMAP oldbmp = (HBITMAP)SelectObject(mydc, mybmp);
BitBlt(mydc,0,0,width,height,desktopdc,0,0, SRCCOPY|CAPTUREBLT);
SelectObject(mydc, oldbmp);
}
const wchar_t* filename = (argc > 1) ? argv[1] : L"screenshot.png";
Bitmap* b = Bitmap::FromHBITMAP(mybmp, NULL);
CLSID encoderClsid;
Status stat = GenericError;
if (b && GetEncoderClsid(L"image/png", &encoderClsid) != -1) {
stat = b->Save(filename, &encoderClsid, NULL);
}
if (b)
delete b;
// cleanup
GdiplusShutdown(gdiplusToken);
ReleaseDC(NULL, desktopdc);
DeleteObject(mybmp);
DeleteDC(mydc);
DeleteDC(desktopdc);
return stat == Ok;
}
正如您在评论中提到的,真正的问题是,当您试图对调用屏幕截图程序(使用exec
)的Tcl进程所拥有的GUI窗口进行屏幕截图时,代码会挂起。这是因为Windows希望进程为其消息泵(Tcl映射到事件循环)提供服务,但由于Tcl正忙于阻塞等待子进程完成,因此消息处理已停止。(这就是exec
的工作方式,也是它一直以来的工作方式。)这会将所有东西都阻塞在死锁中,而且听起来解锁处理得不太优雅。
由于您的程序不读取任何输入或产生任何输出(好吧,它确实同时执行这两种操作,但通过参数和"已知"文件),因此您需要的是一种在后台运行程序的方法,同时仍然能够发现程序何时完成。这意味着我们不想使用exec
;它只有两种操作模式:阻塞等待或完全断开异步(如果最后一个参数是&
)。前者是问题所在,而后者没有给我们所需的信息。
相反,我们需要一个管道,这样我们就可以在后台拍摄屏幕截图,同时仍在处理事件,我们需要设置一个fileevent
,这样我们就能知道事情何时完成(因为这是一种隐藏的事件)。
set thepipeline [open |[list screenshot.exe $filename $windowname] r]
fileevent $thepipeline readable [list doneScreenshot $thepipeline $filename]
proc doneScreenshot {pipe filename} {
# Pipelines become readable when either:
# 1. there's some data to read, or
# 2. the pipe gets closed.
# It's option 2 that happens here.
close $pipe
puts "screenshot now available in $filename"
}
由于基于Tk的Tcl程序无论如何都会运行事件循环,因此我们不会使用vwait
来创建事件循环。(将代码编写为异步有时可能有点令人费解;请考虑"回调"而不是"程序流"。除非您使用Tcl 8.6,否则我们可以使用协程来解决问题。)
旁注:您可能不想将$filename
传递给screenshot.exe
,而是希望传递[file nativename [file normalize $filename]]
,但这不是问题所在。并且您不需要手动在$windowname
周围添加引号;Tcl运行时将在必要时执行此操作(假设您使用MSVC运行时执行屏幕截图程序,而不是自己进行奇怪的处理)
- 运行程序时出现问题
- 分段错误当我试图运行程序时出错
- C++ 每次运行程序时我都会"nan"输出的问题
- 通过 g++ 运行程序时没有这样的文件或目录
- 重新运行程序和字符串流?
- 无法在 VS Code 上使用代码运行程序运行C++文件
- 使用有限的 RAM 运行 c++ 程序
- 继续运行程序而无需任何干预,直到要求退出为止
- 如何在每次运行程序时写入文件的下一行?
- 控制台在运行C ++程序后立即关闭(无需调试)
- 运行程序时找不到共享对象库,但在编译过程中链接了它
- 远程运行程序
- 在 Heroku 上运行 C++ 程序
- 如何在Powershell中运行C++程序,就像CMD一样?
- 为什么与Java和Python相比,使用Cmake运行C++程序每次都需要这么长时间?
- C++随机数生成器通常在 Visual Studio 中运行程序时在 2 次后停止生成数字
- 崇高文本 3 - 在终端中编译并运行 C++ 程序(路径包含空格)
- 我收到阻止我运行程序的警告,但不确定如何解决
- C++ Visual Studio 无法完全运行程序
- 在SIGABRT c ++信号之后继续运行程序