internal static extern Standard.HRESULT PropVariantClear(PROPVARIANT pvar);
private static IShellLinkW CreateLinkFromJumpTask(JumpTask jumpTask, bool allowSeparators) { Debug.Assert(jumpTask != null); // Title is generally required. If it's missing we need to treat this like a separator. // Everything else can still appear on separator elements, // but separators can only exist in the Tasks category. if (string.IsNullOrEmpty(jumpTask.Title)) { if (!allowSeparators || !string.IsNullOrEmpty(jumpTask.CustomCategory)) { // Just treat this situation as an InvalidItem. return null; } } var link = (IShellLinkW)Activator.CreateInstance(Type.GetTypeFromCLSID(new Guid(CLSID.ShellLink))); try { string appPath = _FullName; if (!string.IsNullOrEmpty(jumpTask.ApplicationPath)) { appPath = jumpTask.ApplicationPath; } link.SetPath(appPath); // This is optional. Don't set it if the app hasn't explicitly requested it. if (!string.IsNullOrEmpty(jumpTask.WorkingDirectory)) { // Don't verify this. It's possible that the directory doesn't exist now, but it will later. // Shell handles this fine when we try to set an improperly formatted path. link.SetWorkingDirectory(jumpTask.WorkingDirectory); } if (!string.IsNullOrEmpty(jumpTask.Arguments)) { link.SetArguments(jumpTask.Arguments); } // -1 is a sentinel value indicating not to use the icon. if (jumpTask.IconResourceIndex != -1) { string resourcePath = _FullName; if (!string.IsNullOrEmpty(jumpTask.IconResourcePath)) { // Shell bug (Windows 7 595770): IShellLink doesn't correctly limit icon location path to MAX_PATH. // It's really too bad we have to enforce this here. When the shortcut gets // serialized it streams the full string. On deserialization it only retrieves // MAX_PATH for this field leaving junk behind for subsequent gets, leading to data corruption. // Because we don't want to allow the app to do create something that we know may // be corrupt we have to enforce this ourselves. If Shell fixes this later then // we need to remove this check to let them handle this as they see fit. // If they fix it by supporting longer paths, then we're artificially constraining this value... if (jumpTask.IconResourcePath.Length >= Win32Value.MAX_PATH) { // we could throw the exception here, but we're already globally catching everything. return null; } resourcePath = jumpTask.IconResourcePath; } link.SetIconLocation(resourcePath, jumpTask.IconResourceIndex); } if (!string.IsNullOrEmpty(jumpTask.Description)) { link.SetDescription(jumpTask.Description); } IPropertyStore propStore = (IPropertyStore)link; using (var pv = new PROPVARIANT()) { PKEY pkey = default(PKEY); if (!string.IsNullOrEmpty(jumpTask.Title)) { pv.SetValue(jumpTask.Title); pkey = PKEY.Title; } else { pv.SetValue(true); pkey = PKEY.AppUserModel_IsDestListSeparator; } propStore.SetValue(ref pkey, pv); } propStore.Commit(); IShellLinkW retLink = link; link = null; return retLink; } catch (Exception) { // IShellLinkW::Set* methods tend to return E_FAIL when trying to set invalid data. // The create methods don't explicitly check for these kinds of errors. // If we aren't able to create the item for any reason just return null to indicate an invalid item. return null; } finally { Utility.SafeRelease(ref link); } }
private static JumpItem GetJumpItemForShellObject(object shellObject) { var shellItem = shellObject as IShellItem2; var shellLink = shellObject as IShellLinkW; if (shellItem != null) { JumpPath path = new JumpPath { Path = shellItem.GetDisplayName(SIGDN.DESKTOPABSOLUTEPARSING), }; return path; } if (shellLink != null) { var pathBuilder = new StringBuilder((int)Win32Value.MAX_PATH); shellLink.GetPath(pathBuilder, pathBuilder.Capacity, null, SLGP.RAWPATH); var argsBuilder = new StringBuilder((int)Win32Value.INFOTIPSIZE); shellLink.GetArguments(argsBuilder, argsBuilder.Capacity); var descBuilder = new StringBuilder((int)Win32Value.INFOTIPSIZE); shellLink.GetDescription(descBuilder, descBuilder.Capacity); var iconBuilder = new StringBuilder((int)Win32Value.MAX_PATH); int iconIndex; shellLink.GetIconLocation(iconBuilder, iconBuilder.Capacity, out iconIndex); var dirBuilder = new StringBuilder((int)Win32Value.MAX_PATH); shellLink.GetWorkingDirectory(dirBuilder, dirBuilder.Capacity); JumpTask task = new JumpTask { // Set ApplicationPath and IconResources, even if they're from the current application. // This means that equivalent JumpTasks won't necessarily compare property-for-property. ApplicationPath = pathBuilder.ToString(), Arguments = argsBuilder.ToString(), Description = descBuilder.ToString(), IconResourceIndex = iconIndex, IconResourcePath = iconBuilder.ToString(), WorkingDirectory = dirBuilder.ToString(), }; using (PROPVARIANT pv = new PROPVARIANT()) { var propStore = (IPropertyStore)shellLink; PKEY pkeyTitle = PKEY.Title; propStore.GetValue(ref pkeyTitle, pv); // PKEY_Title should be an LPWSTR if it's not empty. task.Title = pv.GetValue() ?? ""; } return task; } // Unsupported type? Debug.Assert(false); return null; }
/// <summary> /// Generate a unique string for the ShellLink that can be used for equality checks. /// </summary> private static string ShellLinkToString(IShellLinkW shellLink) { var pathBuilder = new StringBuilder((int)Win32Value.MAX_PATH); shellLink.GetPath(pathBuilder, pathBuilder.Capacity, null, SLGP.RAWPATH); string title = null; // Need to use the property store to get the title for the link. using (PROPVARIANT pv = new PROPVARIANT()) { var propStore = (IPropertyStore)shellLink; PKEY pkeyTitle = PKEY.Title; propStore.GetValue(ref pkeyTitle, pv); // PKEY_Title should be an LPWSTR if it's not empty. title = pv.GetValue() ?? ""; } var argsBuilder = new StringBuilder((int)Win32Value.INFOTIPSIZE); shellLink.GetArguments(argsBuilder, argsBuilder.Capacity); // Path and title should be case insensitive. // Shell treats arguments as case sensitive because apps can handle those differently. return pathBuilder.ToString().ToUpperInvariant() + title.ToUpperInvariant() + argsBuilder.ToString(); }
internal static extern HRESULT PropVariantClear(PROPVARIANT pvar);