Skip to main content
GameDev.net gamedev.net
🔒 Locked

[.net] Problems with IShellFolder.BindToObject:

Started by CodeReaver Jan 7, 2007 at 7:34 PM 4 replies 3.3k views
Original Post
CodeReaver
CodeReaver
I'm trying to use shell32.dll in my app to navigate through the "My Computer" and "Desktop" folders and others, but I can't seem to figure out what's causing the error. I get an access violation when I call the BindToObject function and another one when I try to use this to get the SHITEMID struct:
Object obj0 = Marshal.GetTypedObjectForIUnknown(pidl, SHITEMIDtype);
SHITEMID itemStruct = obj0;
I assume it's because it's not an object, but I don't know how to convert an intptr to a struct. Does anyone have any experience with this? I can send the code where I import the dll, but it seemed like was a lot to post at once. Also, I'd been trying to only include the functions I make use of. I don't know if that would make any difference.
            // Folder list stuff:
            System.Type tShellFolderType = System.Type.GetType("AMF.IShellFolder");

            IntPtr pDeskTopFolder;
            Shell32.SHGetDesktopFolder(out pDeskTopFolder);
            Object obj1 = Marshal.GetTypedObjectForIUnknown(pDeskTopFolder, tShellFolderType);
            IShellFolder DeskTopFolder = (IShellFolder)obj1;
			
			IntPtr pidl;
            Shell32.SHGetSpecialFolderLocation(IntPtr.Zero, (short)Shell32.CSIDL.CSIDL_DRIVES, out pidl);
            // Shell32.SHGetFolderLocation(IntPtr.Zero, (short)Shell32.CSIDL.CSIDL_WINDOWS, IntPtr.Zero, 0, out pidl);

            IMalloc pMalloc = ShellFunctions.GetMalloc();

            System.Type SHITEMIDtype = System.Type.GetType("AMF.SHITEMID");

            // Get the parent of the desktop?
            IntPtr pMyComputerFolder;
            Guid IID_IShellFolder = new Guid("000214E6-0000-0000-C000-000000000046");
            // ACCESS VIOLATION HERE
            DeskTopFolder.BindToObject(pidl, IntPtr.Zero, IID_IShellFolder, out pMyComputerFolder);
            Object obj2 = Marshal.GetTypedObjectForIUnknown(pMyComputerFolder, tShellFolderType);
            IShellFolder MyComputerFolder = (IShellFolder)obj2;

            System.Type tEnumIDListType = System.Type.GetType("AMF.IEnumIDList");

            IntPtr pEnumIdList;
            MyComputerFolder.EnumObjects(IntPtr.Zero, (int) Shell32.SHCONTF.SHCONTF_FOLDERS, out pEnumIdList);
            Object obj3 = Marshal.GetTypedObjectForIUnknown(pEnumIdList, tEnumIDListType);
            IEnumIDList EnumIDList = (IEnumIDList)obj3;

            uint pceltFetched = 0xFFFFFFFF;
            Shell32.STRRET lpName;
            String name = "";
            // Object obj0 = Marshal.GetTypedObjectForIUnknown(pidl, SHITEMIDtype);
            SHITEMID itemStruct = new SHITEMID(); // (SHITEMID)obj0;
            do
            {
                EnumIDList.Next(1, ref itemStruct, ref pceltFetched);
                if (pceltFetched == 0) continue;
                MyComputerFolder.GetDisplayNameOf(pidl, (uint)Shell32.SHGNO.SHGDN_FORPARSING, out lpName);
                name = (string) Marshal.GetTypedObjectForIUnknown(lpName.pOleStr, name.GetType());
            }
            while (pceltFetched != 0);

            // Get the path from a PIDL
            // System.Text.StringBuilder path2 = new System.Text.StringBuilder(256);
            // Shell32.SHGetPathFromIDList(pidl, path2);
            // MessageBox.Show(path2.ToString());

            // Get the special folder path directly (without getting its PIDL)
            // System.Text.StringBuilder path = new System.Text.StringBuilder(256);
            // Shell32.SHGetFolderPath(IntPtr.Zero, (short)Shell32.CSIDL.CSIDL_WINDOWS, IntPtr.Zero, (uint)Shell32.SHGFP_TYPE.SHGFP_TYPE_CURRENT, path);
            // MessageBox.Show(path.ToString());

            if (pidl != IntPtr.Zero)
            {
                pMalloc.Free(pidl);
            }

            if (pDeskTopFolder != IntPtr.Zero)
            {
                pMalloc.Free(pDeskTopFolder);
            }

            /*if (pMyComputerFolder != IntPtr.Zero)
            {
                pMalloc.Free(pMyComputerFolder);
            }*/

            System.Runtime.InteropServices.Marshal.ReleaseComObject(pMalloc);



Here's what I was trying to copy: http://www.swissdelphicenter.ch/en/showcode.php?id=1598
TheTroll
TheTroll
Just wondering why you are just shell32.dll to do this when there is .net functionality to do it much easier.

First you can get the Desktop/My Computer paths using Enviroment.GetFolderPath() method.

Next you get get the directory using the Directory class.

Maybe if I know what the goal is I can help you.

Just seems like a very painful way of doing it if you don't need to.

theTroll
CodeReaver
CodeReaver
The problem is this bit:

From MSDN:
Quote:
Return Value

The path to the specified system special folder, if that folder physically exists on your computer; otherwise, the empty string ("").

A folder will not physically exist if the operating system did not create it, the existing folder was deleted, or the folder is a virtual directory, such as My Computer, which does not correspond to a physical path.


I need to get to My Computer and other virtual folders. I'm designing a dialog for saving a lot of files at once and I think users would be prefer if the browsing behaves th same as the built in ones.
davepermen
davepermen
Filesystem List View (CodeProject.com)

Filebrowser (CodeProject.com)

You want something like that, right? Possibly these can help you?

Btw, nice that you try to make your save-dialog user friendly by providing standard behavior! I know of too much who just don't give a damn :(

The other idea would be to modify the save-as dialog (which is possible), but I don't know if that would make sense for your situation.
If that's not the help you're after then you're going to have to explain the problem better than what you have. - joanusdmentia
My Page davepermen.net | My Music on Bandcamp and on Soundcloud
CodeReaver
CodeReaver
I had been using one of those as a guideline, but I had decided to only use the code that was absolutely neccessary for geting the names of the virtual folders. I didn't want to just copy the whole lot because I wouldn't learn how it worked that way and I'd bloat my program with stuff I didn't need. Unfortunately I've gotten something wrong and I can't get any usefull information about what's causing the error. All it tells me is "AccessViolationException was unhandled: Attempted to read or write protected memory. This is often an indication that other memory is corrupt", when it gets to the BindToObject part. I think the problem is in the casts to IntPtrs and stuff that needs to be done to make Shell32 work with C#. Could I have missed allocating something or allocated something I shouldn't. I read in a google search that someone had a similar problem with referencing the Guid or something, but the discussion didn't describe how to implement the fix they came up with. It might not have been the same problem anyway. I had problems with converting an IntPtr to a Shell32.STRRET too. I do the rest by getting an object from the IntPts, but STRRET is a struct.
CodeReaver
CodeReaver
OK, I've sort of got it working, but I can't get any of the details of subfolders. In the part where I loop through the subfolders I can get the folder names, but I also need items that I can pass to the GetSubfolders method again when I want to move to one of them. I also use the method SHGetFileInfo in the loop to try to get the icons, but it returns nothing, so I assume I'm passing the wrong thing into it? Do I need to use something else instead of pCurrent? IShellFolder DesktopFolder and IShellFolder CurrentFolder are class members.

This is what's in the constructor:

            // Get the "Desktop" folder            IntPtr pDesktopFolder;            Shell32.SHGetDesktopFolder(out pDesktopFolder);            DesktopFolder = (IShellFolder)Marshal.GetTypedObjectForIUnknown(pDesktopFolder, typeof(IShellFolder));            // Marshal.FreeCoTaskMem(pDesktopFolder);            // Get the ITEMIDLIST for the starting folder            IntPtr pStartingFolder;            Shell32.SHGetSpecialFolderLocation(IntPtr.Zero, Shell32.CSIDL.DRIVES, out pStartingFolder);            // Shell32.SHFILEINFO info = new Shell32.SHFILEINFO();            // Shell32.SHGetFileInfo(            //     pStartingFolder, 0, ref info, Shell32.cbFileInfo, Shell32.SHGFI.PIDL | Shell32.SHGFI.DISPLAYNAME            // );            // Get the IShellFolder for starting folder            IntPtr pCurrentFolder;            DesktopFolder.BindToObject(pStartingFolder, IntPtr.Zero, ref Shell32.IID_IShellFolder, out pCurrentFolder);            CurrentFolder = (IShellFolder)Marshal.GetTypedObjectForIUnknown(pCurrentFolder, typeof(IShellFolder));            GetSubfolders(CurrentFolder);


And this is what's in the function for getting the subfolders:

        void GetSubfolders(IShellFolder Folder)        {            // FOLDER LIST STUFF            // folderList.Items.Clear();            // folderList.SmallImageList = new ImageList();            // NAVIGATION STUFF            // Get the IEnumIDList containing the subfolders of "My Computer"            IntPtr pEnumIDList;            Folder.EnumObjects(IntPtr.Zero, Shell32.SHCONTF.FOLDERS, out pEnumIDList);            IEnumIDList EnumIDList = (IEnumIDList)Marshal.GetTypedObjectForIUnknown(pEnumIDList, typeof(IEnumIDList));            // Structures to hold the folder data            IntPtr lpName = Marshal.AllocCoTaskMem(Shell32.MAX_PATH * 2 + 4);            Marshal.WriteInt32(lpName, 0, 0);            StringBuilder strBuilder = new StringBuilder(Shell32.MAX_PATH);            Shell32.SHFILEINFO info;            // Loop through all the subfolders            IntPtr pCurrent = IntPtr.Zero;            int iNumFound = 0;            string name = "";            while (EnumIDList.Next(1, out pCurrent, out iNumFound) == Shell32.S_OK)            {                iNumFound = 0;                if (Folder.GetDisplayNameOf(pCurrent, Shell32.SHGNO.NORMAL, lpName) == Shell32.S_OK)                {                    Shell32.StrRetToBuf(lpName, pCurrent, strBuilder, Shell32.MAX_PATH);                    name = strBuilder.ToString();                }                info = new Shell32.SHFILEINFO();                Shell32.SHGetFileInfo(                    pCurrent, 0, ref info, Shell32.cbFileInfo, Shell32.SHGFI.PIDL | Shell32.SHGFI.DISPLAYNAME                );                // folderList.SmallImageList.Images.Add(                //     GetFolderIcon(name, IconSize.Small, FolderType.Closed)                // );                // folderList.Items.Add(name, 0);            }            Marshal.FreeCoTaskMem(lpName);            Marshal.FreeCoTaskMem(pCurrent);            return;        }

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.