18 #define ENUM_NAME_ROOT L"Root" 107 for (NextEntry = DeviceExtension->DeviceListHead.
Flink;
108 NextEntry != &DeviceExtension->DeviceListHead;
109 NextEntry = NextEntry->
Flink)
169 InstancePath[0] =
L'\\';
195 WCHAR InstancePath[5];
201 HANDLE EnumHandle, DeviceKeyHandle =
NULL, InstanceKeyHandle;
217 DPRINT1(
"ExAllocatePoolWithTag() failed\n");
228 DPRINT(
"ExAllocatePoolWithTag() failed\n");
233 Device->DeviceID = DevicePath;
234 RtlInitEmptyUnicodeString(&DevicePath,
NULL, 0);
250 DPRINT1(
"Failed to open registry key\n");
261 (
PWSTR)DeviceKeyHandle,
265 for (NextInstance = 0; NextInstance <= 9999; NextInstance++)
267 _snwprintf(InstancePath,
sizeof(InstancePath) /
sizeof(
WCHAR),
L"%04lu", NextInstance);
273 if (NextInstance > 9999)
280 _snwprintf(InstancePath,
sizeof(InstancePath) /
sizeof(
WCHAR),
L"%04lu", NextInstance);
284 DPRINT1(
"NextInstance value is corrupt! (%lu)\n", NextInstance);
286 (
PWSTR)DeviceKeyHandle,
293 (
PWSTR)DeviceKeyHandle,
297 sizeof(NextInstance));
300 DPRINT1(
"Failed to write new NextInstance value! (0x%x)\n",
Status);
326 if (FullInstancePath)
328 FullInstancePath->MaximumLength =
Device->DeviceID.Length + PathSep.
Length +
Device->InstanceID.Length;
329 FullInstancePath->Length = 0;
331 if (!FullInstancePath->Buffer)
353 DPRINT(
"IoCreateDevice() failed with status 0x%08lx\n",
Status);
387 if (DeviceKeyHandle !=
NULL)
437 if (BinaryValue ==
NULL)
440 *
Buffer->Data = BinaryValue;
470 DPRINT(
"ExAllocatePoolWithTag() failed\n");
477 Device->DeviceID = *DevicePath;
478 RtlInitEmptyUnicodeString(DevicePath,
NULL, 0);
481 DPRINT1(
"RtlCreateUnicodeString() failed\n");
491 DPRINT1(
"IopOpenRegistryKeyEx() failed for '%wZ' with status 0x%lx\n",
537 DeviceExtension->DeviceListCount++;
541 if (DeviceKeyHandle !=
NULL)
564 HANDLE InstanceKey, ControlKey;
571 if (!pKeyValueFullInformation)
581 Status = ZwOpenKey(&InstanceKey,
596 DPRINT(
"No 'DeviceReported' value\n");
605 if (pKeyValueFullInformation->
Type !=
REG_DWORD || pKeyValueFullInformation->
DataLength !=
sizeof(DeviceReported))
613 ASSERT(DeviceReported == 1);
621 Status = ZwOpenKey(&ControlKey,
627 DPRINT(
"No 'Control' key\n");
642 DPRINT(
"No 'DeviceReported' value\n");
650 if (pKeyValueFullInformation->
Type !=
REG_DWORD || pKeyValueFullInformation->
DataLength !=
sizeof(DeviceReported))
657 ASSERT(DeviceReported == 1);
675 ULONG KeyInfoSize, SubKeyInfoSize;
677 ULONG Index1, Index2;
692 DPRINT(
"ExAllocatePoolWithTag() failed\n");
696 SubKeyInfoSize = KeyInfoSize;
702 DPRINT(
"ExAllocatePoolWithTag() failed\n");
736 ASSERT(KeyInfoSize < ResultSize);
737 KeyInfoSize = ResultSize;
744 DPRINT1(
"ExAllocatePoolWithTag(%lu) failed\n", KeyInfoSize);
752 DPRINT(
"ZwEnumerateKey() failed with status 0x%08lx\n",
Status);
772 DPRINT(
"IopOpenRegistryKeyEx() failed for '%wZ' with status 0x%lx\n",
795 ASSERT(SubKeyInfoSize < ResultSize);
796 SubKeyInfoSize = ResultSize;
803 DPRINT1(
"ExAllocatePoolWithTag(%lu) failed\n", SubKeyInfoSize);
811 DPRINT(
"ZwEnumerateKey() failed with status 0x%08lx\n",
Status);
816 SubKeyInfo->Name[SubKeyInfo->NameLength /
sizeof(
WCHAR)] = 0;
826 DPRINT1(
"ExAllocatePoolWithTag() failed\n");
833 DPRINT(
"Found device %wZ\\%S!\n", &DevicePath, SubKeyInfo->Name);
855 DPRINT(
"Skipping device %wZ\\%S (not reported yet)\n", &DevicePath, SubKeyInfo->Name);
908 DPRINT(
"EnumerateDevices() failed with status 0x%08lx\n",
Status);
925 DPRINT(
"ExAllocatePoolWithTag() failed\n");
932 Relations->
Count = OtherRelations->Count;
941 NextEntry = NextEntry->
Flink)
960 DPRINT(
"IoCreateDevice() failed with status 0x%08lx\n",
Status);
1024 DPRINT(
"IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS\n");
1029 DPRINT(
"IRP_MJ_PNP / IRP_MN_START_DEVICE\n");
1044 DPRINT(
"IRP_MJ_PNP / IRP_MN_STOP_DEVICE\n");
1076 DPRINT(
"IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / TargetDeviceRelation\n");
1080 DPRINT(
"ExAllocatePoolWithTag() failed\n");
1086 Relations->
Count = 1;
1146 return Irp->IoStatus.Status;
1180 return Irp->IoStatus.Status;
1202 DPRINT(
"IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_TEXT / DeviceTextDescription\n");
1216 DPRINT(
"IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_TEXT / DeviceTextLocationInformation\n");
1222 DPRINT1(
"IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_TEXT / unknown query id type 0x%lx\n", DeviceTextType);
1247 DPRINT(
"IRP_MJ_PNP / IRP_MN_QUERY_ID / BusQueryDeviceID\n");
1267 DPRINT(
"IRP_MJ_PNP / IRP_MN_QUERY_ID / BusQueryInstanceID\n");
1279 DPRINT1(
"IRP_MJ_PNP / IRP_MN_QUERY_ID / unknown query id type 0x%lx\n",
IdType);
1302 &GUID_BUS_TYPE_INTERNAL,
1340 DPRINT(
"IRP_MJ_PNP / IRP_MN_START_DEVICE\n");
1349 DPRINT(
"IRP_MJ_PNP / IRP_MN_QUERY_CAPABILITIES\n");
1354 DPRINT(
"IRP_MJ_PNP / IRP_MN_QUERY_RESOURCES\n");
1359 DPRINT(
"IRP_MJ_PNP / IRP_MN_QUERY_RESOURCE_REQUIREMENTS\n");
1364 DPRINT(
"IRP_MJ_PNP / IRP_MN_QUERY_RESOURCE_REQUIREMENTS\n");
1369 DPRINT(
"IRP_MJ_PNP / IRP_MN_FILTER_RESOURCE_REQUIREMENTS\n");
1407 DPRINT(
"IRP_MJ_PNP / IRP_MN_QUERY_PNP_DEVICE_STATE\n");
1411 DPRINT(
"IRP_MJ_PNP / IRP_MN_QUERY_BUS_INFORMATION\n");
1528 DPRINT(
"IoCreateDevice() failed with status 0x%08lx\n",
Status);
1545 &DeviceExtension->
Ldo);
1548 DPRINT(
"IoAttachDeviceToDeviceStackSafe() failed with status 0x%08lx\n",
Status);
1554 DPRINT(
"Done AddDevice()\n");
1618 &IopPfnDumpDeviceObject);
1621 DPRINT1(
"Creating PFN Dump device failed with %lx\n",
Status);
#define DO_DEVICE_INITIALIZING
#define STATUS_REVISION_MISMATCH
IN PUNICODE_STRING IN POBJECT_ATTRIBUTES ObjectAttributes
_In_ PCWSTR _Inout_ _At_ QueryTable _Pre_unknown_ PRTL_QUERY_REGISTRY_TABLE QueryTable
static NTSTATUS EnumerateDevices(IN PDEVICE_OBJECT DeviceObject)
NTSTATUS PnpRootRegisterDevice(IN PDEVICE_OBJECT DeviceObject)
#define IRP_MN_QUERY_RESOURCES
enum _BUS_QUERY_ID_TYPE BUS_QUERY_ID_TYPE
#define IRP_MN_REMOVE_DEVICE
#define STATUS_INSUFFICIENT_RESOURCES
UNICODE_STRING DeviceDescription
#define STATUS_NO_MORE_ENTRIES
_Must_inspect_result_ _Out_ PNDIS_STATUS _In_ NDIS_HANDLE _In_ PNDIS_STRING SubKeyName
NTSYSAPI NTSTATUS WINAPI RtlQueryRegistryValues(ULONG, PCWSTR, PRTL_QUERY_REGISTRY_TABLE, PVOID, PVOID)
#define OBJ_CASE_INSENSITIVE
#define IRP_MN_QUERY_POWER
_Must_inspect_result_ _Out_ PNDIS_STATUS _In_ NDIS_HANDLE _In_ ULONG _Out_ PNDIS_STRING _Out_ PNDIS_HANDLE KeyHandle
VOID FASTCALL KeAcquireGuardedMutex(IN PKGUARDED_MUTEX GuardedMutex)
struct _PNPROOT_PDO_DEVICE_EXTENSION * PPNPROOT_PDO_DEVICE_EXTENSION
struct _DEVICE_OBJECT * PDEVICE_OBJECT
NTSYSAPI NTSTATUS NTAPI ZwClose(_In_ HANDLE Handle)
#define IRP_MN_FILTER_RESOURCE_REQUIREMENTS
VOID NTAPI MmDumpArmPfnDatabase(IN BOOLEAN StatusOnly)
BOOLEAN NTAPI IoForwardIrpSynchronously(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
PCM_RESOURCE_LIST ResourceList
#define IRP_MN_QUERY_RESOURCE_REQUIREMENTS
PDEVICE_OBJECT Objects[1]
_Must_inspect_result_ _In_ PDRIVER_OBJECT _In_ PCUNICODE_STRING RegistryPath
PPNPROOT_DEVICE DeviceInfo
static NTSTATUS PdoQueryId(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp)
#define REGSTR_PATH_SYSTEMENUM
struct _PNPROOT_DEVICE * PPNPROOT_DEVICE
static NTSTATUS CreateDeviceFromRegistry(_Inout_ PPNPROOT_FDO_DEVICE_EXTENSION DeviceExtension, _Inout_ PUNICODE_STRING DevicePath, _In_ PCWSTR InstanceId, _In_ HANDLE SubKeyHandle)
#define STATUS_INVALID_DEVICE_REQUEST
IN PVOID IN PVOID IN USHORT IN USHORT Size
struct _PNPROOT_COMMON_DEVICE_EXTENSION * PPNPROOT_COMMON_DEVICE_EXTENSION
static NTSTATUS PdoQueryDeviceRelations(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp)
static NTSTATUS PdoQueryBusInformation(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp)
PDEVICE_OBJECT PhysicalDeviceObject
#define InsertTailList(ListHead, Entry)
static NTSTATUS PnpRootFdoPnpControl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
static NTSTATUS IopShouldProcessDevice(IN HANDLE SubKey, IN PCWSTR InstanceID)
_Must_inspect_result_ _In_ WDFKEY _In_ PCUNICODE_STRING _In_ ULONG _Out_opt_ PULONG _Out_opt_ PULONG ValueType
enum _DEVICE_TEXT_TYPE DEVICE_TEXT_TYPE
#define OBJ_KERNEL_HANDLE
_Must_inspect_result_ _In_ WDFIORESREQLIST _In_opt_ PWDF_OBJECT_ATTRIBUTES _Out_ WDFIORESLIST * ResourceList
static NTSTATUS PdoQueryResources(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp)
PNPROOT_COMMON_DEVICE_EXTENSION Common
#define STATUS_BUFFER_TOO_SMALL
FORCEINLINE BOOLEAN RemoveEntryList(_In_ PLIST_ENTRY Entry)
FORCEINLINE VOID IoCopyCurrentIrpStackLocationToNext(_Inout_ PIRP Irp)
_In_ PDEVICE_OBJECT DeviceObject
PNPROOT_DEVICE_STATE State
struct _PNPROOT_FDO_DEVICE_EXTENSION * PPNPROOT_FDO_DEVICE_EXTENSION
_Must_inspect_result_ _In_ WDFDEVICE _In_ WDFSTRING String
PRTL_QUERY_REGISTRY_ROUTINE QueryRoutine
NTSTATUS(* NTAPI)(IN PFILE_FULL_EA_INFORMATION EaBuffer, IN ULONG EaLength, OUT PULONG ErrorOffset)
#define FILE_DEVICE_CONTROLLER
NTSYSAPI NTSTATUS WINAPI RtlWriteRegistryValue(ULONG, PCWSTR, PCWSTR, ULONG, PVOID, ULONG)
#define REGSTR_KEY_ROOTENUM
PDRIVER_EXTENSION DriverExtension
_Must_inspect_result_ _In_opt_ PVOID _In_opt_ PVOID InstanceId
struct _DEVICE_RELATIONS * PDEVICE_RELATIONS
_In_ WDF_WMI_PROVIDER_CONTROL Control
_In_ PCWSTR _Inout_ _At_ QueryTable EntryContext
int _snwprintf(wchar_t *buffer, size_t count, const wchar_t *format,...)
#define RTL_QUERY_REGISTRY_SUBKEY
#define DO_BUS_ENUMERATED_DEVICE
struct _PNP_BUS_INFORMATION * PPNP_BUS_INFORMATION
#define IoCompleteRequest
#define DeviceCapabilities
NTSTATUS NTAPI PnpRootAddDevice(IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT PhysicalDeviceObject)
_Must_inspect_result_ _In_ WDFDEVICE _In_ PCUNICODE_STRING KeyName
_In_ BUS_QUERY_ID_TYPE IdType
#define FILE_AUTOGENERATED_DEVICE_NAME
PFLT_MESSAGE_WAITER_QUEUE CONTAINING_RECORD(Csq, DEVICE_EXTENSION, IrpQueue)) -> WaiterQ.mLock) _IRQL_raises_(DISPATCH_LEVEL) VOID NTAPI FltpAcquireMessageWaiterLock(_In_ PIO_CSQ Csq, _Out_ PKIRQL Irql)
_Must_inspect_result_ _In_ PDRIVER_OBJECT DriverObject
#define REG_OPTION_NON_VOLATILE
NTSTATUS PnpRootCreateDevice(IN PUNICODE_STRING ServiceName, IN OPTIONAL PDRIVER_OBJECT DriverObject, OUT PDEVICE_OBJECT *PhysicalDeviceObject, OUT OPTIONAL PUNICODE_STRING FullInstancePath)
NTSTATUS RtlAppendUnicodeToString(IN PUNICODE_STRING Str1, IN PWSTR Str2)
struct _LIST_ENTRY * Flink
#define IRP_MN_STOP_DEVICE
NTSTATUS NTAPI IoAttachDeviceToDeviceStackSafe(IN PDEVICE_OBJECT SourceDevice, IN PDEVICE_OBJECT TargetDevice, IN OUT PDEVICE_OBJECT *AttachedToDeviceObject)
#define NT_SUCCESS(StatCode)
NTSYSAPI NTSTATUS WINAPI RtlDuplicateUnicodeString(int, const UNICODE_STRING *, UNICODE_STRING *)
#define STATUS_NO_SUCH_DEVICE
struct _PNPROOT_PDO_DEVICE_EXTENSION PNPROOT_PDO_DEVICE_EXTENSION
#define IRP_MN_START_DEVICE
_In_ GUID _In_ PVOID ValueData
NTSTATUS NTAPI PnpRootDriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
NTSYSAPI VOID NTAPI RtlFreeUnicodeString(PUNICODE_STRING UnicodeString)
_In_ PUNICODE_STRING _Inout_ PUNICODE_STRING Destination
#define IRP_MN_QUERY_DEVICE_TEXT
_Must_inspect_result_ _In_ PWDFDEVICE_INIT _In_ PCUNICODE_STRING InstanceID
#define RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE
_Must_inspect_result_ _Out_ PNDIS_STATUS _In_ NDIS_HANDLE _In_ PNDIS_STRING _Out_ PNDIS_HANDLE SubKeyHandle
#define STATUS_UNSUCCESSFUL
#define ExAllocatePoolWithTag(hernya, size, tag)
#define IRP_MN_QUERY_BUS_INFORMATION
_Must_inspect_result_ _In_ WDFKEY _In_ PCUNICODE_STRING ValueName
PDRIVER_OBJECT IopRootDriverObject
NTSTATUS NTAPI ObCloseHandle(IN HANDLE Handle, IN KPROCESSOR_MODE AccessMode)
NTSYSAPI BOOLEAN NTAPI RtlCreateUnicodeString(PUNICODE_STRING DestinationString, PCWSTR SourceString)
static NTSTATUS PnpRootQueryDeviceRelations(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
static NTSTATUS NTAPI QueryBinaryValueCallback(IN PWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData, IN ULONG ValueLength, IN PVOID Context, IN PVOID EntryContext)
NTSYSAPI NTSTATUS WINAPI RtlDeleteRegistryValue(ULONG, PCWSTR, PCWSTR)
#define ExAllocatePool(type, size)
#define RTL_REGISTRY_HANDLE
static PDEVICE_OBJECT PnpRootDeviceObject
_Must_inspect_result_ _In_ WDFDEVICE Device
__drv_aliasesMem FORCEINLINE PIO_STACK_LOCATION IoGetCurrentIrpStackLocation(_In_ PIRP Irp)
VOID NTAPI IoDeleteDevice(IN PDEVICE_OBJECT DeviceObject)
PDRIVER_ADD_DEVICE AddDevice
VOID FASTCALL KeInitializeGuardedMutex(OUT PKGUARDED_MUTEX GuardedMutex)
#define STATUS_OBJECT_NAME_NOT_FOUND
NTSTATUS NTAPI IopOpenRegistryKeyEx(PHANDLE KeyHandle, HANDLE ParentKey, PUNICODE_STRING Name, ACCESS_MASK DesiredAccess)
#define STATUS_BUFFER_OVERFLOW
VOID NTAPI PoStartNextPowerIrp(IN PIRP Irp)
#define InitializeListHead(ListHead)
_In_ PIO_STACK_LOCATION IrpSp
NTSYSAPI NTSTATUS NTAPI RtlAppendUnicodeStringToString(PUNICODE_STRING Destination, PUNICODE_STRING Source)
static NTSTATUS NTAPI PnpRootPnpControl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
PNPROOT_COMMON_DEVICE_EXTENSION Common
#define FIELD_OFFSET(t, f)
struct _PNPROOT_COMMON_DEVICE_EXTENSION PNPROOT_COMMON_DEVICE_EXTENSION
KGUARDED_MUTEX DeviceListLock
#define FILE_DEVICE_UNKNOWN
#define IRP_MN_QUERY_DEVICE_RELATIONS
NTSYSAPI BOOLEAN NTAPI RtlPrefixUnicodeString(IN PUNICODE_STRING String1, IN PUNICODE_STRING String2, IN BOOLEAN CaseInSensitive)
UNICODE_STRING * PUNICODE_STRING
VOID FASTCALL KeReleaseGuardedMutex(IN OUT PKGUARDED_MUTEX GuardedMutex)
static NTSTATUS LocateChildDevice(IN PPNPROOT_FDO_DEVICE_EXTENSION DeviceExtension, IN PCUNICODE_STRING DeviceId, IN PCWSTR InstanceId, OUT PPNPROOT_DEVICE *ChildDevice OPTIONAL)
#define ObReferenceObject
UNICODE_STRING InstanceID
LIST_ENTRY DeviceListHead
NTSTATUS NTAPI IoCreateDevice(IN PDRIVER_OBJECT DriverObject, IN ULONG DeviceExtensionSize, IN PUNICODE_STRING DeviceName, IN DEVICE_TYPE DeviceType, IN ULONG DeviceCharacteristics, IN BOOLEAN Exclusive, OUT PDEVICE_OBJECT *DeviceObject)
NTSYSAPI VOID NTAPI RtlInitUnicodeString(PUNICODE_STRING DestinationString, PCWSTR SourceString)
#define RtlZeroMemory(Destination, Length)
#define InitializeObjectAttributes(p, n, a, r, s)
char * cleanup(char *str)
#define RtlCopyMemory(Destination, Source, Length)
_Must_inspect_result_ _In_ WDFDEVICE _In_ DEVICE_REGISTRY_PROPERTY _In_ ULONG _Out_ PULONG ResultLength
_In_ UINT _In_ UINT _In_ PNDIS_PACKET Source
#define FILE_DEVICE_SECURE_OPEN
static NTSTATUS PdoQueryCapabilities(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp)
static NTSTATUS PdoQueryDeviceText(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp)
static NTSTATUS PdoQueryResourceRequirements(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp)
struct _NAMED_PIPE_CREATE_PARAMETERS * Parameters
PIO_RESOURCE_REQUIREMENTS_LIST ResourceRequirementsList
#define FILE_DEVICE_BUS_EXTENDER
#define RTL_QUERY_REGISTRY_REQUIRED
#define ExFreePoolWithTag(_P, _T)
static NTSTATUS NTAPI PnpRootPowerControl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
NTSYSAPI BOOLEAN NTAPI RtlEqualUnicodeString(PUNICODE_STRING String1, PUNICODE_STRING String2, BOOLEAN CaseInSensitive)
static NTSTATUS PnpRootPdoPnpControl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
#define IRP_MN_QUERY_PNP_DEVICE_STATE
struct _PNPROOT_DEVICE PNPROOT_DEVICE
VOID NTAPI KeBugCheckEx(_In_ ULONG BugCheckCode, _In_ ULONG_PTR BugCheckParameter1, _In_ ULONG_PTR BugCheckParameter2, _In_ ULONG_PTR BugCheckParameter3, _In_ ULONG_PTR BugCheckParameter4)
#define RTL_QUERY_REGISTRY_DIRECT
static NTSTATUS NTAPI QueryStringCallback(IN PWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData, IN ULONG ValueLength, IN PVOID Context, IN PVOID EntryContext)
PDEVICE_NODE FASTCALL IopGetDeviceNode(IN PDEVICE_OBJECT DeviceObject)
#define IRP_MN_QUERY_CAPABILITIES
struct _PNPROOT_FDO_DEVICE_EXTENSION PNPROOT_FDO_DEVICE_EXTENSION
#define KEY_ENUMERATE_SUB_KEYS
#define RTL_CONSTANT_STRING(s)
_Must_inspect_result_ _In_ WDFKEY _In_ PCUNICODE_STRING _In_ ULONG ValueLength
PULONG MinorVersion OPTIONAL