现在再也没有人会怀疑Windows将取代DOS了,但由于种种原因,特别是在拥有大量正在运行的DOS程序的情况下,软件开发还只能逐步从DOS向Windows过渡,如何使DOS程序与Windows程序进行数据交换就成了一个重要的问题。在介绍我们的解决方案之前,先简单回顾一下经典的方法[1]。一般是由处于实模式的DOS程序先申请一块常规内存,然后由Windows程序利用Windows
3.x API中的三个函数:AllocSelector()、SetSelectorBase()和SetSelectorLimit(),在保护模式下读写这块内存,从而实现Windows和DOS程序的数据交换。这种方法在实时数据采集方面有很好的效果,但它也有两个比较大的缺点:一个是Windows程序设计比较麻烦,特别是初学者容易导致GPF错误,而且难于用Visual
Basic和FoxPro实现;另一个是在Win32 API中取消了这三个函数,使得Windows 95程序不能再采用这一方法。我们的方法是让DOS程序直接读写Windows的剪贴板,这样可以象其他Windows程序一样利用剪贴板交换数据。这种方法可以在一些经典方法不能使用的场合获得较好的效果。
我们知道,DOS程序调用系统功能都是通过中断实现,而Windows程序则是通过一组API来实现的,但这并不等于说Windows就不使用中断了。在Windows中不光是文件操作使用了DOS中断,它自身特有的许多功能也是通过中断实现,这其中就包括了我们所要用到的剪贴板函数。不单单是Windows
3.x使用了内部中断,Windows 95也使用了大量的内部中断。Windows的内部中断主要是利用了INT 2FH,所有在Windows下运行的程序(包括MS-DOS应用程序)都可以调用这些中断。所以DOS程序可以象Windows程序一样完全合法地调用这些Windows功能,只不过这种方法是秘密的,在Windows
SDK手册上没有介绍。剪贴板函数使用的都是INT 2FH的17号功能,即AH=17H。比如在Windows程序读写剪贴板之前都要调用OpenClipboard()函数,这个函数对应着17号功能的1号子功能,可以用下面这段代码来实现:
MOV AX, 1701H
INT 2FH
类似地,我们可以实现Windows API中的EmptyClipboard()、CloseClipboard()
、SetClipboardData()和GetClipboardData()函数。除此以外,我们还发现了一个在SDK手册上没有的GetClipboardDataSize()函数,这个函数返回剪贴板的数据大小,这个数值通常是16的整数倍,它比剪贴板上实际字符串的长度要大,这个函数对你在读剪贴板之前进行内存分配很有帮助。下面这段示例程序包括了这些函数的实现。在调用这些中断之前先要确保Windows已经运行,示例中的IsWindowsRunning()函数就是完成这一工作的。你甚至可以用老掉牙的编译系统Turbo
C 2.0来运行它。示例程序先在剪贴板上写入一个字符串,然后从剪贴板读回这个字符串。你可以发现整个示例程序的编程同Windows程序的差别是很小的,这是此种方法的一个优点:你对DOS程序的修改可以减少到最少,而Windows程序则不需要任何修改。可以预见,如果你把DOS部分移植到Windows上来,此处所需的改动也是很少的。
#include <stdlib.h>
#include <dos.h>
#include <string.h>
union REGS r;
struct SREGS sr;
/*
The OpenClipboard function opens the clipboard. Other applications
will not be able to modify the clipboard until the CloseClipboard
function is called.
*/
void OpenClipboard()
{
r.x.ax = 0x1701;
int86(0x2f, &r, &r);
}
/*
The EmptyClipboard function empties the clipboard and frees handles
to data in the clipboard.
*/
void EmptyClipboard()
{
r.x.ax = 0x1702;
int86(0x2f, &r, &r);
}
/*
The CloseClipboard function closes the clipboard.
*/
void CloseClipboard()
{
r.x.ax = 0x1708;
int86(0x2f, &r, &r);
}
/*
The SetClipboardData function sets the data in the clipboard. The
application must have called the OpenClipboard function before
calling the SetClipboardData function.
*/
int SetClipboardData(char* s)
{
int Len;
Len = strlen(s) + 1;
r.x.ax = 0x1703;
r.x.dx = 1;
r.x.si = 0;
r.x.cx = Len;
r.x.bx = FP_OFF(s);
sr.es = FP_SEG(s);
int86x(0x2f, &r, &r, &sr);
return r.x.ax;
}
/*
The GetClipboardDataSize function retrieves the size of the current
clipboard data. This function is undocumented in Windows SDK.
*/
int GetClipboardDataSize()
{
r.x.ax = 0x1704;
r.x.dx = 1;
int86(0x2f, &r, &r);
return r.x.ax;
}
/*
The GetClipboardData function retrieves a handle of the current
clipboard data having string format. The clipboard must have been
opened previously.
*/
int GetClipboardData(char* s)
{
r.x.ax = 0x1705;
r.x.dx = 1;
r.x.bx = FP_OFF(s);
sr.es = FP_SEG(s);
int86x(0x2f, &r, &r, &sr);
return r.x.ax;
}
/*
The IsWindowsRunning function detects whether Windows is running.
The application must have called this function before calling all
Clipboard functions.
*/
int IsWindowsRunning()
{
r.x.ax = 0x1700;
int86(0x2f, &r, &r);
return r.x.ax != 0x1700;
}
void main()
{
char s[256],s2[]="This string is passed by an MS-DOS application.";
if (!IsWindowsRunning()) {
printf("Windows is NOT running!\n");
exit(- 1);
}
OpenClipboard();
SetClipboardData(s2);
printf("Data size=%d, string length=%d\n", GetClipboardDataSize(s2),
strlen(s2));
GetClipboardData(s);
printf("%s\n", s);
CloseClipboard();
getch();
}
我们不需要再编写一个Windows的示例程序,因为你可以在任何一个能够进行文本粘贴的Windows 3.x或Windows
95的程序中,比如NotePad,来检验这一结果。从Windows使用者的角度来看,你甚至感觉不到这个字符串是来自DOS的。如果打开Windows的剪贴板查看程序,你会发现此时剪贴板上的数据格式为“文本”和“OEM文本”两种。
由于Windows利用剪贴板交换图形数据使用的是位图句柄,而这不易于DOS应用程序操作,所以利用本方法交换图形时最好不使用Windows定义的位图格式。本方法也不是万能的,它对于时序要求严格的或数据更新很快的场合不太适用。另外,我们还没有找到如何截取Windows的DDE消息的办法,所以目前这种方法不能实现DOS程序和Windows程序间的DDE连接。我们的工作权且算作抛砖引玉,希望你能找到更好的方法。
参考文献
[1]纪秀华,一种实现Windows与DOS应用程序间数据交换的简单方法,计算机世界报,1996年4月15日第195版。
如果您有任何建议,请给我发电子邮件:
。
版权所有 李海,热情软件屋 1997-2006