Closed Rtw915 closed 2 months ago
Seems like something changes in your environment that invalidates the old config files. Next time it happens you should manually fix the setup and export a new config file and then compare the old and new config file and see what's different. You can also post the files here and I can take a look at them.
Good idea! I compared the two files, but I don't quite understand the differences. I had to change the file extensions to upload them to GitHub.
1ScreenProfile.txt 1ScreenProfileOldBusted.txt
Maybe this has somethiing to do with it? https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-displayconfig_path_target_info#members
Do you think I should update the config file on line 546 to this?
<ToString>DISPLAYCONFIG_TARGET_FORCIBLE</ToString>
<U32>2</U32>
You are using HDMI. "Forcing" a display is exclusive to analog signals like VGA as far as I can tell, see: https://github.com/MicrosoftDocs/windows-driver-docs/blob/staging/windows-driver-docs-pr/display/forced-versus-connected-targets.md
All analog target types are considered forceable, and all digital targets are not considered forceable.
I suspect the issue is caused by me not handling your cloned display properly. I remember having to reset some IDs in the Enable-Display
command code when changing from cloned to extended so it's likely a similar problem here. I'll dig deeper and get back to you in a couple of days.
Sorry, I didn't look close enough at your PowerShell code. You need to use the -UpdateAdapterIds
in Use-DisplayConfig
to reliably import exported configurations as the adapter LUIDs may change whenever the system is rebooted. AllowChanges
is also redundant because the config should contain the exact refresh rate values. Try this snippet instead:
$CurrentDisplayConfig = Get-DisplayConfig
$DisplayCount = (Get-DisplayInfo | Where-Object -Property Active -EQ $true).Count
if ($DisplayCount -ge 3)
{
$CurrentDisplayConfig | Export-Clixml $home\4ScreensProfile.xml
Import-Clixml $home\1ScreenProfile.xml | Use-DisplayConfig -UpdateAdapterIds
} elseif ($DisplayCount -eq 1)
{
$CurrentDisplayConfig | Export-Clixml $home\1ScreenProfile.xml
Import-Clixml $home\4ScreensProfile.xml | Use-DisplayConfig -UpdateAdapterIds
}
Try it out and let me know how it goes.
I appreciate your help, but unfortunately, it's now completely broken. I'm encountering the same error in both directions, with only the line number differing. Additionally, all five screens are set to extend, not duplicate (clone). Also, I realize that I labeled the configuration for four screens, but that was because I forgot to include the laptop screen in the count.
It's probably worth noting that I haven't rebooted in 15 days, but this issue has occurred at least three times during that period. The laptop is almost always plugged in and is set to never sleep or hibernate, although the screens do turn off after 5 minutes of inactivity.
Error Details:
PSMessageDetails :
Exception : System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.
at System.ThrowHelper.ThrowKeyNotFoundException()
at System.Collections.Generic.Dictionary`2.get_Item(TKey key)
at MartinGC94.DisplayConfig.API.DisplayConfig.UpdateAdapterIds()
at MartinGC94.DisplayConfig.Commands.UseDisplayConfigCommand.ProcessRecord()
TargetObject : MartinGC94.DisplayConfig.API.DisplayConfig
CategoryInfo : NotSpecified: (MartinGC94.Disp...I.DisplayConfig:DisplayConfig) [Use-DisplayConfig], KeyNotFoundException
FullyQualifiedErrorId : AdapterUpdateError,MartinGC94.DisplayConfig.Commands.UseDisplayConfigCommand
ErrorDetails :
InvocationInfo : System.Management.Automation.InvocationInfo
ScriptStackTrace : at <ScriptBlock>, C:\Users\Ryan\Documents\Screens.ps1: line 5
PipelineIterationInfo : {}
PS C:\Windows\System32\WindowsPowerShell\v1.0> $error.InvocationInfo
MyCommand : Use-DisplayConfig
BoundParameters : {}
UnboundArguments : {}
ScriptLineNumber : 5
OffsetInLine : 46
HistoryId : 57
ScriptName : C:\Users\Ryan\Documents\Screens.ps1
Line : Import-Clixml $home\1ScreenProfile.xml | Use-DisplayConfig -UpdateAdapterIds
PositionMessage : At C:\Users\Ryan\Documents\Screens.ps1:5 char:46
+ ... Clixml $home\1ScreenProfile.xml | Use-DisplayConfig -UpdateAdapterIds
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PSScriptRoot : C:\Users\Ryan\Documents
PSCommandPath : C:\Users\Ryan\Documents\Screens.ps1
InvocationName : Use-DisplayConfig
PipelineLength : 0
PipelinePosition : 0
ExpectingInput : False
CommandOrigin : Internal
DisplayScriptPosition :
The issue you had in your 1ScreenProfile.txt and 1ScreenProfileOldBusted.txt profiles was because the DisplayId order had changed. This was because I didn't take multiple display adapters into account. This has been fixed and you can try it out with the changes I've made in my dev branch: https://github.com/MartinGC94/DisplayConfig/tree/Dev note however that you need to create new profiles from scratch because the old ones will have a different order. Also note that you still need to use the UpdateAdapterIds
parameter I mentioned before.
If you don't want to build locally I will also attach the dll to this comment and you can just download and import that one: import-module C:\PathTo.Dll
.
DisplayConfig.txt
rename to .dll and check that the MD5 hash matches 7939B14F1B791E1BE237E2C7AAB985AD
I cannot reproduce the KeyNotFoundException error you posted most recently but it seems quite strange. The dictionary is built using the available paths so I guess it happens when I'm looping over the ModeArray? https://github.com/MartinGC94/DisplayConfig/blob/main/src/DisplayConfig/API/DisplayConfig.cs#L886 seems weird that the mode array would contain an adapter not in the path array. I could presumably solve this problem by also looping through the ModeArray when creating the old -> new adapter mappings. However, I'd like to get 2 sample profiles where I can see this for myself before I make any changes here. So if you see this error again, make sure to send me a before and after display profile where you've fixed it manually.
So try my most recent changes for a week or two and report back if you experience any issues. If I don't hear anything I will assume it's good and make an official release.
I appreciate your help so far. I deleted the existing configuration XML files and started fresh by removing the old module and importing the development version. However, I might have missed something because it’s still not working as expected.
Using the same code as before, with -UpdateAdapterIds
, I encountered this error:
Use-DisplayConfig : The input object cannot be bound to any parameters for the command either because the command does not take pipeline input or the input and its properties do not match any of the parameters that take pipeline input.
At line:1 char:42
+ ... Clixml $home\1ScreenProfile.xml | Use-DisplayConfig -UpdateAdapterIds
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (MartinGC94.Disp...I.DisplayConfig:PSObject) [Use-DisplayConfig], ParameterBindingException
+ FullyQualifiedErrorId : InputObjectNotBound,MartinGC94.DisplayConfig.Commands.UseDisplayConfigCommand
I also tried running it without the -UpdateAdapterIds
parameter, but I got the same error.
Next, I attempted this approach:
$5ScreensProfile = Import-Clixml $home\5ScreensProfile.xml
Use-DisplayConfig -DisplayConfig $5ScreensProfile -UpdateAdapterIds
But I ran into a different error:
PSMessageDetails :
Exception : System.Management.Automation.ParameterBindingException: Cannot bind parameter 'DisplayConfig'. Cannot convert value "MartinGC94.DisplayConfig.API.DisplayConfig" to type "MartinGC94.DisplayConfig.API.DisplayConfig". Error: "Cannot
convert the "MartinGC94.DisplayConfig.API.DisplayConfig" value of type "Deserialized.MartinGC94.DisplayConfig.API.DisplayConfig" to type "MartinGC94.DisplayConfig.API.DisplayConfig"." --->
System.Management.Automation.PSInvalidCastException: Cannot convert value "MartinGC94.DisplayConfig.API.DisplayConfig" to type "MartinGC94.DisplayConfig.API.DisplayConfig". Error: "Cannot convert the
"MartinGC94.DisplayConfig.API.DisplayConfig" value of type "Deserialized.MartinGC94.DisplayConfig.API.DisplayConfig" to type "MartinGC94.DisplayConfig.API.DisplayConfig"." ---> System.Management.Automation.PSInvalidCastException:
Cannot convert the "MartinGC94.DisplayConfig.API.DisplayConfig" value of type "Deserialized.MartinGC94.DisplayConfig.API.DisplayConfig" to type "MartinGC94.DisplayConfig.API.DisplayConfig".
at System.Management.Automation.LanguagePrimitives.ThrowInvalidCastException(Object valueToConvert, Type resultType)
at System.Management.Automation.LanguagePrimitives.SetObjectProperties(Object o, PSObject psObject, Type resultType, MemberNotFoundError memberNotFoundErrorAction, MemberSetValueError memberSetValueErrorAction, IFormatProvider
formatProvider, Boolean recursion, Boolean ignoreUnknownMembers)
at System.Management.Automation.LanguagePrimitives.ConvertViaNoArgumentConstructor.Convert(Object valueToConvert, Type resultType, Boolean recursion, PSObject originalValueToConvert, IFormatProvider formatProvider, TypeTable
backupTable, Boolean ignoreUnknownMembers)
--- End of inner exception stack trace ---
at System.Management.Automation.LanguagePrimitives.ConvertViaNoArgumentConstructor.Convert(Object valueToConvert, Type resultType, Boolean recursion, PSObject originalValueToConvert, IFormatProvider formatProvider, TypeTable
backupTable, Boolean ignoreUnknownMembers)
at System.Management.Automation.LanguagePrimitives.ConvertViaNoArgumentConstructor.Convert(Object valueToConvert, Type resultType, Boolean recursion, PSObject originalValueToConvert, IFormatProvider formatProvider, TypeTable
backupTable)
at System.Management.Automation.LanguagePrimitives.ConvertCheckingForCustomConverter.Convert(Object valueToConvert, Type resultType, Boolean recursion, PSObject originalValueToConvert, IFormatProvider formatProvider, TypeTable
backupTable)
at System.Management.Automation.LanguagePrimitives.ConversionData`1.Invoke(Object valueToConvert, Type resultType, Boolean recurse, PSObject originalValueToConvert, IFormatProvider formatProvider, TypeTable backupTable)
at System.Management.Automation.LanguagePrimitives.ConvertTo(Object valueToConvert, Type resultType, Boolean recursion, IFormatProvider formatProvider, TypeTable backupTypeTable)
at System.Management.Automation.ParameterBinderBase.CoerceTypeAsNeeded(CommandParameterInternal argument, String parameterName, Type toType, ParameterCollectionTypeInformation collectionTypeInfo, Object currentValue)
--- End of inner exception stack trace ---
at System.Management.Automation.ParameterBinderBase.CoerceTypeAsNeeded(CommandParameterInternal argument, String parameterName, Type toType, ParameterCollectionTypeInformation collectionTypeInfo, Object currentValue)
at System.Management.Automation.ParameterBinderBase.BindParameter(CommandParameterInternal parameter, CompiledCommandParameter parameterMetadata, ParameterBindingFlags flags)
at System.Management.Automation.CmdletParameterBinderController.BindParameter(CommandParameterInternal argument, MergedCompiledCommandParameter parameter, ParameterBindingFlags flags)
at System.Management.Automation.CmdletParameterBinderController.BindParameter(UInt32 parameterSets, CommandParameterInternal argument, MergedCompiledCommandParameter parameter, ParameterBindingFlags flags)
at System.Management.Automation.CmdletParameterBinderController.BindParameters(UInt32 parameterSets, Collection`1 arguments)
at System.Management.Automation.CmdletParameterBinderController.BindCommandLineParametersNoValidation(Collection`1 arguments)
at System.Management.Automation.CmdletParameterBinderController.BindCommandLineParameters(Collection`1 arguments)
at System.Management.Automation.CommandProcessor.BindCommandLineParameters()
at System.Management.Automation.CommandProcessor.Prepare(IDictionary psDefaultParameterValues)
at System.Management.Automation.CommandProcessorBase.DoPrepare(IDictionary psDefaultParameterValues)
at System.Management.Automation.Internal.PipelineProcessor.Start(Boolean incomingStream)
at System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(Object input)
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(Object input)
at System.Management.Automation.PipelineOps.InvokePipeline(Object input, Boolean ignoreInput, CommandParameterInternal[][] pipeElements, CommandBaseAst[] pipeElementAsts, CommandRedirection[][] commandRedirections,
FunctionContext funcContext)
at System.Management.Automation.Interpreter.ActionCallInstruction`6.Run(InterpretedFrame frame)
at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
TargetObject :
CategoryInfo : InvalidArgument: (:) [Use-DisplayConfig], ParameterBindingException
FullyQualifiedErrorId : CannotConvertArgumentNoMessage,MartinGC94.DisplayConfig.Commands.UseDisplayConfigCommand
ErrorDetails :
InvocationInfo : System.Management.Automation.InvocationInfo
ScriptStackTrace : at <ScriptBlock>, <No file>: line 1
PipelineIterationInfo : {}
My mistake. I forgot that the module had other dependencies outside of the .dll.
Copy the .dll into your existing displayconfig module folder, replacing the existing .dll. Then import the module like you normally would (either with just the name, or a path to the .psd1).
Apologies for the delayed response. I encountered some challenges while trying to place the DLL in the appropriate folder due to a UAC permissions issue. After finally managing to copy it over, I'm now facing a new problem when attempting to import the module. Here’s the error I’m receiving:
PSMessageDetails :
Exception : System.IO.FileLoadException: Could not load file or assembly 'file:///C:\Program
Files\WindowsPowerShell\Modules\DisplayConfig\1.1.1\DisplayConfig.dll' or one of its dependencies. Operation is not
supported. (Exception from HRESULT: 0x80131515)
File name: 'file:///C:\Program Files\WindowsPowerShell\Modules\DisplayConfig\1.1.1\DisplayConfig.dll' --->
System.NotSupportedException: An attempt was made to load an assembly from a network location which would have
caused the assembly to be sandboxed in previous versions of the .NET Framework. This release of the .NET Framework
does not enable CAS policy by default, so this load may be dangerous. If this load is not intended to sandbox the
assembly, please enable the loadFromRemoteSources switch. See http://go.microsoft.com/fwlink/?LinkId=155569 for more
information.
at System.Management.Automation.Runspaces.InitialSessionState.Bind_LoadAssemblies(ExecutionContext context)
at System.Management.Automation.Runspaces.InitialSessionState.Bind(ExecutionContext context, Boolean updateOnly,
PSModuleInfo module, Boolean noClobber, Boolean local)
at System.Management.Automation.Runspaces.InitialSessionState.Bind(ExecutionContext context, Boolean updateOnly)
at Microsoft.PowerShell.Commands.ModuleCmdletBase.LoadModuleManifest(String moduleManifestPath,
ExternalScriptInfo manifestScriptInfo, Hashtable data, Hashtable localizedData, ManifestProcessingFlags
manifestProcessingFlags, Version minimumVersion, Version maximumVersion, Version requiredVersion, Nullable`1
requiredModuleGuid, ImportModuleOptions& options, Boolean& containedErrors)
TargetObject :
CategoryInfo : InvalidOperation: (:) [Import-Module], FileLoadException
FullyQualifiedErrorId : FormatXmlUpdateException,Microsoft.PowerShell.Commands.ImportModuleCommand
ErrorDetails :
InvocationInfo : System.Management.Automation.InvocationInfo
ScriptStackTrace : at <ScriptBlock>, <No file>: line 1
PipelineIterationInfo : {}
A quick google search indicates that the error is caused by the alternative data stream that indicates that the file was downloaded from the internet. Unblock the file with Unblock-File
, or right click -> Properties -> Unblock.
I had a moment of oversight where I unchecked the Unblock-File
box but forgot to relaunch ISE, which led to the same error reappearing.
Even after updating to the new DisplayConfig.dll
, I’m still encountering the following error:
writeErrorStream : True
PSMessageDetails :
Exception : System.Management.Automation.ParameterBindingException: The input object cannot be bound to any parameters for the
command either because the command does not take pipeline input or the input and its properties do not match any of
the parameters that take pipeline input.
TargetObject : MartinGC94.DisplayConfig.API.DisplayConfig
CategoryInfo : InvalidArgument: (MartinGC94.Disp...I.DisplayConfig:PSObject) [Use-DisplayConfig], ParameterBindingException
FullyQualifiedErrorId : InputObjectNotBound,MartinGC94.DisplayConfig.Commands.UseDisplayConfigCommand
ErrorDetails :
InvocationInfo : System.Management.Automation.InvocationInfo
ScriptStackTrace : at <ScriptBlock>, C:\Users\Ryan\Documents\Screens.ps1: line 5
PipelineIterationInfo : {0, 1, 0}
I also tried with this code:
$CurrentDisplayConfig = Get-DisplayConfig
$DisplayCount = (Get-DisplayInfo | Where-Object -Property Active -EQ $true).Count
if ($DisplayCount -ge 3) {
$CurrentDisplayConfig | Export-Clixml $home\5ScreenProfile.xml
$DisplayConfig = Import-Clixml $home\1ScreenProfile.xml
Use-DisplayConfig -DisplayConfig $DisplayConfig -UpdateAdapterIds
} elseif ($DisplayCount -eq 1) {
$CurrentDisplayConfig | Export-Clixml $home\1ScreenProfile.xml
$DisplayConfig = Import-Clixml $home\5ScreenProfile.xml
Use-DisplayConfig -DisplayConfig $DisplayConfig -UpdateAdapterIds
And I get this error:
PSMessageDetails :
Exception : System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.
at System.ThrowHelper.ThrowKeyNotFoundException()
at System.Collections.Generic.Dictionary`2.get_Item(TKey key)
at MartinGC94.DisplayConfig.API.DisplayConfig.UpdateAdapterIds()
at MartinGC94.DisplayConfig.Commands.UseDisplayConfigCommand.ProcessRecord()
TargetObject : MartinGC94.DisplayConfig.API.DisplayConfig
CategoryInfo : NotSpecified: (MartinGC94.Disp...I.DisplayConfig:DisplayConfig) [Use-DisplayConfig], KeyNotFoundException
FullyQualifiedErrorId : AdapterUpdateError,MartinGC94.DisplayConfig.Commands.UseDisplayConfigCommand
ErrorDetails :
InvocationInfo : System.Management.Automation.InvocationInfo
ScriptStackTrace : at <ScriptBlock>, C:\Users\Ryan\Documents\Screens.ps1: line 10
PipelineIterationInfo : {}
The parameter binding error that mentions PSObject is caused by it not converting the PSObject from Import-CliXML
into an actual object. There are 2 scenarios where this can happen: 1: You import the .dll directly instead of the .psd1 manifest. 2: You run Import-CliXML
before importing the module (I'm guessing this is what happened in this scenario).
As for the KeyNotFoundException error, I'd like to compare a working config with a broken one. So when you see it, send me the config file that causes this error as well as a fresh config file created with Get-DisplayConfig | Export-Clixml
.
I’m not quite following what’s happening in your scenarios. Here’s what I did:
Get-Module
Remove-Module DisplayConfig
Get-Module
# Copied the DLL
Import-Module "C:\Program Files\WindowsPowerShell\Modules\DisplayConfig\1.1.1\DisplayConfig.psd1"
Get-Module
However, now the KeyNotFoundException
error occurs every time, and the script no longer functions as expected.
I think I have found the cause of the KeyNotFoundException and have added a workaround. As far as I can tell it only happens if you happen to have an adapter that isn't used for any displays. Here's the compiled fix:
DisplayConfig.txt
Same procedure as before: Download, unblock, check hash and copy into the module folder. MD5 hash: 85B1666F451D651487D04A5FDFF3A575
Great news! Everything is working again. For completeness, here's the code I ran that succeeded without needing to recreate the screen profiles from yesterday's attempt:
$CurrentDisplayConfig = Get-DisplayConfig
$DisplayCount = (Get-DisplayInfo | Where-Object -Property Active -EQ $true).Count
if ($DisplayCount -ge 3) {
$CurrentDisplayConfig | Export-Clixml $home\5ScreenProfile.xml
$DisplayConfig = Import-Clixml $home\1ScreenProfile.xml
Use-DisplayConfig -DisplayConfig $DisplayConfig -UpdateAdapterIds
} elseif ($DisplayCount -eq 1) {
$CurrentDisplayConfig | Export-Clixml $home\1ScreenProfile.xml
$DisplayConfig = Import-Clixml $home\5ScreenProfile.xml
Use-DisplayConfig -DisplayConfig $DisplayConfig -UpdateAdapterIds
}
I also tried uising the pipeline, and that was successfull too!
$CurrentDisplayConfig = Get-DisplayConfig
$DisplayCount = (Get-DisplayInfo | Where-Object -Property Active -EQ $true).Count
if ($DisplayCount -ge 3) {
$CurrentDisplayConfig | Export-Clixml $home\5ScreenProfile.xml
Import-Clixml $home\1ScreenProfile.xml | Use-DisplayConfig -UpdateAdapterIds
} elseif ($DisplayCount -eq 1) {
$CurrentDisplayConfig | Export-Clixml $home\1ScreenProfile.xml
Import-Clixml $home\5ScreenProfile.xml | Use-DisplayConfig -UpdateAdapterIds
}
Thank you so much for resolving this issue and dedicating your time to help. Since the original problem was intermittent, would it be possible to keep this ticket open for 7 days to ensure consistent reliability? Or do you believe the solution has fully addressed the issue?
Excellent. I'm going to leave it alone for a week or two and if I don't hear anything from you I'll merge the changes into main and make a new release.
This has been fixed in the 2.0 release. I've already published it to the gallery so you can update to the latest version with Update-Module DisplayConfig
. I'd advice you to also update to this version as it includes some other fixes in addition to the ones you already have.
Issue Description: When running version 1.1.1, I encounter the following error about once per week, which requires me to manually adjust the screens and recreate the profiles.
Error Details:
Script Snippet:
Environment Details:
Frequency and Pattern:
Steps to Reproduce:
Temporary Workarounds:
Question: Why is this error occurring?