Setup.InstFunc.Ole.pas 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. unit Setup.InstFunc.Ole;
  2. {
  3. Inno Setup
  4. Copyright (C) 1997-2024 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, SysUtils, PathFunc, Shared.CommonFunc, Setup.InstFunc, Setup.MainFunc,
  21. SetupLdrAndSetup.Messages, Shared.SetupMessageIDs,
  22. ActiveX, ComObj, PropSys, ShellAPI, ShlObj;
  23. procedure AssignWorkingDir(const SL: IShellLink; const WorkingDir: String);
  24. { Assigns the specified working directory to SL. If WorkingDir is empty then
  25. we select one ourself as best we can. (Leaving the working directory field
  26. empty is a security risk.) Note: SL.SetPath must be called first. }
  27. var
  28. Dir: String;
  29. Buf: array[0..1023] of Char;
  30. begin
  31. { Try any caller-supplied WorkingDir first }
  32. if WorkingDir <> '' then
  33. { SetWorkingDirectory *shouldn't* fail, but we might as well check }
  34. if SL.SetWorkingDirectory(PChar(WorkingDir)) = S_OK then
  35. Exit;
  36. { Otherwise, try to extract a directory name from the shortcut's target
  37. filename. We use GetPath to retrieve the filename as it will expand any
  38. environment strings. }
  39. if SL.GetPath(Buf, SizeOf(Buf) div SizeOf(Buf[0]), TWin32FindData(nil^), 0) = S_OK then begin
  40. Dir := PathExtractDir(PathExpand(Buf));
  41. if SL.SetWorkingDirectory(PChar(Dir)) = S_OK then
  42. Exit;
  43. end;
  44. { As a last resort, use the system directory }
  45. Dir := GetSystemDir;
  46. SL.SetWorkingDirectory(PChar(Dir));
  47. end;
  48. function GetResultingFilename(const PF: IPersistFile;
  49. const OriginalFilename: String): String;
  50. { Determines the actual resulting filename. IPersistFile::Save doesn't always
  51. save to the specified filename; it may rename the extension to .pif if the
  52. shortcut points to an MS-DOS application. }
  53. var
  54. CurFilename: PWideChar;
  55. OleResult: HRESULT;
  56. begin
  57. Result := '';
  58. CurFilename := nil;
  59. OleResult := PF.GetCurFile(CurFilename);
  60. if SUCCEEDED(OleResult) and Assigned(CurFilename) then begin
  61. if OleResult = S_OK then
  62. Result := WideCharToString(CurFilename);
  63. CoTaskMemFree(CurFilename);
  64. end;
  65. { If GetCurFile didn't work, we have no choice but to try to guess the filename }
  66. if Result = '' then begin
  67. if NewFileExists(OriginalFilename) then
  68. Result := OriginalFilename
  69. else if NewFileExists(PathChangeExt(OriginalFilename, '.pif')) then
  70. Result := PathChangeExt(OriginalFilename, '.pif')
  71. else begin
  72. { Neither exist? Shouldn't happen, but return something anyway }
  73. Result := OriginalFilename;
  74. end;
  75. end;
  76. end;
  77. function CreateShellLink(const Filename, Description, ShortcutTo, Parameters,
  78. WorkingDir: String; IconFilename: String; const IconIndex, ShowCmd: Integer;
  79. const HotKey: Word; const AppUserModelID: String;
  80. const AppUserModelToastActivatorCLSID: PGUID;
  81. const ExcludeFromShowInNewInstall, PreventPinning: Boolean): String;
  82. { Creates a lnk file named Filename, with a description of Description, with a
  83. HotKey hotkey, which points to ShortcutTo. Filename should be a full path.
  84. NOTE! If you want to copy this procedure for use in your own application
  85. be sure to call CoInitialize at application startup and CoUninitialize at
  86. application shutdown. See the bottom of this unit for an example. But this
  87. is not necessary if you are using Delphi 3 and your project already 'uses'
  88. the ComObj RTL unit. }
  89. const
  90. PKEY_AppUserModel_ID: TPropertyKey = (
  91. fmtid: (D1:$9F4C2855; D2:$9F79; D3:$4B39; D4:($A8,$D0,$E1,$D4,$2D,$E1,$D5,$F3));
  92. pid: 5);
  93. PKEY_AppUserModel_ExcludeFromShowInNewInstall: TPropertyKey = (
  94. fmtid: (D1:$9F4C2855; D2:$9F79; D3:$4B39; D4:($A8,$D0,$E1,$D4,$2D,$E1,$D5,$F3));
  95. pid: 8);
  96. PKEY_AppUserModel_PreventPinning: TPropertyKey = (
  97. fmtid: (D1:$9F4C2855; D2:$9F79; D3:$4B39; D4:($A8,$D0,$E1,$D4,$2D,$E1,$D5,$F3));
  98. pid: 9);
  99. PKEY_AppUserModel_StartPinOption: TPropertyKey = (
  100. fmtid: (D1:$9F4C2855; D2:$9F79; D3:$4B39; D4:($A8,$D0,$E1,$D4,$2D,$E1,$D5,$F3));
  101. pid: 12);
  102. PKEY_AppUserModel_ToastActivatorCLSID: TPropertyKey = (
  103. fmtid: (D1:$9F4C2855; D2:$9F79; D3:$4B39; D4:($A8,$D0,$E1,$D4,$2D,$E1,$D5,$F3));
  104. pid: 26);
  105. APPUSERMODEL_STARTPINOPTION_NOPINONINSTALL = 1;
  106. var
  107. OleResult: HRESULT;
  108. Obj: IUnknown;
  109. SL: IShellLink;
  110. PS: PropSys.IPropertyStore;
  111. PV: TPropVariant;
  112. PF: IPersistFile;
  113. WideAppUserModelID, WideFilename: WideString;
  114. begin
  115. Obj := CreateComObject(CLSID_ShellLink);
  116. SL := Obj as IShellLink;
  117. SL.SetPath(PChar(ShortcutTo));
  118. SL.SetArguments(PChar(Parameters));
  119. AssignWorkingDir(SL, WorkingDir);
  120. if IconFilename <> '' then begin
  121. { Work around a 64-bit Windows bug. It replaces pf32 with %ProgramFiles%
  122. which is wrong. This causes an error when the user tries to change the
  123. icon of the installed shortcut. Note that the icon does actually display
  124. fine because it *also* stores the original 'non replaced' path in the
  125. shortcut. }
  126. if IsWin64 then
  127. StringChangeEx(IconFileName, ExpandConst('{pf32}\'), '%ProgramFiles(x86)%\', True);
  128. SL.SetIconLocation(PChar(IconFilename), IconIndex);
  129. end;
  130. SL.SetShowCmd(ShowCmd);
  131. if Description <> '' then
  132. SL.SetDescription(PChar(Description));
  133. if HotKey <> 0 then
  134. SL.SetHotKey(HotKey);
  135. if (AppUserModelID <> '') or (AppUserModelToastActivatorCLSID <> nil) or ExcludeFromShowInNewInstall or PreventPinning then begin
  136. PS := Obj as PropSys.IPropertyStore;
  137. { According to MSDN the PreventPinning property should be set before the ID property. In practice
  138. this doesn't seem to matter - at least not for shortcuts - but do it first anyway. }
  139. if PreventPinning then begin
  140. PV.vt := VT_BOOL;
  141. PV.boolVal := True;
  142. OleResult := PS.SetValue(PKEY_AppUserModel_PreventPinning, PV);
  143. if OleResult <> S_OK then
  144. RaiseOleError('IPropertyStore::SetValue(PKEY_AppUserModel_PreventPinning)', OleResult);
  145. end;
  146. if AppUserModelID <> '' then begin
  147. WideAppUserModelID := AppUserModelID;
  148. PV.vt := VT_BSTR;
  149. PV.bstrVal := PWideChar(WideAppUserModelID);
  150. OleResult := PS.SetValue(PKEY_AppUserModel_ID, PV);
  151. if OleResult <> S_OK then
  152. RaiseOleError('IPropertyStore::SetValue(PKEY_AppUserModel_ID)', OleResult);
  153. end;
  154. if IsWindows10 and (AppUserModelToastActivatorCLSID <> nil) then begin
  155. PV.vt := VT_CLSID;
  156. PV.puuid := AppUserModelToastActivatorCLSID;
  157. OleResult := PS.SetValue(PKEY_AppUserModel_ToastActivatorCLSID, PV);
  158. if OleResult <> S_OK then
  159. RaiseOleError('IPropertyStore::SetValue(PKEY_AppUserModel_ToastActivatorCLSID)', OleResult);
  160. end;
  161. if ExcludeFromShowInNewInstall then begin
  162. PV.vt := VT_BOOL;
  163. PV.boolVal := True;
  164. OleResult := PS.SetValue(PKEY_AppUserModel_ExcludeFromShowInNewInstall, PV);
  165. if OleResult <> S_OK then
  166. RaiseOleError('IPropertyStore::SetValue(PKEY_AppUserModel_ExcludeFromShowInNewInstall)', OleResult);
  167. if IsWindows8 then begin
  168. PV.vt := VT_UI4;
  169. PV.ulVal := APPUSERMODEL_STARTPINOPTION_NOPINONINSTALL;
  170. OleResult := PS.SetValue(PKEY_AppUserModel_StartPinOption, PV);
  171. if OleResult <> S_OK then
  172. RaiseOleError('IPropertyStore::SetValue(PKEY_AppUserModel_StartPinOption)', OleResult);
  173. end;
  174. end;
  175. OleResult := PS.Commit;
  176. if OleResult <> S_OK then
  177. RaiseOleError('IPropertyStore::Commit', OleResult);
  178. end;
  179. PF := SL as IPersistFile;
  180. WideFilename := Filename;
  181. OleResult := PF.Save(PWideChar(WideFilename), 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. WideFilename: WideString;
  189. OleResult: HRESULT;
  190. TypeLib: ITypeLib;
  191. begin
  192. WideFilename := PathExpand(Filename);
  193. OleResult := LoadTypeLib(PWideChar(WideFilename), TypeLib);
  194. if OleResult <> S_OK then
  195. RaiseOleError('LoadTypeLib', OleResult);
  196. OleResult := RegisterTypeLib(TypeLib, PWideChar(WideFilename), 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. WideFilename: WideString;
  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. WideFilename := PathExpand(Filename);
  218. OleResult := LoadTypeLib(PWideChar(WideFilename), 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. CLSID_StartMenuPin: TGUID = (
  235. D1:$a2a9545d; D2:$a0c2; D3:$42b4; D4:($97,$08,$a0,$b2,$ba,$dd,$77,$c8));
  236. IID_StartMenuPinnedList: TGUID = (
  237. D1:$4CD19ADA; D2:$25A5; D3:$4A32; D4:($B3,$B7,$34,$7B,$EE,$5B,$E3,$6B));
  238. IID_ShellItem: TGUID = (
  239. D1:$43826D1E; D2:$E718; D3:$42EE; D4:($BC,$55,$A1,$E2,$61,$C3,$7B,$FE));
  240. type
  241. IStartMenuPinnedList = interface(IUnknown)
  242. ['{4CD19ADA-25A5-4A32-B3B7-347BEE5BE36B}']
  243. function RemoveFromList(const pitem: IShellItem): HRESULT; stdcall;
  244. end;
  245. var
  246. SHCreateItemFromParsingNameFunc: function(pszPath: LPCWSTR; const pbc: IBindCtx;
  247. const riid: TIID; var ppv): HResult; stdcall;
  248. { Attempt to unpin a shortcut. Returns True if the shortcut was successfully
  249. removed from the list of pinned items and/or the taskbar, or if the shortcut
  250. was not pinned at all. http://msdn.microsoft.com/en-us/library/bb774817.aspx }
  251. function UnpinShellLink(const Filename: String): Boolean;
  252. var
  253. WideFileName: WideString;
  254. ShellItem: IShellItem;
  255. StartMenuPinnedList: IStartMenuPinnedList;
  256. begin
  257. WideFilename := PathExpand(Filename);
  258. if Assigned(SHCreateItemFromParsingNameFunc) and
  259. SUCCEEDED(SHCreateItemFromParsingNameFunc(PWideChar(WideFilename), nil, IID_ShellItem, ShellItem)) and
  260. SUCCEEDED(CoCreateInstance(CLSID_StartMenuPin, nil, CLSCTX_INPROC_SERVER, IID_StartMenuPinnedList, StartMenuPinnedList)) then
  261. Result := StartMenuPinnedList.RemoveFromList(ShellItem) = S_OK
  262. else
  263. Result := True;
  264. end;
  265. procedure InitOle;
  266. var
  267. OleResult: HRESULT;
  268. begin
  269. OleResult := CoInitialize(nil);
  270. if FAILED(OleResult) then
  271. raise Exception.CreateFmt('CoInitialize failed (0x%.8x)', [OleResult]);
  272. { ^ doesn't use a SetupMessage since messages probably aren't loaded
  273. during 'initialization' section below, which calls this procedure }
  274. end;
  275. initialization
  276. InitOle;
  277. SHCreateItemFromParsingNameFunc := GetProcAddress(SafeLoadLibrary(AddBackslash(GetSystemDir) + shell32,
  278. SEM_NOOPENFILEERRORBOX), 'SHCreateItemFromParsingName');
  279. finalization
  280. CoUninitialize;
  281. end.