Page 1 of 1

Lock Screen Image - Windows 10

Posted: 14 Nov 2017, 16:26
by egocarib
I would like to programmatically update the Windows 10 lock screen image.

There appears to be a new UserProfilePersonalizationSettings API for this starting in Windows 10, but I'm not sure how to access it from AHK. I imagine there is probably some way to access this object using DllCall or similar. Anyone have any ideas?

UserProfilePersonalizationSettings Class
LockScreen Class


Edit:
Looks like one could potentially do this using lexikos' .NET framework interop. If someone else has a simpler solution in mind, let me know. Otherwise I'll post back here if I manage to figure it out.

Re: Lock Screen Image - Windows 10

Posted: 14 Nov 2017, 20:42
by egocarib
Here is the code I came up with so far
.NET Framework Interop, updated for current AHK v2
My code
I am having trouble because the code requires using Windows.System.UserProfile;, however, this does not seem to be able to be loaded like other namespaces. If I try to load it as Windows.System.UserProfile.dll (as in my code pasted above), I encounter the error: Error CS0006 on line 0: Metadata file 'Windows.System.UserProfile.dll' could not be found. After doing a bit more research, it seems the problem might be that Windows.System refers to the Windows Runtime API - which sounds like it can't be loaded quite like other .dll files. I am not sure how to access it through the AHK .NET framework.

Anyone know if that's possible? Or if there are any alternatives I can pursue?

Re: Lock Screen Image - Windows 10

Posted: 20 Nov 2017, 01:18
by qwerty12
egocarib wrote:Looks like one could potentially do this using lexikos' .NET framework interop. If someone else has a simpler solution in mind, let me know. Otherwise I'll post back here if I manage to figure it out.[/i]
JsRT would probably work: https://github.com/Lexikos/ActiveScript.ahk

As I'd rather give myself an enema with a bicycle pump than write JavaScript, here's something to do it with raw COM:

Code: Select all

#NoEnv  ; Recommended for performance and compatibility with future AutoHotkey releases.

; DllCall("ole32.dll\OleUninitialize")
; DllCall("combase.dll\RoInitialize", "UInt", RO_INIT_MULTITHREADED := 1)

; 1. Create StorageFile obj for pic
VarSetCapacity(IIDStorageFileStatics, 16), VA_GUID(IIDStorageFileStatics := "{5984C710-DAF2-43C8-8BB4-A4D3EACFD03F}")
StorageFile := new rtstr("Windows.Storage.StorageFile")
if (!DllCall("combase.dll\RoGetActivationFactory", "Ptr", StorageFile.str, "Ptr", &IIDStorageFileStatics, "Ptr*", instStorageFile)) {
	if (!DllCall(NumGet(NumGet(instStorageFile+0)+6*A_PtrSize), "Ptr", instStorageFile, "Ptr", (_ := new rtstr("C:\Users\Me\Pictures\Wallpapers\affinity_street_romain_trystram_01.jpg")).str, "Ptr*", sfileasyncwrapper)) {
		; 2. Said SF obj gets created async. Keep checking (in a sync manner) to see if actual SF obj is created
		sfileasyncinfo := ComObjQuery(sfileasyncwrapper, IID_IAsyncInfo := "{00000036-0000-0000-C000-000000000046}")
		while (!DllCall(NumGet(NumGet(sfileasyncinfo+0)+7*A_PtrSize), "Ptr", sfileasyncinfo, "UInt*", status) && !status)
			Sleep 100
		if (status != 1)
			ExitApp 1
		
		; 3. It has! Finally take pointer to sf obj
		DllCall(NumGet(NumGet(sfileasyncwrapper+0)+8*A_PtrSize), "Ptr", sfileasyncwrapper, "Ptr*", sfile)
		
		; 4. Create LockScreen obj
		VarSetCapacity(IIDLockScreenStatics, 16), VA_GUID(IIDLockScreenStatics := "{3EE9D3AD-B607-40AE-B426-7631D9821269}")
		lockScreen := new rtstr("Windows.System.UserProfile.LockScreen")
		if (!DllCall("combase.dll\RoGetActivationFactory", "Ptr", lockScreen.str, "Ptr", &IIDLockScreenStatics, "Ptr*", instLockScreen)) {
			; Tell ls obj to set ls pic from sf obj
			DllCall(NumGet(NumGet(instLockScreen+0)+8*A_PtrSize), "Ptr", instLockScreen, "Ptr", sfile, "Ptr*", Operation)
			Sleep 1000 ; Note: it's better to do the sfileasyncinfo stuff again instead of a simple sleep like this to help ensure the lockScreen pic is set. mind, if your script is persistent, it mightn't matter
			ObjRelease(Operation)
			ObjRelease(instLockScreen)
		}
		
		ObjRelease(sfile)
		ObjRelease(sfileasyncinfo)
		ObjRelease(sfileasyncwrapper)
	}
	ObjRelease(instStorageFile)
}

class rtstr {
	static lpWindowsCreateString := DllCall("GetProcAddress", "Ptr", DllCall("GetModuleHandle", "Str", "combase.dll", "Ptr"), "AStr", "WindowsCreateString", "Ptr")
	static lpWindowsDeleteString := DllCall("GetProcAddress", "Ptr", DllCall("GetModuleHandle", "Str", "combase.dll", "Ptr"), "AStr", "WindowsDeleteString", "Ptr")

	__New(sourceString, length := 0) {
		this.str := !DllCall(rtstr.lpWindowsCreateString, "WStr", sourceString, "UInt", length ? length : StrLen(sourceString), "Ptr*", string) ? string : 0
	}

	__Delete() {
		DllCall(rtstr.lpWindowsDeleteString, "Ptr", this.str)
	}
}

; From Lexikos' VA.ahk: Convert string to binary GUID structure.
VA_GUID(ByRef guid_out, guid_in="%guid_out%") {
    if (guid_in == "%guid_out%")
        guid_in :=   guid_out
    if  guid_in is integer
        return guid_in
    VarSetCapacity(guid_out, 16, 0)
	DllCall("ole32\CLSIDFromString", "wstr", guid_in, "ptr", &guid_out)
	return &guid_out
}