HOWTO:将 IOCTL 发送到筛选器驱动程序

察看本文应用于的产品

<script type="text/javascript">function loadTOCNode(){}</script>

文章编号 : 262305
最后修改 : 2003年10月23日
修订 : 1.4
本文的发布号曾为 CHS262305

概要

<script type="text/javascript">loadTOCNode(1, 'summary');</script>

本文说明如何通过创建独立的控件 deviceobject 而不是打开由筛选器驱动程序注册的专用设备接口 (IoRegisterDeviceInterface),将 IOCTL 请求发送到即插即用 (PNP) 筛选器驱动程序。

更多信息

<script type="text/javascript">loadTOCNode(1, 'moreinformation');</script>

正在编写筛选器驱动程序的开发人员可能需要将自定义设备 I/O 控制请求从应用程序或另一个驱动程序发送到它们的筛选器驱动程序,以便控制其行为。在基于 Windows 2000 的计算机上,Microsoft 建议驱动程序在 AddDevice 例程中为要与它们进行交互的其他应用程序或驱动程序注册设备接口。

按照此建议,筛选器驱动程序编写者通过在 PhysicalDeviceObject (PDO) 上注册他们自己的 GUID(在 AddDevice 例程中收到),编写自定义应用程序以枚举此接口 GUID 并将 IOCTL 发送到驱动程序。

status = IoRegisterDeviceInterface (
PhysicalDeviceObject,
(LPGUID) &MY_SPECIAL_INTERFACE_GUID,
NULL,
&devExt->InterfaceName);

只要筛选器是堆栈中最上面的筛选器(类筛选器),此方法就会起作用,因为该筛选器首先处理请求。如果筛选器驱动程序位于堆栈中的其他任何位置,则 IOCTL 请求可能被其他筛选器或它上面的功能驱动程序拒绝。筛选器驱动程序拒绝它不知道的请求是不适当的;但是,功能驱动程序可能会这样做。因此,如果筛选器驱动程序是一个低层筛选器,功能驱动程序肯定会拒绝不知道的所有 IOCTL 请求。

避免此问题的唯一方法是创建另一个名为 controlobject 的独立控件和一个符号链接(如通常对 Microsoft Windows NT 4.0 驱动程序所做的那样),而不是在 PDO 上注册接口。应用程序可以打开符号链接并将 IOCTL 发送到筛选器。这些 I/O 请求会被直接发送到控件 deviceobject,而不是遍历整个堆栈,不管筛选器驱动程序位于堆栈中的什么位置。

以下代码显示如何为通用 toaster 筛选器示例 (Filter.c) 提供自定义 IOCTL 支持,该示例位于 Windows 2000 和 Windows XP DDK 中,在 <DDK 根目录>/Src/General/Toaster/Filter/ 下。提供 IOCTL 支持所需的所有代码都位于条件编译指令内。您必须在源文件中定义 IOCTL_INTERFACE(-DIOCTL_INTERFACE=1),以包括 IOCTL 接口支持。

与在 Windows 2000 DDK 中提供的示例相比,除了为演示 IOCTL 接口而添加的代码之外,在代码中还有一些增强和错误更正。

#include <ntddk.h>
#include "filter.h"
#ifdef ALLOC_PRAGMA
#pragma alloc_text (INIT, DriverEntry)
#pragma alloc_text (PAGE, FilterAddDevice)
#pragma alloc_text (PAGE, FilterDispatchPnp)
#pragma alloc_text (PAGE, FilterUnload)
#endif
//
// Define IOCTL_INTERFACE in your sources file if  you want your
// app to have private interaction with the filter driver.Read KB Q262305
// for more information.
//
#ifdef IOCTL_INTERFACE
#ifdef ALLOC_PRAGMA
#pragma alloc_text (PAGE, FilterCreateControlObject)
#pragma alloc_text (PAGE, FilterDeleteControlObject)
#pragma alloc_text (PAGE, FilterDispatchIo)
#endif
FAST_MUTEX ControlMutex;
ULONG InstanceCount = 0;
PDEVICE_OBJECT ControlDeviceObject;
#endif
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT  DriverObject,
IN PUNICODE_STRING RegistryPath
)
/*++
例程说明:
可安装的驱动程序初始化入口点。
此入口点由 I/O 系统直接调用。
参数:
DriverObject - 指向驱动程序对象的指针
RegistryPath - 指向表示路径的 unicode 字符串,该路径是
注册表中驱动程序特定的键的路径。
返回值:
STATUS_SUCCESS if successful,
STATUS_UNSUCCESSFUL otherwise.
--*/
{
NTSTATUS            status = STATUS_SUCCESS;
ULONG               ulIndex;
PDRIVER_DISPATCH  * dispatch;
UNREFERENCED_PARAMETER (RegistryPath);
DebugPrint (("Entered the Driver Entry/n"));
//
// Create dispatch points
//
for (ulIndex = 0, dispatch = DriverObject->MajorFunction;
ulIndex <= IRP_MJ_MAXIMUM_FUNCTION;
ulIndex++, dispatch++) {
*dispatch = FilterPass;
}
DriverObject->MajorFunction[IRP_MJ_PNP]            = FilterDispatchPnp;
DriverObject->MajorFunction[IRP_MJ_POWER]          = FilterDispatchPower;
DriverObject->DriverExtension->AddDevice           = FilterAddDevice;
DriverObject->DriverUnload                         = FilterUnload;
#ifdef IOCTL_INTERFACE
//
// Set the following dispatch points as we will be doing
// something useful to these requests instead of just
// passing them down.
//
DriverObject->MajorFunction[IRP_MJ_CREATE]     =
DriverObject->MajorFunction[IRP_MJ_CLOSE]      =
DriverObject->MajorFunction[IRP_MJ_CLEANUP]    =
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] =
FilterDispatchIo;
//
// Mutex is to synchronize multiple threads creating & deleting
// control deviceobjects.
//
ExInitializeFastMutex (&ControlMutex);
#endif
return status;
}
NTSTATUS
FilterAddDevice(
IN PDRIVER_OBJECT  DriverObject,
IN PDEVICE_OBJECT PhysicalDeviceObject
)
/*++
例程说明:
"即插即用"子系统为我们提供了一个全新的 PDO,我们要
(通过 INF 注册的方法)为其提供驱动程序。
我们需要确定是否需要处于该设备的驱动程序堆栈中。
创建功能设备对象以连接到堆栈
初始化该设备对象
返回状态成功。
请记住:实际上我们不能将任何非 pnp IRPS 发送到给定的驱动程序
堆栈,除非已经接收到了 IRP_MN_START_DEVICE。
参数:
DeviceObject - 指向设备对象的指针。
PhysicalDeviceObject - 指向
最下层总线驱动程序创建的设备对象的指针。
返回值:
NT status code.
--*/
{
NTSTATUS            status = STATUS_SUCCESS;
PDEVICE_OBJECT          deviceObject = NULL;
PDEVICE_EXTENSION       deviceExtension;
ULONG                   deviceType = FILE_DEVICE_UNKNOWN;
PAGED_CODE ();
//
// IoIsWdmVersionAvailable(1, 0x20) returns TRUE on os after Windows 2000.
//
if (!IoIsWdmVersionAvailable(1, 0x20)) {
//
// Win2K system bugchecks if the filter attached to a storage device
// doesn't specify the same DeviceType as the device it's attaching
// to.This bugcheck happens in the filesystem when you disable
// the devicestack whose top level deviceobject doesn't have a VPB.
// To workaround we will get the toplevel object's DeviceType and
// specify that in IoCreateDevice.
//
deviceObject = IoGetAttachedDeviceReference(PhysicalDeviceObject);
deviceType = deviceObject->DeviceType;
ObDereferenceObject(deviceObject);
}
//
// Create a filter device object.
//
status = IoCreateDevice (DriverObject,
sizeof (DEVICE_EXTENSION),
NULL,  // No Name
deviceType,
FILE_DEVICE_SECURE_OPEN,
FALSE,
&deviceObject);
if (!NT_SUCCESS (status)) {
//
// Returning failure here prevents the entire stack from functioning,
// but most likely the rest of the stack will not be able to create
// device objects either, so it is still OK.
//
return status;
}
DebugPrint (("AddDevice PDO (0x%x) FDO (0x%x)/n",
PhysicalDeviceObject, deviceObject));
deviceExtension = (PDEVICE_EXTENSION) deviceObject->DeviceExtension;
deviceExtension->NextLowerDriver = IoAttachDeviceToDeviceStack (
deviceObject,
PhysicalDeviceObject);
//
// Failure for attachment is an indication of a broken plug & play system.
//
if(NULL == deviceExtension->NextLowerDriver) {
IoDeleteDevice(deviceObject);
return STATUS_UNSUCCESSFUL;
}
deviceObject->Flags |= deviceExtension->NextLowerDriver->Flags &
(DO_BUFFERED_IO | DO_DIRECT_IO |
DO_POWER_PAGABLE );
deviceObject->DeviceType = deviceExtension->NextLowerDriver->DeviceType;
deviceObject->Characteristics =
deviceExtension->NextLowerDriver->Characteristics;
deviceExtension->Self = deviceObject;
//
// Set the initial state of the Filter DO
//
INITIALIZE_PNP_STATE(deviceExtension);
DebugPrint(("AddDevice:%x to %x->%x /n", deviceObject,
deviceExtension->NextLowerDriver,
PhysicalDeviceObject));
deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
return STATUS_SUCCESS;
}
NTSTATUS
FilterPass (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
例程说明:
默认的调度例程。如果此驱动程序不识别
IRP,那么它应该将它不经修改就向下发送。
如果该设备正在处理其他一些 IRP,此 IRP 必须在设备扩展中排队
不需要完成例程。
只是为了进行演示,我们才会将所有(非"即插即用")Irp 向下传递
到堆栈上(因为我们是筛选器驱动程序)。实际的驱动程序可能会选择
为这些 Irp 中的一部分提供服务。
由于我们不清楚正在传递哪个函数,所以,我们不能假定自己是否在比较高的 IRQL 上被调用。
因此,此函数必须置于非分页的池
(也称默认位置)中。
参数:
DeviceObject - 指向设备对象的指针。
Irp - 指向 I/O 请求数据包的指针。
返回值:
NT 状态代码
--*/
{
PDEVICE_EXTENSION       deviceExtension;
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
IoSkipCurrentIrpStackLocation (Irp);
return IoCallDriver (deviceExtension->NextLowerDriver, Irp);
}
NTSTATUS
FilterDispatchPnp (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
例程说明:
"即插即用"调度例程。
这些驱动程序中,多数都将会完全忽略。
在所有情况下,它都必须把 IRP 传递到较低级别的驱动程序。
参数:
DeviceObject - 指向设备对象的指针。
Irp - 指向 I/O 请求数据包的指针。
返回值:
NT 状态代码
--*/
{
PDEVICE_EXTENSION       deviceExtension;
PIO_STACK_LOCATION          irpStack;
NTSTATUS                    status;
PAGED_CODE ();
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
irpStack = IoGetCurrentIrpStackLocation(Irp);
DebugPrint(("FilterDO %s IRP:0x%x /n",
PnPMinorFunctionString(irpStack->MinorFunction), Irp));
switch (irpStack->MinorFunction) {
case IRP_MN_START_DEVICE:
//
// The device is starting.
//
// We cannot touch the device (send it any non pnp irps) until a
// start device has been passed down to the lower drivers.
//
IoCopyCurrentIrpStackLocationToNext(Irp);
IoSetCompletionRoutine(Irp,
(PIO_COMPLETION_ROUTINE) FilterStartCompletionRoutine,
NULL,
TRUE,
TRUE,
TRUE);
return IoCallDriver (deviceExtension->NextLowerDriver, Irp);
case IRP_MN_REMOVE_DEVICE:
IoSkipCurrentIrpStackLocation (Irp);
status = IoCallDriver(deviceExtension->NextLowerDriver, Irp);
SET_NEW_PNP_STATE(deviceExtension, Deleted);
#ifdef IOCTL_INTERFACE
FilterDeleteControlObject();
#endif
IoDetachDevice(deviceExtension->NextLowerDriver);
IoDeleteDevice(DeviceObject);
return status;
case IRP_MN_QUERY_STOP_DEVICE:
SET_NEW_PNP_STATE(deviceExtension, StopPending);
status = STATUS_SUCCESS;
break;
case IRP_MN_CANCEL_STOP_DEVICE:
//
// Check to see whether you have received cancel-stop
// without first receiving a query-stop.This could happen if someone
// above us fails a query-stop and passes down the subsequent
// cancel-stop.
//
if(StopPending == deviceExtension->DevicePnPState)
{
//
// We did receive a query-stop, so restore.
//
RESTORE_PREVIOUS_PNP_STATE(deviceExtension);
}
status = STATUS_SUCCESS; // We must not fail this IRP.
break;
case IRP_MN_STOP_DEVICE:
SET_NEW_PNP_STATE(deviceExtension, Stopped);
status = STATUS_SUCCESS;
break;
case IRP_MN_QUERY_REMOVE_DEVICE:
SET_NEW_PNP_STATE(deviceExtension, RemovePending);
status = STATUS_SUCCESS;
break;
case IRP_MN_SURPRISE_REMOVAL:
SET_NEW_PNP_STATE(deviceExtension, SurpriseRemovePending);
status = STATUS_SUCCESS;
break;
case IRP_MN_CANCEL_REMOVE_DEVICE:
//
// Check to see whether you have received cancel-remove
// without first receiving a query-remove.This could happen if
// someone above us fails a query-remove and passes down the
// subsequent cancel-remove.
//
if(RemovePending == deviceExtension->DevicePnPState)
{
//
// We did receive a query-remove, so restore.
//
RESTORE_PREVIOUS_PNP_STATE(deviceExtension);
}
status = STATUS_SUCCESS; // We must not fail this IRP.
break;
case IRP_MN_DEVICE_USAGE_NOTIFICATION:
//
// On the way down, pagable might become set.Mimic the driver
// above us.If no one is above us, just set pagable.
//
if ((DeviceObject->AttachedDevice == NULL) ||
(DeviceObject->AttachedDevice->Flags & DO_POWER_PAGABLE)) {
DeviceObject->Flags |= DO_POWER_PAGABLE;
}
IoCopyCurrentIrpStackLocationToNext(Irp);
IoSetCompletionRoutine(
Irp,
FilterDeviceUsageNotificationCompletionRoutine,
NULL,
TRUE,
TRUE,
TRUE
);
return IoCallDriver (deviceExtension->NextLowerDriver, Irp);
default:
//
// If you don't handle any IRP you must leave the
// status as is.
//
status = Irp->IoStatus.Status;
break;
}
//
// Pass the IRP down and forget it.
//
Irp->IoStatus.Status = status;
return FilterPass(DeviceObject, Irp);
}
NTSTATUS
FilterStartCompletionRoutine(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP             Irp,
IN PVOID            Context
)
/*++
例程说明:
一个完成例程,在调用我们的筛选器设备对象所附加的低层设备对象时
使用该例程。
参数:
DeviceObject - 指向 deviceobject 的指针
Irp          - 指向 PnP Irp 的指针。
上下文      - NULL
返回值:
返回 NT 状态。
--*/
{
PDEVICE_EXTENSION       deviceExtension;
UNREFERENCED_PARAMETER(Context);
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
if (Irp->PendingReturned) {
IoMarkIrpPending(Irp);
}
if (NT_SUCCESS (Irp->IoStatus.Status)) {
//
// As we are successfully now back, we will
// first set our state to Started.
//
SET_NEW_PNP_STATE(deviceExtension, Started);
//
// On the way up inherit FILE_REMOVABLE_MEDIA during Start.
// This characteristic is available only after the driver stack is started!.
//
if (deviceExtension->NextLowerDriver->Characteristics & FILE_REMOVABLE_MEDIA) {
DeviceObject->Characteristics |= FILE_REMOVABLE_MEDIA;
}
#ifdef IOCTL_INTERFACE
//
// If the PreviousPnPState is stopped then we are being stopped temporarily
// and restarted for resource rebalance.
//
if(Stopped != deviceExtension->PreviousPnPState) {
//
// Device is started for the first time.
//
FilterCreateControlObject(DeviceObject);
}
#endif
}
return STATUS_SUCCESS;
}
NTSTATUS
FilterDeviceUsageNotificationCompletionRoutine(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP             Irp,
IN PVOID            Context
)
/*++
例程说明:
一个完成例程,在调用我们的筛选器 deviceobject 所附加的低层设备对象时
使用该例程。
参数:
DeviceObject - 指向 deviceobject 的指针
Irp          - 指向 PnP Irp 的指针。
上下文      - NULL
返回值:
返回 NT 状态。
--*/
{
PDEVICE_EXTENSION       deviceExtension;
UNREFERENCED_PARAMETER(Context);
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
if (Irp->PendingReturned) {
IoMarkIrpPending(Irp);
}
//
// On the way up, pagable might become clear.Mimic the driver below us.
//
if (!(deviceExtension->NextLowerDriver->Flags & DO_POWER_PAGABLE)) {
DeviceObject->Flags &= ~DO_POWER_PAGABLE;
}
return STATUS_SUCCESS;
}
NTSTATUS
FilterDispatchPower(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
例程说明:
此例程是 power irp 的调度例程。
参数:
DeviceObject - 指向设备对象的指针。
Irp - 指向请求数据包的指针。
返回值:
NT 状态代码
--*/
{
PDEVICE_EXTENSION       deviceExtension;
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
PoStartNextPowerIrp(Irp);
IoSkipCurrentIrpStackLocation (Irp);
return PoCallDriver(deviceExtension->NextLowerDriver, Irp);
}
VOID
FilterUnload(
IN PDRIVER_OBJECT DriverObject
)
/*++
例程说明:
释放 DriverEntry 等中所有已分配的资源。
参数:
DriverObject - 指向驱动程序对象的指针。
返回值:
VOID.
--*/
{
PAGED_CODE ();
//
// The device object(s) should be NULL now
// (since we unload, all the devices objects associated with this
// driver must be deleted.
//
ASSERT(DriverObject->DeviceObject == NULL);
//
// We should not be unloaded until all the devices we control
// have been removed from our queue.
//
DebugPrint (("unload/n"));
return;
}
#ifdef IOCTL_INTERFACE
NTSTATUS
FilterCreateControlObject(
IN PDEVICE_OBJECT    DeviceObject
)
{
UNICODE_STRING      ntDeviceName;
UNICODE_STRING      symbolicLinkName;
PCONTROL_DEVICE_EXTENSION   deviceExtension;
NTSTATUS                    status;
ExAcquireFastMutex (&ControlMutex);
//
// If this is a first instance of the device, then create a controlobject
// and register dispatch points to handle ioctls.
//
if(1 == ++InstanceCount)
{
//
// Initialize the unicode strings
//
RtlInitUnicodeString(&ntDeviceName, NTDEVICE_NAME_STRING);
RtlInitUnicodeString(&symbolicLinkName, SYMBOLIC_NAME_STRING);
//
// Create a named deviceobject so that applications or drivers
// can directly talk to us without going throuhg the entire stack.
// This call could fail if there are not enough resources or
// another deviceobject of same name exists (name collision).
//
status = IoCreateDevice(DeviceObject->DriverObject,
sizeof(CONTROL_DEVICE_EXTENSION),
&ntDeviceName,
FILE_DEVICE_UNKNOWN,
FILE_DEVICE_SECURE_OPEN,
FALSE,
&ControlDeviceObject);
if (NT_SUCCESS( status )) {
ControlDeviceObject->Flags |= DO_BUFFERED_IO;
status = IoCreateSymbolicLink( &symbolicLinkName, &ntDeviceName );
if (!NT_SUCCESS (status)) {
IoDeleteDevice(ControlDeviceObject);
DebugPrint(("IoCreateSymbolicLink failed %x/n", status));
goto End;
}
deviceExtension = ControlDeviceObject->DeviceExtension;
deviceExtension->ControlData = NULL;
ControlDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
}else {
DebugPrint(("IoCreateDevice failed %x/n", status));
}
}
End:
ExReleaseFastMutex (&ControlMutex);
return status;
}
VOID
FilterDeleteControlObject(
)
{
UNICODE_STRING      symbolicLinkName;
ExAcquireFastMutex (&ControlMutex);
//
// If this is the last instance of the device then delete the controlobject
// and symbolic link to enable the pnp manager to unload the driver.
//
if(!(--InstanceCount) && ControlDeviceObject)
{
RtlInitUnicodeString(&symbolicLinkName, SYMBOLIC_NAME_STRING);
IoDeleteSymbolicLink(&symbolicLinkName);
IoDeleteDevice(ControlDeviceObject);
ControlDeviceObject= NULL;
}
ExReleaseFastMutex (&ControlMutex);
}
NTSTATUS
FilterDispatchIo(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
例程说明:
此例程是非 passthru irp 的调度例程。
我们将检查输入设备对象以了解请求是否
要用于控制设备对象。如果是,我们将
处理并完成 IRP,如果不是,我们会将它向下传递到
较低级别的驱动程序。
参数:
DeviceObject - 指向设备对象的指针。
Irp - 指向请求数据包的指针。
返回值:
NT 状态代码
--*/
{
PIO_STACK_LOCATION          irpStack;
NTSTATUS                    status;
PAGED_CODE ();
//
// Please note that this is a common dispatch point for controlobject and
// filter deviceobject attached to the pnp stack.
//
if(DeviceObject != ControlDeviceObject) {
//
// We will just  the request down as we are not interested in handling
// requests that come on the PnP stack.
//
return FilterPass(DeviceObject, Irp);
}
//
// Else this is targeted at our control deviceobject so let's handle it.
// Here we will handle the IOCTl requests that come from the app.
//
status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
irpStack = IoGetCurrentIrpStackLocation(Irp);
switch (irpStack->MajorFunction) {
case IRP_MJ_CREATE:
DebugPrint(("Create /n"));
break;
case IRP_MJ_CLOSE:
DebugPrint(("Close /n"));
break;
case IRP_MJ_CLEANUP:
DebugPrint(("Cleanup /n"));
break;
case  IRP_MJ_DEVICE_CONTROL:
DebugPrint(("DeviceIoControl/n"));
switch (irpStack->Parameters.DeviceIoControl.IoControlCode) {
//
//case IOCTL_CUSTOM_CODE:
//
default:
status = STATUS_INVALID_PARAMETER;
break;
}
default:
break;
}
Irp->IoStatus.Status = status;
IoCompleteRequest (Irp, IO_NO_INCREMENT);
return status;
}
#endif 

对于附加了筛选器的设备的所有实例,该控件设备对象是公用的。因此,当启动设备的第一个实例时,会创建它 (FilterCreateControlObject);当删除设备的最后一个实例时,会删除它 (FilterDeleteControlObject)。如果在移除设备的最后一个实例之前没有删除控件对象,则控件对象将阻止筛选器动态卸载。此示例使用了一个互斥体,还使用一个全局计数器来跟踪实例。之所以将互斥体用于同步而不是使用自旋锁,是因为访问全局计数器的所有例程都在 IRQL PASSIVE_LEVEL 上运行。

HOWTO:将 IOCTL 发送到筛选器驱动程序相关推荐

  1. ssrs筛选器_SSRS ReportServer:服务性能计数器指南

    ssrs筛选器 SSRS性能计数器 (SSRS performance counters) Measurements of the Reporting Services service (SSRS) ...

  2. Tableau中的筛选器

      Tableau中常用的筛选器类型有:数据提取筛选器.数据源筛选器.上下文筛选器.维度筛选器.度量筛选器和表计算筛选器.这些筛选器的执行优先级从高到低依次如下图所示.下面依次对各个筛选器进行介绍. ...

  3. 统一写入筛选器(UWF)常用命令

    自己整理了一份统一写入筛选器(UWF)命令如下: 参数列表: filter 配置和显示 UWF 设置,例如筛选状态. overlay 配置和显示覆盖设置. volume 配置和显示卷筛选设置. fil ...

  4. 【JIRA 学习】制作筛选器:将搜索条件保存为一键直达的视图

    一.筛选器介绍 有时候我们需要在JIRA中搜索查看指定条件的issue,比如查看指派给我的任务.小组内未解决的bug等场景,那么怎样才能快速地定位到这些issue呢? JIRA是支持跨项目搜索的,也支 ...

  5. 小技巧(3):peco、bat,软链接与硬链接,Linux Shell Options,Windows 展台模式,与统一写入筛选器

    Windows 下的 cat 和 grep ls.cat.grep这三个命令,往往是Linux初学者最先了解的.日常使用Linux时不可或缺的.对于Windows系统而言,同样有三个命令可以完成上述工 ...

  6. Tableau参数:使用参数创建开始日期和结束日期筛选器

    Tableau默认的日期筛选样式有相对日期.日期范围.开始日期.结束日期等,可以根据自己的需要选择合适的方式. 相对日期:以一个时间点为基准,可以选择这个基准前(后)N天/周/季度/年. 日期范围:可 ...

  7. 如何更新 Exchange2003 SP2 中的智能邮件筛选器版本

    概要 本文介绍了可用于维护 Microsoft Exchange 2003 Service Pack 2 (SP2) 所附带的智能邮件筛选器版本 2 的更新过程. 在每月的第一个和第三个星期三,将通过 ...

  8. c#学习之基础篇(filter 筛选器)

    提供的筛选器字符串无效.筛选器字符串必须包含筛选器的说明,后跟竖线(|)和筛选模式.不同筛选选项的字符串还必须以竖线分隔.例如:"文本文件(*.txt)|*.txt|所有文件(*.*)|*. ...

  9. 通过BCS对象模型使用筛选器

    在上一篇博文中,我们介绍了如何使用SharePoint服务器上的业务连接服务对象模型连接到BCS服务元数据存储,来获取一个企业核心业务系统对象.外部内容类型及其方法,以及如何执行一个外部内容类型的方法 ...

最新文章

  1. R语言setdiff函数集合作差运算实战
  2. BZOJ 3669: [Noi2014]魔法森林( LCT )
  3. jsp页面,在浏览器端显示时会出现乱码解决方法
  4. MySQL中如何通过修改表实现约束的添加与删除
  5. GIS实用小技巧(三)-CASS怎么添加图例?
  6. CentOS开启samba服(附smb.conf手册)
  7. 第一课 GoC简介和演示
  8. 串口读取gps信息php,Linux串口读取GPS数据
  9. OK插件安装常见问题集锦(PowerPoint版)|OneKeyTools Lite安装说明
  10. 用Python写游戏脚本原来这么简单
  11. usertoken_华为手机usertoken已过期
  12. 中文乱码问题—字符集utf8、uf8mb4与排序规则
  13. Kaggle实战之 房价预测案例
  14. min_25 JZOJ5594 最大真因数
  15. QT编程从入门到精通之一:“第一章:认识QT”之“1.1 Qt简介”
  16. vmware虚拟机15 安装centos764 并且配置网络
  17. 身份证号码验证--C/C++ 实现
  18. Windows PowerShell 入門(10)-デバッグ編
  19. 一些常用的sql命令记录
  20. 当当网CEO李国庆微博上发飙,真牛!

热门文章

  1. 2020游戏直播行业数据报告
  2. 2020中国男士美妆市场洞察报告
  3. 不属于python循环结构的是( )_Python语句print(type(['a','1',2,3]))的输出结果是哪一项?_学小易找答案...
  4. 华为鸿蒙二代支持的手机,关于华为鸿蒙,国产厂商中只有2家表示支持
  5. 作者:韩芳(1987-),女,中国科学院计算机网络信息中心工程师
  6. 【Git】GitHub同名项目实现丰富多彩的README.md
  7. 密码学在区块链中的应用 【八】
  8. EOS 连接同步主网
  9. IdentityServer4
  10. 物联网,中国市场上演的精彩碰撞