Jump to content
Attila Kovacs

UnPinnable App

Recommended Posts

this makes an app unpinnable:

 

uses
  Winapi.Windows,
  Winapi.ShlObj,
  Winapi.ShellAPI,
  Winapi.ActiveX,
  Winapi.PropSys,
  Vcl.Forms,
  ..
  
function MarkWindowAsUnpinnable(Ahwnd: hwnd): HResult;
const
  VARIANT_TRUE = WordBool(-1);
  PKEY_AppUserModel_PreventPinning: TPropertyKey = (fmtid: '{9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}'; pid: 9);
var
  pps: IPropertyStore;
  hr: HResult;
  v: PROPVARIANT;
begin
  hr := SHGetPropertyStoreForWindow(Ahwnd, IID_IPropertyStore, pointer(pps));
  if Succeeded(hr) then
  begin
    // pps._AddRef;
    v.vt := VT_BOOL;
    v.boolVal := VARIANT_TRUE;
    hr := pps.SetValue(PKEY_AppUserModel_PreventPinning, v);
    // pps._Release;
  end;
  Result := hr;
end;

...

  Application.CreateForm(TMainForm, MainForm);
  MarkWindowAsUnpinnable(Application.MainForm.Handle);

 

I'm not sure about the pps._addref and pps._release, I would appreciate some help on that.

 

 

thx

 

 

Edited by Attila Kovacs

Share this post


Link to post
2 hours ago, Attila Kovacs said:

I'm not sure about the pps._addref and pps._release, I would appreciate some help on that.

Get rid of the _Release. It's already called implicitly when the interface reference goes out of scope.

  • Thanks 1

Share this post


Link to post

Ok, with the changed signature (out param) following happens:

1. try pps = nil

2. pps.IntfClear on nil

3. SHGetPropertyStoreForWindow => pps.refcount = 1

4. program run

5. finally pps.InfClear end;

 

with var parameter, casting to pointer:

 

1. try pps = nil

2. SHGetPropertyStoreForWindow => pps.refcount = 1

3. program run

4. finally pps.InfClear end;

 

1. With my additional addref()/release() the refcount number goes up to 2 but will be released at the end in any way because pps is going out of scope (Am I right?)

2. So I'd think, because pps is a local variable, I don't need any manual intervention to its refcount and therefore I don't understand the signature change to an "out param".

    I mean, it could be the proper translation, it also looks better without the pointer cast, but I can't see any advantages compared to the already implemented signature.

 

 

 

 

Edited by Attila Kovacs

Share this post


Link to post

I'm not sure I fully understand your explanation.

 

Anyway, now that I think of it you should get rid of both the _AddRef and _Release.
The interface being returned by SHGetPropertyStoreForWindow will have a ref count of 1. Your pointer() hard cast ensures that it stays at 1 even though you are storing the reference in a Delphi interface var. When this reference goes out of scope the reference count should become 0.

 

2 hours ago, Attila Kovacs said:

I don't understand the signature change to an "out param"

I'm not sure what change you're referencing here.

I don't know what the Pascal declaration of SHGetPropertyStoreForWindow looks like but in this case it doesn't really make a difference if ppv is declared as a pointer pointer, an interface or pointer var or out parameter. The semantics are different but the result is the same. I believe the semantics of out best describe what's actually going on here (return of a value with disregard for the current value of the passed var).
Look at the declaration (and implementation) of QueryInterface in classes.pas for another way to declare an out parameter returning an arbitrary interface.

Share this post


Link to post

Okay. "var pps: pointer" matches the documented API (so it's correct) but if you want to use an out parameter instead then the correct declaration would be:

function SHGetPropertyStoreForWindow(hwnd: HWND; const riid: TGUID; out ppv): HResult; stdcall;

because ppv is documented as:

Quote

When this function returns, contains the interface pointer requested in riid. This is typically IPropertyStore.

which means that it can return another interface if you request that.

Note the untyped out parameter, like QueryInterface. Because the parameter is untyped it is your responsibility to pass a var declared as the interface you're requesting. The compiler will not stop you passing in something that doesn't make sense:

var
  Nonsense: IMomsSpaghetti;
begin
  SHGetPropertyStoreForWindow(Handle, IPropertyStore, Nonsense);
  Nonsense.Vomit; // Boom!
end;

 

What is the actual problem you're experiencing, by the way?

  • Like 1

Share this post


Link to post

Nothing, I was unsure how to translate it properly without surprises later. I took some C or C# example as a base.

Edited by Attila Kovacs

Share this post


Link to post
On 2/13/2022 at 4:39 PM, Attila Kovacs said:

 


Application.CreateForm(TMainForm, MainForm);
MarkWindowAsUnpinnable(Application.MainForm.Handle);

 

In addition to everthing said about AddRef/Release(), just note that if your Form's window is ever recreated at runtime, you will loose your marking and have to re-apply it on the new window.  Best way to avoid that is to have your Form class override the virtual CreateWnd() method to call MarkWindowAsUnpinnable(), don't call it after creating the Form object.

  • Thanks 1

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×