Various technical articles, IT-related tutorials, software information, and development journals
Monday, December 31, 2018
The Classes branch is in another hive
HKEY_CURRENT_USER contains several subkeys, but not all of them are stored in same Registry hive file. As expected, most are from ntuser.dat in the root of the user profile, but the Classes subtree gets a hive to itself. It's stored in AppData\Local\Microsoft\Windows\UsrClass.dat. Interestingly, that file doesn't exist under the default user profile. It appears that the classes hive is initialized empty when profiles are created.
Sunday, December 30, 2018
Markeen maps update 12/30
While re-playtesting all the Markeen-inspired maps I've finished so far, I noticed that one of them was utterly devoid of decorations and therefore a bit dull. So I sprinkled a few background arrangements around. I've also selected the next randomly-generated map to polish up. It's moderately messy, but I think it could become interesting:
Saturday, December 29, 2018
Markeen maps update 12/29
Today I finished up the seventh Markeen-inspired map.
Since the level is small and not very complicated, I used creatures to provide most of the challenge. On Hard mode, there are two areas with a Shelley dive-bombing in from both sides, which can get exciting.
Since the level is small and not very complicated, I used creatures to provide most of the challenge. On Hard mode, there are two areas with a Shelley dive-bombing in from both sides, which can get exciting.
Friday, December 28, 2018
Markeen maps update 12/28
With the sixth Markeen-inspired map all cleaned up, today I moved onto a new one. I selected this Markeen-generated starting point:
It's significantly smaller (less than 1/5 the area) and less messy than the previous level I polished. I've already fixed the foreground, determined the level's flow, added a couple secrets, and started adding sprites.
It's significantly smaller (less than 1/5 the area) and less messy than the previous level I polished. I've already fixed the foreground, determined the level's flow, added a couple secrets, and started adding sprites.
Thursday, December 27, 2018
Markeen maps update 12/27
Today I did some more playtesting and refining of the sixth Markeen-inspired map. There were a few tiling errors and point imbalances lingering, which were easy to correct. Weirdly, there are two versions of the solid platform top tiles - one set as front and one not. I'm not sure when one want might to use the non-front one; I had to replace it with the front version to make climbing look right.
I noticed that part of the space secret area was inaccessible, and when I initially fixed that, it became an inescapable trap. I worked around that by providing a one-way ride out of the hazardous part. (Riding the short lower loop back toward the long loop is deadly.)
When I tested on Hard mode, I found that I had exceeded the objects limit, so I had to take out a few infoplane items. But now the level could be considered done!
I noticed that part of the space secret area was inaccessible, and when I initially fixed that, it became an inescapable trap. I worked around that by providing a one-way ride out of the hazardous part. (Riding the short lower loop back toward the long loop is deadly.)
When I tested on Hard mode, I found that I had exceeded the objects limit, so I had to take out a few infoplane items. But now the level could be considered done!
Wednesday, December 26, 2018
Markeen maps update 12/26
I'm still working on the sixth Markeen-inspired map. Today I fixed the points distribution and most of the decorations. Previously I just connected all the areas together with secret passages, so most of the secrets weren't worth anything. Now there are appropriate rewards for all of them. The high-scoring point items that Markeen likes to scatter generously have been replaced with normal items.
To put the stars in the space background, I used the random mode of Abiathar's Tile Instance Remapper, though I had to use Contextual Help to remember how to operate it. Pipes are also cleaned up, but I still need to add background decorations for variety.
To put the stars in the space background, I used the random mode of Abiathar's Tile Instance Remapper, though I had to use Contextual Help to remember how to operate it. Pipes are also cleaned up, but I still need to add background decorations for variety.
Monday, December 24, 2018
Markeen maps update 12/24
I'm still working on the sixth Markeen-inspired map. Today I populated the level with creatures, put in the first sketch of the background, and fixed a bunch of solidity errors. Having the background really makes it look like it's coming together. I still need to add some background details to break up the monotony, fix the points, and clean up the pipe decorations.
Sunday, December 23, 2018
Markeen-based level project continues
Today I made a bit more progress refining the very large Markeen-generated level. All tiling errors have been corrected. All optional areas are now reachable through (quite a few) secret passages. The level can be won, though I've barely started placing creatures, so it's not very challenging yet. I am also yet to start on the background and pipe decorations.
The original level generated by Markeen |
The level as refined so far |
Saturday, December 22, 2018
Continuing the Markeen-inspired project
With some time on my hands now, I'm looking at the projects I left in progress. One was to create a Keen 5 level pack by starting from random Markeen-generated levels. I previously finished five levels but got stuck on a particularly large one. Yesterday and today I chipped away at it by fixing incomplete platforms and smoothing out the structure. That's nearly done now; the next phase is to make it playable.
Thursday, December 20, 2018
Mod demos on Keen Modding Live
I've been corresponding with another PCKF user about using the Keen Modding Live platform to host demos of mods. This will make it possible to embed demos on web sites like the KeenWiki. It will require some special considering from KML since uploading an entire mod doesn't fit the "user level plus standard episode package" model. I don't plan to deeply integrate the two kinds of players. The one for mod demos will probably not be linked at all on the main site; it'll just be for embedding.
Sunday, December 16, 2018
Creating an object with New-Object -Property won't preserve property order
A convenient way to create PowerShell objects with a given set of properties and values is to make a psobject with New-Object and supply the data in a hashtable literal to the -Property switch. But since hashtables are unordered, this will add the properties to the object in a jumbled order. For example, New-Object psobject -Property @{'a' = 1; 'b' = 2; 'c' = 3; 'd' = 4} produces:
c d b a
- - - -
3 4 2 1
If you're going to display the objects raw and want a nice order to their properties, you need to create a plain psobject and add the properties one at a time in the desired order with Add-Member.
c d b a
- - - -
3 4 2 1
If you're going to display the objects raw and want a nice order to their properties, you need to create a plain psobject and add the properties one at a time in the desired order with Add-Member.
Tuesday, December 11, 2018
Volley's retry mechanism works better than manual retry
I recently discovered Volley's setRetryPolicy feature to automatically retry requests that time out. I had previously been restarting the web request in the error callback up to a certain number of times, which worked but was messy. When I replaced that logic with Volley's mechanism, I found that my app became more likely to successfully get the data. In my testing, I've almost never needed to use the UI's retry feature anymore.
Saturday, December 8, 2018
onCreateOptionsMenu can be called before onCreate
Today I investigated a NullPointerException crash inside an activity's onCreateOptionsMenu. It was strange because the reference that was null is always initialized by onCreate. Apparently, onCreateOptionsMenu can be called first, though apparently that's not usually the case. I was able to trigger it by opening the activity in question, switching to the Settings app, revoking a permission from my app, and switching back to my app.
Friday, December 7, 2018
Volley can automatically retry timed-out requests
I had previously added some logic to my app to try a Volley request a few times if it failed due to a timeout. Today I learned that Volley can handle this automatically. Request objects have a setRetryPolicy method that takes a RetryPolicy, probably a DefaultRetryPolicy specifically.
Monday, December 3, 2018
Export-PfxCertificate won't export non-exportable private keys
Upon import to the certificate store, private keys can be marked as non-exportable. A commenter on Super User pointed out that PowerShell's Export-PfxCertificate cmdlet fails on such certificates. That makes sense, though I had hoped the non-exportability restriction was only enforced by UI. There are unofficial/unsupported ways to get around this by using tools like Mimikatz.
Friday, November 23, 2018
Putting items in the New context menu above the separator line
One user wanted to put a custom New submenu entry above the separator line (right under Folder and Shortcut) instead of among the normal entries. At first I assumed this was impossible and that the above-separator entries were hardcoded, but then I did some disassembling.
shell32.dll has a CNewMenu::_BeforeSeparator function that determines whether a given entry will be placed above the separator. It first checks the file type against a hardcoded list, but if that check fails, it also checks a flag in the NEWOBJECTINFO structure. It looks like those structures are set up by the _GetNewObjectInfoForKey structure, which calls _GetConfigFlags on the Config subkey of ShellNew to populate the field containing that flag. _GetConfigFlags checks for the existence of a bunch of entries, setting flags as it goes along. The Registry entry for the flag of interest is named BeforeSeparator.
So, placing a value named BeforeSeparator in the Config subkey under ShellNew for a New menu item will move that menu entry above the separator:
shell32.dll has a CNewMenu::_BeforeSeparator function that determines whether a given entry will be placed above the separator. It first checks the file type against a hardcoded list, but if that check fails, it also checks a flag in the NEWOBJECTINFO structure. It looks like those structures are set up by the _GetNewObjectInfoForKey structure, which calls _GetConfigFlags on the Config subkey of ShellNew to populate the field containing that flag. _GetConfigFlags checks for the existence of a bunch of entries, setting flags as it goes along. The Registry entry for the flag of interest is named BeforeSeparator.
So, placing a value named BeforeSeparator in the Config subkey under ShellNew for a New menu item will move that menu entry above the separator:
Thursday, November 22, 2018
SprintDLL - v1.2
Previously I had been planning to release a new version of SprintDLL after making it easier to call COM methods. But then I realized I'd been sitting on a pile of really useful progress that hadn't even be pushed to GitHub yet. So today I put all my changes in the GitHub repository, compiled a Release build, and published v1.2.
Wednesday, November 21, 2018
Sometimes it's easier to blow away the entire profile
Today I investigated an issue that only appeared for one user on one specific computer in a domain. In this organization, local user profiles don't contain any important information - all work is done on a centralized database. It would have been possible to hunt the problem down, but finding the specific Registry entry would have taken quite a while. So I simply deleted the affected user's local profile using the User Profiles section of the Advanced tab on the System Properties window. When the user next logged on, they received a fresh profile unaffected by the glitch.
(I did later find what caused the problem, but was not able to narrow it down to the Registry entry that got the problematic application stuck.)
(I did later find what caused the problem, but was not able to narrow it down to the Registry entry that got the problematic application stuck.)
Monday, November 19, 2018
Crashes in innocuous destructors can indicate memory corruption elsewhere
Earlier today I was debugging a double-free crash in a C++ application. A backtrace from gdb showed that the crash occurred in the destructor of a vector of ints, which should be foolproof - all the memory is managed by the vector. Debugging with Valgrind found some bad memory accesses in a completely different method. Fixing that issue solved the crash problem. If memory corruption is in play, bizarre things can happen.
Sunday, November 18, 2018
Windows Forms DPI scaling goes better on Windows 10 than Windows 7
I noticed that, despite all Windows Forms settings I tried for a .NET application, some forms would scale improperly on Windows 7. Specifically, some controls would be scaled, but the form wouldn't be, cutting off some controls. I couldn't even find a way to disable scaling entirely for the program. Happily, this issue doesn't seem to occur on Windows 10.
Monday, November 12, 2018
TestNG results in Android Studio can be stale if there are build failures
One student asked about a puzzling failure in a TestNG test in Android Studio. Their code seemed correct and in fact worked on my machine. Apparently there was a compilation failure somewhere in the project that of course prevented a new executable from being generated, causing the test suite to test an outdated version. Build | Rebuild Project (to do a recompile and show the Build tab) helped identify the problem.
Sunday, November 11, 2018
Mysterious checkstyle failures are sometimes fixed by reinstalling the plugin
Occasionally students have run into an "Unable to create listeners: formatters" error from checkstyle run in a Gradle task in Android Studio. I haven't yet figured out what actually causes it, but I have noticed that uninstalling and reinstalling the CheckStyle-IDEA plugin has fixed the problem for some people. It's also possible for Gradle to use checkstyle without having the IDE plugin installed - the task can still use checkstyle results, but the warnings won't be displayed in the code editor window.
Invalidate Caches / Restart can also help "no tests found in the package"
A couple students received "no tests found in the package" errors from TestNG in Android Studio even though the tests could compile successfully. Everything seemed to be in order, and an abridged version of the test results even appeared in the [build] tab. I asked the students to try a Gradle sync and a File | Invalidate Caches / Restart and that appeared to fix it. In general, mysterious problems tend to go away with a resync or cache clear.
Thursday, November 8, 2018
When test results vanish from IntelliJ
Several times, students have reported seeing some test results not show up in the Test results pane in IntelliJ or Android Studio. Only successful tests disappeared; all the ones that still appear are failed. This happens because of accidental pressing of the green check toggle button in the upper left of the results pane. When that button is not pushed in, passing tests are hidden.
I can see why multiple people have been confused - it's really easy to accidentally toggle that button when trying to expand the tests list view (the dropdown and button are really close) and not realize what happened.
I can see why multiple people have been confused - it's really easy to accidentally toggle that button when trying to expand the tests list view (the dropdown and button are really close) and not realize what happened.
Monday, November 5, 2018
Daylight Savings Time and hardcoded date formats
This morning I woke up to discover that my app was broken. It was unable to parse date-times from the API it uses because of a format mismatch. When I originally wrote that bit of code, I saw that there was a timezone fragment (containing the offset from UTC) that was always the same, since the app is for a transportation system within one time zone. But when Daylight Savings Time ended, the offset shifted by an hour, making the strings not match the literal timezone fragment. I suppose the moral of the story is to be very careful with timezones because DST transitions will wreak havoc.
Sunday, November 4, 2018
Android Studio sometimes destroys run configurations
I've observed Android Studio deleting all the run configurations when the project is imported or (sometimes) opened, for unclear reasons. One solution I've seen used is to keep the run configurations in a separate folder and copy them over to .idea/runConfigurations in a Gradle task set as a finalizedBy on every other task. Crude but effective.
Saturday, November 3, 2018
Writing PSThar scripts that compile C# code
PowerShell run scripts inside the PSThar extension for Abiathar automatically have access to Abiathar/FleexCore2 features. But if that PowerShell code tries to use Add-Type to compile and run C# code, it may encounter errors about not being able to find a type or namespace. The solution is to provide the three Abiathar assemblies to Add-Type:
For most operations, only the first assembly (the Abiathar extension API) should be necessary. The second is FleexCore2, which is needed if using that directly instead of through Abiathar's API. The Abiathar assembly itself shouldn't usually need to be referenced; relying on internal details will lead to fragile scripts.
Add-Type $code -ReferencedAssemblies ([Abiathar.Interop.IAbiatharState].Assembly, [FMod.GalaxyLevels].Assembly, $Abiathar.GetType().Assembly)
For most operations, only the first assembly (the Abiathar extension API) should be necessary. The second is FleexCore2, which is needed if using that directly instead of through Abiathar's API. The Abiathar assembly itself shouldn't usually need to be referenced; relying on internal details will lead to fragile scripts.
Friday, November 2, 2018
TestNG's assertEquals will iterate an iterable
Today I saw a confusing test failure message from TestNG. I was using assertEquals to see if two objects were the same and expected the check to use reference equality, since the objects' class didn't override equals. Instead there was a message about some other objects not being equal and an iterator differing at a certain position. Since I hadn't overriden equals to examine objects referred to, I was confused for a moment until I remembered that the class was iterable. Apparently TestNG checks for that and will do an item-by-item comparison if possible. To just check for reference equality, I switched to assertSame.
Saturday, October 27, 2018
Gradle tasks as before-launch tasks won't stop TestNG if they fail
An Android Studio project I'm involved in uses a TestNG run configuration to run some tests. To make sure the tests operate on the newest version of the code, the run configuration has Gradle run a "build" task first to compile just the one module we test. We've noticed that even if the Gradle-induced build fails, the test suite goes ahead trying to test whatever compiled artifacts are around and Android Studio switches to the Test tab in the output pane, making it difficult to see that something strange is going on. I have not found a way around this that only compiles a specific module, but if that wasn't a concern, using a Build before-launch task would be fine.
Wednesday, October 24, 2018
If the checkstyle Gradle task fails, reinstall the checkstyle plugin
Some students have been encountering strange Gradle build failures in Android Studio that mention various checkstyle-related failures. Some of these are caused by clearly fixable things like severe syntax errors that prevent checkstyle from parsing the file, but others are more puzzling. One solution that's worked for some students is to uninstall the checkstyle plugin in File | Settings and reinstall it. I personally don't even have the plugin installed and the checkstyle Gradle task works fine.
Sunday, October 21, 2018
TestNG "cannot find class in classpath" can mean the tests didn't compile
I've recently been trying to help students troubleshoot issues with running TestNG tests in Android Studio. A common error is "cannot find class in classpath", referring to the class containing the tests. This often indicates that there's a problem causing the compilation of the test suite to fail. More specific error messages might be visible on the [build] tab of the lower pane where the test results appear.
Thursday, October 18, 2018
Allowing the build to complete even with checkstyle errors
The checkstyle plugin for IntelliJ and Android Studio can trigger a build failure if style violations are detected. This appears to be the default behavior for some versions of the tool. The build can be allowed to proceed anyway by changing the severity setting to "warning" instead of "error." Add this line inside the <module name="Checker"> element:
<property name="severity" value="warning" />
<property name="severity" value="warning" />
Monday, October 15, 2018
IML files from Gradle projects don't need to be in version control
IntelliJ and Android Studio projects include .iml files that seem to represent the project or module. However, that doesn't mean they need to be checked into version control repositories - Gradle projects can automatically generate them when the repository is first imported onto each computer. This JetBrains support article lists all the IDE metadata files that should or should not be in version control.
Friday, October 12, 2018
If IntelliJ mysteriously can't find a package, force a Gradle resync
I'm a course developer for an introductory CS class that uses Java in IntelliJ with the Gradle build system. A semi-common problem students encounter is that the IDE sometimes decides that it can't find the test framework package, so the test suite can't be run. Invalidating IDE caches with Invalidate Caches / Restart doesn't always work. What I have found that works is...
- Open the build.gradle file
- Type some nonsense into it so that the "sync now" notification appears
- Press "sync now" - this should fail due to the nonsense
- Delete the nonsense
- Try the sync again - this should refresh everything
Thursday, October 4, 2018
Tharify package for Keen 3
The PCKF community is continuing its relatively new tradition of minimalistic community level packs. Levels are very constrained in what tiles they may use, making design/gameplay the focus as opposed to aesthetics. The first time this kind of pack was done, some people had technical issues with the usual Vorticons editors, so I whipped up a development environment based on Abiathar and Tharify. Abiathar is an unusual choice for this application because it's a Galaxy editor - I only suggested it as a last resort if no Vorticons editor would run - but apparently it worked well for people, and they requested a Keen 3 setup for this pack. So yesterday I put it together. It was a fairly simple matter of tweaking the Tharify-invoking batch file to work on Keen 3 and once again placing all allowed tiles in the level for people's convenience.
Monday, September 17, 2018
Why is Y the Abiathar hotkey for Grid?
Here's a little tidbit of Abiathar history.
The keyboard shortcut to toggle the visibility of the grid is Y, not G. Before version 1.0, the menu item to start a new project was called Gather because project creation involves gathering up the several files that represent a level set. (The window to do that, which survived the entire 1.x series and was only retired in 2.0, was internally called the Dependency Gatherer. I don't recall whether or for how long that name was displayed.) The first letter of Gather is G, so that was the hotkey. When I added the grid, I figured that Y was a decent choice because it's close to G on the keyboard.
Feedback I received on the pre-release version informed me that the "gather" terminology was confusing, so I changed the menu item's text to New and the hotkey to N. The grid hotkey apparently didn't cross my mind when I was making that change because it didn't get changed to take advantage of the now-free G.
In version 2.0, the two tools that had no hotkey were given shortcuts. G and H went to the Tile Instance Remapper (effectively a find-and-replace tool) and Tile Instance Locator (find), respectively. H was for "highlight" (the TIL added a highlight to the desired tile) and G was next to it (replace is a related operation to find). Later down the line, the Tile Instance Locator tool became the Find Highlight view mode, but the TIR is still a tool with hotkey G.
The keyboard shortcut to toggle the visibility of the grid is Y, not G. Before version 1.0, the menu item to start a new project was called Gather because project creation involves gathering up the several files that represent a level set. (The window to do that, which survived the entire 1.x series and was only retired in 2.0, was internally called the Dependency Gatherer. I don't recall whether or for how long that name was displayed.) The first letter of Gather is G, so that was the hotkey. When I added the grid, I figured that Y was a decent choice because it's close to G on the keyboard.
Feedback I received on the pre-release version informed me that the "gather" terminology was confusing, so I changed the menu item's text to New and the hotkey to N. The grid hotkey apparently didn't cross my mind when I was making that change because it didn't get changed to take advantage of the now-free G.
In version 2.0, the two tools that had no hotkey were given shortcuts. G and H went to the Tile Instance Remapper (effectively a find-and-replace tool) and Tile Instance Locator (find), respectively. H was for "highlight" (the TIL added a highlight to the desired tile) and G was next to it (replace is a related operation to find). Later down the line, the Tile Instance Locator tool became the Find Highlight view mode, but the TIR is still a tool with hotkey G.
Sunday, September 16, 2018
Retrieving old file versions from Git in IntelliJ
IntelliJ can integrate nicely with Git for version control. One very nice feature is the ability to see and roll back to old versions of specific files. Right-clicking a file in the Project pane and choosing Git | Show History produces a window with the commits affecting that file. Those commit entries can be double-clicked to see the file as it was at that commit. The version can be restored by right-clicking the commit and choosing Get.
Thursday, September 13, 2018
Artificial Intelligence Stack Exchange gets a sponsor
Last week, Artificial Intelligence Stack Exchange got a nice surprise announcement from Stack Exchange the company. IBM signed on as a sponsor, so AI.SE got a really neat new design, plus a "sponsored by IBM" logo in the upper right. Apparently IBM's Win with AI conference mentioned our site, so we might get an influx of traffic. Other than the design and help with promotion, site workings will proceed as normal.
Tuesday, September 11, 2018
Showing a message to the interactive session
One user needed a program in a background session (like from a service) to display a simple message to the currently logged-in user. If no fancy UI or choice-making features are required, the msg command will work great. It can use a session name to specify the session that receives the message, so you don't even need to determine the account name of the interactive user:
msg console Your message here
msg console Your message here
Monday, September 10, 2018
getLastLocation isn't necessarily the current location
My Android app uses the fused location provider client's getLastLocation method to get the user's location when needed. Usually this works great. I have noticed, however, that it sometimes gets a stale location (where the user was during a previous fetch). When having the exact, most up-to-date location is necessary, I use requestLocationUpdates and have found that this works well.
Sunday, September 9, 2018
Use the activity context for UI-related operations
Many Android API calls require a Context. Activity objects serve as contexts, but a different context can be acquired with getApplicationContext(). One might be inclined to call that function from inside an inner class when a Context is necessary, but when doing anything UI-related, using the enclosing activity class's this is a much better idea. Using the application context is likely to use a different theme than the activity, which can make the elements of inflated layouts look weird. In my application, that made text views use the wrong color and become extremely difficult to read on some (but not all!) devices.
Saturday, September 8, 2018
Put a unique constraint on columns that should be unique
One of my applications retrieves data from a third-party service and caches it in an SQL database. The external service's responses use strings as the ID of certain records, but in my database those records are also given a numeric primary key. When correlating other responses for different record types from the third-party service, I often need to get the numeric ID corresponding to the service's text ID. This is a very simple subquery, but it broke recently due to an unfortunate concurrency issue. The text-ID-containing items got inserted into the table twice, so the subquery started returning multiple values, which removed the ability to put the result into a single field and therefore broke other parts of the system.
The duplication could have been prevented with transactions (which I have now put in place for that component - others already used transactions), but the cascade failure could have even more easily been prevented by putting a unique constraint on the textual ID column.
The duplication could have been prevented with transactions (which I have now put in place for that component - others already used transactions), but the cascade failure could have even more easily been prevented by putting a unique constraint on the textual ID column.
Wednesday, September 5, 2018
Updating the Ubuntu installation in Bash on Ubuntu on Windows
Recently I had some trouble installing a package inside the Bash on Ubuntu on Windows environment. I found that I needed to update my Ubuntu distribution. As I read in this Super User answer, that can be accomplished with:
sudo do-release-upgrade -d
In the process, there will probably be some prompts about replacing altered configuration files. I found that choosing any option other than a simple yes or no (the ones that let you explore the differences between the two config versions) makes it extremely easy to get stuck and unintentionally terminate the whole procedure. When I accidentally did that, I found that just running the upgrade command again mostly picked up from where it left off.
sudo do-release-upgrade -d
In the process, there will probably be some prompts about replacing altered configuration files. I found that choosing any option other than a simple yes or no (the ones that let you explore the differences between the two config versions) makes it extremely easy to get stuck and unintentionally terminate the whole procedure. When I accidentally did that, I found that just running the upgrade command again mostly picked up from where it left off.
Tuesday, September 4, 2018
Excluding certain file extensions from a recursive Copy-Item
One user was using PowerShell's Copy-Item cmdlet to copy an entire directory structure and wanted to avoid copying certain file extensions. Copy-Item has a -Filter parameter, but more conveniently it has an -Exclude parameter that takes a simple array of file name patterns to skip. So using -Exclude '*.mp3', '*.exe' will skip copying MP3 and EXE files.
Monday, September 3, 2018
findstr supports wildcards in the form of regular expressions
One user wanted to use a batch script to search for a wildcard-enabled pattern in a file. findstr can do this, but with a somewhat different wildcard format. A period matches any single character (the equivalent of a question mark in usual batch wildcard style). Putting an asterisk after the period to make .* matches any run of characters, the equivalent of an asterisk in the usual batch style. Supplying the /c switch disables regex support and uses the pattern as a literal string. The end of the output of findstr /? has a quick regex reference.
Sunday, September 2, 2018
Doing a rebuild might work even if normal runs cause bizarre compile errors
My Android app project has developed a bizarre tendency. When I do a normal run from Android Studio, the build usually fails with an error about being unable to find a class that clearly exists in the project. But doing a Build | Rebuild Project works perfectly, and then a run will successfully deploy the result of that compilation. The downside of this is that a full rebuild takes a while.
Exporting an EFS key requires being logged in as that user
One user wanted to move an EFS certificate/key to a different Windows installation. They were interested in taking the key out of the offline system, but this would be very difficult because of how the keys are stored. The key material is itself encrypted by another key derived from a variety of things. To export the key, one can use certmgr.msc or, if it doesn't want to export the private key, do a bit of PowerShell. Unfortunately, both methods require being logged in as the key's owner on the machine.
Wednesday, August 29, 2018
Don't try to store references from within FragmentPagerAdapter.getItem
In my app, I have some activities with tab views managed by FragmentPagerAdapters that need to receive notifications from the containing activity. My first strategy was to store a reference to them in an instance variable on the activity class from within the getItem method, between creating the object and returning it. However, this leads to a crash if the screen is rotated. The activity is recreated, but the fragments don't need to be reinstantiated, so getItem isn't called for the rotated activity and the references remain null.
A better way is to override the instantiateItem method on FragmentPagerAdapter. Calling the superclass's implementation will get or create the fragment object, which can be stored in a reference and then returned.
A better way is to override the instantiateItem method on FragmentPagerAdapter. Calling the superclass's implementation will get or create the fragment object, which can be stored in a reference and then returned.
Saturday, August 25, 2018
Android static fields can be reset if the app is backgrounded
An Android app I'm working on uses static fields to store data that it downloads at launch. This was working great during development, but after release I observed a lot of null-reference crashes when accessing those static fields. When Android needs to free memory, it terminates processes belonging to apps that aren't currently visible to the user; when the user resumes the app, Android restores the stack of activities, but the process itself is new. Therefore, static fields get reset.
To fix my problem, I wrote a function that checks whether the static data is gone, and if so, launches the activity responsible for downloading the data. I tweaked all activities that require the static data to call this function before doing anything in onCreate or onStart, bailing out if the function indicates that the data needs to be reloaded. An argument to the intent used to relaunch the loading activity indicates whether the loader should just finish() instead of starting a new instance of the main activity.
To fix my problem, I wrote a function that checks whether the static data is gone, and if so, launches the activity responsible for downloading the data. I tweaked all activities that require the static data to call this function before doing anything in onCreate or onStart, bailing out if the function indicates that the data needs to be reloaded. An argument to the intent used to relaunch the loading activity indicates whether the loader should just finish() instead of starting a new instance of the main activity.
Simulating Android background process termination
To free up memory, Android sometimes terminates the processes of apps that are in the background. Therefore, apps need to be able to restore from saved state. To test this from Android Studio:
- Open the app in a connected device or emulator
- Press the home button to put the app in the background
- In the Logcat window in Android Studio, press the Terminate button on the left (might be hidden in a pop-out menu)
- Resume the app from the device's Overview menu
Monday, August 20, 2018
New apps will take a little while to appear in Play store search
Today I published my first app to the Google Play store. Despite the status in the Play Console showing as "published," I couldn't find it in the Play store search even though the direct link to the listing from the Play Console worked. Apparently it can take a little while for changes to appear in the search. After a couple hours, I got an e-mail about the IARC rating certificate. The next time I tried, I was able to find my app in the search - not sure if that's the specific trigger though.
Sunday, August 19, 2018
Android APK signing configurations need to be assigned to the build type too
Today I needed to generate a signed APK to test an Android app in the release configuration. Setting the build variant to "release" and trying to run produced an error about needing to sign the APK. So I created a signing configuration on the Signing tab of the Project Structure dialog (where the error message's Fix button took me), but the problem persisted. Apparently the signing configuration also has to be assigned to the build type; this is not done automatically. The build variants listed on the Built Types tab have a Signing Config field - setting that to the newly created Signing entry does the job.
Friday, August 17, 2018
Nested ListViews may not size themselves correctly
One screen in the Android app I'm working on involves a nested ListView. The outer ListView contains instances of a fragment that involves another ListView. Originally I wanted the inner ones to make themselves as large as necessary to contain all their items, but the usual wrap_content height only made them tall enough to show the first item. Nested scrolling didn't seem to work, and even if it did, that would be a poor user experience. Apparently nesting ListViews only displays reasonably if the inner ones have their height specified. I did that by measuring each item and adding up the heights as in this SO answer. One thing to note is that the measuring didn't seem to take into account line-wrapped text, so I had to prevent wrapping by applying android:maxLines="1" to the inner TextViews.
Thursday, August 16, 2018
ConvertTo-Json has a very shallow -Depth by default
PowerShell's ConvertTo-Json cmdlet can turn a hierarchy of objects into a JSON document. By default, though, objects just two reference layers down will be stringified instead of serialized to JSON. This can be adjusted with the -Depth parameter.
FMod - IMF import crash fix
Today I received a bug report from an Abiathar user. He found that importing a certain IMF song using the Song Mappings dialog caused Abiathar to crash. Investigating, we found that just the specific IMF file was corrupt - its header specified far more notes than the file actually contained, so Abiathar unexpectedly hit the EOF when trying to load it all. So Abiathar didn't have so much an IMF handling problem as an error handling one, but obviously it's bad to bring down the entire application when confronted with invalid resources, so I added the appropriate error handling.
Tuesday, August 14, 2018
OpenStreetMap can put addresses on nodes, ways, or relations
I've been fiddling with OpenStreetMap (specifically Overpass Turbo) to get the locations of the addresses/buildings in a region. At first it looked like all the buildings were "ways," which are collections of nodes (points) and can represent buildings as well as roads. So I looked for tags on the ways to identify the structures. Then I noticed that my query didn't pick up some buildings - especially ones that have interior borders (holes) - and found that there are also "relations." Relations seem to be collections of ways, one way per border. They can have the tags to represent buildings too. After I got all that worked out, I noticed that my query was still missing a few places. Some buildings were entered as nodes sitting in the middle of the area that borders the actual building, with tags on that individual node.
Sunday, August 12, 2018
Android ListViews might cut off the last item
I'm working on an Android app and trying to smooth out the UI. One issue I've noticed is that ListViews sometimes get cut off at the bottom of the screen. In my project, this seems to only happen when they fill the entirety of a fragment that's presented as a tab in a tabbed activity. I hear that switching to a RecyclerView fixes this, but filling items in a RecyclerView is more complicated than in a plain ListView. Adding some padding to the bottom of the list (android:paddingBottom="30dp", with more padding if the items are thicker) works as a workaround, but I'm still looking for something more elegant that always works.
Saturday, August 11, 2018
When windows become unresponsive after another program goes fullscreen
I've noticed something interesting on my current installation of Windows 10 that I don't remember seeing before. When one application (DOSBox specifically) went fullscreen, windows belonging to other programs stopped responding to mouse movements or clicks. They can be made normal again by pressing Windows+Down then Windows+Up, thereby restoring and re-maximizing them.
Friday, August 10, 2018
Rearranging the language list with PowerShell
The region/language section of the Settings app allows reordering the language preferences by using the up and down arrows that appear when a language entry is selected. The order can also be changed with PowerShell. The Get-WinUserLanguageList cmdlet retrieves a list. The items can be swapped just like in any other collection, then the revised list can be made the new order with Set-WinUserLanguageList.
Tuesday, August 7, 2018
NTRights will adjust privilege lists even when set by Group Policy
Group Policy Objects can define privilege lists in the User Rights Assignment section. When configured with Group Policy, these lists cannot be changed with the Local Security Policy snap-in on the client machine. I noticed, however, that the NTRights utility (from the Windows Server 2003 Resource Kit) is perfectly happy to adjust the privilege lists anyway. The change is reflected in the Local Security Policy editor, which still won't allow editing the list. More importantly, the change actually takes effect (at the next logon of affected users, like usual). The caveat is that the domain's policy will be reapplied if the client is rebooted or if the responsible GPO is edited.
Monday, August 6, 2018
Forwarding the current cmdlet's arguments to another
One user had a cmdlet that took several parameters (some optional) and wanted to pass all the cmdlet's parameters to a different cmdlet. Their first approach was to use Invoke-Expression. While that is a good way to run a dynamically generated command, there is a simpler method in this case. The call operator supports real bound parameters (not stringified), $PSBoundParameters is a hashtable of the current cmdlet's parameters, and splatting allows using a hashtable as the parameters to a cmdlet. These things together make for a very elegant command:
& $cmdletName @PSBoundParameters
If the calling cmdlet has extra parameters relative to the callee, $PSBoundParameters can be .Clone()'d, then the clone can be edited down and passed on.
& $cmdletName @PSBoundParameters
If the calling cmdlet has extra parameters relative to the callee, $PSBoundParameters can be .Clone()'d, then the clone can be edited down and passed on.
Sunday, August 5, 2018
Keen Modding Live - Adjusted version tree in Abiathar Live Studio
My original rendition of the version selection dialog for Abiathar Live Studio assumed that an approved/visible version could never be derived from an unapproved/hidden one. Its call to the "get versions" API supplied the "approved versions only" switch when appropriate, so it didn't even get the hidden versions. (I might either remove that API switch or change it so that a useful tree can be constructed from its response.) I adjusted Abiathar Live Studio's tree generation code to request all versions of the level but cut hidden ones out of the lineage while keeping relationships between approved versions intact, just like the level info page on the web.
Friday, August 3, 2018
Keen Modding Live - Comments unattached to versions
I decided that it would be good to allow comments on Keen Modding Live comments unattached to specific versions, that is, comments made directly from the level page as opposed to a version player page. Once comment notifications (or any kind of notifications at all) become a thing, people will often want to reply to comments on their levels, so the page they get directed to should have reply functionality. The version player page is very heavy (several MB), so I won't want to direct users to a section of that from comment notifications.
So I tweaked the API and database schema to allow leaving the version field null. The timestamp is still recorded, of course, so attentive readers will be able to see whether a comment is obsolete (due to a new version being uploaded) by looking at the date it was posted.
So I tweaked the API and database schema to allow leaving the version field null. The timestamp is still recorded, of course, so attentive readers will be able to see whether a comment is obsolete (due to a new version being uploaded) by looking at the date it was posted.
Thursday, August 2, 2018
Stopping a user from deleting a file they own
One person wanted to stop a specific user from deleting their own files from a specific directory while still allowing them to create and edit files. Permissions to delete files are interesting in that either one of these will be sufficient:
- Delete permissions on the file to be deleted
- Delete-child permissions on the folder that contains the file to be deleted
Therefore, the permissions on the containing folder and the file need a delete right denied to the restricted user. Since the user owns any files they create and could just change the ACL, an OWNER RIGHTS rule is needed to suppress the default grants.
Tuesday, July 31, 2018
PSThar's ForEach-LevelTile command creates a new scope
I noticed while writing PSThar scripts, most recently this one, that the ForEach-LevelTile cmdlet (abbreviated %t) has different scoping behavior than the normal PowerShell ForEach-Object (abbreviated %). The script block that runs for each level tile is more of a distinct scope from the parent than is the script block run for each object by %. Using variables from outside the %t block in the block pretty much requires explicitly scoping those variables with script: every time they're mentioned, otherwise the variable name inside the block doesn't refer to the variable of the same name outside the block. This is very inconvenient and I will look into tweaking PSThar to fix it.
Monday, July 30, 2018
Keen Modding Live - Level comments API
Today I started on a comment system for Keen Modding Live. For logged-in users, comments will appear at the bottom of the player page and possibly the level information page; I haven't decided yet. Comments record what version they were created on (to reduce confusion from level changes across versions) but the same set will appear for all versions' player pages, since not all changes will necessarily affect all comments. I'm currently planning on a flat chronological list of comments without any fancy threads. Replies (to notify a given comment's author) could still be possible.
So far I've written the four necessary API endpoints for comment support: add, edit, delete, and list. At the moment I'm not storing revision history, but the time of last edit or deletion is recorded.
So far I've written the four necessary API endpoints for comment support: add, edit, delete, and list. At the moment I'm not storing revision history, but the time of last edit or deletion is recorded.
Sunday, July 29, 2018
Setting the hotkey for context menu items created with the Registry
One user was experimenting with creating file context menu entries with the Registry and encountered some interesting behavior about the hotkeys for activating them with the keyboard. If multiple entries have the same hotkey (shown with the underline under a letter), pressing that letter cycles the focus through them; if the hotkey is unique, pressing it immediately activates the corresponding entry.
The hotkey for a custom entry can be set by putting an ampersand before an instance of the desired hotkey letter. Interestingly, for that to work properly, the entry's name has to be set in the default value of the entry's Registry key, not just with the key name. If an ampersand is included in the key name, the hotkey letter will be underlined, but pressing the key will do nothing.
The hotkey for a custom entry can be set by putting an ampersand before an instance of the desired hotkey letter. Interestingly, for that to work properly, the entry's name has to be set in the default value of the entry's Registry key, not just with the key name. If an ampersand is included in the key name, the hotkey letter will be underlined, but pressing the key will do nothing.
Saturday, July 28, 2018
Start-Process with -Credential specified may have problems with interactive programs
One user encountered a problem while using PowerShell to start a command prompt as another user. They used the -Credential parameter to pass a credentials object (acquired with Get-Credential) to the Start-Process cmdlet, but found that the resulting command prompt didn't accept keyboard input. I don't know why it's doing that, but I do have a workaround: use the .NET Framework's Process.Start method directly:
[System.Diagnostics.Process]::Start('cmd', $creds.UserName, $creds.Password, '')
[System.Diagnostics.Process]::Start('cmd', $creds.UserName, $creds.Password, '')
Friday, July 27, 2018
Keen Modding Live - Approve/disapprove versions
Today I made the approve/disapprove toggle in Keen Modding Live's version tree actually do something. First I added an "edit version" API endpoint that currently only allows changing the approved status. Originally I was planning on having it be impossible for an approved version to be derived from an unapproved one, but then I thought it made sense to be able to hide uninteresting/broken intermediates. So I adjusted the grid preparer to show descendants of hidden versions as direct descendants of the hidden versions' parents if disapproved versions are not shown. Then, of course, I wired up the toggle link to the API endpoint.
An unapproved version showing in the chain leading to the current version |
The same chain with the unapproved version hidden |
Thursday, July 26, 2018
Keen Modding Live - Version tree
Today I wrote a bit of JavaScript to render the version tree for a given Keen Modding Live level. It's still rough from a UI perspective, but it exists:
Lower records are newer/derived versions. The two at the very bottom are both derived from the one with the green box. (Connecting lines/arrows are not yet implemented.) The green box indicates the current version. Faded out records are "unapproved" versions that only appear if "show all" is checked. The "fork" link goes to the upload-level page with that version preselected. "Set current" sets the level's current/default version to that one. "Approve"/"disapprove" toggles whether the version is approved - this is not yet implemented.
Lower records are newer/derived versions. The two at the very bottom are both derived from the one with the green box. (Connecting lines/arrows are not yet implemented.) The green box indicates the current version. Faded out records are "unapproved" versions that only appear if "show all" is checked. The "fork" link goes to the upload-level page with that version preselected. "Set current" sets the level's current/default version to that one. "Approve"/"disapprove" toggles whether the version is approved - this is not yet implemented.
Wednesday, July 25, 2018
Keen Modding Live - Starting the version tree
Today I started working on the version tree: the bit of UI on the Keen Modding Live level info page that should show the versions of the level and the derivation relationships between them. This is turning out to be quite difficult to do in HTML. The approach I've settled on is to first translate the version information into a two-dimensional array like this:
A
B b
C c
D
("A" is the base version, the main chain of work is all the capital letters, "b" is an alternative version to "B" also forked off of "A", and "c" is an alternative to "C" still derived from "B".) I think I have the grid part done; now I need to render it into an HTML table and make it look nice/understandable.
A
B b
C c
D
("A" is the base version, the main chain of work is all the capital letters, "b" is an alternative version to "B" also forked off of "A", and "c" is an alternative to "C" still derived from "B".) I think I have the grid part done; now I need to render it into an HTML table and make it look nice/understandable.
Tuesday, July 24, 2018
Keen Modding Live - More info on player page
Previously, Keen Modding Live had no way to get from a player page to the level's information page. So today I populated the player page with some useful information: level description, version comment, level owner, version author (if different than the level owner), and version creation time. While I was at it, I made the player page return an HTTP error and bail out if the level or version ID can't be found instead of spewing dozens of PHP errors. The link that goes back to the level information page reads "see other versions of this level," so I suppose the next thing to do is actually render the version tree on the level page.
Monday, July 23, 2018
Keen Modding Live - Level info page
I've recently continued fleshing out the Keen Modding Live level information page. It now shows the level's name, author, description, creation time, episode, and zone. If the current user owns the level, a "delete level" link appears that, unsurprisingly, deletes the level. Administrators have a link that sets the level's owner to the ID Software pseudo-user. I plan to use that to upload the original levels while making it clear that they're not mine. While testing, I noticed that the delete-level API endpoint is broken: it did not in fact delete the level. There was an issue with foreign key dependencies in the database; setting the level's current version ID to null before cascade-deleting it and the level versions fixed the problem.
Sunday, July 22, 2018
Adding the Backup Operators group to a Home edition
Home editions of Windows have fewer built-in groups than Pro editions. Some of those missing groups, however, still have their SID appear in various default ACLs and privilege lists. One such group is Backup Operators.
Creating a new group with the same name as a missing built-in group won't have any effect because the SID will be different. There is also no way to specify the SID of a new security principal... without editing the SAM database in the Registry. To access the SAM of a live system, we need to open the Registry Editor as the SYSTEM account. That can be accomplished with PsExec:
psexec -s -i regedit
Information on built-in groups is stored under this key:
HKEY_LOCAL_MACHINE\SAM\SAM\Domains\Builtin\Aliases
It has one subkey for each group. The subkey names are the zero-padded eight-digit hexadecimal representation of the last fragment of the group's SID. The Backup Operators SID is S-1-5-32-551, so its subkey is 00000227. The Aliases subkey also has a Names subkey, which likewise has a subkey for each built-in group. This time, the subkey names are the group names. The default value of each name subkey is an empty binary value - what matters is the type code of the default value, which stores the last SID fragment.
Both keys are necessary for the group to work properly. They can be exported from a Windows installation that has a desired group and imported into one that doesn't. The group will appear after a reboot.
Creating a new group with the same name as a missing built-in group won't have any effect because the SID will be different. There is also no way to specify the SID of a new security principal... without editing the SAM database in the Registry. To access the SAM of a live system, we need to open the Registry Editor as the SYSTEM account. That can be accomplished with PsExec:
psexec -s -i regedit
Information on built-in groups is stored under this key:
HKEY_LOCAL_MACHINE\SAM\SAM\Domains\Builtin\Aliases
It has one subkey for each group. The subkey names are the zero-padded eight-digit hexadecimal representation of the last fragment of the group's SID. The Backup Operators SID is S-1-5-32-551, so its subkey is 00000227. The Aliases subkey also has a Names subkey, which likewise has a subkey for each built-in group. This time, the subkey names are the group names. The default value of each name subkey is an empty binary value - what matters is the type code of the default value, which stores the last SID fragment.
Both keys are necessary for the group to work properly. They can be exported from a Windows installation that has a desired group and imported into one that doesn't. The group will appear after a reboot.
Most privileges will be removed by UAC
Extra privileges can be assigned to non-administrative users with User Rights Assignment or the NTRights utility. However, UAC will remove all privileges from the user's token except the standard five (shutdown, change-notify, undock, increase working set, and change timezone). Just like with normal administrator accounts, getting the extra privileges requires elevation. If a user is not a member of any UAC-protected groups, though, the UAC elevation dialog will prompt for the credentials of an administrator instead of elevating the current account, making it impossible to take advantage of the privileges. It may also be necessary to be interactively logged on, as opposed to started in a different user's session with "run as different user."
Friday, July 20, 2018
DCOM configuration permissions are Registry key permissions
The DCOM Configuration section of the Component Services snap-in allows modifying the permissions of COM applications. The ability to change the permissions on the Security tab is governed by the ACL in the Configuration Permissions section. Even going into the Advanced dialog on the ACL editor, though, doesn't allow administrative ownership-taking, so even administrators can't always adjust the permissions.
The Configuration Permissions are actually the permissions on the Registry key describing the COM application:
HKEY_LOCAL_MACHINE\SOFTWARE\Classes\AppID\{GUID}
The GUID is the application ID from the General tab of the application's properties window. The Advanced ACL editor in the Registry Editor can be used to take ownership and adjust configuration permissions. After a reboot, the DCOM Configuration snap-in can be used to graphically adjust Launch and Activation or Access permissions.
The Configuration Permissions are actually the permissions on the Registry key describing the COM application:
HKEY_LOCAL_MACHINE\SOFTWARE\Classes\AppID\{GUID}
The GUID is the application ID from the General tab of the application's properties window. The Advanced ACL editor in the Registry Editor can be used to take ownership and adjust configuration permissions. After a reboot, the DCOM Configuration snap-in can be used to graphically adjust Launch and Activation or Access permissions.
Thursday, July 19, 2018
Backup Operators may not be able to create shadow copies
Yesterday I learned that SeBackupPrivilege and SeRestorePrivilege are not sufficient to create a volume shadow copy. I assumed that membership in the official Backup Operators group would do the job, but after doing some testing today, that may not be the case. Historically, I've used the code in this Stack Overflow answer to create shadow copies while running as an elevated administrator. When I tried it as an elevated backup operator, the first line didn't throw an error as it does for normal users, but the response object it returned was blank and indicated an access-denied condition. No shadow copy was created.
Evidently, even though membership in Backup Operators allows connection to the Volume Shadow Copy Service (or so it appears from the ACL in DCOM Configuration), only administrators can create shadow copies. That matches the response to this other Stack Overflow question.
Evidently, even though membership in Backup Operators allows connection to the Volume Shadow Copy Service (or so it appears from the ACL in DCOM Configuration), only administrators can create shadow copies. That matches the response to this other Stack Overflow question.
SeBackupPrivilege may not be enough to use the Volume Shadow Copy Service
A new answer to a Super User question I addressed a while back informed me of something I had missed. I thought that the backup-related abilities of the Backup Operators group were all from the backup and restore privileges granted to it, but apparently use of the Volume Shadow Copy Service requires real membership in that group (or in Administrators). It looks like this might be enforced by the security in the DCOM configuration - only SYSTEM, Administrators, and Backup Operators are granted launch or activation permissions on the application. Users (even administrators) are not allowed by default to edit the ACL on that application, so altering it will be tricky. I will continue to investigate.
Monday, July 16, 2018
Using an existing CSR in Let's Encrypt
I recently needed to get an SSL certificate for a site whose hosting provider generates the CSR. I use Let's Encrypt to get free SSL certificates. I was happy to find that the Certbot client is easily able to request certificates based on a CSR:
certbot certonly -d example.com --csr /path/to/file.csr
certbot certonly -d example.com --csr /path/to/file.csr
Sunday, July 15, 2018
Viewing an execution plan for SQL Anywhere 11
I recently needed to investigate what part of a query to an SQL Anywhere 11 database was taking so long. The database software comes with a program called DBISQL, or "Interactive SQL." Today I discovered that it has a section for doing exactly what I needed: Tools | Plan Viewer. Paste/type the query in the top box and press Get Plan, then information will appear below. The most helpful part - the tree - is in a tiny left pane by default and probably not visible, so that will probably need to be expanded. Nodes in the tree can be clicked for more information, like the table being accessed.
Saturday, July 14, 2018
Using SetOwner on an access control list may require SeTakeOwnershipPrivilege
One user was having some trouble gaining control of a file. Taking ownership and adding a Full Control access rule to a file's ACL can be done with PowerShell, but there's a significant wrinkle. The SetOwner function on the ACL object, which would allow an administrator to take ownership of the file, requires the process to have turned on SeTakeOwnershipPrivilege. Running elevated gives the process the permission, but doesn't enable it. As far as I know, there's no pure PowerShell way to enable the privilege - a mess of embedded C# to P/Invoke the native API is the only way I've found.
Friday, July 13, 2018
Keen Modding Live - Player page cleanup
I've continued to tweak the design of Keen Modding Live's player page. I realized that it's not really ever useful to keep the mouse pointer around in fullscreen, so the checkbox that controls that has been removed. The difficulty and keybinding controls have been moved up next to the DOSBox widget. (I had to struggle a bit with CSS to keep the DOSBox centered and get all the controls floating where they should be.) I also made the entire game-and-controls section hidden until all downloading and initialization is finished - that way, the user won't see weird half-loaded elements jumping around.
Thursday, July 12, 2018
Finding the path to a file mentioned in the USN journal
One user was going through the output of fsutil usn readjournal and wondered about the "file ID" and "parent file ID" rows. The output of that command appears to correspond (with the rows somewhat out of order) to the USN_RECORD_V3 Windows API structure. The IDs mentioned are NTFS object identifiers. They can be converted to a full path with fsutil file queryFileNameById. For example:
fsutil file queryFileNameById C:\ 0x0000000000000000001200000019ab0e
fsutil file queryFileNameById C:\ 0x0000000000000000001200000019ab0e
Keen Modding Live - Unifying player page style
I made a couple minor adjustments to Keen Modding Live's web site today:
- The player page now has the same template as all the others. There's still a lot of design that needs to go into that, since it's probably the most important page.
- Level information pages now have an "upload a new version of this level" link that goes to the upload page with the level selected.
Tuesday, July 10, 2018
Guessing the type of an absent Registry value from Process Monitor logs
One user was trying to use Process Monitor to figure out what a program is looking for in the Registry. The relevant value was not present, so the RegQueryValue operation's result was NAME NOT FOUND. When the query is successful, Process Monitor reports the data type, but it does not when the value is not found. This is because the API call used to query the Registry does not take the desired data type - the Registry just stores chunks of data for the application to interpret. When a value is found, the Registry returns its kind, but if the value isn't there, there's no kind to report.
There is one piece of information that can allow a guess to be made about the value type (in some cases). Applications need to pass in a buffer for the returned data and the buffer's length. Process Monitor reports a "length" even for unsuccessful Registry queries, but (at least on 64-bit systems), its number is 12 bytes more than the application supplied. So subtracting 12 from the "length" gives the size the application is expecting for the results. A DWord occupies 4 bytes and a QWord occupies 8 bytes. All the other kinds (mostly strings) are variable-length.
There is one piece of information that can allow a guess to be made about the value type (in some cases). Applications need to pass in a buffer for the returned data and the buffer's length. Process Monitor reports a "length" even for unsuccessful Registry queries, but (at least on 64-bit systems), its number is 12 bytes more than the application supplied. So subtracting 12 from the "length" gives the size the application is expecting for the results. A DWord occupies 4 bytes and a QWord occupies 8 bytes. All the other kinds (mostly strings) are variable-length.
Monday, July 9, 2018
Setting the icon for an entry in the New context menu
Entries in the New submenu of the Explorer right-click menu are generated by ShellNew subkeys in the Registry. One user wanted to know how the icon for those entries is determined. By default, Explorer will use the file type's icon, but this can be overridden by placing an IconPath string value in the ShellNew key. The format is the same as most resource identifiers: the path to the file containing the icon, a comma, and the numeric resource ID. For example, %ProgramFiles%\Windows Mail\wab.exe,10 is an open book icon. Changes to the New-related Registry entries require a restart of Explorer before taking effect.
Sunday, July 8, 2018
Making a shortcut to troubleshoot compatibility for a specific program
One user wanted a fast way to open the Program Compatibility Troubleshooter and start troubleshooting a specific program. The PCT window is provided by msdt.exe, but Process Explorer reveals that it doesn't get passed the path to the program to be troubleshot. Rather, it receives a path to an XML file written by the very short-lived pcwrun.exe. That process exits so fast that I couldn't get a command line out of it, but simply passing the path to the incompatible program's EXE works just like clicking the "run compatibility troubleshooter" button in the Properties window.
Friday, July 6, 2018
Policy Plus gets its first pull request
This morning, a Policy Plus user submitted a pull request. Apparently, some ADMX files (like the Office ones) have blank values for some XML attributes specifying numeric default values. I'm pretty sure that's not compliant with the ADMX specification, but if the Local Group Policy Editor supports it, Policy Plus should too. The pull request fixes the problem by assuming the appropriate type's default (in the .NET sense) value if the attribute cannot be parsed into that type.
Thursday, July 5, 2018
Keen Modding Live - URL parsing expansion
The only part of Abiathar Live Studio that needs to know about the non-API parts of the Keen Modding Live web site is the URL format of the level and version pages. The dialog for selecting a level is supposed to be able to extract level or version information from the URL on the clipboard, which it can only do if its idea of what the web site is doing matches up with reality. So today I expanded the pasted-URL parser to deal with level information pages (in addition to version player pages).
While testing, I accidentally chose a Keen 1 level and found that the level picker was happy to go ahead with it. The level search dialog filters out levels of non-Abiathar-compatible episodes, but the URL-pasting route did no such checks. I'm sure all kinds of bizarre behavior would occur if a Vorticons level was selected, so I added some logic to stop that.
While testing, I accidentally chose a Keen 1 level and found that the level picker was happy to go ahead with it. The level search dialog filters out levels of non-Abiathar-compatible episodes, but the URL-pasting route did no such checks. I'm sure all kinds of bizarre behavior would occur if a Vorticons level was selected, so I added some logic to stop that.
Wednesday, July 4, 2018
Keen Modding Live - New versions via web
A conspicuously absent feature from the Keen Modding Live web site up to this point was the ability to upload new versions of existing levels. It was possible in Abiathar Live Studio through the API, but no such program exists for Vorticons levels. Today I set to work adding that feature to the web site.
I decided that, to avoid duplicating effort, I would extend the existing level upload page to optionally take a level ID as an URL parameter. I started off with a light sprinkle of embedded PHP to pre-fill and disable the fields that were already determined at the level's creation if a level is specified. While I was at it, I added fields for version comment and level description, which weren't possible to set via the web before.
To populate the list of versions (from which to choose the base version for the new one), I embedded a larger chunk of PHP that echoes a JSON array into a JavaScript fragment that then populates the dropdown with it. I would have just used the API from JavaScript like I did with the episode and zone dropdowns, but I wanted to show the usernames of the version authors, and doing that from JavaScript would require at least one more round trip. And I would have had PHP render the <option> tags, but I didn't want to risk messing up the escaping and causing security issues.
I successfully used this new system to upload a new version of my tiny Keen 6 test level, after which I noticed that I forgot to add a way to mark the new version as recommended. So that's the next step.
I decided that, to avoid duplicating effort, I would extend the existing level upload page to optionally take a level ID as an URL parameter. I started off with a light sprinkle of embedded PHP to pre-fill and disable the fields that were already determined at the level's creation if a level is specified. While I was at it, I added fields for version comment and level description, which weren't possible to set via the web before.
To populate the list of versions (from which to choose the base version for the new one), I embedded a larger chunk of PHP that echoes a JSON array into a JavaScript fragment that then populates the dropdown with it. I would have just used the API from JavaScript like I did with the episode and zone dropdowns, but I wanted to show the usernames of the version authors, and doing that from JavaScript would require at least one more round trip. And I would have had PHP render the <option> tags, but I didn't want to risk messing up the escaping and causing security issues.
I successfully used this new system to upload a new version of my tiny Keen 6 test level, after which I noticed that I forgot to add a way to mark the new version as recommended. So that's the next step.
Tuesday, July 3, 2018
Keen Modding Live - ZXC fix
Today while poking around the Keen Modding Live site, I found that Keen 1 and Keen 4 levels were completely failing to load when the keyboard preference was Z/X/C (as opposed to Ctrl/Alt/Space). The problem was that I had mistakenly kept the CTLPANEL/CONFIG file in the game folder while packaging those episodes. When the ZXC-enabled configuration file was injected, the DOSBox JavaScript thingy I'm using complained about a duplicate file and keeled over. Deleting extra files and repackaging fixed the problem.
Monday, July 2, 2018
Keen Modding Live - Cleaning up files
With Abiathar Live Studio effectively done, it's time to give Keen Modding Live an actual web site. The pages and scripts that are there made reference to several pages that are either blank placeholders or not there whatsoever. Apparently when I first started arranging things, I had plans for a "dashboard" page for logged-in users, distinct from the homepage. Now I think it would be nicer and simpler to just have a feed of recently published levels be the homepage for guests and members, with another feed showing up on the side for members' personal events. So I went through and cleaned up all the redirects to the nonexistent dashboard page.
Saturday, June 30, 2018
A newer CK5Patch fixes the %egadict issue
Only a day or so after releasing Abiathar v2.11.1, including among other things a workaround for CK5Patch's incorrect %egadict command, I was informed that there is a more direct fix. NY00123 released a new unofficial version of CKPatch a couple years ago that fixes CK5Patch itself. When I was originally embedding CKPatch, I went with the newest official version I could find, but I might have to consider updating to this new version.
Friday, June 29, 2018
Compressing images in an Excel workbook is very similar to in PowerPoint
Quite a while back, I devised a PowerShell script to convert all images in a PowerPoint presentation to JPG and compress them. More recently, someone needed to do the same thing, but for an Excel workbook. It turns out that XLSX archives have essentially the same structure as PPTX archives except that Excel-specific data is under the xl folder instead of the ppt folder for PowerPoint. Word DOCX archives use word, so the script should also work for them after tweaking the path.
Thursday, June 28, 2018
FMod - v2.11.1
Today I released the pending Abiathar changes as v2.11.1. That includes:
- The Level Inspector's new switch-related checks
- The Resource Accountant's new ability to show the number of each kind of item
- The fix for the vertical lines in the selected tile crosshairs not scrolling properly
- The workaround for CK5Patch's %egadict malfunction
Auto-update to v2.11.1 only works in v2.11 because of my hosting provider's recent security protocol change.
Tuesday, June 26, 2018
FMod - Level Inspector for switches
One new level creator had some trouble with switches in Keen 4. Unlike episode 6, episodes 4 and 5 have two different kinds of switches: one that affects the infoplane (to control moving platforms) and one that affects the foreground (to open/close bridges). Using the wrong kind of switch will produce absolutely no visible effect. To make navigating this issue easier for new modders, I added some new checks to the Level Inspector. It will now warn about pointerless switches, switches that point outside the level, and switches with the wrong kind of target.
Wednesday, June 20, 2018
Function-call-like parentheses in PowerShell cmdlet invocations will probably not do what you expect
One user tried to do something along these lines to call a function in PowerShell and pass the result as a parameter to a cmdlet:
Do-Thing -Information SomeFunc($abc)
This is an entirely reasonable thing to try, but PowerShell interprets it in a surprising way. First, it tries to be helpful by interpreting SomeFunc as a string literal. PowerShell function calls (distinct from calling .NET functions form PowerShell) don't use parentheses. In this case, PowerShell treats the parentheses as separating the $abc argument from SomeFunc, so the string "SomeFunc" becomes the value for the -Information switch and $abc, being unaffiliated with a switch, becomes a positional parameter. Usually this will result in error messages about a value not being suitable for any positional parameter.
To make PowerShell do what you likely wanted, wrap the entire function call in parentheses:
Do-Thing -Information (SomeFunc $abc)
Do-Thing -Information SomeFunc($abc)
This is an entirely reasonable thing to try, but PowerShell interprets it in a surprising way. First, it tries to be helpful by interpreting SomeFunc as a string literal. PowerShell function calls (distinct from calling .NET functions form PowerShell) don't use parentheses. In this case, PowerShell treats the parentheses as separating the $abc argument from SomeFunc, so the string "SomeFunc" becomes the value for the -Information switch and $abc, being unaffiliated with a switch, becomes a positional parameter. Usually this will result in error messages about a value not being suitable for any positional parameter.
To make PowerShell do what you likely wanted, wrap the entire function call in parentheses:
Do-Thing -Information (SomeFunc $abc)
Tuesday, June 19, 2018
Keen Modding Live - Untag permission adjustment
Today I realized it would be good for Keen Modding Live users to be able to remove tags they own from any levels, while still not being able to affix non-private tags to others' levels. This only matters for open tags that later close. I added a bit of logic to the "tag level" API to only do the level ownership check if trying to add the tag.
Monday, June 18, 2018
FMod - Resource Accountant improvement
One Abiathar user requested the ability to count how many of each kind of resource item are in the level, e.g. the number of 500-point items, in addition to how many total resources of each type there are. I implemented that today along with a new configuration option to control whether that new information is shown. The list is currently sorted by instances of the resource item kind, though that can look a little strange and might be changed.
Resource Account's results with item instance counts enabled |
Sunday, June 17, 2018
FMod - Keen 5 %egadict fix
Today I adjusted Abiathar's patch generator to use a %patchfile command instead of %egadict for Keen 5. It would have been extremely easy and barely worth mentioning if done when the patch generator was first being written, but the patch generator configuration is in project files, so changes to it involve some fiddling. Specifically, the existing templates file and any given project file need to have the new fields' values copied into them from the appropriate fresh template.
Unrelatedly, I noticed that the vertical lines in the selected tile crosshairs don't scroll with the tileset. I adjusted the vertical line rendering to consider the tileset's Y offset and the crosshairs now render correctly.
Unrelatedly, I noticed that the vertical lines in the selected tile crosshairs don't scroll with the tileset. I adjusted the vertical line rendering to consider the tileset's Y offset and the crosshairs now render correctly.
Saturday, June 16, 2018
Need to adjust for faulty %egadict command
A user recently posted to Keen Modding about some trouble importing modified Keen 5 graphics with KeenGraph. KeenGraph's author showed up and determined that the problem is that CK5Patch's %egadict command (used to patch in KeenGraph's updated EGADICT) patches the file in at the wrong address. This issue can be worked around by manually specifying the address with %patchfile. I'll need to adjust Abiathar's patch generator to support special-case replacements for %egadict so that it can automatically write working patches for Keen 5.
Friday, June 15, 2018
SprintDLL - Arrays are possible now
Yesterday's improvements to SprintDLL's copyslot command made it possible to allocate chunks of memory and access them like arrays using constant indexes. That's very nice, but it would be even more helpful if the index could be determined at runtime from a slot's value. So today I made the pointer adjustment phrases and length switches accept either a constant number or a slot name. Therefore, things like this work:
allocslot native myInts: 4 sizesof int
newslot int toStore = 3
copyslot myInts dereferenced offset 1 sizesof int = toStore
newslot int index = 1
newslot int retrieve = 0
copyslot retrieve = myInts dereferenced offset index sizesof int
readslot /raw retrieve
// Prints 3
allocslot native myInts: 4 sizesof int
newslot int toStore = 3
copyslot myInts dereferenced offset 1 sizesof int = toStore
newslot int index = 1
newslot int retrieve = 0
copyslot retrieve = myInts dereferenced offset index sizesof int
readslot /raw retrieve
// Prints 3
Thursday, June 14, 2018
Not all context menu items are directly in the Registry
One user wanted to find the command line equivalent of context menu items. This is straightforward for items that are configured in the Registry to launch a process with arguments (in the command subkey under a subkey of shell, which is a subkey of a file type key in HKEY_CLASSES_ROOT). However, some context menu items are actually created at runtime by shell extensions or the shell itself. Context-menu-affecting shell extensions are listed in the ContextMenuHandlers subkey under shellex. Of course, those are COM objects that can do anything they want in response to the menu item being clicked, so there's no simple way to invoke their functionality from the command line. One strategy is to use Process Explorer to see the command line of any new processes that result from clicking the item.
Wednesday, June 13, 2018
SprintDLL - More powerful copyslot
Today I finished the improvements to SprintDLL's copyslot command. It can now handle multiple qualifiers on a slot. Things along these lines now work, for example:
newslot int a
newslot block b = blockptr(blockptr(int 1))
copyslot a = b dereferenced dereferenced
readslot /raw a
// Prints "1"
When the appropriate copy region length can't be determined, a length switch is required, which can be /bytes (to set the length in bytes), /length (to specify the length with a unit), or /lengthof (to specify the kind of the value, especially useful for native).
newslot int a
newslot block b = blockptr(blockptr(int 1))
copyslot a = b dereferenced dereferenced
readslot /raw a
// Prints "1"
When the appropriate copy region length can't be determined, a length switch is required, which can be /bytes (to set the length in bytes), /length (to specify the length with a unit), or /lengthof (to specify the kind of the value, especially useful for native).
Tuesday, June 12, 2018
SprintDLL - Starting copyslot improvements
SprintDLL's copyslot command supports some offsets and pointer derefererencing, but not combinations of those. Therefore, it always takes multiple instructions to get the value pointed to by a field, and it's literally impossible to deal with arrays. So today I started working on improvements to the instruction to allow things like someSlot field 5 dereferenced as a source or destination. I have a function to parse multiple qualifiers, but there's a snag: it's not possible for SprintDLL to know the kind of value at such an adjusted location, so it can't know how much to copy. Therefore I'm going to need to introduce a switch on copyslot to allow the user to specify the size of the destination region.
I also discovered a bug in the existing implementation: specifying qualifiers for the destination but not the source causes the destination's qualifiers to be ignored because of an ill-conceived optimization. The new implementation will fix that.
There's now a predefined zero slot that, unsurprisingly, holds a bunch of zero bytes. It's suitable for zeroing out a region by serving as a source slot in copyslot.
I also discovered a bug in the existing implementation: specifying qualifiers for the destination but not the source causes the destination's qualifiers to be ignored because of an ill-conceived optimization. The new implementation will fix that.
There's now a predefined zero slot that, unsurprisingly, holds a bunch of zero bytes. It's suitable for zeroing out a region by serving as a source slot in copyslot.
Monday, June 11, 2018
SprintDLL - Linebreak escape characters
I realized today that it was virtually impossible to get a linebreak into a SprintDLL string. A literal linebreak in the command would be interpreted as the end of the instruction and would cause a syntax error. Passing a blockptr to a collection of short literals (for character code points) in place of an lpwstr works, but is of course supremely inconvenient. So today I adjusted the parser to recognize a few more escape sequences in quoted strings. \r and \n now become a carriage return and line feed, as appropriate, and \N becomes a CR/LF sequence.
call kernel32.dll!GetStdHandle /return native /into out (int -11) call kernel32.dll!WriteConsoleW (slotdata out, lpwstr "Hello world!\N", int 14, nullptr, nullptr)
Sunday, June 10, 2018
PowerShell aliases can't include parameters
I recently helped a user who was having some trouble with PowerShell aliases. He was trying to define a handful of aliases, each passing a different parameter to a function. That was a problem because PowerShell aliases are just alternate names for commands; they cannot specify parameters. (That's why, for example, mkdir is a function that has to do some fancy stuff to wrap New-Item, specifying -ItemType Directory but passing the other parameters straight through.) To work around that, you can use Invoke-Expression to run commands that create simple functions to call the more general function.
iex "function $NewFuncName {SomeFunction $TheParameter}"
iex "function $NewFuncName {SomeFunction $TheParameter}"
Friday, June 8, 2018
Different browsers may print on different numbers of pages
One of my programs generates paper forms as HTML documents and brings up a browser for the user to print them. I had made sure the form would fit on one page, but one user was getting it printed on two. Upon investigating, I found that user's default browser was a moderately old version of Internet Explorer, which I had not tested on. Apparently not all browsers will size elements or the margin the same when printing, so even extremely simple arrangements should be tested on many browsers.
Wednesday, June 6, 2018
Keen Modding Live - Personal tags
I realized after implementing tagging yesterday that letting everyone affix arbitrary tags to everyone else's levels could conceivably go poorly. Attaching a normal tag now requires owning both the level and the tag; attaching an open tag still just requires owning the level. At creation, tags can be made "personal," in which case they can again be affixed by their owner to any level, but they will not be visible to any other non-admins.
Tuesday, June 5, 2018
Keen Modding Live - Tags
Today I made it possible to control whether a version newly published from Abiathar Live Studio becomes the official/suggested one. The "upload level" endpoint now has a parameter specifying whether to try to suggest the new version, which will only work if the version creator owns the level. When publishing a new version of an existing level, Abiathar Live Studio prompts for a version description and whether to try to suggest it.
For community level packs or compilations in general, it would be neat to have a way to tag levels. There are now API endpoints for creating, editing, and deleting tags, one to apply or remove a tag on a level, and one to get tag relationships by tag or level. Tag creators can mark the tags as open for submissions, in which case level owners can apply the tag to their own levels; otherwise, only the tag creator can apply the tag.
For community level packs or compilations in general, it would be neat to have a way to tag levels. There are now API endpoints for creating, editing, and deleting tags, one to apply or remove a tag on a level, and one to get tag relationships by tag or level. Tag creators can mark the tags as open for submissions, in which case level owners can apply the tag to their own levels; otherwise, only the tag creator can apply the tag.
Monday, June 4, 2018
Keen Modding Live - Search for levels
Today I implemented the Search Levels dialog, which allows selecting a level to clone/fork. It downloads information on all levels, then allows filtering them by title, episode, and zone. Once a level is selected, the program prompts for the specific version.
I adjusted the Level Properties dialog to allow changing which version is the current/suggested/official one.
I added a database field in the level versions table to store a description, like a commit comment. Currently there isn't a way in the UI to set it, but if present it is displayed in the version selector.
I adjusted the Level Properties dialog to allow changing which version is the current/suggested/official one.
I added a database field in the level versions table to store a description, like a commit comment. Currently there isn't a way in the UI to set it, but if present it is displayed in the version selector.
Sunday, June 3, 2018
Keen Modding Live - Preload static data
A lot of information in Keen Modding Live won't change very often at all, like the list of zones or of episodes. Previously, Abiathar Live Studio would contact the API endpoint every time that information was needed. Today I adjusted it to download all the effectively-static information just once at logon/startup. This allows a couple dialogs like New Live Level to open immediately instead of after an API request.
Saturday, June 2, 2018
The downloadable Windows Update troubleshooter might work better than the built-in one
I recently tried to get a Windows 10 1709 VM to update to 1803. It consistently failed about 78% into the first installation phase. The Windows Update troubleshooter from Settings | Update & Security | Troubleshoot didn't seem to change anything. The equivalent troubleshooter downloadable from Microsoft, however, did seem to have an effect. The update still failed for me, but at least it got noticeably further into the process. (I'm still trying to figure out what issue it's running into.)
Friday, June 1, 2018
FMod - v2.11
After consulting with an Abiathar tester today, I published all recent changes as v2.11. I made one tiny tweak before finalizing it: to make sure it wouldn't crash on Windows XP (which supports only .NET Framework version 4.0), I made sure to handle any errors generated by the attempt to enable support for TLS 1.1 and 1.2. When running on .NET 4.0, the constants signifying those settings are not valid values for ServicePointManager.SecurityProtocol. The auto-updater will still not work on Windows XP under the current hosting setup, but at least it can make the attempt to contact the update server.
If an operation seems extremely common, check if it's done for you
I've been working with an application that uses an SQL Anywhere 11 database. Like hopefully all SQL implementations, SQL Anywhere supports autoincrementing columns. Instead of taking advantage of that, though, this application maintains a table of the next available primary key value for each table. To generate IDs for new rows, it calls upon a stored procedure.
The application has been around for a long time, and I'm sure if the authors were developing it today, they would make a better choice. I've certainly rigged up systems that were weirdly engineered or not necessary. Something I've learned is that very general, very useful functionality is probably already implemented in the platform. If it's implemented in a well developed, widely used piece of software, it is likely a solid solution. That's not always the case, but it's at least worth checking over. Before deciding to reimplement something already done in the platform, you should have a specific reason, like performance or a missing capability.
The application has been around for a long time, and I'm sure if the authors were developing it today, they would make a better choice. I've certainly rigged up systems that were weirdly engineered or not necessary. Something I've learned is that very general, very useful functionality is probably already implemented in the platform. If it's implemented in a well developed, widely used piece of software, it is likely a solid solution. That's not always the case, but it's at least worth checking over. Before deciding to reimplement something already done in the platform, you should have a specific reason, like performance or a missing capability.
Wednesday, May 30, 2018
List of cmdlets removed in PowerShell 6
PowerShell 6 (also known as PowerShell Core) has significantly fewer cmdlets than PowerShell 5 (the latest Windows-only version). That's because many cmdlets relied on native Windows functions, so if they weren't re-implemented in a platform-independent manner, they had to be removed. For this Super User answer, I generated a list of cmdlets that are gone in one version relative to the other:
- 1139 commands are in PowerShell 5 but removed in PowerShell 6
- 71 commands are new in PowerShell 6
Tuesday, May 29, 2018
Wildcard certificates from Let's Encrypt
Let's Encrypt started offering wildcard certificates a while back, but not all client programs are able to take advantage of the new API. This Microsoft blog post is a very good guide to using the official certbot client under Bash on Ubuntu on Windows. Two things to note:
- *.example.com does not cover example.com, so you need two domains on the certificate: -d example.com -d *.example.com. This will require putting two different TXT records into the same DNS zone.
- It can take a while for the DNS changes to become visible to Let's Encrypt. I used Google's dig tool to check that both TXT records were consistently being returned. (Different nameservers may pick up the changes at different times.) Only press Enter on the second verification after both records are live.
Monday, May 28, 2018
Windows feature updates might fix the desktop not appearing
I recently worked with a Windows 10 computer that, on only one user account, had stopped displaying the desktop and task bar (all Explorer components). Explorer tried to start, but pegged one CPU core and never got around to displaying anything. The Windows Push Notifications User service for the stuck account displayed similar behavior. I found a handful of purported fixes on the Internet for the lack of desktop, but none of them helped. What did help was the 1803 feature update that happened to be coming down - after the system installed it and rebooted, everything was functioning normally.
Sunday, May 27, 2018
Forcing a Start menu layout without restarting Explorer
One user was trying to set the Windows 10 Start menu layout with Group Policy's Start Layout setting. They found that changes to the layout XML file only took effect after restarting Explorer or logging out then back in. For a while I thought it was impossible to make Explorer reload the XML (none of the usual Win32 change notification functions worked), but then I found that restarting just the Windows Shell Experience Host process (shellexperiencehost.exe) will do the job without interrupting other Explorer functions. It'll automatically come back right after being terminated.
Saturday, May 26, 2018
PSThar - v1.2
I was hoping to add Simultaneous Tileset support to PSThar so it could set both $CurLevel and $CurTileset in the same console, but in the interest of getting the other new features out to users as soon as possible, I decided to release what I have as v1.2. The only change I didn't mention yet was that, to make it clear which mode the console was opened in, "Level" or "Tileset" is added to the caption of the console window as appropriate.
Subscribe to:
Posts (Atom)