Export (0) Print
Expand All
Around the World with Visual Basic
Asynchronous Method Execution Using Delegates
Building a Progress Bar that Doesn't Progress
Calling All Operators
Create a Graphical Editor Using RichTextBox and GDI+
Creating A Breadcrumb Control
Creating a Five-Star Rating Control
Creating and Managing Secondary Threads
Data Binding Radio Buttons to a List
Deploying Assemblies
Designing With Custom Attributes
Digital Grandma
Doing Async the Easy Way
Extracting Data from .NET Assemblies
Implementing Callbacks with a Multicast Delegate
Naming and Building Assemblies in Visual Basic .NET
Programming Events of the Framework Class Libraries
Programming I/O with Streams in Visual Basic .NET
Reflection in Visual Basic .NET
Remembering User Information in Visual Basic .NET
Advanced Basics: Revisiting Operator Overloading
Scaling Up: The Very Busy Background Compiler
Synchronizing Multiple Windows Forms
Thread Synchronization
Updating the UI from a Secondary Thread
Using Inheritance in the .NET World
Using the ReaderWriterLock Class
Visual Basic: Simplify Common Tasks by Customizing the My Namespace
What's My IP Address?
Windows Forms Controls: Z-order and Copying Collections
Expand Minimize

Microsoft Office Thumbnails in SharePoint

Visual Studio .NET 2003
 

Ryan Ackley
SoftArtisans

July 2005

Summary: As corporate intranets grow, so does the need for managing the information contained in Microsoft Office documents. Thumbnail previews provide a simple way for making these easier to browse from the Web. (18 printed pages)

Contents

Introduction
Extracting Thumbnails
Converting the Bitmap Into Something Useful
Example: Previewing Office Documents in SharePoint
Conclusion
Code Listing 1
Code Listing 2
Code Listing 3

Introduction

The thumbnail view option of Windows Explorer not only lets me browse my images as thumbnails, but I can also browse previews of files created with Microsoft Office and other applications without having to open a single file. It can be a lifesaver when you are trying to locate one document or presentation in a large collection of files.

What makes this possible is that Microsoft has provided a way for the shell to be extended to display a preview of any file type. Windows comes with several of these shell extensions out of the box. These are thumbnail viewing extensions for common image formats, Office files, and HTML (in Windows XP Service Pack 1, HTML thumbnails have been disabled, see Knowledge Base article 327833).

For Office documents to display a preview, Microsoft Office needs to embed a thumbnail image in the file when it is saved. This feature is enabled by default for PowerPoint presentations but not other file types. For other Office applications, go to File->Properties and select the Save Preview Picture check box at the bottom for each individual file. This can be done for Word, Excel, PowerPoint and Project. If you experiment with this you will find that some thumbnails are more useful than others. Most Word documents show up as rows of squiggly lines. (What do you expect when the font has been shrunk to 2pt?)

This article shows you how to extract and enlarge the thumbnail from Office files and how to save these images into a useful file format. As an example, I extend Microsoft SharePoint Services with this functionality.

Figure 1. Windows Explorer thumbnail view

Extracting Thumbnails

The popularity of document management and content management systems such as Microsoft SharePoint are rising. These types of applications could really use a preview image of documents on the server to add value for users. User could browse items on the Web just like they were in Windows Explorer. For example, a search page for the company intranet could be enhanced so that when a user searches for a document, the results are displayed next to a thumbnail of each document. This could help people find what they are looking for.

The Windows Shell provides a native COM interface for accessing a file's preview image called IExtractImage. I will show you how to access a file's instance of this interface with C++ or Visual Basic .NET using COM interop. To do this, I will use the Windows Shell API. It provides a programming interface to the virtual objects that Windows Explorer displays and manages such as folders, files, printers, etc.

Our first step into the shell is to get the desktop's IShellFolder interface by calling SHGetDesktopFolder. The IShellFolder interface is a key interface in the Shell API. It represents a folder in the Windows Shell. An important thing to note is the difference between the shell's virtual file system and the computer's physical file system. Your desktop folder is likely located at C:\Windows\Desktop on your computer's hard drive. However, when you open up Windows Explorer, there is a virtual Desktop object that sits at the top of the folder hierarchy. This is how the shell views the desktop. The IShellFolder that SHGetDesktopFolder returns is the root of the shell's virtual file system.

From the Desktop folder we can programmatically navigate to anywhere in the file system. To do this, the shell uses what are called item identifier lists, more commonly referred to as PIDLs (pointer to item identifier list), to reference items below the current IShellFolder. Thankfully, the IShellFolder interface provides a method for transforming a path name into a PIDL. It is called ParseDisplayName. This method takes a relative file or folder path and returns a PIDL. Since the desktop folder is the root, it is the one folder where an absolute physical path can be parsed. Once a folder's PIDL is known, the BindToObject method in the IShellFolder interface can be used to get the IShellFolder interface of a subfolder.

After obtaining the IShellFolder that represents the parent folder of the file of interest, the GetUIObjectOf can be used to obtain various interfaces that represent graphical extensions to the shell. One of these interfaces happens to be the IExtractImage interface.

A quick summary of the steps to get the IExtractImage interface of a file:

  1. Use SHGetDesktopFolder to get the desktop folder.
  2. Using the desktop's IShellFolder, pass the file's parent folder path name into ParseDisplayName to get its PIDL.
  3. Using the desktop's IShellFolder, pass the PIDL into the BindToObject method and get the IShellFolder interface of the file's parent folder.
  4. Using the parent folder's IShellFolder, pass the file name into ParseDisplayName to get its PIDL.
  5. Using the parent folder's IShellFolder, pass the file's PIDL into the GetUIObjectOf. method to get the IExtractImage interface.

To illustrate these steps, I have created two examples: One in managed C++ and another in Visual Basic. The complete C++ implementation can be found in Code Listing 1. The Visual Basic implementation uses DllImport and COM interop to gain access to the Shell API and it is listed in Code Listing 2. Also, it was necessary to create Visual Basic versions of the IShellFolder and IExtractImage interfaces because there is no type library to import. These are listed in Code Listing 3 along with supporting structures.

C++

IShellFolder* pDesktop = NULL;
IShellFolder* pSub = NULL;
IExtractImage pIExtract = NULL;
LPITEMIDLIST pidl = NULL;

HRESULT hr;
//step 1
hr = SHGetDesktopFolder(&pDesktop);            
//step 2
hr = pDesktop->ParseDisplayName(NULL, NULL, L"C:\WorkDocuments", NULL, &pidl, NULL);
//step 3
hr = pDesktop->BindToObject(pList, NULL, IID_IShellFolder, (void**)&pSub);
//step 4
hr = pSub->ParseDisplayName(NULL, NULL, L"SalesPitch.ppt", NULL, &pidl, NULL);
//step 5
hr = pSub ->GetUIObjectOf(NULL, 1, &pidl, IID_IExtractImage, NULL, (void**)& pIExtract);

Visual Basic

Dim desktopFolder As IShellFolder
Dim someFolder As IShellFolder
Dim extract As IExtractImage
Dim pidl As IntPtr
Dim filePidl As IntPtr
Dim IID_IShellFolder = New Guid("000214E6-0000-0000-C000-000000000046")
Dim IID_IExtractImage = New Guid("BB2E617C-0920-11d1-9A0B-00C04FC2D6C1")

'step 1
ShellInterop.SHGetDesktopFolder(desktopFolder)
'step 2
desktopFolder.ParseDisplayName(IntPtr.Zero, IntPtr.Zero, folderName, 0, pidl, 0)
'step 3
desktopFolder.BindToObject(pidl, IntPtr.Zero, IID_IShellFolder, someFolder)
'step4
someFolder.ParseDisplayName(IntPtr.Zero, IntPtr.Zero, "testThis.doc", 0, filePidl, 0)
'step5
someFolder.GetUIObjectOf(IntPtr.Zero, 1, filePidl, IID_IExtractImage, 0, extract)
      

You should note that in the C++ sample code above, I've omitted error checking for brevity. Every function that returns an HRESULT should be checked with the SUCCEEDED or FAILED macro. The LPITEMIDLIST structure is what stores the PIDL in the C++ example and I use an IntPtr for the same purpose in Visual Basic. One very important thing to remember when working with PIDLs from managed code is that they are allocated in unmanaged memory. This makes it necessary to delete them using the FreeCoTaskMem static method found in System.Runtime.InteropServices.Marshal when they are no longer needed.

The example retrieves the IExtractImage interface for the PowerPoint presentation "C:\WorkDocuments\SalesPitch.ppt". Now I will show you how to get the actual preview image using this interface.

The IExtractImage interface defines two methods: GetLocation and Extract. The first thing you have to do is use the GetLocation method to tell the interface the size, color depth, and other miscellaneous features of the image. The values you should pass in for these settings will depend on your application and environment. The size should be big enough so the image will be useful enough for your needs. As I stated earlier, a 12 pt. font in a Word document shows up as rows of squiggly lines in the shell's display of the thumbnail. This is a direct result of the image being scrunched down to one and half inches high. My own experimentation shows that the letters of a 12 pt. font are easily recognizable at a height of 500 pixels. The color depth is the number of bits used to represent the color of a single pixel in your resultant bitmap (this should be 24 for most versions of Windows).

Finally, you call the Extract method and this will give you an HBITMAP. The example below shows this simple process.

C++

SIZE size;
size.cx = 500;
size.cy = 500;

DWORD dwFlags = IEIFLAG_ORIGSIZE | IEIFLAG_QUALITY;

HBITMAP hBmp = NULL;

// Set up the options for the image
OLECHAR pathBuffer[MAX_PATH];
hr = pIExtract->GetLocation(pathBuffer, MAX_PATH, NULL, &size, colorDepth, &dwFlags);

// Get the image
hr = pIExtract ->Extract(&img);

Visual Basic

Dim size As SIZE
size.cx = 500
size.cy = 500

Dim flags = IEIFLAG.ORIGSIZE Or IEIFLAG.QUALITY
Dim bmp As IntPtr
Dim thePath = Marshal.AllocHGlobal(MAX_PATH)

' An exception will be thrown if the file does not have a thumbnail
Try
        extract.GetLocation(thePath, MAX_PATH, 0, size, colorDepth, flags)
        extract.Extract(bmp)
Catch ex As Exception
End Try

By passing in the IEIFLAG_ORIGSIZE flag in the above example, I tell Windows to make the image as close to my size as possible but also to retain the original image's aspect ratio. I also use the flag IEIFLAG_QUALITY to tell the shell that I want the highest quality image possible. This is important because I want the text to be readable.

The C++ example gets an HBITMAP and the Visual Basic example ends up with an IntPtr that is actually the HBITMAP in managed form. The returned HBITMAP is a bitmap stored in memory. You can use GDI+ to convert this to a compressed image format like JPG or GIF.

Converting the Bitmap Into Something Useful

Bitmaps can take up a lot of memory or disk space. This is a problem when you want to transmit an image over the Web. With the addition of GDI+, Microsoft added a full-featured imaging API to the Platform SDK. The Image class in managed GDI+ has the static method FromHbitmap that will convert an HBITMAP into a Bitmap object. The Bitmap class extends the Image class. The Image class provides the Save method that can be used to convert the bitmap into a compressed image format like JPEG or PNG.

In my example project, I've created a managed class called ThumbnailExtractor. It has one static method called GetThumbnailImage that returns an Image object for a given file. I call it using Visual Basic in the example below and then save it as a jpg image.

Dim img As Image = ThumbnailExtractor.GetThumbnailImage(CmdArgs(0), 500, 24)
If Not img Is Nothing Then
  img.Save(CmdArgs(1), ImageFormat.Jpeg)
End If

Example: Previewing Office Documents in SharePoint

One practical use for extracting thumbnails would be to display document previews on the Web using Microsoft SharePoint Services. SharePoint Services is the Microsoft enterprise portal server for Windows Server 2003 that allows people to collaborate in some really interesting ways. Two of the many features of SharePoint are document management and document collaboration. In addition, SharePoint provides several different ways to customize and extend its features.

The most visible way to customize SharePoint is to create what is called a Web part. A Web part encapsulates information that can be displayed to a user on a SharePoint portal. Some examples of Web parts include a company vacation calendar, document lists, image galleries, and announcements. Each Web part is like a building block. A user can take this building block and customize the information a portal will display. They can set the location and appearance of each one.

Your first step when developing a Web part should be to download the SharePoint Products and Technologies Templates: Web Part Templates for Visual Studio .NET. You can also find many excellent tutorials on the subject of creating SharePoint Web parts on MSDN. I recommend Creating a Basic Web Part for a good introduction.

Creating a SharePoint Web part is very similar to creating an ASP.NET Web Form control. This is because Microsoft.SharePoint.WebPartPages.WebPart, the class that all Web parts inherit from, is a child class of System.Web.UI.Control. The WebPart class overrides the Render method from the Control class and seals it so it can't be overridden. The WebPart class expects children to override the RenderWebPart method to add functionality to custom Web parts.

For my example, I created a Web part that displays a preview for every document that has been added to a SharePoint site (that actually has a thumbnail).

SharePoint Services provides an extensive .NET API for programmatically manipulating its objects. My Web part iterates through all documents that the current site and any sub-sites contains and displays a preview for them. The first step into the object model is to get a handle to the SPSite object for the server.

' Entry point into the sharepoint object model
Dim mySite As SPSite = SPControl.GetContextSite(Context)

Something of a misnomer, an SPSite is a collection of SharePoint Services sites on a virtual server. The SPWeb class represents an individual site. Another key concept in SharePoint is the idea of lists. A list represents a collection of information that can be shared by the users of the SharePoint site. A document library in SharePoint is actually a list. I iterate through all sub-sites' document libraries by calling GetListsOfType on each SPWeb object and get the individual documents as SPFile objects.

' get all of the sub-sites for this site.
Dim webs As SPWebCollection = mySite.AllWebs
For Each web As SPWeb In webs
    ' iterate through all of the sub-sites and get the doc libraries
    Dim lists As SPListCollection = web.GetListsOfType(SPBaseType.DocumentLibrary)
    For Each list As SPList In lists
        Dim items As SPListItemCollection = list.Items
  For Each item As SPListItem In items
      Dim itemFile As SPFile = item.File

The SPFile class represents a file stored in SharePoint. One problem that I had when trying to create this example is that the files have to exist in the file system so the shell can properly extract the thumbnail. SharePoint Services stores uploaded files inside of the database. So it was necessary for my Web part to copy the file to some kind of temporary directory, then get the preview thumbnail for the file at this location. SharePoint provides Web parts with a directory called wpresources for special needs like this.

Dim resDir As String = Page.Server.MapPath("/wpresources")

Dim fileName As String = Path.Combine(resDir, listID + itemID + 
Path.GetExtension(itemFile.Name))

Dim fs As FileStream = New FileStream(fileName, FileMode.Create)
Dim bw As BinaryWriter = New BinaryWriter(fs)
bw.Write(itemFile.OpenBinary())
bw.Close()
fs.Close()

' Call to the library for accessing the
' IExtractImage interface
Dim img As System.Drawing.Image = 
ThumbnailExtractor.GetThumbnailImage(fileName, 500, 32)

After extracting the thumbnail from the file as a bitmap, I save the thumbnail back to the wpresources directory as a jpg file and display the thumbnail in my Web part using a URL pointing to the image in the wpresources directory. Figure 2 shows a screenshot of my Web part in action. I only have one document on my site so that is the only preview that appears.

This Web part is a demonstration of the technologies covered in this article. There are several ways it could be improved. For example, the Web part shows all documents for all sites underneath the current site. It could just show a single selectable file from the current site instead.

Click image to see a larger version.

Figure 2. SharePoint Web part that displays an image preview of a Word document

Conclusion

I used SharePoint Services as an example use of the preview image extraction described in this article, but there are many types of Web applications that use files created by Microsoft Office. These include document and content management systems, search engines, resume repositories, and many others. As corporate intranets grow, so does the need for managing the information contained in Microsoft Office documents. Thumbnail previews provide a simple way for making these easier to browse from the Web.

Ryan Ackley is a software engineer at SoftArtisans (http://www.softartisans.com). There he develops collaboration and reporting solutions for Microsoft Office. He also holds a master's degree in Computer Engineering from the University of Central Florida.

Code Listing 1

Image* ThumbnailExtractor::GetThumbnailImage(String* fileName, int longestEdge, int colorDepth)
{
  // allocate some unmanaged memory for our strings and divide the file name
  // into a folder path and file name.
  IntPtr dirPtr = Marshal::StringToHGlobalUni(Path::GetDirectoryName(fileName));
  IntPtr filePtr = Marshal::StringToHGlobalUni(Path::GetFileName(fileName));
  WCHAR* wDirName = static_cast<WCHAR*>(dirPtr.ToPointer());
  WCHAR* wFileName = static_cast<WCHAR*>(filePtr.ToPointer());

  IShellFolder* pDesktop = NULL;
  IShellFolder* pSub = NULL;
  IExtractImage* pIExtract = NULL;
  LPITEMIDLIST pList = NULL;
  Bitmap* pImg = NULL;

  // get the desktop directory
  if (SUCCEEDED(SHGetDesktopFolder(&pDesktop)))
  {   
    // get the pidl for the directory
    HRESULT hr = pDesktop->ParseDisplayName(NULL, NULL, wDirName, NULL, &pList, NULL);
    if (FAILED(hr))
    {
      throw new Exception(S"Failed to parse the directory name");
    }

    // get the directory IShellFolder interface
    hr = pDesktop->BindToObject(pList, NULL, IID_IShellFolder, (void**)&pSub);
    if (FAILED(hr))
    {
      throw new Exception(S"Failed to bind to the directory");
    }

    // get the file's pidl
    hr = pSub->ParseDisplayName(NULL, NULL, wFileName, NULL, &pList, NULL);
    if (FAILED(hr))
    {
      throw new Exception(S"Failed to parse the file name");
    }

    // get the IExtractImage interface
    LPCITEMIDLIST pidl = pList;
    hr = pSub->GetUIObjectOf(NULL, 1, &pidl, IID_IExtractImage,
                            NULL, (void**)&pIExtract);

    // set our desired image size
    SIZE size;
    size.cx = longestEdge;
    size.cy = longestEdge;      
                 
    if(pIExtract == NULL)
    { 
      return NULL;
    }         

    HBITMAP hBmp = NULL;

    // The IEIFLAG_ORIGSIZE flag tells it to use the original aspect
    // ratio for the image size. The IEIFLAG_QUALITY flag tells the 
    // interface we want the image to be the best possible quality.
    DWORD dwFlags = IEIFLAG_ORIGSIZE | IEIFLAG_QUALITY;      

    OLECHAR pathBuffer[MAX_PATH];
    hr = pIExtract->GetLocation(pathBuffer, MAX_PATH, NULL, &size, colorDepth, &dwFlags);         
    if (FAILED(hr))
    {
      throw new Exception(S"The call to GetLocation failed");
    }

    hr = pIExtract->Extract(&hBmp);

    // It is possible for Extract to fail if there is no thumbnail image
    // so we won't check for success here

    pIExtract->Release();

    if (hBmp != NULL)
    {
      pImg = Image::FromHbitmap(hBmp);            
    }      
  }

  // Release the COM objects we have a reference to.
  pDesktop->Release();
  pSub->Release(); 

  // delete the unmanaged memory we allocated
  Marshal::FreeCoTaskMem(dirPtr);
  Marshal::FreeCoTaskMem(filePtr);
     
  return pImg;
}

Codel Listing 2

Public Shared Function GetThumbnailImage(ByVal fileName As String, _
  ByVal longestEdge As Integer, ByVal colorDepth As Integer) As Image

  Dim desktopFolder As IShellFolder
  Dim someFolder As IShellFolder
  Dim extract As IExtractImage
  Dim pidl As IntPtr
  Dim filePidl As IntPtr

  'Manually define the IIDs for IShellFolder and IExtractImage
  Dim IID_IShellFolder = New Guid("000214E6-0000-0000-C000-000000000046")
  Dim IID_IExtractImage = New Guid("BB2E617C-0920-11d1-9A0B-00C04FC2D6C1")

  'Divide the file name into a path and file name
  Dim folderName = Path.GetDirectoryName(fileName)
  Dim shortFileName = Path.GetFileName(fileName)

  'Get the desktop IShellFolder
  ShellInterop.SHGetDesktopFolder(desktopFolder)

  'Get the parent folder IShellFolder
  desktopFolder.ParseDisplayName(IntPtr.Zero, IntPtr.Zero, folderName, 0, pidl, 0)
  desktopFolder.BindToObject(pidl, IntPtr.Zero, IID_IShellFolder, someFolder)

  'Get the file's IExtractImage
  someFolder.ParseDisplayName(IntPtr.Zero, IntPtr.Zero, shortFileName, 0, filePidl, 0)
  someFolder.GetUIObjectOf(IntPtr.Zero, 1, filePidl, IID_IExtractImage, 0, extract)

  'Set the size
  Dim size As SIZE
  size.cx = 500
  size.cy = 500

  Dim flags = IEIFLAG.ORIGSIZE Or IEIFLAG.QUALITY
  Dim bmp As IntPtr
  Dim thePath = Marshal.AllocHGlobal(MAX_PATH)

  'Interop will throw an exception if one of these calls fail.
  Try
    extract.GetLocation(thePath, MAX_PATH, 0, size, colorDepth, flags)
    extract.Extract(bmp)
  Catch ex As Exception
  End Try


  'Free the global memory we allocated for the path string
  Marshal.FreeHGlobal(thePath)

  'Free the pidls. The Runtime Callable Wrappers 
  'should automatically release the COM objects
  Marshal.FreeCoTaskMem(pidl)
  Marshal.FreeCoTaskMem(filePidl)

  If Not bmp.Equals(IntPtr.Zero) Then
    GetThumbnailImage = Image.FromHbitmap(bmp)
  Else
    GetThumbnailImage = Nothing
  End If
End Function

Codel Listing 3

Imports System
Imports System.Text
Imports System.Runtime.InteropServices


Public Enum IEIFLAG As Integer
  ASYNC = &H1
  CACHE = &H2
  ASPECT = &H4
  OFFLINE = &H8
  GLEAM = &H10
  SCREEN = &H20
  ORIGSIZE = &H40
  NOSTAMP = &H80
  NOBORDER = &H100
  QUALITY = &H200
End Enum

<StructLayout(LayoutKind.Sequential)> _
Public Structure STRRET_CSTR
  Public uType As Integer
  <FieldOffset(4), MarshalAs(UnmanagedType.LPWStr)> _
  Public pOleStr As String
  <FieldOffset(4)> _
  Public uOffset As Integer
  <FieldOffset(4), MarshalAs(UnmanagedType.ByValArray, SizeConst:=520)> _
  Public strName As Byte()
End Structure

<StructLayout(LayoutKind.Sequential)> _
Public Structure SIZE
  Public cx As Integer
  Public cy As Integer
End Structure

<ComImportAttribute(), _
 GuidAttribute("BB2E617C-0920-11d1-9A0B-00C04FC2D6C1"), _
 InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)> _
Public Interface IExtractImage

  Sub GetLocation( _
      ByVal pszPathBuffer As IntPtr, _
      ByVal cch As Integer, _
      ByRef pdwPriority As Integer, _
      ByRef prgSize As SIZE, _
      ByVal dwRecClrDepth As Integer, _
      ByRef pdwFlags As Integer)

  Sub Extract(ByRef phBmpThumbnail As IntPtr)

End Interface

<ComImportAttribute(), _
GuidAttribute("000214E6-0000-0000-C000-000000000046"), _
InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)> _
Public Interface IShellFolder

  Sub ParseDisplayName( _
    ByVal hWnd As IntPtr, _
    ByVal pbc As IntPtr, _
    ByVal pszDisplayName As String, _
    ByRef pchEaten As Integer, _
    ByRef ppidl As System.IntPtr, _
    ByRef pdwAttributes As Integer)

  Sub EnumObjects( _
    ByVal hwndOwner As IntPtr, _
    <MarshalAs(UnmanagedType.U4)> ByVal grfFlags As Integer, _
    <Out()> ByRef ppenumIDList As IntPtr)

  Sub BindToObject( _
    ByVal pidl As IntPtr, _
    ByVal pbcReserved As IntPtr, _
    ByRef riid As Guid, _
    ByRef ppvOut As IShellFolder)

  Sub BindToStorage( _
    ByVal pidl As IntPtr, _
    ByVal pbcReserved As IntPtr, _
    ByRef riid As Guid, _
    <Out()> ByVal ppvObj As IntPtr)

  <PreserveSig()> _
  Function CompareIDs( _
    ByVal lParam As IntPtr, _
    ByVal pidl1 As IntPtr, _
    ByVal pidl2 As IntPtr) As Integer

  Sub CreateViewObject( _
    ByVal hwndOwner As IntPtr, _
    ByRef riid As Guid, _
    ByVal ppvOut As Object)

  Sub GetAttributesOf( _
    ByVal cidl As Integer, _
    ByVal apidl As IntPtr, _
    <MarshalAs(UnmanagedType.U4)> ByRef rgfInOut As Integer)

  Sub GetUIObjectOf( _
    ByVal hwndOwner As IntPtr, _
    ByVal cidl As Integer, _
    ByRef apidl As IntPtr, _
    ByRef riid As Guid, _
    <Out()> ByVal prgfInOut As Integer, _
    <Out(), MarshalAs(UnmanagedType.IUnknown)> ByRef ppvOut As Object)

  Sub GetDisplayNameOf( _
    ByVal pidl As IntPtr, _
    <MarshalAs(UnmanagedType.U4)> ByVal uFlags As Integer, _
    ByRef lpName As STRRET_CSTR)

  Sub SetNameOf( _
    ByVal hwndOwner As IntPtr, _
    ByVal pidl As IntPtr, _
    <MarshalAs(UnmanagedType.LPWStr)> ByVal lpszName As String, _
    <MarshalAs(UnmanagedType.U4)> ByVal uFlags As Integer, _
    ByRef ppidlOut As IntPtr)

End Interface

Public Class ShellInterop
  <DllImport("shell32.dll", CharSet:=CharSet.Auto)> _
  Public Shared Function SHGetDesktopFolder( _
    <Out()> ByRef ppshf As IShellFolder) As Integer
  End Function
End Class
Show:
© 2015 Microsoft