If I create a File handle using CreateFile for a path like "\\?\NUL" or "\\?\pipe\", the handle is mapped to a File object that's opened for the "\Device\Null" or "\Device\NamedPipe" kernel Device object. Since the GetFinalPathNameByHandle function supports the VOLUME_NAME_NT property, which already returns strings like "\Device\HarddiskVolume1\", I thought I would be able to obtain a similar path for a device handle. However, the call always fails, either with ERROR_INVALID_FUNCTION, or ERROR_INVALID_PARAMETER, depending on the access flags the file was opened with.
In fact, almost any call to similar functions fails -- like GetFileInformationByHandle, GetFileInformationByHandleEx, and even calls to NT functions like NtQueryInformationFile -- returning STATUS_INVALID_PARAMETER. The only functions that don't fail are GetFileType (able to identify a pipe), GetVolumeInformationByHandle (able to identify the driver), and NtQueryInformationFile with FileModeInformation.
All these functions work when used on any standard file, but they are not supported for device file handles. How can I obtain path information from a device handle? Are there some Nt or Io functions that would work? Is there some other way to identify a device if the only thing I have is the handle?
As RbMm and eryksun have pointed out, the driver which implements the object must be able to handle IRP_MJ_QUERY_INFORMATION, but if it doesn't, the name of the object can be obtained via NtQueryObject, passing ObjectNameInformation (1) to it, which will obtain the OBJECT_NAME_INFORMATION structure with the object name.
Since I intended to call it with C#, here is the P/Invoke code for it:
static class Ntdll
{
[DllImport("ntdll.dll")]
static extern int NtQueryObject(
IntPtr Handle, int ObjectInformationClass,
IntPtr ObjectInformation, int ObjectInformationLength,
out int ReturnLength
);
public static int NtQueryObject(IntPtr Handle, int ObjectInformationClass, IntPtr ObjectInformation, int ObjectInformationLength)
{
int length;
int status = NtQueryObject(Handle, ObjectInformationClass, ObjectInformation, ObjectInformationLength, out length);
if(status != 0) throw new Win32Exception(RtlNtStatusToDosError(status));
return length;
}
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
public struct OBJECT_NAME_INFORMATION
{
public ushort Length;
public ushort MaximumLength;
public string Buffer;
}
[DebuggerStepThrough]
public static void NtQueryObject(IntPtr Handle, out OBJECT_NAME_INFORMATION ObjectNameInformation)
{
IntPtr buffer = Marshal.AllocHGlobal(1024);
try{
Ntdll.NtQueryObject(Handle, 1, buffer, 1024);
ObjectNameInformation = Marshal.PtrToStructure<Ntdll.OBJECT_NAME_INFORMATION>(buffer);
}finally{
Marshal.FreeHGlobal(buffer);
}
}
}
The path can be then constructed by prepending "\\?\GlobalRoot" to the Buffer member.
Related
Some context:
I have an application that opens the first webcam device on Windows 10 64bit (whatever index 0 is during enumeration of devices) and does some processing on the frames. The application's source code is not accessible.
Question:
I need to make this application to work with two webcams at the same time. I thought maybe there is a way to do the following:
hide webcam 2
run application (picks up webcam 1)
hide webcam 1, unhide webcam 2
run application (picks up webcam 2)
Is there a way to do this without interrupting camera's operation? Note that both applications are running at the same time so hard-disabling a camera is not an option. Calling either a Win32 api or doing this in PowerShell is acceptable.
Thanks!
Thanks to comments on my original question, I managed to solve my problem by hooking into CM_Get_Device_Interface_List_ExW Win32 API call.
I had to verify what API is being called, so I used and API tracer tool (API monitor v2 64bit). Debugger should work too but for some reason my VS debugger did not show me any symbols (possibly missing pdbs).
The original process I tried to hook into is written in C# so I hooked into the call via an injected C# DLL containing EasyHook. Here is my code snippet (actual injection code left out):
using System;
using System.Runtime.InteropServices;
using EasyHook;
public class HookDevices : IEntryPoint
{
LocalHook FunctionLocalHook;
// construct this to hook into calls
HookDevices()
{
try
{
FunctionLocalHook = LocalHook.Create(
LocalHook.GetProcAddress("CfgMgr32.dll", "CM_Get_Device_Interface_List_ExW"),
new FunctionHookDelegate(CM_Get_Device_Interface_List_Ex_Hooked),
this);
FunctionLocalHook.ThreadACL.SetExclusiveACL(new Int32[] { 0 });
}
catch (Exception ExtInfo)
{
Debug.LogException(ExtInfo);
return;
}
}
[UnmanagedFunctionPointer(CallingConvention.StdCall,
CharSet = CharSet.Unicode,
SetLastError = true)]
delegate uint FunctionHookDelegate(
ref Guid interfaceClassGuid,
string deviceID,
IntPtr buffer,
uint bufferLength,
uint flags,
IntPtr hMachine);
[DllImport("CfgMgr32.dll",
CharSet = CharSet.Unicode,
SetLastError = true,
CallingConvention = CallingConvention.StdCall)]
static extern uint CM_Get_Device_Interface_List_ExW(
ref Guid interfaceClassGuid,
string deviceID,
IntPtr buffer,
uint bufferLength,
uint flags,
IntPtr hMachine);
// this is where we are intercepting all API accesses!
static uint CM_Get_Device_Interface_List_Ex_Hooked(
ref Guid interfaceClassGuid,
string deviceID,
IntPtr buffer,
uint bufferLength,
uint flags,
IntPtr hMachine)
{
// pass-through original API
uint ret = CM_Get_Device_Interface_List_ExW(
ref interfaceClassGuid,
deviceID,
buffer,
bufferLength,
flags,
hMachine);
// do custom logic here and re-arrange "buffer"
return ret;
}
}
I have a fairly complex requirement. My STA COM object is implemented in a DLL (can't move it to out-of-process EXE). By the means of DllSurrogate I am hosting my object in a dllhost.exe process. My object has an UI attached to it (a plain modeless dialog) but I need the PreTranslateAccelerator mechanism in order for some shortcuts to work, etc. Since COM activates my object and hosts it in the default dllhost.com, I am obviously not controlling the message pump.
Is there still a way to pre-translate messages in this scenario? I doubt COM has foreseen such a specific scenario but maybe I am missing something.
Okay here it is. I hope I didn't leave out anything important. Basically, I have created a custom CMyComCreator instead of the default one. Instead of just creating a COM object and returning an interface pointer, I spin a worker UiThread. I use MyData structure to pass data across threads. Once the worker thread has finished setting up, I use the CComGITPtr to transfer the marshalled interface pointer from the UiThread back to the main. The consumers (out-of-process) end up with interface pointers that talk directly to the UiThread bypassing the main thread. You may think of CMyDialog as a modeless dialog which sends a PostQuitMessage on destruction to terminate the message loop. That's all. May look cumbersome but it works good.
struct MyData
{
ATL::CComGITPtr<IUnknown> Unk;
ATL::CEvent Event;
HRESULT hr;
MyData() : hr(E_OUTOFMEMORY), Event(FALSE, FALSE) { }
};
static CMessageLoop * MessageLoop;
class CMyComCreator
{
public:
static HRESULT WINAPI CreateInstance(
_In_opt_ void* pv,
_In_ REFIID riid,
_COM_Outptr_ LPVOID* ppv)
{
ATLASSERT(ppv != NULL);
if (ppv == NULL)
return E_POINTER;
*ppv = NULL;
HRESULT hRes = E_OUTOFMEMORY;
MyData* data = NULL;
ATLPREFAST_SUPPRESS(6014 28197)
/* prefast noise VSW 489981 */
ATLTRY(data = _ATL_NEW MyData)
ATLPREFAST_UNSUPPRESS()
if (data != NULL)
{
HANDLE thread = (HANDLE)_beginthreadex(NULL, 0, UiThread, (void *)data, 0, NULL);
if (thread)
{
WaitForSingleObject(data->Event, INFINITE);
CloseHandle(thread);
hRes = data->hr;
if (SUCCEEDED(hRes))
{
ATL::CComPtr<IUnknown> unk;
hRes = data->Unk.CopyTo(&unk);
if (SUCCEEDED(hRes))
{
hRes = unk->QueryInterface(riid, ppv);
}
}
}
delete data;
}
return hRes;
}
};
typedef CMyComCreator _CreatorClass;
static unsigned __stdcall UiThread(void * param)
{
CoInitializeEx(0, COINIT_APARTMENTTHREADED);
MyData * data = (MyData *)param;
ATL::CComObject<CMyDialog> * bb;
data->hr = ATL::CComObject<CMyDialog>::CreateInstance(&bb);
ATL::CComPtr<IUnknown> unk((IDispatch *) bb);
data->Unk = unk;
unk.Release();
data->Event.Set();
if (SUCCEEDED(data->hr))
{
CMessageLoop theLoop;
MessageLoop = &theLoop;
int nRet = theLoop.Run();
MessageLoop = NULL;
}
CoUninitialize();
return 0;
}
I needed to pack everything in a single DLL.
In which case, DllSurrogate is not the only way of doing this. There's also Rundll32:
INFO: Windows Rundll and Rundll32 Interface
This would allow you to run your own message loop inside the DLL's EntryPoint and have complete control over message processing, including PreTranslateMessage. You can copy the message loop logic from an ATL EXE server.
Bear in mind, there's still 32-bit and 64-bit version of "RunDll32.exe" in every 64-bit Windows OS. Use the one which matches the bit-ness of your DLL.
I have the following c++ function which I cannot alter (3rd-Party):
[c++]
int __stdcall TEST(wchar_t **xml, int &result_size)
{
// xml is instantiated here!
}
[c#]
class native
{
[DllImport("somedll.dll")]
public static extern int TEST(StringBuilder a, ref int size);
{
}
}
Example:
StringBuilder b = new StringBuilder();
int size = 0;
native.Test(b,ref size)
The stringbuilder object only contains first character . If I resize the object:
b.Length = size; The data is incorrect except first character.
Is this the correct way to pass wchar_t** from c++ to c#?
Regards,
John
The function would be p/invoked like this:
[DllImport(#"mylib.dll")]
static extern int TEST(out IntPtr xml);
I removed the size paramter since it is not needed since you can use a null-terminated string.
Call the function like this:
IntPtr xmlptr;
int retval = TEST(out xmlptr);
string xml = Marshal.PtrToStringUni(xmlptr);
// deallocate xmlptr somehow
The tricky bit is to deallocate the memory allocated on the native side. Either use a shared allocator, e.g. the COM allocator. Or export a deallocator from the native code.
Personally I'd re-design the interface to use COM BSTR. I'd have the C++ return a BSTR and on the managed side use [MarshalAs(UnmanagedType.BStr)]. Then the framework handles all the deallocation and marshalling for you.
Using EasyHook I have successfully hooked both exported functions and known vtable functions for various C++ classes. In all these cases target programs have used DLLs.
Provided I know the address of a function's entry point, is it possible to do the same when a library has been linked into the target program as opposed to being a separate library?
It appears with EasyHook you can hook any subroutine whose address is calculable.
In my case hooking static linked SSL_read and SSL_write in OpenSSL was as simple as identifying the offsets with my favourite debugger and then installing the hooks.
// delegate for EasyHook:
[UnmanagedFunctionPointer(CallingConvention.Cdecl,
SetLastError = true, CharSet = CharSet.Ansi)]
delegate Int32 SLL_readDelegate(IntPtr SSL_ptr, IntPtr buffer, Int32 length);
// import SSL_read (I actually did it manually, but this will work in most cases)
/* proto from ssl_lib.c -> int SSL_read(SSL *s,void *buf,int num) */
[DllImport("ssleay32.dll", SetLastError = true)]
public static extern Int32 SSL_read(IntPtr ssl, IntPtr buffer, Int32 len);
// the new routine
static Int32 SSL_readCallback(IntPtr SSL_ptr, IntPtr buffer, Int32 length)
{
/* call the imported SSL_read */
int ret = SSL_read(SSL_ptr, buffer, length);
/* TODO: your code here, e.g:
* string log_me = Marshal.PtrToString(buffer, ret);
*/
return ret;
}
Now all that's left is to install the hook:
private LocalHook sslReadHook;
public void Run(RemoteHooking.IContext InContext, String InArg1)
{
// ... initialization code omitted for brevity
/* the value for ssl_read_addr is made up in this example
* you'll need to study your target and how it's loaded(?) to
* identify the addresses you want to hook
*/
int ssl_read_addr = 0x12345678; /* made up for examples sake */
sslReadHook = LocalHook.Create(new IntPtr(ssl_read_addr),
new SSL_readDelegate(SSL_readCallback), this);
// ...
}
I should mention that in this example you'll need libeay32.dll and ssleay32.dll as the latter depends on the former.
Happy hooking!
I can display and select a single file in windows explorer like this:
explorer.exe /select, "c:\path\to\file.txt"
However, I can't work out how to select more than one file. None of the permutations of select I've tried work.
Note: I looked at these pages for docs, neither helped.
https://support.microsoft.com/kb/314853
http://web.archive.org/web/20100716112458/http://www.infocellar.com:80/Win98/explorer-switches.htm
This should be possible with the shell function SHOpenFolderAndSelectItems
EDIT
Here is some sample code showing how to use the function in C/C++, without error checking:
//Directory to open
ITEMIDLIST *dir = ILCreateFromPath(_T("C:\\"));
//Items in directory to select
ITEMIDLIST *item1 = ILCreateFromPath(_T("C:\\Program Files\\"));
ITEMIDLIST *item2 = ILCreateFromPath(_T("C:\\Windows\\"));
const ITEMIDLIST* selection[] = {item1,item2};
UINT count = sizeof(selection) / sizeof(ITEMIDLIST);
//Perform selection
SHOpenFolderAndSelectItems(dir, count, selection, 0);
//Free resources
ILFree(dir);
ILFree(item1);
ILFree(item2);
The true way of selecting multiple files in Explorer is the next
Unmanaged code looks like this (compiled from China code posts with fixing its bugs)
static class NativeMethods
{
[DllImport("shell32.dll", ExactSpelling = true)]
public static extern int SHOpenFolderAndSelectItems(
IntPtr pidlFolder,
uint cidl,
[In, MarshalAs(UnmanagedType.LPArray)] IntPtr[] apidl,
uint dwFlags);
[DllImport("shell32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr ILCreateFromPath([MarshalAs(UnmanagedType.LPTStr)] string pszPath);
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("000214F9-0000-0000-C000-000000000046")]
public interface IShellLinkW
{
[PreserveSig]
int GetPath(StringBuilder pszFile, int cch, [In, Out] ref WIN32_FIND_DATAW pfd, uint fFlags);
[PreserveSig]
int GetIDList([Out] out IntPtr ppidl);
[PreserveSig]
int SetIDList([In] ref IntPtr pidl);
[PreserveSig]
int GetDescription(StringBuilder pszName, int cch);
[PreserveSig]
int SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName);
[PreserveSig]
int GetWorkingDirectory(StringBuilder pszDir, int cch);
[PreserveSig]
int SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir);
[PreserveSig]
int GetArguments(StringBuilder pszArgs, int cch);
[PreserveSig]
int SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs);
[PreserveSig]
int GetHotkey([Out] out ushort pwHotkey);
[PreserveSig]
int SetHotkey(ushort wHotkey);
[PreserveSig]
int GetShowCmd([Out] out int piShowCmd);
[PreserveSig]
int SetShowCmd(int iShowCmd);
[PreserveSig]
int GetIconLocation(StringBuilder pszIconPath, int cch, [Out] out int piIcon);
[PreserveSig]
int SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon);
[PreserveSig]
int SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, uint dwReserved);
[PreserveSig]
int Resolve(IntPtr hwnd, uint fFlags);
[PreserveSig]
int SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile);
}
[Serializable, StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode), BestFitMapping(false)]
public struct WIN32_FIND_DATAW
{
public uint dwFileAttributes;
public FILETIME ftCreationTime;
public FILETIME ftLastAccessTime;
public FILETIME ftLastWriteTime;
public uint nFileSizeHigh;
public uint nFileSizeLow;
public uint dwReserved0;
public uint dwReserved1;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string cFileName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
public string cAlternateFileName;
}
public static void OpenFolderAndSelectFiles(string folder, params string[] filesToSelect)
{
IntPtr dir = ILCreateFromPath(folder);
var filesToSelectIntPtrs = new IntPtr[filesToSelect.Length];
for (int i = 0; i < filesToSelect.Length; i++)
{
filesToSelectIntPtrs[i] = ILCreateFromPath(filesToSelect[i]);
}
SHOpenFolderAndSelectItems(dir, (uint) filesToSelect.Length, filesToSelectIntPtrs, 0);
ReleaseComObject(dir);
ReleaseComObject(filesToSelectIntPtrs);
}
private static void ReleaseComObject(params object[] comObjs)
{
foreach (object obj in comObjs)
{
if (obj != null && Marshal.IsComObject(obj))
Marshal.ReleaseComObject(obj);
}
}
}
it cannot be done through explorer.exe
Depending on what you actually want to accomplish you may be able to do it with AutoHotKey. It is an amazing free tool for automating things you normally can't do. It should come with Windows. This script will select your file and highlight the next two files below it when you hit F12.
F12::
run explorer.exe /select`, "c:\path\to\file.txt"
SendInput {Shift Down}{Down}{Down}{Shift Up}
return
It is also possible to just put those two middle lines in a text file and then pass it is a parm to autohotkey.exe. They have an option to compile the script also, which would make it a standalone exe that you could call. Works great with a great help file.
#Orion, It is possible to use autohotkey from C#. You can make an autohotkey script into a standalone executable (about 400k) that can be launched by your C# app (just the way you are launching explorer). You can also pass it command line parameters. It does not have any runtime requirements.
There are COM Automation LateBinding IDispatch interfaces, these are easy to use from PowerShell, Visual Basic.NET and C#, some sample code:
$shell = New-Object -ComObject Shell.Application
function SelectFiles($filesToSelect)
{
foreach ($fileToSelect in $filesToSelect)
{
foreach ($window in $shell.Windows())
{
foreach ($folderItem in $window.Document.Folder.Items())
{
if ($folderItem.Path -eq $fileToSelect)
{
$window.Document.SelectItem($folderItem, 1 + 8)
}
}
}
}
}
-
Option Strict Off
Imports Microsoft.VisualBasic
Public Class ExplorerHelp
Shared ShellApp As Object = CreateObject("Shell.Application")
Shared Sub SelectFile(filepath As String)
For Each i In ShellApp.Windows
For Each i2 In i.Document.Folder.Items()
If i2.Path = filepath Then
i.Document.SelectItem(i2, 1 + 8)
Exit Sub
End If
Next
Next
End Sub
End Class
https://learn.microsoft.com/en-us/windows/win32/shell/shellfolderview-selectitem
This is one of those questions where it may be good to consider what you're trying to achieve, and whether there's a better method.
To add some more context -
Our company develops a C# client application, which allows users to load files and do stuff with them, kind of like how iTunes manages your MP3 files without showing you the actual file on disk.
It's useful to select a file in the application, and do a 'Show me this file in Windows Explorer` command - this is what I'm trying to achieve, and have done so for single files.
We have a ListView which allows users to select multiple files within the application, and move/delete/etc them. It would be nice to have this 'show me this file in windows' command work for multiple selected files - at least if all the source files are in the same directory, but if it's not possible then it's not a major feature.
I suppose you can use FindWindowEx to get the SysListView32 of Windows Explorer, then use SendMessage with LVM_SETITEMSTATE to select the items. The difficulty being to know the position of the items... Perhaps LVM_FINDITEM can be used for this.
Grr i would like to do this as well. Media Player does it when you select 2+ files and right click and do "open file location" but not exactly sure how (nor do i really feel like spending the time w/ procmon to figure it out).