2025年3月13日 星期四 甲辰(龙)年 月十二 设为首页 加入收藏
rss
您当前的位置:首页 > 计算机 > 编程开发 > C语言

怎样把数据从一个程序传给另一个程序?

时间:01-03来源:作者:点击数:52

有好几种基本的方法可以完成这项任务----你可以通过文件或内存来传递这些数据。这些方法的步骤都相当简洁:首先,定义在何处存放数据,如何获取数据,以及如何通知另一个程序来获取或设置数据;然后,你就可以获取或设置数据了,尽管使用文件的技术定义和实现起来都比较简单,但它的速度往往比较慢(并且容易引起混乱)。因此,这里重点讨论内存数据转移技术。下面将依次详细地分析这一过程的每一个环节:

定义在何处存放数据。当你编写要共享数据的两个程序时,你应该让程序知道要访问的数据存放在何处。这个环节同样有几种实现方法:你可以在一个(或每个)程序中建立一个固定的内部缓冲区,并在两个程序之间传递指向这个缓冲区的指针;你也可以为数据分配动态内存,并在两个程序之间传递指向该数据的指针;如果要传递的数据很小,你还可以通过CPU的通用寄存器来传递数据(这种可能性很小,因为x86结构的寄存器很少)。分配动态内存是最灵活和模块性最强的方法。

定义获取数据的方法。这个环节非常简洁——你可以使用fmemcpy()或等价的内存拷贝函数。显然,在获取和设置数据时都可以使用这个函数。

定义通知另一个程序的方法。因为DOS并不是一个多任务操作系统,所以其中一个(或两个)程序的一部分必须已经驻留在内存中,并且可以接受来自另一个程序的调用。同样,这个环节也有几种方法可供选择:第一个程序可以是一个列入CONFIG.SYS中的驱动程序,它在系统启动时就被装入内存;第一个程序也可以是一个TSR(终止并驻留)程序,在它退出时会把与第二个程序相互作用的那部分程序驻留在内存中;此外,你也可以在第一个程序中利用system()或spawn()函数(见20.11)来启动第二个程序。你可以根据需要选择合适的方法。因为有关DOS驱动程序的数据传递在DOS文档中已经有详尽的描述,而有关system()和spawn()函数的内容也已经在前文中介绍过,因此下面介绍TSR方法。

下面的例子给出了两个程序:第一个程序是一个完整的TSR程序,但为了突出整个过程中的关键环节,它写得比较单薄(见20.15中的解释)。这个TSR程序先是安装了一个中断63H的中断服务程序,然后调用终止并驻留退出函数,在执行这个TSR程序后,执行下文给出的另一个程序。这个程序只是简单地初始化一个对中断63H的调用(类似于使用中断21H调用),并且把“Hello There”传送给上述TSR程序

  • # include <stdlib.h>
  • # include <dos.h>
  • # include <string.h>
  • void SetupPointers (void) ;
  • void OutputString(char * );
  • # define STACKSIZE 4096
  • unsigned int near OldStackPtr;
  • unsigned int near OldStackSeg;
  • unsigned int _near MyStackOff ;
  • unsigned int _near MyStackSeg;
  • unsigned char_near MyStack[STACKSIZE];
  • unsigned char far * MyStackPtr= (unsigned char_far * )MyStack;
  • unsigned short AX, BX,CX, DX,ES;
  • /* My interrupt handler */
  • void_interrupt_far_cdecl NewCommVector (
  • unsigned short es, unsigned short ds, unsigned short di,
  • unsigned short si, unsigned short bp, unsigned short sp,
  • unsigned short bx, unsigned short dx, unsigned short cx,
  • unsigned short ax, unsigned short ip, unsigned short cs,
  • unsigned short flags) ;
  • /* Pointers to the previous interrupt handier */
  • void(_interrupt_far_cdecl * CommVector)();
  • union REGS regs;
  • struet SREGS segregs ;
  • # define COMM_VECTOR 0x63 /* Software interrupt vector */
  • /* This is where the data gets passed into the TSR */
  • char_far * eallerBufPtr;
  • char localBuffer[255]; /* Limit of 255 bytes to transfer */
  • char_far * localBufPtr=(ehar_far * )loealBuffer;
  • unsigned int ProgSize= 276; /* Size of the program in paragraphs */
  • void
  • main(int argc,char * * argv)
  • {
  • int i, idx;
  • /* Set up all far pointers */
  • SetupPointers () ;
  • /* Use a cheap hack to see if the TSR is already loaded
  • tf it is, exit,doing nothing */
  • comm_veetor =_dos_getvect (COMM_VECTOR) ;
  • if(((long)eomm_vector & 0xFFFFL) ==
  • ((long) NewCommVector & OxFFFFL ) ) {
  • OutputString("Error :TSR appears to already be loaded. \n");
  • return ;
  • /* If everything's set,then chain in the TSR */
  • _dos_setvect (COMM_VECTOR ,NewCommVector) ;
  • /* Say we are loaded */
  • OutputString("TSR is now loaded at 0x63\n");
  • /* Terminate, stay resident */
  • dos_keep (0, ProgSize ) ;
  • }
  • /* Initializes all the pointers the program will use */
  • void
  • Set upPointers ( )
  • {
  • int idx ;
  • /* Save segment and offset of MyStackPtr for stack switching */
  • MyStackSeg = FP_SEG (MyStackPtr) ;
  • MyStackOff = FP_OFF (MyStackPtr) ;
  • /* Initialize my stack to hex 55 so I can see its footprint
  • if I need to do debugging */
  • for (idx = 0 ;idx<STACKSIZE ; idx ++ ) {
  • MyStack [idx] = 0x55 ;
  • }
  • }
  • void _interrupt_ far_cdecl NewCommVector (
  • unsigned short es, unsigned short ds, unsigned short di,
  • unsigned short si, unsigned short bp, unsigned short sp,
  • unsigned short bx, unsigned short dx, unsigned short cx,
  • unsigned short ax, unsigned short ip, unsigned short cs,
  • unsigned short flags)
  • {
  • AX = ax;
  • BX = bx ;
  • CX = cx;
  • DX = dx ;
  • ES = es ;
  • /* Switch to our stack so we won't run on somebody else's */
  • _asm {
  • ;set up a local stack
  • eli ; stop interrupts
  • mov OldStackSeg,ss ; save stack segment
  • mov OldStackPtr,sp ; save stack pointer (offset)
  • mov ax,ds ; replace with my stack s
  • mov ss,ax ; ditto
  • mov ax,MyStackOff ; replace with my stack s
  • add ax,STACKSIZE-2 ;add in my stack size
  • mov sp ,ax ; ditto
  • sti ; OK for interrupts again
  • }
  • switch (AX) {
  • case 0x10; /* print string found in ES:BX */
  • /* Copy data from other application locally */
  • FP_ SEG (callerBufPtr) = ES ;
  • FP_OFF (callerBufPtr) = BX ;
  • _fstrcpy (localBufPtr, callerBufPtr ) ;
  • /* print buffer 'CX' number of times */
  • for(; CX>0; CX--)
  • OutputString (localBufPtr) ;
  • AX=1; /* show success */
  • break ;
  • case 0x30: /* Unload~ stop processing interrupts */
  • _dos_setvect (COMM_VECTOR ,comm_vector) ;
  • AX=2; /* show success */
  • break ;
  • default :
  • OutputString (" Unknown command\r\n" ) ;
  • AX= 0xFFFF; /* unknown command-1 */
  • break ;
  • }
  • /* Switch back to the caller's stack */
  • asm {
  • cli ;turn off interrupts
  • mov ss,OldStackSeg ;reset old stack segment
  • mov sp,OldStackPtr ;reset old stack pointer
  • sti ;back on again
  • }
  • ax=AX; /* use return value from switch() */
  • }
  • /* avoids calling DOS to print characters */
  • void
  • OutputString(char * str)
  • {
  • int i ;
  • regs. h. ah = 0x0E ;
  • regs. x. bx = 0 ;
  • for(i=strlen(str) ; i>0; i--,str++){
  • regs. h. al= * str;
  • int86 (0xl0, &regs, &regs) ;
  • }
  • }

上述程序是这两个程序中的TSR程序。这个程序中有一个NewCommVector()函数,它被安装在中断63H(63H通常是一个可用的向量)处作为中断服务程序。当它被安装好后,它就可以接收命令了。switch语句用来处理输入的命令,并作出相应的反应。笔者随意选择了0x1O和0x30来代表这样两条命令:“从ES:BX处复制数据,并打印到屏幕上,CX中的数值为打印次数”;“脱离中断63H,并停止接收命令”。下面是第二个程序——向中断63H发送命令的程序(注意它必须在Large模式下编译)。

  • # include <stdlib.h>
  • # include <dos.h>
  • # define COMM VECTOR 0x63
  • union REGS regs;
  • struct SREGS segregs ;
  • char buffer[80];
  • char _far * buf=(char_far *)buffer;
  • main (int argc,char * * argv)
  • {
  • intcnt;
  • cnt = (argo= =1 ? 1:atoi(argv[1])) ;
  • strcpy (bur, "Hello There\r\n" ) ;
  • regs. x. ax= 0x10;
  • regs. x. cx=cnt ;
  • regs. x. bx=FP OFF(buf);
  • segregs, es=FP SEG(buf) ;
  • int86x(COMM_VECTOR ,&regs, &segregs) ;
  • printf ("TSR returned %d\n" ,regs. x. ax) ;
  • }

你可能会认为这个短小的程序看上去和那些通过调用int 21或int 10来在DOS中设置或检索信息的程序差不多。如果你真的这么想,那就对了。唯一的区别就是现在你所用的中断号是63H,而不是21H或10H。上述程序只是简单地调用前文中的TSR程序,并要求后者把es:bX所指向的字符串打印到屏幕上,然后,它把中断处理程序(即那个TSR程序)的返回值打印到屏幕上。

当字符串"Hello There”被打印到屏幕上后,在两个程序之间传递数据的全部必要步骤就都完成了。这个例子的真正价值在于它能够举一反三。现在你能很轻松地编写一个这样的程序,它将发送一条类似于“把要求你打印的最后一个字符串传递给我”的命令。你所要做的就是在前述TSR程序的switch语句中加入这条命令,然后再写一个程序来发送这条命令。此外,你也可以在第二个程序中利用20.11中所介绍的system()或spawn()函数来启动前述TSR程序。由于TSR程序会检查自己是否已被装入,因此你只需装入一次TSR程序,就可以多次运行第二个程序了。在所有要和前述TSR程序通信的程序中,你都可以使用这里所说的方法。

在建立前述TSR程序时,需要有几个前提条件。其一就是没有其它重要的中断服务程序也在处理中断63H。例如,笔者原来在程序中使用的是中断67H,结果该程序能正常装入并运行,但此后笔者就无法编译程序了,因为Microsoft用来运行C编译程序的DOS扩展程序也要使用中断67H。在笔者发送了命令0x30(让程序卸载自身)后,编译程序又能正常运行了,因为DOS扩展程序的中断处理程序已被该程序恢复了。

第二个前提条件与驻留检查在关。笔者假设永远不会有另一个中断处理程序使用和NewCommVector()相同的近程型地址,尽管这种巧合的可能性极小,但读者应该知道该程序并不是万无一失的。在该程序中,笔者特意让NewCommVector()使用自己的栈,以避免它运行在调用它的程序的栈上,但是,笔者还是假设调用所需的任何函数都是安全的。注意,该程序没有调用printf(),因为它占用较多的内存,并且要调用DOS(int 21)来打印字符。在该程序中,当中断63H发生时,笔者不知道DOS是否可以被调用,因此不能假设可以使用DOS调用。

注意,在该程序中,可以调用那些没有用到DOS int21服务程序的函数来完成所需的任务,如果必须使用一个DOS服务程序,你可以在中断63H发生时检查DOS忙标志,以确定当时DOS是否可以被调用。最后,对dos_keep()作一点说明:该函数要求知道在程序退出时要在内存中保留多少段(每段16字节)数据。在本例这个TSR程序中,提供给该函数的段数(276)稍大于整个可执行程序的大小。当你的程序变大时,提供给该函数的段数也必须增大,否则就会出现一些异常现象。

方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门