I've used SHGetDesktopFolder() to get a PIDL and then walked down it's contents using IShellFolder.
I now have a particular PIDL referencing a filesystem location, and I can use BindToStorage and IStorage to .OpenStream() and .Write() a file.
This is all well and good if the interesting things live on the filesystem, but more interesting things live in "Shell Namespace Extensions".
In particular, I have a Pocket PC 2002 device (Specificly, a Symbol PDT8146) that is hooked up to my Windows 7 machine using Windows Mobile Device Center. This application creates a shell namespace folder that I can use from within explorer to read/write files to it.
What I cannot do is write files to it using the command line or win32 APIs.
Following the strategy I outlined above, I can get a PIDL and IShellFolder instance referring to the device, and I can list it's files. However, IShellFolder.BindToStorage() fails with 'No such interface supported' when I try to access IStorage.
Is there another shell interface I should investigate to read/write files on this stubborn device?
Try IShellFolder.BindToObject:
IStream *stream;
if (FAILED(shellfolder->BindToObject (pidl, NULL, IID_IStream, &stream)))
return E_FAIL;
But I'm not sure if this works with writing files as well.
Related
Is there an API or straight forward way to simulate any data as a windows explorer drive/file-hierarchy? I don't want to create actual files I want to have a view on my data as though it were.
E.g. if my real data was in a database but I want it to look like a drive/folder/files?
You can create shell namespace extension or a virtual file system.
Shell namespace extension has many shortcomings but if you need files to be only exposed and copied, it can work.
Virtual file system or disk can be created only with help of third-party solutions such as our Callback File System or CallbackDisk. This approach fully emulates the local disk and the filesystem up to possibility to manipulate individual sectors (but this depends on the way used, i.e. whether you emulate the disk or the filesystem).
I am working on a simple device driver I want to comunicate with the driver from user-mode using IRP.
I am having trouble opening the device driver. Using DeviceTree I am able to see the device name eg \Device\MyDevice.
But when I try to open it like this :
hand := CreateFile('\Device\MyDevice', GENERIC_WRITE, 0, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
I always get INVALID_HANDLE_VALUE and GetLastError is (The System cannot find the path specified)
What I am doing wrong ? I know the driver works because I can see it running and printing stuff in DebugView. So any tips ?
Here is a good explanation by Tim Robinson, MVP (Windows SDK):
Names of the form \Device\xxx are internal NT object manager names which are
inaccessible to Win32. You will only be able to access your device if it
creates a symbolic link to \Device\MyDevice from the \??\ directory. Objects
in the \??\ kernel directory show up through \\.\ in Win32. Use Winobj in
the DDK (or download it from www.sysinternals.com) to check.
NOTE: Nowadays NT namespace root is exposed via GLOBALROOT symbolic link, so any NT path is accessible to Win32 including \Device\xxx: use \\.\GLOBALROOT\Device\xxx. A device symlink is not required in such case.
I have a big collection of folders for projects I'm working on. I've been trying to find a better way to sort them all for a long time and I want to write an app that creates groups based on whatever criteria I say, such as "folders from 2011" or "folders containing a x type of file" etc.
This is fairly straightforward, and wouldn't present much of a problem to code using its own UI in winForms or WPF or something. But I think it would be far better if I could make these folders appear to be part of the filesystem, so other apps (like existing file explorers) can see them.
Is this possible? Would it cause problems I haven't considered? How do I go about doing it if it is possible?
One way I thought of doing it would be to have the app monitor the filesystem and create folder shortcuts every time there's a change, but I'm curious about whether its possible to actually present a fake filesystem to explorer through a 'gateway' folder
EDIT: Ok it's obviously possible since http://www.virtualfolder.net/ can do it, and now that I think of it so can TrueCrypt, although it would be nice if it didn't have to appear as a separate drive. So the question becomes, how do I implement it?
You can create a Shell Namespace Extension that gathers the file information you want and displays it within Windows Explorer any way you wish. You can choose where your extension is located, whether as its own top-level node, a child of another system virtual folder/extension, or as a child of a file system folder.
Writing a SNE is not trivial, but it is a lot easier then writing a lower-level file system driver, and it does not require special driver-oriented compilers. Any compiler that supports developing COM objects will work.
This is accomplished using filesystem drivers or filesystem filter drivers. First let you create a virtual filesystem and mount it to a drive letter and also to a folder on NTFS drive (folder must exist but its contents are "replaced" with a virtual filesystem directory tree). Filesystem filter drivers let you introduce virtual files and folders in existing folders without replacing them.
VirtualFolder uses filesystem driver as it creates a drive letter.
Both types of drivers are written in C and work in kernel-mode. Writing them requires deep knowledge of Windows internals and experience with driver development (since filesystem drivers are one of the most complicated driver types).
We offer several products related to virtual storage. One of them, Callback File System, is a filesystem driver. It calls your user-mode code to perform actual filesystem functions. Another product, CallbackFilter, is an FS filter driver (and it also calls your user-mode code). However, current version of CallbackFilter doesn't let you introduce virtual files and folders (this would be implemented in the next release).
There's also Pismo File Mount product available, they use filter driver techniques. You can check with them if what you need can be accomplished.
From what I gather you are looking for a way to present the results of predefined file queries to appear as though they are located at a specific location in the file system. If that is correct you may want to look into Hard Links and Junctions. There are limits on what you can do with these file system services. However it is really straight forward to implement.
Do you know what is the API, or sequence of API calls that windows uses to accomplish the "Eject" function which is available on the shell context menu for removable volumes?
So far I've tried two things:
using CM_Request_Device_Eject, I enumerate the removable disks (using the SetupDiXXX APIs), find the one that I'm interested in, walk the device manager hierarchy (using CM_XXX APIs) and finally call CM_Request_Device_Eject on the devInst of the device I'm interesed in. This works in the sense that it does remove the volumes from My Computer and makes the device "safe to remove" (ready to be removed) but it is not the same as the shell context menu "Eject" function. The way I know this is because the device that I'm trying to eject is supposed to do something when it is ejected and that something is not happening when I do the eject using CM_Request_Device_Eject.
using DeviceIoControl with the IOCTL_STORAGE_EJECT_MEDIA control code. The sequence of events is:
obtain a handle to the volume I'm interested in using CreateFile as suggested in the documentation
try to lock the volume with FSCTL_LOCK_VOLUME
try to dismount it using FSCTL_DISMOUNT_VOLUME
disable the prevent storage media removal using IOCTL_STORAGE_MEDIA_REMOVAL
and finally execute the IOCTL_STORAGE_EJECT_MEDIA function.
This doesn't work at all. Each one of the DeviceIoControl calls fails with ERROR_IVALID_FUNCTION (0x00000001). I don't know why the calls fail. I've verified that other calls to DeviceIoControl work fine for the same file handle (such as IOCTL_STORAGE_GET_DEVICE_NUMBER)
Finally, my development machine is running Windows 7 x64, and in order to get the second method to work I've tried running my application with Administrator privileges and that did not change anything.
EDIT
Eventually, I found out where I was making a mistake with approach #2. It turns out that for some reason I was not setting the desired access correctly when opening the handle to the volume using CreateFile. The correct access mode is GENERIC_READ | GENERIC_WRITE and I was passing 0. After correcting my error I was able to successfully eject the device using DeviceIoControl - IOCTL_STORAGE_EJECT_MEDIA, as well as with method #1, using CM_Request_Device_Eject.
And it turns out that method #2 is indeed the method used by the shell context menu's "Eject" function. Using this method the device reacts correctly.
Eventually, I found out where I was making a mistake with approach #2.
It turns out that for some reason I was not setting the desired access correctly when opening the handle to the volume using CreateFile.
The correct access mode is GENERIC_READ | GENERIC_WRITE and I was passing 0. After correcting my error I was able to successfully eject the device using DeviceIoControl - IOCTL_STORAGE_EJECT_MEDIA, as well as with method #1, using CM_Request_Device_Eject.
Finally, it turns out that method #2 is indeed the method used by the shell context menu's "Eject" function. Using this method the device reacts correctly.
I came here accidentally while doing a search on "CM_Request_Device_Eject", and saw that it was similar to a solution I'd recently done by pulling together similar pieces of a solution. Forgive the late answer.
I've summarized the steps I've done for this on my project in this SO answer.
In this document,
http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx#paths
To make these device objects accessible by Windows applications, the device drivers create a symbolic link (symlink) in the Win32 namespace, "Global??", to their respective device objects. For example, COM0 and COM1 under the "Global??" subdirectory are simply symlinks to Serial0 and Serial1, "C:" is a symlink to HarddiskVolume1, "Physicaldrive0" is a symlink to DR0, and so on. Without a symlink, a specified device "Xxx" will not be available to any Windows application using Win32 namespace conventions as described previously. However, a handle could be opened to that device using any APIs that support the NT namespace absolute path of the format "\Device\Xxx".
What are the APIs? Let me know some such functions please.
For example, we can have a device sitting in the GLOBAL?? namespace:
GLOBAL??\
COM227
This device we can successfully open using CreateFile:
//Note: we have to prefix it with \\.\ in order to tell CreateFile that
//we want to open something from the Global device namespace.
//Otherwise it will try to open a file
HANDLE hdev = CreateFile("\\.\COM227", GENERIC_READ, 0, null, OPEN_EXISTING, 0, 0);
if (hdev == INVALID_HANDLE_VALUE)
raise new EWin32Exception(GetLastError);
This device (along with every other device in the Win32 Global?? namespace), actually a symbolic link to the "real" device:
GLOBAL??\
COM227 (SymbolicLink) ==> \Device\VCP0
Device\
VCP0 (Device)
So we try to open this real device:
HANDLE hdev = CreateFile("\\.\Device\VCP0", GENERIC_READ, 0, null, OPEN_EXISTING, 0, 0);
if (hdev == INVALID_HANDLE_VALUE)
raise new EWin32Exception(GetLastError);
But it fails with error code 3 (The system cannot find the specified file).
Short:
Works: COM227 (which is an alias of \Device\VCP0)
Fails: \Device\VCP0
The problem is that
#paulsm4 says that CreateFile should work
#larryostermm agrees, and even gives the device path syntax (e.g. \Device\Xxx)
except that it doesn't work
Which means that CreateFile is not one of the "APIs that support the NT namespace absolute path format of \Device\Xxx".
However, a handle could be opened to that device using any APIs that support the NT namespace absolute path of the format "\Device\Xxx".
What are the APIs?
The answers provided so far are misleading at best. They do not answer your question or cover the important distinction between the NT namespace and the other namespaces.
When accessing the NT namespace you need to use the API calls that start with Nt, such as NtOpenFile, if you want to access devices that are only found in the NT namespace of the kernel. For example, a device in \Devices with no symbolic link in \GLOBAL??.
The other calls mentioned above work fine if you are accessing the Win32 device namespace but these require the driver to create a symbolic link in that namespace.
If you want to access a device that is only found in the NT namespace then use NtOpenFile. This is really a very old API call and has drifted in and out of the userpace header files. It is available again and works just fine.
Benjamin -
The simple fact is that you CAN open a "special device file" in Windows, very much as you do in *nix. This is what I tried to say in my original reply. I stand by everything I said in my first post. And I believe the MSDN link I referred to there does a very good job of explaining this, too.
The syntax for a *nix device file is "/dev/SOME_DEVICE". Multiple devices are (by convention, not necessity) distinguished as "/dev/SOME_DEVICE0", "/dev/SOME_DEVICE1", etc. Device files can also be "aliased" using *nix "symbolic links".
The syntax for a Windows device file is a UNC name.
I'm sure you're familiar with UNC shares (for example, "\\myserver\c$").
In all the examples we've discussed above, the server happens to be the local host. Hence "\\.\SOME_RESOURCE_NAME".
It's really as simple as that.
And it DOES work.
Please let me know if you have any further questions.
Thank you in advance .. PSM
The concept of treating a "device" as a "file" is common in *nix (Unix, Linux, Mac OS, etc).
Basically, the MSDN article means that any Win32 API that opens a "file" (either a local disk file, or a UNC resource) could just as easily open a "special device".
A couple of examples:
http://msdn.microsoft.com/en-us/library/aa363858%28VS.85%29.aspx
CreateFile
WriteFile
ReadFile
CloseHandle