0

I need to allow the user to select an icon, so I've implemented the PickIconDlg function from shell32. The problem is that if the user selects a path that is longer than the initial path I declare, the resultant value is the user-selected path truncated to length of the initial path.

For instance, if I set the initial path to "C:\Windows\System32\shell32.dll" and the user selects "C:\Users\Public\Documents\TheIcons\Library.dll", the updated string value comes back as "C:\Users\Public\Documents\TheIc" (i.e. the first 31 characters of the user-selected path, because the initial path is 31 characters long).

I have tried adjusting the 'nMaxFile' value I pass to PickIconDlg, which I understand is supposed to set the max length of the path variable. This doesn't seem to make a difference.

Declare Unicode Function PickIconDlg Lib "Shell32" Alias "PickIconDlg" (ByVal hwndOwner As IntPtr, ByVal lpstrFile As String, ByVal nMaxFile As Integer, ByRef lpdwIconIndex As Integer) As Integer

Public Function GetIconLoc(frmform As Form) As Object()
    Dim iconfile As String = Environment.GetFolderPath(Environment.SpecialFolder.System) + "\shell32.dll"
    Dim iconindex As Integer ' Will store the index of the selected icon

    If PickIconDlg(frmform.Handle, iconfile, 50, iconindex) = 1 Then
        MessageBox.Show(iconfile)
        Return {iconfile, iconindex}
    Else
        Return Nothing
    End If
End Function

I expect the string variable iconfile to contain the full user-selected path, as its length is less than the defined max of 50 characters. Instead, only a portion of the path is returned, as described above.

LarsTech
  • 80,625
  • 14
  • 153
  • 225
Zane H.
  • 3
  • 1
  • When you need a file path, you specify `MAX_PATH = 260`, both in the constructor of the buffer that returns the string and the path's length. – Jimi Jun 24 '19 at 15:48
  • BTW, it's also clearly stated in the Docs about [PickIconDlg](https://learn.microsoft.com/en-us/windows/desktop/api/shlobj_core/nf-shlobj_core-pickicondlg). This applies to other similar Win32 functions. You can also use a `StringBuilder` instead of a string, here. – Jimi Jun 24 '19 at 15:51
  • BTW2, your `Function` could be declared as `Public Function GetIconLoc(frmform As Form) As (IconFile As String, IconIndex As Integer)`, so you can return named values as `Return (iconfile, iconindex)` – Jimi Jun 24 '19 at 16:16
  • Wow, do I feel stupid. Thanks for pointing that out, @Jimi ! – Zane H. Jun 25 '19 at 15:11

1 Answers1

0

Append a Null character plus empty space after the original file name to create a string buffer large enough to contain the result.

Dim iconfile As String = _
    Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), "shell32.dll") & _
    vbNullChar & Space(256)

Then pass the total length when calling the function

PickIconDlg(frmform.Handle, iconfile, Len(iconfile), iconindex)

And finally cut the extra space from the result

iconfile = Left(iconfile, InStr(iconfile, vbNullChar) - 1)

Also, use Path.Combine instead of concatenating the path yourself. Path.Combine adds missing or removes extra backslashes (\) automatically.

Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188