I am writing an AHK script that uses audio devices. I'm using the Vista Audio library, and I have added some more functions to get additional data about the devices beyond their name. These functions are to get the device description and device icon:
GetDeviceDesc(device)
{
static PKEY_Device_DeviceDesc
if !VarSetCapacity(PKEY_Device_DeviceDesc)
VarSetCapacity(PKEY_Device_DeviceDesc, 20)
,VA_GUID(PKEY_Device_DeviceDesc :="{A45C254E-DF1C-4EFD-8020-67D146A850E0}")
,NumPut(2, PKEY_Device_DeviceDesc, 16)
VarSetCapacity(prop, 16)
VA_IMMDevice_OpenPropertyStore(device, 0, store)
; store->GetValue(.., [out] prop)
DllCall(NumGet(NumGet(store+0)+5*A_PtrSize), "ptr", store, "ptr", &PKEY_Device_DeviceDesc, "ptr", &prop)
ObjRelease(store)
VA_WStrOut(deviceDesc := NumGet(prop,8))
return deviceDesc
}
GetDeviceIcon(device)
{
static PKEY_DrvPkg_Icon
if !VarSetCapacity(PKEY_DrvPkg_Icon)
VarSetCapacity(PKEY_DrvPkg_Icon, 20)
,VA_GUID(PKEY_DrvPkg_Icon :="{CF73BB51-3ABF-44A2-85E0-9A3DC7A12132}")
,NumPut(4, PKEY_DrvPkg_Icon, 16)
VarSetCapacity(prop, 16)
VA_IMMDevice_OpenPropertyStore(device, 0, store)
; store->GetValue(.., [out] prop)
DllCall(NumGet(NumGet(store+0)+5*A_PtrSize), "ptr", store, "ptr", &PKEY_DrvPkg_Icon, "ptr", &prop)
ObjRelease(store)
VA_WStrOut(deviceIcon := NumGet(prop,8))
return deviceIcon
}
Both functions are slightly modified versions of the VA_GetDeviceName function from VA.ahk:
VA_GetDeviceName(device)
{
static PKEY_Device_FriendlyName
if !VarSetCapacity(PKEY_Device_FriendlyName)
VarSetCapacity(PKEY_Device_FriendlyName, 20)
,VA_GUID(PKEY_Device_FriendlyName :="{A45C254E-DF1C-4EFD-8020-67D146A850E0}")
,NumPut(14, PKEY_Device_FriendlyName, 16)
VarSetCapacity(prop, 16)
VA_IMMDevice_OpenPropertyStore(device, 0, store)
; store->GetValue(.., [out] prop)
DllCall(NumGet(NumGet(store+0)+5*A_PtrSize), "ptr", store, "ptr", &PKEY_Device_FriendlyName, "ptr", &prop)
ObjRelease(store)
VA_WStrOut(deviceName := NumGet(prop,8))
return deviceName
}
The first of my functions, to get the device description works, but the other to get the icon path returns nothing. In addition to trying to get the DrvPkg_Icon, I have also attempted to get the DeviceClass_Icon and DeviceClass_IconPath with no success.
I have the PKEY reference here and the IProperty method number reference here.
I can't figure out what I'm doing wrong. Is it possible that the code is working correctly and that these values are empty (according to the WinAPI documentation, a device is only guaranteed to have a friendly name, a description, and an interface friendly description)? In which case is there another way for me to get the device icons?
I just don't get why it's not working. Thank you
Related
I am trying to implement the IFileOperation interface and need help with understanding the punkItems parameter of the IFileOperation::DeleteItems method.
On the documentation page the punkItems parameter is described as a
Pointer to the IUnknown of the IShellItemArray, IDataObject, or
IEnumShellItems object which represents the group of items to be
copied.
The following code shows how I am using the SHCreateShellItemArrayFromIDLists() function to generate the IShellItemArray:
Files := [A_ScriptDir "\foo1.txt", A_ScriptDir "\foo2.txt"]
IID := "{947aab5f-0a5c-4c13-b4d6-4bf7836fc9f8}"
CLSID := "{3ad05575-8857-4850-9277-11b85bdb8e09}"
FileOperation := ComObjCreate(CLSID, IID)
VTBL := NumGet(FileOperation + 0, 0, "Ptr")
DeleteItems := NumGet(VTBL + 0, 19 * A_PtrSize, "Ptr") ; IFileOperation::DeleteItems
PerformOperations := NumGet(VTBL + 0, 21 * A_PtrSize, "Ptr") ; IFileOperation::PerformOperations
VarSetCapacity(PCIDLIST, A_PtrSize * Files.Count(), 0)
PIDLISTS := []
for Each, File in Files {
PIDLISTS[Each] := DllCall("Shell32.dll\ILCreateFromPath", "Str", File, "Ptr")
NumPut(PIDLISTS[Each], PCIDLIST, A_PtrSize * (Each - 1), "Ptr")
}
DllCall("Shell32.dll\SHCreateShellItemArrayFromIDLists", "UInt", Files.Count(), "Ptr", &PCIDLIST, "PtrP", pItems, "Int")
for Each, PIDLIST in PIDLISTS
DllCall("Shell32.dll\ILFree", "Ptr", PIDLIST)
MsgBox % DllCall(DeleteItems, "Ptr", FileOperation, "Ptr", pItems, "UInt") "`n" ErrorLevel "`n" A_LastError
DllCall(PerformOperations, "Ptr", FileOperation, "Int")
ObjRelease(pItems)
but it feels like I am missing something, e.g. the IUnknown part of the parameter description. The above code works but A_LastError yields ERROR_NO_TOKEN - An attempt was made to reference a token that does not exist (1008). Can you help me understand how would I might get the "Pointer to the IUnknown of the IShellItemArray"?
Thank you.
IFileOperation::DeleteItems supports different types of input.
Just pass in the instance pointer you have (IShellItemArray, IDataObject, IEnumShellItems or IPersistIDList) and the DeleteItems method will treat the input as a IUnknown and call QueryInterface until it finds a type it understands.
You can imagine that the implementation looks something like this:
HRESULT DeleteItems(IUnknown*p)
{
if (!p->QueryInterface(IShellItemArray)) ...
else if (!p->QueryInterface(IDataObject)) ...
...
else if (!p->QueryInterface(IPersistIDList)) ...
else return E_NOINTERFACE;
}
From my webserver app i need to check the physical sector size of the harddrive where the app is located. For this i use DeviceIoControl with IOCTL_STORAGE_QUERY_PROPERTY to query StorageAccessAlignmentProperty. The problem is that when i try to run these commands from the webserver i got "Access is denied" error.
How can i retrieve the physical sector size of the harddrive where inetpub is located from the webserver app ??
I know from https://msdn.microsoft.com/windows/compatibility/advanced-format-disk-compatibility-update that with Windows 8 Microsoft has introduced a new API that enables Calling from an unprivileged app. The API is in the form of a new info class FileFsSectorSizeInformation with associated structure FILE_FS_SECTOR_SIZE_INFORMATION, but i don't know how to make it working with Delphi
This is my actual code that don't work (written in Delphi) :
{~~~~~~~~~~~~~~~~~~~~~~~~~}
procedure _CheckSectorSize;
type
STORAGE_PROPERTY_ID = (StorageDeviceProperty = 0,
StorageAdapterProperty,
StorageDeviceIdProperty,
StorageDeviceUniqueIdProperty,
StorageDeviceWriteCacheProperty,
StorageMiniportProperty,
StorageAccessAlignmentProperty,
StorageDeviceSeekPenaltyProperty,
StorageDeviceTrimProperty,
StorageDeviceWriteAggregationProperty,
StorageDeviceDeviceTelemetryProperty,
StorageDeviceLBProvisioningProperty,
StorageDevicePowerProperty,
StorageDeviceCopyOffloadProperty,
StorageDeviceResiliencyProperty,
StorageDeviceMediumProductType,
StorageAdapterCryptoProperty,
StorageDeviceIoCapabilityProperty = 48,
StorageAdapterProtocolSpecificProperty,
StorageDeviceProtocolSpecificProperty,
StorageAdapterTemperatureProperty,
StorageDeviceTemperatureProperty,
StorageAdapterPhysicalTopologyProperty,
StorageDevicePhysicalTopologyProperty,
StorageDeviceAttributesProperty);
STORAGE_QUERY_TYPE = (PropertyStandardQuery = 0,
PropertyExistsQuery = 1,
PropertyMaskQuery = 2,
PropertyQueryMaxDefined = 3);
_STORAGE_PROPERTY_QUERY = packed record
PropertyId: STORAGE_PROPERTY_ID;
QueryType: STORAGE_QUERY_TYPE;
AdditionalParameters: array[0..9] of Byte;
end;
_STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR = packed record
Version: DWORD; // Contains the size of this structure, in bytes. The value of this member will change as members are added to the structure.
Size: DWORD; // Specifies the total size of the data returned, in bytes. This may include data that follows this structure.
BytesPerCacheLine: DWORD; // The number of bytes in a cache line of the device.
BytesOffsetForCacheAlignment: DWORD; // The address offset necessary for proper cache access alignment, in bytes.
BytesPerLogicalSector: DWORD; // The number of bytes in a logical sector of the device.
BytesPerPhysicalSector: DWORD; // The number of bytes in a physical sector of the device.
BytesOffsetForSectorAlignment: DWORD; // The logical sector offset within the first physical sector where the first logical sector is placed, in bytes.
end;
var
aVolumePath: array[0..MAX_PATH] of AnsiChar;
aVolumeName: array[0..MAX_PATH] of AnsiChar;
hFile: THANDLE;
inbuf: _STORAGE_PROPERTY_QUERY;
outbuf: _STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR;
dwLen: DWORD;
i: integer;
begin
// Convert the directory to a Volume Name
aVolumePath[0] := #$00;
if not GetVolumePathNameA(pAnsiChar(DFRooter_HayStackDirectory), // _In_ LPCTSTR lpszFileName,
aVolumePath, // _Out_ LPTSTR lpszVolumePathName,
length(aVolumePath)) then raiseLastOsError; // _In_ DWORD cchBufferLength
aVolumeName[0] := #$00;
if not GetVolumeNameForVolumeMountPointA(aVolumePath, // _In_ LPCTSTR lpszVolumeMountPoint,
aVolumeName, // _Out_ LPTSTR lpszVolumeName,
length(aVolumeName)) then raiseLastOsError; // _In_ DWORD cchBufferLength
// Opening a physical device so no trailing '\'. Trailing '\' would open the ROOT DIR instead of the volume
for i := 1 to High(aVolumeName) do
if aVolumeName[i] = #0 then begin
if aVolumeName[i-1] = '\' then aVolumeName[i-1] := #0;
break;
end;
//create the file
hFile := CreateFileA(PAnsiChar(#aVolumeName[0]), // _In_ LPCTSTR lpFileName,
GENERIC_READ, // _In_ DWORD dwDesiredAccess,
FILE_SHARE_READ or FILE_SHARE_WRITE, //_In_ DWORD dwShareMode,
0, // _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
OPEN_EXISTING, // _In_ DWORD dwCreationDisposition,
FILE_ATTRIBUTE_NORMAL, // _In_ DWORD dwFlagsAndAttributes,
0); // _In_opt_ HANDLE hTemplateFile
if (hFile = INVALID_HANDLE_VALUE) then raiseLastOsError;
try
ZeroMemory(#inbuf, SizeOf(inbuf));
ZeroMemory(#outbuf, SizeOf(outbuf));
inbuf.QueryType := PropertyStandardQuery;
inbuf.PropertyId := StorageAccessAlignmentProperty;
outbuf.Size := sizeOf(outbuf);
if not DeviceIoControl(hFile, // _In_ HANDLE hDevice,
IOCTL_STORAGE_QUERY_PROPERTY, // _In_ DWORD dwIoControlCode,
#inbuf, // _In_opt_ LPVOID lpInBuffer,
sizeof(inbuf), // _In_ DWORD nInBufferSize,
#outbuf, // _Out_opt_ LPVOID lpOutBuffer,
sizeof(outbuf), // _In_ DWORD nOutBufferSize,
dwLen, // _Out_opt_ LPDWORD lpBytesReturned,
nil) then raiseLastOsError; // _Inout_opt_ LPOVERLAPPED lpOverlapped
finally
CloseHandle(hFile);
end;
end;
let look for IOCTL_STORAGE_QUERY_PROPERTY definition:
CTL_CODE(IOCTL_STORAGE_BASE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS) - FILE_ANY_ACCESS used here. this mean that any file handle, with any access rights is ok for this IOCTL. but how you open device for send this ioctl ? you use GENERIC_READ in call CreateFileA (and why not CreateFileW ?!). exactly at this point i guess you got access denied error. also for get sector size you can use say IOCTL_DISK_GET_DRIVE_GEOMETRY - it also use FILE_ANY_ACCESS. so, if you have exactly device name, you can use next code (c/c++):
HANDLE hFile = CreateFileW(DeviceName, 0, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
if (hFile != INVALID_HANDLE_VALUE)
{
DISK_GEOMETRY dg;
STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR sad;
static STORAGE_PROPERTY_QUERY spq = { StorageAccessAlignmentProperty, PropertyStandardQuery };
ULONG BytesReturned;
if (!DeviceIoControl(hFile, IOCTL_STORAGE_QUERY_PROPERTY, &spq, sizeof(spq), &sad, sizeof(sad), &BytesReturned, 0))
{
GetLastError();
}
if (!DeviceIoControl(hFile, IOCTL_DISK_GET_DRIVE_GEOMETRY, 0, 0, &dg, sizeof(dg), &BytesReturned, 0))
{
GetLastError();
}
CloseHandle(hFile);
}
else
{
GetLastError();
}
this code perfectly worked even from low integrity process. no any privileges or administrator sid require for this.
note that DeviceName must be exactly device name, not file/folder name.
this mean name like "\\\\?\\c:" is ok but for name "\\\\?\\c:\\" or "\\\\?\\c:\\anypath" you already got ERROR_INVALID_PARAMETER (or STATUS_INVALID_PARAMETER), if disk is mounted by filesystem. this is because IOCTL_STORAGE_QUERY_PROPERTY or IOCTL_DISK_GET_DRIVE_GEOMETRY is handled only by disk device object. but when disk is mounted by filesystem - io subsystem redirect request to filesystem device object instead via VPB (unless you open file by exactly device name and with very low access rights ). file system device just return STATUS_INVALID_PARAMETER on any IOCTL (IRP_MJ_DEVICE_CONTROL) if this is not volume open, but file or directory. otherwise it pass it down to disk device object (not confuse this with FSCTL (IRP_MJ_FILE_SYSTEM_CONTROL) - the DeviceIoControl internal call or ZwDeviceIoControlFile (send ioctl) or ZwFsControlFile (send fsctl))
another option, get disk sector information - query file system about this, of course in case disk is mounted by some filesystem. we can use for this NtQueryVolumeInformationFile FileFsSectorSizeInformation (begin from win8) or FileFsSizeInformation. again - for this request we can open file handle with any access. we not need have GENERIC_READ
HANDLE hFile = CreateFileW(FileName, 0, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
if (hFile != INVALID_HANDLE_VALUE)
{
FILE_FS_SECTOR_SIZE_INFORMATION ffssi;
FILE_FS_SIZE_INFORMATION ffsi;
IO_STATUS_BLOCK iosb;
NtQueryVolumeInformationFile(hFile, &iosb, &ffsi, sizeof(ffsi), FileFsSizeInformation);
NtQueryVolumeInformationFile(hFile, &iosb, &ffssi, sizeof(ffssi), FileFsSectorSizeInformation);
CloseHandle(hFile);
}
note - that here we can use any file path and exactly device path too (with one important note) - so "\\\\?\\c:" and "\\\\?\\c:\\" and say "\\\\?\\c:\\windows\\notepad.exe" - all will be ok here. however in case exactly device name ("\\\\?\\c:") you need use say FILE_EXECUTE access to device in call CreateFileW, otherwise instead file system device will be opened disk device and FO_DIRECT_DEVICE_OPEN will be set in file object. as result request will be send to disk device object, which not handle it and you got STATUS_INVALID_DEVICE_REQUEST
fanny that msdn say
Using this (IOCTL_STORAGE_QUERY_PROPERTY) IOCTL to get the physical
sector size does have several limitations. It:
Requires elevated privilege; if your app is not running with privilege, you may need to write a Windows Service Application as
noted above
this is mistake or conscious lie - again not need any elevated privilege for this. this code worked even from guest account too with low integrity level. we can of course use and STANDARD_RIGHTS_READ (note - this is not GENERIC_READ - use GENERIC_READ is critical error here) in call CreateFileW, but can use and 0 (in this case CreateFile actually use FILE_READ_ATTRIBUTES | SYNCHRONIZE access request). so documentation is bad and wrong
I know this is two years old at this point, but I fought with this one today for a while myself and wasn't satisfied with any answers I could find due to their complexity/lack of completeness. Perhaps this answer will save trouble for others.
In order to get the sector information the old way, the application must open the physical device associated with the location in which the file is stored. The process in a nutshell is as follows:
Get the volume path for a file in the location - GetVolumePathName
Open the volume - CreateFile
Get the volume extents - VOLUME_DISK_EXTENTS (may have to be called twice)
For each volume extent...
Open the associated device - CreateFile
Get the alignment descriptor - STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR
Merge the alignment descriptors from all extents
This is a good deal of trouble for applications designed for Windows 8 and newer when there is a new file information class available that effectively does all of this in the following two steps:
Open a file in the location - CreateFile
Get the file's storage information - GetFileInformationByHandleEx(FileStorageInfo)
This will yield all the relevant information one could want about sector sizes and alignment regardless of the underlying device technology in the form of a FILE_STORAGE_INFO structure having the following definition:
typedef struct _FILE_STORAGE_INFO {
ULONG LogicalBytesPerSector;
ULONG PhysicalBytesPerSectorForAtomicity;
ULONG PhysicalBytesPerSectorForPerformance;
ULONG FileSystemEffectivePhysicalBytesPerSectorForAtomicity;
ULONG Flags;
ULONG ByteOffsetForSectorAlignment;
ULONG ByteOffsetForPartitionAlignment;
} FILE_STORAGE_INFO, *PFILE_STORAGE_INFO;
I'm trying to get a serial number of a disk, using IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER:
HANDLE h = CreateFile ("\\\\.\\PhysicalDrive0", GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
OPEN_EXISTING,
FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING, 0);
if (h != INVALID_HANDLE_VALUE) {
struct {
USHORT Reserved;
USHORT SerialNumberLength;
UCHAR SerialNumber[252];
} dsn;
DWORD nr;
memset(&dsn, '\0', sizeof dsn);
if ((DeviceIoControl(h, IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER,
NULL, 0, &dsn, sizeof(dsn), &nr, 0))) {
printf("Serial number: %s\n", dsn.SerialNumber);
} else {
printf("No serial number, error %d.\n", (int)GetLastError());
}
}
However, GetLastError() returns ERROR_INVALID_FUNCTION.
The disk does exist, and it has a serial number, see this registry entry:
How can I retrieve the serial number from C code without using the registry?
we can use IOCTL_STORAGE_QUERY_PROPERTY with StorageDeviceProperty (Indicates that the caller is querying for the device descriptor, STORAGE_DEVICE_DESCRIPTOR)
and use SerialNumberOffset member of STORAGE_DEVICE_DESCRIPTOR
Specifies the byte offset from the beginning of the structure to a
NULL-terminated ASCII string that contains the device's serial number.
If the device has no serial number, this member is zero.
code can look like this:
ULONG GetSerial(HANDLE hFile)
{
static STORAGE_PROPERTY_QUERY spq = { StorageDeviceProperty, PropertyStandardQuery };
union {
PVOID buf;
PSTR psz;
PSTORAGE_DEVICE_DESCRIPTOR psdd;
};
ULONG size = sizeof(STORAGE_DEVICE_DESCRIPTOR) + 0x100;
ULONG dwError;
do
{
dwError = ERROR_NO_SYSTEM_RESOURCES;
if (buf = LocalAlloc(0, size))
{
ULONG BytesReturned;
if (DeviceIoControl(hFile, IOCTL_STORAGE_QUERY_PROPERTY, &spq, sizeof(spq), buf, size, &BytesReturned, 0))
{
if (psdd->Version >= sizeof(STORAGE_DEVICE_DESCRIPTOR))
{
if (psdd->Size > size)
{
size = psdd->Size;
dwError = ERROR_MORE_DATA;
}
else
{
if (psdd->SerialNumberOffset)
{
DbgPrint("SerialNumber = %s\n", psz + psdd->SerialNumberOffset);
dwError = NOERROR;
}
else
{
dwError = ERROR_NO_DATA;
}
}
}
else
{
dwError = ERROR_GEN_FAILURE;
}
}
else
{
dwError = GetLastError();
}
LocalFree(buf);
}
} while (dwError == ERROR_MORE_DATA);
return dwError;
}
also for open device we can use CreateFileW (L"\\\\.\\PhysicalDrive0", 0, 0, 0, OPEN_EXISTING, 0, 0); - in place dwDesiredAccess we can use 0 because IOCTL_STORAGE_QUERY_PROPERTY defined as
CTL_CODE(IOCTL_STORAGE_BASE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS) - so FILE_ANY_ACCESS - accept any file access and FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING have sense only for file system devices (more general which use cache) - for disk devices - this is irrelevant
I have tried different approaches and figured out that sending IOCTL_STORAGE_QUERY_PROPERTY doesn't work as expected for different USB devices in both User and Kernel mode code. For some USB mass storages it doesn't return serial number. I'd assume that there are 2 correct ways to do that:
IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER (a few AV products do that?)
create and send URB_CONTROL_DESCRIPTOR_REQUEST
Update 1.
I saw using IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER in the one file system mini-filter driver that was used like the following:
FltGetDiskDeviceObject( FltObjects->Volume, &pDevice );
Irp = IoBuildDeviceIoControlRequest(
IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER,
Device,
...
Irp = IoBuildDeviceIoControlRequest(
IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER,
Device,....
I haven't tested this yet in KernelMode code, unfortunately, but trying to make it works in user mode code shows that this IOCTL mostly
is not supported by different devices, maybe this IOCTL is reserved for the future as a standard way to get the serial number and will be
required by USB standards later?
Also, "wmic diskdrive get name, serialnumber" returns in my case for USB Mass Storage incorrect serial number = "E" the same result as we would use IOCTL_STORAGE_QUERY_PROPERTY.
So, the correct way to get the serial number of USB mass storage is creating a USB request block in KernelMode code and using DeviceIoControl to the hub driver in the UserMode code.
USBVIEW (UserMode code) gets serial number by sending IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX ioctl to the HUB driver which
returns USB_NODE_CONNECTION_INFORMATION_EX that contains USB_DEVICE_DESCRIPTOR. iSerialNumber member of USB_DEVICE_DESCRIPTOR is used later in the
IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION ioctl request to the hub driver which finally gets serial number.
Another approach I see is maybe using some 3-rd party libraries like libusb to simplicate all these actions...
Update 2.
I took a look at USBSTOR disassembled code. USBSTOR_DeviceControl routine has the following code for the IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER
++v3->CurrentLocation;
++v3->Tail.Overlay.CurrentStackLocation;
v8 = IofCallDriver(*(PDEVICE_OBJECT *)(v6 + 24), v3);
So, it passes the IRP down the stack to the usbhub driver as was expected. So maybe this functionlaty is expected to be realized in
the usbhub driver sometime ? That would be great as for me...
I'm trying to create a script that will trigger a sound effect when a hotkey is pressed, this might not seem too difficult, but I also want to be able to output it to a specific audio device, a virtual audio cable in my case.
I've looked around on the internet a bit and I saw many possible solutions, but none have worked out for me (yet). It appears that the normal SoundPlay function in AHK can not output to a specific audio device, but I'm also looking into other options, like playing the sound via a batch script with wmplayer or other media players, but I can't find a solutions that will let me output the sound to a specific audio devices...
So my question is, what's the best way to play sound to a specific non-default audio device (a virtual audio cable) which can be done inside a command prompt or inside autohotkey?
So I managed to do what I was trying to accomplish. I'll tell you how I did it:
After some searching around on the internet I came across a C# library called IrrKlang. Using IrrKlang I made a little console app program which I can call by playsound soundfile.mp3 0, playsound is the the name of the .exe, the first parameter is the path to the soundfile from the location of the playsound.exe, and the last parameter is a number which is used to choose the audio device, which number this should be is still guess work but after some trial and error you can find the number of your virtual audio cable or other audio device.
For the people who come here in the future I've put my code up on github.
Everything you need is here:
Lexikos's Vista Audio Control Functions
Note: SoundSet and SoundGet on AutoHotkey v1.1.10 and later support Vista and later natively. You don't need VA.ahk unless you want to use advanced functions not supported by SoundSet/SoundGet.
https://autohotkey.com/board/topic/21984-vista-audio-control-functions/
To change the default output device, you can script the Sound properties in cpanel:
Run, mmsys.cpl
WinWait, Sound
ControlSend, SysListView321,{Down num} ' num matches device position
Sleep, 100
ControlClick, &Set Default
Sleep, 100
ControlClick, OK
WinWaitClose, Sound
Hth,
I modified one code i found, and add volume up and down.
You have to chance device1 and device2 names
Alt + Wheel Up = Volume Up
Alt + Wheel Down = Volume Down
Ctrl + F12 = Chance Device
glhf!
device1:="Speakers / Headphones"
device2:="Communications Headphones"
; http://www.daveamenta.com/2011-05/programmatically-or-command-line-change-the-default-sound-playback-device-in-windows-7/
Devices := {}
IMMDeviceEnumerator := ComObjCreate("{BCDE0395-E52F-467C-8E3D-C4579291692E}", "{A95664D2-9614-4F35-A746-DE8DB63617E6}")
; IMMDeviceEnumerator::EnumAudioEndpoints
; eRender = 0, eCapture, eAll
; 0x1 = DEVICE_STATE_ACTIVE
DllCall(NumGet(NumGet(IMMDeviceEnumerator+0)+3*A_PtrSize), "UPtr", IMMDeviceEnumerator, "UInt", 0, "UInt", 0x1, "UPtrP", IMMDeviceCollection, "UInt")
ObjRelease(IMMDeviceEnumerator)
; IMMDeviceCollection::GetCount
DllCall(NumGet(NumGet(IMMDeviceCollection+0)+3*A_PtrSize), "UPtr", IMMDeviceCollection, "UIntP", Count, "UInt")
Loop % (Count)
{
; IMMDeviceCollection::Item
DllCall(NumGet(NumGet(IMMDeviceCollection+0)+4*A_PtrSize), "UPtr", IMMDeviceCollection, "UInt", A_Index-1, "UPtrP", IMMDevice, "UInt")
; IMMDevice::GetId
DllCall(NumGet(NumGet(IMMDevice+0)+5*A_PtrSize), "UPtr", IMMDevice, "UPtrP", pBuffer, "UInt")
DeviceID := StrGet(pBuffer, "UTF-16"), DllCall("Ole32.dll\CoTaskMemFree", "UPtr", pBuffer)
; IMMDevice::OpenPropertyStore
; 0x0 = STGM_READ
DllCall(NumGet(NumGet(IMMDevice+0)+4*A_PtrSize), "UPtr", IMMDevice, "UInt", 0x0, "UPtrP", IPropertyStore, "UInt")
ObjRelease(IMMDevice)
; IPropertyStore::GetValue
VarSetCapacity(PROPVARIANT, A_PtrSize == 4 ? 16 : 24)
VarSetCapacity(PROPERTYKEY, 20)
DllCall("Ole32.dll\CLSIDFromString", "Str", "{A45C254E-DF1C-4EFD-8020-67D146A850E0}", "UPtr", &PROPERTYKEY)
NumPut(14, &PROPERTYKEY + 16, "UInt")
DllCall(NumGet(NumGet(IPropertyStore+0)+5*A_PtrSize), "UPtr", IPropertyStore, "UPtr", &PROPERTYKEY, "UPtr", &PROPVARIANT, "UInt")
DeviceName := StrGet(NumGet(&PROPVARIANT + 8), "UTF-16") ; LPWSTR PROPVARIANT.pwszVal
DllCall("Ole32.dll\CoTaskMemFree", "UPtr", NumGet(&PROPVARIANT + 8)) ; LPWSTR PROPVARIANT.pwszVal
ObjRelease(IPropertyStore)
ObjRawSet(Devices, DeviceName, DeviceID)
}
ObjRelease(IMMDeviceCollection)
Return
$!WheelUp::Send {Volume_Up 5}
$!WheelDown::Send {Volume_Down 5}
currentDevice:=false
^F12::
currentDevice:=!currentDevice
if currentDevice
SetDefaultEndpoint( GetDeviceID(Devices, device1) )
else
SetDefaultEndpoint( GetDeviceID(Devices, device2) )
return
SetDefaultEndpoint(DeviceID)
{
IPolicyConfig := ComObjCreate("{870af99c-171d-4f9e-af0d-e63df40c2bc9}", "{F8679F50-850A-41CF-9C72-430F290290C8}")
DllCall(NumGet(NumGet(IPolicyConfig+0)+13*A_PtrSize), "UPtr", IPolicyConfig, "UPtr", &DeviceID, "UInt", 0, "UInt")
ObjRelease(IPolicyConfig)
}
GetDeviceID(Devices, Name)
{
For DeviceName, DeviceID in Devices
If (InStr(DeviceName, Name))
Return DeviceID
}
So I use a clock replacement program. The problem is it also hijack clicks on the clock. So whenever I click on the clock in the notification area, program's popup menu launch rather than the default windows clock widget.
I also tried AHK ControlClick on TrayClockWClass. I still didn't get original widget. Is there any way to launch the original widget programmatically? I use Windows 10 1607.
I am fine with RunDll, API, SendMessage or any other way whatsoever.
Somebody upvoted my question today. So maybe they are looking for the answer. I posted it on autohotkey.com forums back then, and I get the answer there.
https://autohotkey.com/boards/viewtopic.php?t=21274
ControlGet, hClock, Hwnd,, TrayClockWClass1, ahk_class Shell_TrayWnd ; https://autohotkey.com/board/topic/70770-win7-taskbar-clock-toggle/
if (hClock) {
VarSetCapacity(IID_IAccessible, 16), DllCall("ole32\CLSIDFromString", "WStr", "{618736e0-3c3d-11cf-810c-00aa00389b71}", "Ptr", &IID_IAccessible)
if (DllCall("oleacc\AccessibleObjectFromWindow", "Ptr", hClock, "UInt", OBJID_CLIENT := 0xFFFFFFFC, "Ptr", &IID_IAccessible, "Ptr*", accTrayClock))
return
VarSetCapacity(variant, A_PtrSize == 8 ? 24 : 16, 0), NumPut(VT_I4 := 3, variant,, "UShort")
if (A_PtrSize == 4) ; https://autohotkey.com/boards/viewtopic.php?p=111355#p111355
DllCall(NumGet(NumGet(accTrayClock+0)+25*A_PtrSize), "Ptr", accTrayClock, "Int64", NumGet(variant, 0, "Int64"), Int64, NumGet(variant, 8, "Int64")) ; IAccessible::DoDefaultAction
else
DllCall(NumGet(NumGet(accTrayClock+0)+25*A_PtrSize), "Ptr", accTrayClock, "Ptr", &variant) ; IAccessible::DoDefaultAction
ObjRelease(accTrayClock)
}