Setup.InstFunc.Ole.pas 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. unit Setup.InstFunc.Ole;
  2. {
  3. Inno Setup
  4. Copyright (C) 1997-2025 Jordan Russell
  5. Portions by Martijn Laan
  6. For conditions of distribution and use, see LICENSE.TXT.
  7. OLE-related installation functions
  8. }
  9. interface
  10. function CreateShellLink(const Filename, Description, ShortcutTo, Parameters,
  11. WorkingDir: String; IconFilename: String; const IconIndex, ShowCmd: Integer;
  12. const HotKey: Word; const AppUserModelID: String;
  13. const AppUserModelToastActivatorCLSID: PGUID;
  14. const ExcludeFromShowInNewInstall, PreventPinning: Boolean): String;
  15. procedure RegisterTypeLibrary(const Filename: String);
  16. procedure UnregisterTypeLibrary(const Filename: String);
  17. function UnpinShellLink(const Filename: String): Boolean;
  18. implementation
  19. uses
  20. Windows, ActiveX, ComObj, PropSys, ShlObj,
  21. SysUtils,
  22. PathFunc,
  23. Shared.CommonFunc, Shared.SetupMessageIDs,
  24. Setup.InstFunc, Setup.MainFunc, SetupLdrAndSetup.Messages;
  25. procedure AssignWorkingDir(const SL: IShellLink; const WorkingDir: String);
  26. { Assigns the specified working directory to SL. If WorkingDir is empty then
  27. we select one ourself as best we can. (Leaving the working directory field
  28. empty is a security risk.) Note: SL.SetPath must be called first. }
  29. var
  30. Dir: String;
  31. Buf: array[0..1023] of Char;
  32. begin
  33. { Try any caller-supplied WorkingDir first }
  34. if WorkingDir <> '' then
  35. { SetWorkingDirectory *shouldn't* fail, but we might as well check }
  36. if SL.SetWorkingDirectory(PChar(WorkingDir)) = S_OK then
  37. Exit;
  38. { Otherwise, try to extract a directory name from the shortcut's target
  39. filename. We use GetPath to retrieve the filename as it will expand any
  40. environment strings. }
  41. if SL.GetPath(Buf, SizeOf(Buf) div SizeOf(Buf[0]), TWin32FindData(nil^), 0) = S_OK then begin
  42. Dir := PathExtractDir(PathExpand(Buf));
  43. if SL.SetWorkingDirectory(PChar(Dir)) = S_OK then
  44. Exit;
  45. end;
  46. { As a last resort, use the system directory }
  47. Dir := GetSystemDir;
  48. SL.SetWorkingDirectory(PChar(Dir));
  49. end;
  50. function GetResultingFilename(const PF: IPersistFile;
  51. const OriginalFilename: String): String;
  52. { Determines the actual resulting filename. IPersistFile::Save doesn't always
  53. save to the specified filename; it may rename the extension to .pif if the
  54. shortcut points to an MS-DOS application. }
  55. var
  56. CurFilename: PChar;
  57. OleResult: HRESULT;
  58. begin
  59. Result := '';
  60. CurFilename := nil;
  61. OleResult := PF.GetCurFile(CurFilename);
  62. if SUCCEEDED(OleResult) and Assigned(CurFilename) then begin
  63. if OleResult = S_OK then
  64. Result := CurFilename;
  65. CoTaskMemFree(CurFilename);
  66. end;
  67. { If GetCurFile didn't work, we have no choice but to try to guess the filename }
  68. if Result = '' then begin
  69. if NewFileExists(OriginalFilename) then
  70. Result := OriginalFilename
  71. else if NewFileExists(PathChangeExt(OriginalFilename, '.pif')) then
  72. Result := PathChangeExt(OriginalFilename, '.pif')
  73. else begin
  74. { Neither exist? Shouldn't happen, but return something anyway }
  75. Result := OriginalFilename;
  76. end;
  77. end;
  78. end;
  79. function CreateShellLink(const Filename, Description, ShortcutTo, Parameters,
  80. WorkingDir: String; IconFilename: String; const IconIndex, ShowCmd: Integer;
  81. const HotKey: Word; const AppUserModelID: String;
  82. const AppUserModelToastActivatorCLSID: PGUID;
  83. const ExcludeFromShowInNewInstall, PreventPinning: Boolean): String;
  84. { Creates a lnk file named Filename, with a description of Description, with a
  85. HotKey hotkey, which points to ShortcutTo. Filename should be a full path.
  86. NOTE! If you want to copy this procedure for use in your own application
  87. be sure to call CoInitialize at application startup and CoUninitialize at
  88. application shutdown. See the bottom of this unit for an example. But this
  89. is not necessary if you are using Delphi 3 and your project already 'uses'
  90. the ComObj RTL unit. }
  91. const
  92. PKEY_AppUserModel_ID: TPropertyKey = (
  93. fmtid: (D1:$9F4C2855; D2:$9F79; D3:$4B39; D4:($A8,$D0,$E1,$D4,$2D,$E1,$D5,$F3));
  94. pid: 5);
  95. PKEY_AppUserModel_ExcludeFromShowInNewInstall: TPropertyKey = (
  96. fmtid: (D1:$9F4C2855; D2:$9F79; D3:$4B39; D4:($A8,$D0,$E1,$D4,$2D,$E1,$D5,$F3));
  97. pid: 8);
  98. PKEY_AppUserModel_PreventPinning: TPropertyKey = (
  99. fmtid: (D1:$9F4C2855; D2:$9F79; D3:$4B39; D4:($A8,$D0,$E1,$D4,$2D,$E1,$D5,$F3));
  100. pid: 9);
  101. PKEY_AppUserModel_StartPinOption: TPropertyKey = (
  102. fmtid: (D1:$9F4C2855; D2:$9F79; D3:$4B39; D4:($A8,$D0,$E1,$D4,$2D,$E1,$D5,$F3));
  103. pid: 12);
  104. PKEY_AppUserModel_ToastActivatorCLSID: TPropertyKey = (
  105. fmtid: (D1:$9F4C2855; D2:$9F79; D3:$4B39; D4:($A8,$D0,$E1,$D4,$2D,$E1,$D5,$F3));
  106. pid: 26);
  107. APPUSERMODEL_STARTPINOPTION_NOPINONINSTALL = 1;
  108. var
  109. OleResult: HRESULT;
  110. Obj: IUnknown;
  111. SL: IShellLink;
  112. PS: PropSys.IPropertyStore;
  113. PV: TPropVariant;
  114. PF: IPersistFile;
  115. begin
  116. Obj := CreateComObject(CLSID_ShellLink);
  117. SL := Obj as IShellLink;
  118. SL.SetPath(PChar(ShortcutTo));
  119. SL.SetArguments(PChar(Parameters));
  120. AssignWorkingDir(SL, WorkingDir);
  121. if IconFilename <> '' then begin
  122. { Work around a 64-bit Windows bug. It replaces pf32 with %ProgramFiles%
  123. which is wrong. This causes an error when the user tries to change the
  124. icon of the installed shortcut. Note that the icon does actually display
  125. fine because it *also* stores the original 'non replaced' path in the
  126. shortcut. }
  127. if IsWin64 then
  128. StringChangeEx(IconFileName, ExpandConst('{pf32}\'), '%ProgramFiles(x86)%\', True);
  129. SL.SetIconLocation(PChar(IconFilename), IconIndex);
  130. end;
  131. SL.SetShowCmd(ShowCmd);
  132. if Description <> '' then
  133. SL.SetDescription(PChar(Description));
  134. if HotKey <> 0 then
  135. SL.SetHotKey(HotKey);
  136. if (AppUserModelID <> '') or (AppUserModelToastActivatorCLSID <> nil) or ExcludeFromShowInNewInstall or PreventPinning then begin
  137. PS := Obj as PropSys.IPropertyStore;
  138. { According to MSDN the PreventPinning property should be set before the ID property. In practice
  139. this doesn't seem to matter - at least not for shortcuts - but do it first anyway. }
  140. if PreventPinning then begin
  141. PV.vt := VT_BOOL;
  142. PV.boolVal := True;
  143. OleResult := PS.SetValue(PKEY_AppUserModel_PreventPinning, PV);
  144. if OleResult <> S_OK then
  145. RaiseOleError('IPropertyStore::SetValue(PKEY_AppUserModel_PreventPinning)', OleResult);
  146. end;
  147. if AppUserModelID <> '' then begin
  148. const WideAppUserModelID: WideString = AppUserModelID;
  149. PV.vt := VT_BSTR;
  150. PV.bstrVal := PWideChar(WideAppUserModelID);
  151. OleResult := PS.SetValue(PKEY_AppUserModel_ID, PV);
  152. if OleResult <> S_OK then
  153. RaiseOleError('IPropertyStore::SetValue(PKEY_AppUserModel_ID)', OleResult);
  154. end;
  155. if IsWindows10 and (AppUserModelToastActivatorCLSID <> nil) then begin
  156. PV.vt := VT_CLSID;
  157. PV.puuid := AppUserModelToastActivatorCLSID;
  158. OleResult := PS.SetValue(PKEY_AppUserModel_ToastActivatorCLSID, PV);
  159. if OleResult <> S_OK then
  160. RaiseOleError('IPropertyStore::SetValue(PKEY_AppUserModel_ToastActivatorCLSID)', OleResult);
  161. end;
  162. if ExcludeFromShowInNewInstall then begin
  163. PV.vt := VT_BOOL;
  164. PV.boolVal := True;
  165. OleResult := PS.SetValue(PKEY_AppUserModel_ExcludeFromShowInNewInstall, PV);
  166. if OleResult <> S_OK then
  167. RaiseOleError('IPropertyStore::SetValue(PKEY_AppUserModel_ExcludeFromShowInNewInstall)', OleResult);
  168. if IsWindows8 then begin
  169. PV.vt := VT_UI4;
  170. PV.ulVal := APPUSERMODEL_STARTPINOPTION_NOPINONINSTALL;
  171. OleResult := PS.SetValue(PKEY_AppUserModel_StartPinOption, PV);
  172. if OleResult <> S_OK then
  173. RaiseOleError('IPropertyStore::SetValue(PKEY_AppUserModel_StartPinOption)', OleResult);
  174. end;
  175. end;
  176. OleResult := PS.Commit;
  177. if OleResult <> S_OK then
  178. RaiseOleError('IPropertyStore::Commit', OleResult);
  179. end;
  180. PF := SL as IPersistFile;
  181. OleResult := PF.Save(PChar(Filename), True);
  182. if OleResult <> S_OK then
  183. RaiseOleError('IPersistFile::Save', OleResult);
  184. Result := GetResultingFilename(PF, Filename);
  185. end;
  186. procedure RegisterTypeLibrary(const Filename: String);
  187. var
  188. ExpandedFilename: String;
  189. OleResult: HRESULT;
  190. TypeLib: ITypeLib;
  191. begin
  192. ExpandedFilename := PathExpand(Filename);
  193. OleResult := LoadTypeLib(PChar(ExpandedFilename), TypeLib);
  194. if OleResult <> S_OK then
  195. RaiseOleError('LoadTypeLib', OleResult);
  196. OleResult := RegisterTypeLib(TypeLib, PChar(ExpandedFilename), nil);
  197. if OleResult <> S_OK then
  198. RaiseOleError('RegisterTypeLib', OleResult);
  199. end;
  200. procedure UnregisterTypeLibrary(const Filename: String);
  201. type
  202. TUnRegTlbProc = function(const libID: TGUID; wVerMajor, wVerMinor: Word;
  203. lcid: TLCID; syskind: TSysKind): HResult; stdcall;
  204. var
  205. UnRegTlbProc: TUnRegTlbProc;
  206. ExpandedFilename: String;
  207. OleResult: HRESULT;
  208. TypeLib: ITypeLib;
  209. LibAttr: PTLibAttr;
  210. begin
  211. { Dynamically import UnRegisterTypeLib since older OLEAUT32.DLL versions
  212. don't have this function }
  213. @UnRegTlbProc := GetProcAddress(GetModuleHandle('OLEAUT32.DLL'),
  214. 'UnRegisterTypeLib');
  215. if @UnRegTlbProc = nil then
  216. Win32ErrorMsg('GetProcAddress');
  217. ExpandedFilename := PathExpand(Filename);
  218. OleResult := LoadTypeLib(PChar(ExpandedFilename), TypeLib);
  219. if OleResult <> S_OK then
  220. RaiseOleError('LoadTypeLib', OleResult);
  221. OleResult := TypeLib.GetLibAttr(LibAttr);
  222. if OleResult <> S_OK then
  223. RaiseOleError('ITypeLib::GetLibAttr', OleResult);
  224. try
  225. with LibAttr^ do
  226. OleResult := UnRegTlbProc(Guid, wMajorVerNum, wMinorVerNum, LCID, SysKind);
  227. if OleResult <> S_OK then
  228. RaiseOleError('UnRegisterTypeLib', OleResult);
  229. finally
  230. TypeLib.ReleaseTLibAttr(LibAttr);
  231. end;
  232. end;
  233. const
  234. IID_StartMenuPinnedList: TGUID = SID_IStartMenuPinnedList;
  235. IID_ShellItem: TGUID = SID_IShellItem;
  236. { Attempt to unpin a shortcut. Returns True if the shortcut was successfully
  237. removed from the list of pinned items and/or the taskbar, or if the shortcut
  238. was not pinned at all. http://msdn.microsoft.com/en-us/library/bb774817.aspx }
  239. function UnpinShellLink(const Filename: String): Boolean;
  240. var
  241. ShellItem: IShellItem;
  242. StartMenuPinnedList: IStartMenuPinnedList;
  243. begin
  244. const ExpandedFilename = PathExpand(Filename);
  245. if Succeeded(SHCreateItemFromParsingName(PChar(ExpandedFilename), nil, IID_ShellItem, ShellItem)) and
  246. Succeeded(CoCreateInstance(CLSID_StartMenuPin, nil, CLSCTX_INPROC_SERVER, IID_StartMenuPinnedList, StartMenuPinnedList)) then
  247. Result := StartMenuPinnedList.RemoveFromList(ShellItem) = S_OK
  248. else
  249. Result := True;
  250. end;
  251. procedure InitOle;
  252. var
  253. OleResult: HRESULT;
  254. begin
  255. OleResult := CoInitialize(nil);
  256. if Failed(OleResult) then
  257. raise Exception.CreateFmt('CoInitialize failed (0x%.8x)', [OleResult]);
  258. { ^ doesn't use a SetupMessage since messages probably aren't loaded
  259. during 'initialization' section below, which calls this procedure }
  260. end;
  261. initialization
  262. InitOle;
  263. finalization
  264. CoUninitialize;
  265. end.