在所有的应用软件当中,几乎都会提供打印结果的功能,但大部分的软件并不提供输出结果到文本文件功能(如速达,用友和金碟应用软件等能够打印,但不能将结果直接输出到文本文件;再比如一些与硬件配套的应用软件类似
PeakNet等软件,也只能打印,不能输出到文本文件)。有时候客户需要对这些结果进行再次处理分析和统计。传统的方法,只能把打印到纸上的数据手工输入电脑,再利用自己的统计分析软件来处理这些数据。如果这些数据量大,仅靠人工输入是不可能的。笔者经过探讨和测试,找到了一个方法,实现了他们的无缝连接。为方便起见,以下称本文最早提到提供打印结果功能的软件为软件A,对这些结果再次进行处理分析和统计的软件为软件B。
问题(一)描述:
由于软件A不提供打印文本数据到文本文件中,但可以通过打印机打印出来。比如有些监控机器的软件一直要监控并且不时产生数据,而这些数据又需要重新处理和统计。我们需要的是文本文件。如何解决这个问题,请详见解决思路1),2),3)。
解决思路:
1) 虽然有些软件(比如Excel, Word等)在打印功能中提供“打印到文件”,但实际上打印出来的文件并不是我们所想要的纯数据的文本文件,而是有一堆的打印控制信息在里面。所以我们必须创建一个文本文件打印机,然后选该文本文件打印机为软件A默认的打印机。这样我们才能得到纯数据的文本文件。
2) 创建一个文本文件打印机:在Windows中创建一个新的本地打印机,选“FILE(打印到文件)”端口(注意不是打印机端口也不是串行口);然后需要选一个打印驱动程序(当然,你是牛人的话,自己可以写一个打印驱动程序,但写打印驱动程序对大多数程序员来说是一件很困难的事情)。
3) 尽管Windows提供了数十个厂家的打印驱动程序,但唯独只有Generic提供了文本文件打印程序,所以只能选Generic的 “Generic/Text only” 作为该文本文件打印机的驱动程序。建好了文本文件打印机后,再将软件A默认的打印机重新指向到该文本打印机。这样我们再打印后就可以得到文本文件了。
问题(二)描述
我们的确可以得到文本文件,但新的问题又来了。当软件A用默认的文本文件打印机打印数据到文件时,会弹出一个输入文件名的对话框,此时如果没有操作员使用计算机,软件A会停留住。不为了不要人工输入文件名,请详见解决思路4),5)。
解决思路:
4) 每次打印到文本文件时候,系统会弹出一个对话框,需要输入文件名。对某些一天24小时不停运作的软件来说,靠人工输入文件名是不可能的。
5) 因此,我们可以设计一个程序,或者在软件B中增加一个功能,它的任务就是先找到该对话框,然后自动模拟键盘输入文件名到这个弹出的对话框,并且模拟鼠标点击“确定”按扭来关闭这个对话框,这样无须人工操作就能生成文本文件。
笔者设计的这个软件就是为了解决问题(二)。该软件不光是针对打印弹出的对话框,也可以针对其他一切类似需要人工输入的对话框。该程序代码还有助于解决如何自动关闭弹出的消息框的问题。软件B的数据来源可以是指定文件名的文本文件即我们刚刚得到的文本文件。它应该会定时地去读取这个文本文件,然后进行分析处理和统计。
笔者主要是采用了Windows API 函数,下面先对几个API函数简单的介绍一下:
FindWindow( LPCTSTR lpszClassName, LPCTSTR lpszWindowName ):
查找指定类名和窗口名的窗口,返回一个窗口句柄。GetWindowText( HWND hWndLPTSTR lpString, int nMaxCount ):
获得窗口的文本,如果该窗口是对话框,则lpString返回它的标题。
FindWindowEx( HWND hwndParent, HWND hwndChildAfter, LPCTSTR lpszClass,
LPCTSTR lpszWindow ):
在指定的父窗口内查找第一个指定类名的子窗口,返回一个窗口句柄。
SendMessage( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam ):
发一个消息给指定窗口,直到消息被处理完才返回。
SendNotifyMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam):
发一个消息给指定窗口。
有了设计思想后,再来选定一种程序语言。由于大部分语言都支持Windows API函数,所以选什么语言并不太重要,比如DELPHI,VC++,C++BUILDER,VB等都可以,笔者采用的是VC++6.0。
HWND a_window,b_window,f_window;
CFileException e;
char* pFileName ;
char pbuf[100];
memset(pbuf,0,100);
LPTSTR lpString;
CString filename=theApp.GetProfileString("Parameter","FileName","result.txt");//从配置文件中读取要填入对话框的文件名,也就是打印到文件的文件名
CString title=theApp.GetProfileString("Parameter","DialogName","打印到文件");//从配置文件中读取要输入文件名的对话框的标题,如果是英文软件就应该是”Print to file”
f_window=::FindWindow((LPCSTR)(DWORD)WC_DIALOG,title);//找到要输入文件名的对话框的窗口,获得它的句柄。
memset(pbuf,0,100);
::GetWindowText(f_window,pbuf,15);
lpString=title.GetBuffer(15);
if(*lpString==*pbuf)
{
pFileName=filename.GetBuffer(20);
CFile::Remove( pFileName );//如果已经有同名文件存在,则先删除。
filename.ReleaseBuffer();
//注意: CEdit和CButton 都是CWnd 的子类,所以都是窗口,也可以用::FindWindowEx查找得到。
a_window=::FindWindowEx(f_window,NULL,"Edit",NULL);//找到要输入文件名的编辑框。
lpString=filename.GetBuffer(15);
::SendMessage(a_window, WM_SETTEXT,NULL,(LPARAM)lpString);//模拟键盘输入文件名
b_window=::FindWindowEx(f_window,NULL,"Button",NULL);// 找到”确定”按扭
::SendNotifyMessage(b_window, WM_LBUTTONDOWN,MK_LBUTTON,NULL);//给”确定”按扭发送一个按下鼠标左键的消息
::SendNotifyMessage(b_window, WM_LBUTTONUP, NULL,NULL);// 再给”确定”按扭发送一个弹起鼠标左键的消息,第三个参数一定要是NULL,否则不成功。
}
title.ReleaseBuffer();
本程序在VC++6.0下编译,并在Window98,window2000,window XP运行通过。
这个解决方案同时也可以应用到其他自动点击按钮的领域