brucezh
驱动老牛
驱动老牛
  • 注册日期2002-01-30
  • 最后登录2007-02-01
  • 粉丝0
  • 关注0
  • 积分2分
  • 威望1点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
阅读:2245回复:21

谁能给一个在DS中处理IrpCancel的例子?(急)

楼主#
更多 发布于:2002-11-11 15:18
我的PCI驱动,用DMA方式传输数据,现在数据传输正常,但用户线程关闭时,驱动对被Cancel的IRP没有进行良好的处理,导致关机不正常。谁有用Driver Studio编程进行Irp Cancel处理的例子或讲讲要点?

我的驱动用KDriverManagedQueueEx对IRP排队,Dma用DriverWorks的标准模型。要不要自己写取消例程还是用KDriverManagedQueueEx原有的Cancel()?DMA的CallBack函数中作怎样的处理?

问题解决后另有高分相送。

最新喜欢:

r2109twr2109t...
打一枪……换个地方……再打一枪……
fracker
驱动太牛
驱动太牛
  • 注册日期2001-06-28
  • 最后登录2021-03-30
  • 粉丝0
  • 关注0
  • 积分219分
  • 威望81点
  • 贡献值0点
  • 好评度23点
  • 原创分0分
  • 专家分1分
  • 社区居民
沙发#
发布于:2002-11-11 15:45
你说的DMA我不懂,不过Cancel IRP我是做过的,当你有一个IRP需要Pending的时候,你用IoSetCancelRoutine设置一个CancelRoutine,当你的应用程序退出的时候,系统会自动调用这个CancelRoutine,然后,你可以在这个函数里面取消掉这个IRP就可以了。在DDK里面是这么做的,我想DS应该也一样。
brucezh
驱动老牛
驱动老牛
  • 注册日期2002-01-30
  • 最后登录2007-02-01
  • 粉丝0
  • 关注0
  • 积分2分
  • 威望1点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
板凳#
发布于:2002-11-11 15:57
谢谢你。DS中基本思想也是这样的,不过由于其对这一过程进行了自己的封装,所以看起来混乱了许多,目前我还没有找到正确的写法。
打一枪……换个地方……再打一枪……
fracker
驱动太牛
驱动太牛
  • 注册日期2001-06-28
  • 最后登录2021-03-30
  • 粉丝0
  • 关注0
  • 积分219分
  • 威望81点
  • 贡献值0点
  • 好评度23点
  • 原创分0分
  • 专家分1分
  • 社区居民
地板#
发布于:2002-11-11 16:10
这就麻烦了,我从来不用DS写程序,不过我想不通阿,你无非是有一个队列吧?你把那个IRP从队列里面删除掉,然后再结束这个IRP不可以吗?
brucezh
驱动老牛
驱动老牛
  • 注册日期2002-01-30
  • 最后登录2007-02-01
  • 粉丝0
  • 关注0
  • 积分2分
  • 威望1点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
地下室#
发布于:2002-11-11 16:21
这就麻烦了,我从来不用DS写程序,不过我想不通阿,你无非是有一个队列吧?你把那个IRP从队列里面删除掉,然后再结束这个IRP不可以吗?

问题的关键在于驱动还要控制硬件的,如果简单的删除IRP并结束。那边硬件传输完了产生中断,然后不能返回,与之相关的资源也得不到释放。
现在测试结果是用户态线程不能马上结束,要一直等到用完NT中规定的5分钟等待时间。在这之前关机就会死在关机画面处。
我参阅了DS帮助文件关于Irp取消的部分,好像如果用DS的KDriverManagedQueueEx对象,它会自动指定自己的Cancel()函数,但现在跟踪发现并没有起作用。
打一枪……换个地方……再打一枪……
brucezh
驱动老牛
驱动老牛
  • 注册日期2002-01-30
  • 最后登录2007-02-01
  • 粉丝0
  • 关注0
  • 积分2分
  • 威望1点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
5楼#
发布于:2002-11-12 11:46
我又跟踪了几次,发现Driver Studio中,正在被驱动程序处理的IRP是不能被Cancel的,正在处理的Irp对应的取消例程设为NULL。
可我的队列里只有一个IRP,这个IRP没有处理完应用层就不会发下一个。我试着让应用层等待一个IRP处理完再终止相应线程,可关机还是死在关机界面。我FAINT!谁来帮帮我啊
打一枪……换个地方……再打一枪……
yjhleaf
驱动小牛
驱动小牛
  • 注册日期2001-11-04
  • 最后登录2005-11-23
  • 粉丝0
  • 关注0
  • 积分15分
  • 威望2点
  • 贡献值0点
  • 好评度1点
  • 原创分0分
  • 专家分0分
6楼#
发布于:2002-11-12 17:00
  你返回给应用层时,你确认是返回的队列中正在处理的IRP吗?你首先应当指定你正在处理的IRP再返回。
忽如一夜春风来,千树万树梨花开。
brucezh
驱动老牛
驱动老牛
  • 注册日期2002-01-30
  • 最后登录2007-02-01
  • 粉丝0
  • 关注0
  • 积分2分
  • 威望1点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
7楼#
发布于:2002-11-12 17:26
 你返回给应用层时,你确认是返回的队列中正在处理的IRP吗?你首先应当指定你正在处理的IRP再返回。

我觉得应该是。因为我的队列中只有一个IRP,如果这个IRP没有处理完应用层就不会再发IRP来。换句话说,这个队列中就只有一个IRP。

在程序中是这样实现的:
应用程序产生事件,把句柄给驱动程序,然后等待事件。
――〉
驱动做好准备后触发事件
――〉
应用程序等到事件,清除事件,发送数据(产生一个IRP)
――〉
驱动将IRP入队(目的是MarkPending),调用DS的DMA adapter 和transfer对象进行传送。每次分割传输后产生中断,进入回调函数中设置下一次传输,直到该次DMA全部完成,返回当前IRP(用m_DriverManagedQueue.CurrentIrp()获得当前IRP),触发事件。
――〉
应用程序收到事件,清除它,再产生一个IRP。
――〉以后就这样循环。
我现在结束用户态线程的地方在等到事件之后,进行下一次传输之前,这时结束线程应该没有新的IRP产生,而上一个IRP也应该完成并返回了(否则事件不会触发)。
但只要传输过数据,就会出现关机时死机,除非程序结束后等待一段时间。这个现象很象IRP没有取消,因为IRP没有正常取消就会使用户线程等待5分钟后才结束。

说的很凌乱。请尽可能指出我的错误,重谢。
打一枪……换个地方……再打一枪……
tigerzd
驱动老牛
驱动老牛
  • 注册日期2001-08-25
  • 最后登录2004-12-13
  • 粉丝0
  • 关注0
  • 积分0分
  • 威望0点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
8楼#
发布于:2002-11-13 13:38
搞不懂你为什么做的那么复杂?
这样不很好吗:

应用程序发送IRP(带数据)
驱动程序返回pending给应用程序
驱动程序发送数据
驱动程序完成IRP
应用程序开始发送下一次IRP

IRP异步处理本身就有事件的功能,而且你的设备每次只处理一个IRP,根本不必要用队列。
犯强汉者,虽远必诛! [img]http://www.driverdevelop.com/forum/upload/tigerzd/2002-12-13_sf10.JPG[/img]
brucezh
驱动老牛
驱动老牛
  • 注册日期2002-01-30
  • 最后登录2007-02-01
  • 粉丝0
  • 关注0
  • 积分2分
  • 威望1点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
9楼#
发布于:2002-11-13 13:57
搞不懂你为什么做的那么复杂?
这样不很好吗:

应用程序发送IRP(带数据)
驱动程序返回pending给应用程序
驱动程序发送数据
驱动程序完成IRP
应用程序开始发送下一次IRP

IRP异步处理本身就有事件的功能,而且你的设备每次只处理一个IRP,根本不必要用队列。
 

老虎斑竹说的很有道理。因为这是我做的第一个驱动,没有把握什么都自己处理,所以尽可能的用DS提供的类实现。比如要自己返回pending等我就没把握能处理好,而DS中提供的队列类很容易。

那么以你的理解,如果一个IRP正在被驱动处理,甚至硬件正在执行相应的DMA,这时应用线程退出了,怎样处理这种情况?其实以现在的程序,应该不会产生这种情况。

到底有什么原因可能导致传输过数据就不能正常关机,而过一阵再关机就完全正常这种怪现象呢?用你所说的方法能避免吗?多谢
打一枪……换个地方……再打一枪……
brucezh
驱动老牛
驱动老牛
  • 注册日期2002-01-30
  • 最后登录2007-02-01
  • 粉丝0
  • 关注0
  • 积分2分
  • 威望1点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
10楼#
发布于:2002-11-13 14:00
老虎斑竹说的很有道理。因为这是我做的第一个驱动,没有把握什么都自己处理,所以尽可能的用DS提供的类实现。比如要自己返回pending等我就没把握能处理好,而DS中提供的队列类很容易。

那么以你的理解,如果一个IRP正在被驱动处理,甚至硬件正在执行相应的DMA,这时应用线程退出了,怎样处理这种情况?其实以现在的程序,应该不会产生这种情况。

到底有什么原因可能导致传输过数据就不能正常关机,而过一阵再关机就完全正常这种怪现象呢?用你所说的方法能避免吗?多谢

不好意思,刚才发的一开始没看到,又发了一篇




    :D :D :D :D

[编辑 -  11/13/02 by  brucezh]
打一枪……换个地方……再打一枪……
tigerzd
驱动老牛
驱动老牛
  • 注册日期2001-08-25
  • 最后登录2004-12-13
  • 粉丝0
  • 关注0
  • 积分0分
  • 威望0点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
11楼#
发布于:2002-11-14 08:52
关于异步Irp的方法,我写过一篇文章的,不知你看过没?
我再贴一下。以下代码是我编写的实际应用的驱动程序中使用的方法,在98/me/2000/xp下使用了很久没有过问题。其中有异步Irp的方法,你参照一下吧。


怎样在驱动层和应用层建立准消息机制
TigerZD
  驱动程序与应用程序运行与不同的环境又紧密合作,但是应用程序通知驱动程序易(IOCTL等),驱动程序通知应用程序却不易。一般的方法是单纯通过EVENT来进行,但是这种方法有其缺点:
 1、EVENT只有信号态和非信号态两种区别,不能有附带的参数,因此一个EVENT只能对应一种事件,同时很多时候EVENT并不是在信号态,此时就需要应用程序在线程中等待,一旦事件较多那么线程就会较多,不但线程同步困难,程序也不易读写。
 2、Windows 98对EVENT操作没有完全支持,像IoCreateXxxEvent并不被支持,因此要获得应用程序创建的事件句柄并不简单,这样驱动程序的通用性也被破坏了。
 基于以上原因,单纯使用EVENT并不好,那么应该怎么做呢?经过实践,我总结出了一个较好的方法,原理是利用OVERLAPPED的异步调用、同时它又可以带参数的特性。具体做法如下:
 1、在驱动程序的设备扩展中定义一个IRP变量,并在适当时候(调用IoCreateDevice初始化设备扩展后)初始化其为NULL。如
   PIRP UserMessageIrp;
 2、定义一个IOCTL,如:
  #define IOCTL_DRIVER_USERMESSAGE         CTL_CODE(FILE_DEVICE_UNKNOWN,  \\
                                                   0x801,\\
                                                   METHOD_BUFFERED,  \\
                                                   FILE_ANY_ACCESS)
 3、应用程序在启动或要监控驱动程序的状态时发送该IOCTL到驱动程序,注意应用程序在用CreateFile打开设备连接时一定要带FILE_FLAG_OVERLAPPED参数。

   HANDLE FileIOWaiter = CreateEvent( NULL, TRUE, FALSE, NULL);
if( FileIOWaiter==NULL)
return GetLastError();
OVERLAPPED ol;
ol.Offset = 0;
ol.OffsetHigh = 0;
ol.hEvent = FileIOWaiter;

ULONG PnpMessage,nBytes;
if(!DeviceIoControl(hDevice,//设备句柄
IOCTL_DRIVER_USERMESSAGE
NULL,
0,
&PnpMessage,
sizeof(PnpMessage),
&nBytes,
&ol))
{
if(GetLastError()==ERROR_IO_PENDING)
{
while(WaitForSingleObject(FileIOWaiter, 100)==WAIT_TIMEOUT)
{//驱动程序没有消息传过来,循环等待
if(bInstanceisExit == TRUE)//应用程序退出标志
{
CancelIo(hDevice);//见5
}
}
GetOverlappedResult(hDevice, &ol, &nBytes, FALSE);//
//驱动程序有消息传过来,见6,得到数据。
}
}
        //处理得到的数据,接7。

  4、驱动程序在接到此IOCTL时如下处理:
     ioControlCode = irpStack->Parameters.DeviceIoControl.IoControlCode;
switch( ioControlCode)
{
...
case IOCTL_DRIVER_USERMESSAGE
ntStatus =
Irp->IoStatus.Status = STATUS_PENDING;
Irp->IoStatus.Information = 0;
IoMarkIrpPending(Irp);
IoSetCancelRoutine(Irp,DriverUserMessageCancelIrp);//见5
deviceExtension->UserMessageIrp = Irp;
return STATUS_PENDING;
...
         }

  5、定义IRP的Cancel例程,这是在应用程序要退出而驱动程序并没有完成该IRP时调用。
VOID DriverUserMessageCancelIrp(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
{
PDEVICE_EXTENSION deviceExtension;

deviceExtension = (PDEVICE_EXTENSION)
DeviceObject->DeviceExtension;

IoReleaseCancelSpinLock(Irp->CancelIrql);

// If this is our queued read, then unqueue it
if( Irp==deviceExtension->UserMessageIrp)
{
deviceExtension->UserMessageIrp = NULL;
}
// Whatever Irp it is, just cancel it
Irp->IoStatus.Status = STATUS_CANCELLED;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp,IO_NO_INCREMENT);
}
 6、在驱动程序需要通知应用程序时,举个例子,设备拔出时在处理IRP_MN_REMOVE_DEVICE时,在调用IoDetachDevice之前:
        (#define PNP_REMOVE_DEVICE 0 //驱动程序和应用程序共同定义的消息类型)
...
ntStatus = DriverProcessUserMessageIrp(DeviceObject,PNP_REMOVE_DEVICE);
        ...
//DriverProcessUserPnpIrp的定义如下:
NTSTATUS
DriverProcessUserMessageIrp(
IN PDEVICE_OBJECT DeviceObject,
ULONG ProcessReason)
{
NTSTATUS ntStatus;
PVOID ioBuffer;
ULONG outputBufferLength;
PIO_STACK_LOCATION irpStack;
PIRP Irp;
PDEVICE_EXTENSION deviceExtension;

deviceExtension = (PDEVICE_EXTENSION)
DeviceObject->DeviceExtension;

Irp = deviceExtension->UserMessageIrp;
if(Irp == NULL)
return STATUS_SUCCESS;//这种情况是在设备启动后,应用程序并没有发送
                                      //过该IRP,设备又要卸载时出现。

irpStack = IoGetCurrentIrpStackLocation (Irp);

     // get pointers and lengths of the caller\'s (user\'s) IO buffer
     ioBuffer           = Irp->AssociatedIrp.SystemBuffer;
     outputBufferLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength;

if(ioBuffer!=NULL && sizeof(ProcessReason)<=outputBufferLength)
{
RtlCopyMemory(ioBuffer,
     &ProcessReason,
     sizeof(ProcessReason));
}
Irp->IoStatus.Status = STATUS_SUCCESS;
     Irp->IoStatus.Information = sizeof(ProcessReason);

IoSetCancelRoutine(Irp,NULL);//取消Cancel例程。
IoCompleteRequest(Irp,IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
 7、此时应用程序的OVERLAPPED的EVENT会置位,WaitForSingleObject结束等待,应用程序应如此处理:
   switch(PnpMessage)//定义见3,是不是很像Windows消息处理例程?:)
{
                ...
case PNP_REMOVE_DEVICE:
                          //处理过程就不必写了吧。
                break;
                ...
}
至此驱动程序和应用程序就完成了一次消息传递,其余可类似循环。
以上程序段在98和2000下都经过测试。
              TigerZD   .2002.7.11.(完)
//
    
犯强汉者,虽远必诛! [img]http://www.driverdevelop.com/forum/upload/tigerzd/2002-12-13_sf10.JPG[/img]
brucezh
驱动老牛
驱动老牛
  • 注册日期2002-01-30
  • 最后登录2007-02-01
  • 粉丝0
  • 关注0
  • 积分2分
  • 威望1点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
12楼#
发布于:2002-11-14 15:39
根据老虎的意见,我做了如下调整:
在受到开始一次DMA传输的Irp时,没有将它入队,而是调用自己写的函数,MarkPending,设取消例程,开始DMA。可现在一走到KDmaTransfer对象初始化的地方就出现页错误。
下面是代码片断:
Irp处理例程:
NTSTATUS **Device::IOCTL_PLAY_START_Handler(KIrp I)
{
t << \"进入Entering TsDevice::TS_IOCTL_PLAY_START_Handler, \" << I << EOL;
I.MarkPending();
CancelSpinLock::Acquire();
I.SetCancelRoutine(LinkTo(CancelProcessingIrp));
m_Irp_Processing = I;
               //m_Irp_Processing是设备类中的成员变量,用来
               //记住正在被处理的Irp,以便DMA回调函数能够
               //正确返回
CancelSpinLock::Release();
t<<\"************a transfer begin开始一次传\\
               送*********\\n\";
NTSTATUS status = dma.Initiate(
                              this,
                              &m_Dma_Ts,
                                   //KDmaAdapter对象
                              I.Mdl(),
                              FromMemoryToDevice,
                              LinkTo(CallBack_Dma),
                                   //回调函数
                              &m_CommonBuffer,
                                   //通用缓冲区对象
                              NULL,
                              FALSE
                             );
if(!NT_SUCCESS(status))
{
   I.Information() = 0;
   //如果适配器不能分配,
                //则在NextIrp例程中结束本次IRP
               //如果适配器得以分配,则IRP的完成是在DMA
                //传输全部结束以后,对于DDK编程
    //是在在DpcForIsr例程中标识,
               //对于DS在用户定义的回调函数中     I.Status() = status;
    I.PnpComplete(this,status);
    return status;
                //初始化失败或第一次分割传输完成
}
      return STATUS_PENDING;
}
Irp取消例程:
VOID TsDevice::CancelProcessingIrp(KIrp I)
{
   m_Irp_Processing = KIrp(NULL);
   I.Information() = 0;
   CancelSpinLock::Release(I->CancelIrql);
   I.PnpComplete(this,STATUS_CANCELLED);
}

出现页错误就在调用dma.Initiate()期间。谁知道这是什么原因,I.Mdl()发生了怎样的变化呢?这个IOCTL是METHOD_IN_DIRECT方式。

要知道我把该Irp入队处理,也是同样调用dma.Initiate(),就没有页错误?难道必须入队吗?
打一枪……换个地方……再打一枪……
tigerzd
驱动老牛
驱动老牛
  • 注册日期2001-08-25
  • 最后登录2004-12-13
  • 粉丝0
  • 关注0
  • 积分0分
  • 威望0点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
13楼#
发布于:2002-11-14 17:32
DS搞的这么复杂啊?晕了。 :D :( :D
犯强汉者,虽远必诛! [img]http://www.driverdevelop.com/forum/upload/tigerzd/2002-12-13_sf10.JPG[/img]
ydyuse
驱动老牛
驱动老牛
  • 注册日期2002-07-25
  • 最后登录2005-03-26
  • 粉丝0
  • 关注0
  • 积分0分
  • 威望0点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
14楼#
发布于:2002-11-15 10:01

根据WDM资料,我怀疑是否
CancelSpinLock::Release();
过早。

在IRP完成时,I/O管理器利用该域指出何时该废除文件对象指针。
所以它有时可能是NULL,即使那时,该IRP仍属于那个文件对象。
而对于堆栈单元,如果IRP正确创建,那么它总是含有正确的文件对象指针。
生命驱动,活力无限!
brucezh
驱动老牛
驱动老牛
  • 注册日期2002-01-30
  • 最后登录2007-02-01
  • 粉丝0
  • 关注0
  • 积分2分
  • 威望1点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
15楼#
发布于:2002-11-15 10:46

根据WDM资料,我怀疑是否
CancelSpinLock::Release();
过早。

在IRP完成时,I/O管理器利用该域指出何时该废除文件对象指针。
所以它有时可能是NULL,即使那时,该IRP仍属于那个文件对象。
而对于堆栈单元,如果IRP正确创建,那么它总是含有正确的文件对象指针。  

具体说来,怎样改有可能成呢?
一般出现页错误都是访问了不改访问的Irp的Mdl吧?我即使去掉设置CancelRoutine的一段一样会页错误 :(
其实我要解决的问题就一个:执行过DMA后就不能正常关机,至于用不用队列倒不重要
打一枪……换个地方……再打一枪……
ydyuse
驱动老牛
驱动老牛
  • 注册日期2002-07-25
  • 最后登录2005-03-26
  • 粉丝0
  • 关注0
  • 积分0分
  • 威望0点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
16楼#
发布于:2002-11-15 11:05
是不是没释放公用缓冲区占用的内存。通常应该在StopDevice例程中,在删除适配器对象之前释放被公用缓冲区占用的内存。
生命驱动,活力无限!
brucezh
驱动老牛
驱动老牛
  • 注册日期2002-01-30
  • 最后登录2007-02-01
  • 粉丝0
  • 关注0
  • 积分2分
  • 威望1点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
17楼#
发布于:2002-11-15 11:29
是不是没释放公用缓冲区占用的内存。通常应该在StopDevice例程中,在删除适配器对象之前释放被公用缓冲区占用的内存。

我注意到了这个问题。这部分代码如下:
NTSTATUS **Device::OnStopDevice(KIrp I)
{
NTSTATUS status = STATUS_SUCCESS;

t << \"Entering TsDevice::OnStopDevice\\n\";
    // Device stopped, release the system
                //resources.
Invalidate();
// TODO: Add device-specific code to stop your device
                //这里一般加什么代码?
return status;
  // The following macro simply allows
              //  compilation at Warning Level 4
 // If you reference this parameter in the
              // function simply remove the macro.
UNREFERENCED_PARAMETER(I);
}

VOID **Device::Invalidate()
{
// It is not necessary to release the system
           // resource for the DMA adapter
// object, since NT provides no mechanism for
           // this.
             //这里明确说明了不需释放DMA adapter对象
m_CommonBuffer.Invalidate();
             //这就是通用缓冲区的处理
m_IoPortRange0.Invalidate();
             //IO空间的释放
if(syncevent.IsValid())
{
syncevent.Clear();
syncevent.Invalidate();
                          //同步事件的释放
}

 // For the interrupt, release the underlying
             // system resource.
m_Irq.Invalidate();
             //中断的释放
}
其中通用缓冲区是设备类的成员函数,在OnStartDevice(KIrp I)中初始化。

以上代码的操作有什么问题吗?
打一枪……换个地方……再打一枪……
tigerzd
驱动老牛
驱动老牛
  • 注册日期2001-08-25
  • 最后登录2004-12-13
  • 粉丝0
  • 关注0
  • 积分0分
  • 威望0点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
18楼#
发布于:2002-11-19 11:54
I.MarkPending();是不是多余?
犯强汉者,虽远必诛! [img]http://www.driverdevelop.com/forum/upload/tigerzd/2002-12-13_sf10.JPG[/img]
ydyuse
驱动老牛
驱动老牛
  • 注册日期2002-07-25
  • 最后登录2005-03-26
  • 粉丝0
  • 关注0
  • 积分0分
  • 威望0点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
19楼#
发布于:2002-11-19 13:17
释放公用缓冲区后把公用缓冲区的指针设为NULL

[编辑 -  11/19/02 by  ydyuse]
生命驱动,活力无限!
上一页
游客

返回顶部