Sunday, December 31, 2017

Invoke functions by pointer

Previously, my P/Invoke command-line tool (which is in desperate need of an actual name) was unable to work with COM objects because the tool requires a library and name for functions it calls. COM objects appear from CoCreateInstance and their methods are accessible in their VTable, which is a member of each object/struct. To use them, I implemented the ability to call methods by a function pointer held in a slot. Currently, doing that requires some seriously ugly script, but at least it's now possible at all.

As a not-super-useful but concept-proving demonstration, this creates an IFhConfigMgr and calls AddRef:
newslot native fhPtr
call ole32.dll!CoCreateInstance /return uint (blockptr(guid {ED43BB3C-09E9-498a-9DF6-2177244C6DB4}), nullptr, int 1, blockptr(guid {6A5FEA5B-BF8F-4EE5-B8C3-44D8A0D7331C}), slotptr fhPtr)
newslot native fh
copyslot fh = fhPtr dereferenced
newslot block vtbl = nullptr, nullptr, nullptr, nullptr
copyslot vtbl = fh dereferenced
newslot native addref
copyslot addref = vtbl field 1
call funcat addref /call thiscall /return uint (slotdata fhPtr)

The funcat (that's "function at", not "fun cat", even though cats are pretty great) keyword on the call command is the new addition that does all the heavy lifting.

Saturday, December 30, 2017

FMod - Multiple tour watchdog planes

When I last left off working on the Abiathar tour, I noticed that since the watchdog render plane can only be registered for one level plane, it won't pick up the user's tile placing if it doesn't happen to affect that plane. So today, I made the tour extension register three watchdog planes, one for each level plane. The foreground is special-cased to be the only plane that triggers advancement past the task that asks the user to adjust the foreground only.

.NET bitness matters when using ODBC drivers

.NET assemblies can be compiled in the AnyCPU configuration, in which they can work on either 32-bit or 64-bit machines but have a preference for one or the other. For most applications, this probably doesn't matter too much. However, it definitely can cause issues if native modules are in play, like ODBC drivers. Some ODBC drivers are only registered for one bitness, so trying to start a connection using one might mysteriously fail on a machine with a different architecture. This caused some trouble for me in a recent deployment.

In these cases, it's probably easiest to compile for only the architecture with known-working ODBC drivers in the deployment environment.

Thursday, December 28, 2017

Where PowerShell stores the Ctrl+R history

One user wanted to know where PowerShell stores the console history that's searched by the Ctrl+R feature so that it could be brought across to a new machine. PSReadline stores it here:

%APPDATA%\Microsoft\Windows\PowerShell\PSReadline\ConsoleHost_history.txt

Migrating the console history is as simple as copying that file over (though some directories might have to be created).

Wednesday, December 27, 2017

SQL Server Management Studio might need to be run as administrator

Today I needed to attach a database to an SQL Server instance. When I had it attempt the attachment, the operation failed, claiming that there was an access denied error about the database file. That seemed strange because my user account was the one that had extracted the database file, so I clearly had the rights to modify it. Nevertheless, restarting the management tool elevated made the operation succeed.

Tuesday, December 26, 2017

FMod - Tour fixed

Today I figured out why the presence of the tour panel made keyboard events not go to the main form. As I suspected, the "exit tour" link gets focus when it's visible. The solution was to respond to the GotFocus event of that link (and also of the Proceed button) and set the focus to some inert label instead.

With that sorted out, I continued writing the tour. It now instructs the user to switch to a different level (since actual levels are more interesting than the world map), pick up tiles, place them, and adjust plane states.

Monday, December 25, 2017

Interacting with elevated windows from Chrome Remote Desktop

Under most circumstances, the assistant in a Chrome Remote Desktop connection cannot interact with windows from elevated programs. This is due to Windows's User Interface Privilege Isolation mechanism. This makes system configuration tasks difficult through Chrome Remote Desktop. I suspect disabling UAC would do it, but there is another workaround if the host would prefer not to do that. These steps apply to the host (nothing special needs to be done by the helper to initiate the session):
  1. Make sure Chrome is closed
  2. Launch Chrome Remote Desktop from the Start menu as administrator
  3. Connect as normal
  4. If necessary, Chrome can be reopened now
  5. Launch an elevated command prompt
  6. Accept the UAC dialog on the host machine
  7. Launch any necessary administrative tools from that command prompt
The helper cannot access the secure desktop, so any elevation prompts will render the session inoperable. Since child processes of elevated processes are also elevated, all elevation must be done by using that command prompt.

Sunday, December 24, 2017

FMod - Struggling with the tour

Today I wrote a barebones watchdog render plane that allows the user to proceed to the next step in the tour after the viewport has been moved around a bit. That part works great, but while testing it, I discovered a problem caused by the presence of the tour panel (probably the "exit tour" link specifically): keystroke events are no longer delivered to the form. I remember going through similar issues back when I added the zoom slider, but the previous solutions do not appear to do anything here. I'm sure it's surmountable, but for the moment it's putting a damper on the whole tour thing.

Friday, December 22, 2017

FMod - Starting the tour

Today I began writing the tour for Abiathar. It will consist of several "stops", each of which has a blurb of text in the left pane that explains things and asks the user to try them out. The extension that manages the tour does so by listening to various event bus stops, then advancing to the next tour stop if the instructions for the current one have been carried out. So far, it can listen for the "register file config" event, which indicates that a new project has been created.

Thursday, December 21, 2017

FMod - Help menu adjustments

In keeping with the next Abiathar version's theme of being more helpful to new users, I recently reviewed and updated the contents of the Help menu. The GitHub issue tracker was never used and isn't as convenient as just posting to the forum, so the Report Bug item has been removed. The PCKF Thread link was still pointing to the temporary domain for the forum, so I changed it to now go to the classic one. The manual PDF is extremely out of date, so the Documentation link has been replaced with a link to the KeenWiki page on Abiathar, which has a brief intro to the editor.

Wednesday, December 20, 2017

FMod - Need a new OOBE

I realized a while back that Abiathar's out-of-box experience, introduced a few versions back, could stand to be improved. Currently it throws up a box with sixteen (!) checkboxes and a paragraph of text. Asking the user to look over that many settings, especially when unsure what effect they have on the editing experience, is probably not very welcoming. Also, I have still not solved the problem of how to guide users in the basics of Abiathar.

Today I decided that all of those issues need to be fixed. I'm introducing a "tour" that will replace the OOBE config screen. On first launch, the user will be given the option to start the tour. Abiathar will then walk the user through the process of creating a level set, getting around the editor, and making some changes.

So far, I've designed the new OOBE window (which is much prettier than the old one), rearranged the contextual help window to also accommodate the tour guide, and set up the infrastructure for tour mode. When the user starts the tour, Abiathar will load an extension that it implements itself (like the standard toolkit). That allows Abiathar to not have all the event bus stops registered all the time, maintaining performance.

Monday, December 18, 2017

Getting a Unicode code point from a .NET string

It's easy to convert a .NET Char value into a number, but that gets UTF-16 code points, which are not necessarily Unicode code points. The code points for emoji, for example, don't fit in two bytes, so UTF-16 (used by .NET strings) stores them as two Chars, a high and low surrogate, neither of which are valid Unicode characters on their own. To get the real Unicode code point that starts at a given position in the string, you can use Char.ConvertToUtf32, supplying the string and the starting index.

Relevant Super User answer.

Thursday, December 14, 2017

Keeping cookies in PowerShell web requests

PowerShell's Invoke-WebRequest cmdlet can be used for all kinds of HTTP requests. For stateful web applications, it might be necessary to retain and retransmit cookies in subsequent requests. Conveniently, the cmdlet has features for maintaining a session.

The first request to the site should specify a variable (by name) into which the session should be saved:

iwr https://example.com/Page1 -SessionVariable session

Then all later requests supply that object as the session:

iwr https://example.com/Page2 -WebSession $session

Based on my Super User answer.

Wednesday, December 13, 2017

Checking whether the domain is available in PowerShell

One user wanted to know how to run a startup script only if no domain controller was available. My solution was to run a PowerShell script that checks if the domain is reachable. That's easily done by running this line:

[System.DirectoryServices.ActiveDirectory.Domain]::GetComputerDomain() | Out-Null

If it throws an exception, the domain is unavailable. If it proceeds without error, a DC could be contacted. A try/catch block can be used to make decisions based on whether the exception appeared.

Monday, December 11, 2017

Searching all event logs with PowerShell

For this Super User answer, I needed to search all event logs - not just the five standard ones under Windows Logs - for a bit of text. PowerShell's Get-WinEvent does the job:
Get-WinEvent -ListLog * | ? { $_.RecordCount -gt 0 } | % { Get-WinEvent -LogName $_.LogName -MaxEvents 100 } | ? { $_.ToXml().Contains('text') }

If you need to look at events far in the past, you can adjust the -MaxEvents 100 or remove it entirely; the process will just take a while.

FMod - Level 0 detection fix

A new Keen Modding user wanted to know how to open a variety of mods. On one of them, Abiathar didn't show the world map and saving the levels caused the game to crash when going to the map. That is because Abiathar didn't see that the first level slot (level 0, the world map) actually had a level; the MAPHEAD pointer to the header was zero because the header was at the very beginning of the GAMEMAPS (which usually doesn't happen). FMod/FleexCore2 has some heuristics to deal with this, but they were not working in this case.

The problem was that, when writing the level 0 detection code, I used Location instead of Length in one spot, so it checked the plane pointer rather than plane length. That check was intended to make sure that the plane array wasn't absurdly long, which would indicate that the "header" was actually random garbage. Fixing the typo (and upping the maximum viable length a bit just for safety) solved the problem.

Friday, December 8, 2017

Extracting information from certificates on disk using PowerShell

One user wanted to determine some properties of a certificate that resides in a CER file on disk rather than in a certificate store. (If it were in a store, it would be accessible from the Cert:\ pseudo-drive.) I'm not sure if there's a cmdlet for this, but it's very easy to do with the X509Certificate2 class, since that has a constructor that takes a filename of a certificate on disk.

[System.Security.Cryptography.X509Certificates.X509Certificate2]::new('C:\path\to\cert.cer')

Keen Modding Live - Import existing levels

Today I implemented the Import Current Level feature for Abiathar Live Studio. This allows a level belonging to a non-KML level set to be transformed into a new KML level held in a new ADEPS with new level files. Importing uses the same UI as creating a new level, but the episode dropdown is automatically set correctly by default by consulting the DefaultSet.

Sunday, December 3, 2017

Keen Modding Live - Found an Abiathar UI bug

Abiathar Live Studio, the desktop client for Keen Modding Live, uses Abiathar's DoVisibleLongOperation API to accomplish work while showing a progress spinning instead of freezing the UI. That dialog was originally written for the AbiatharOS installer (which can take a while to do its work) and only later made into an API. However, I apparently never changed the form's title from "Installing AbiatharOS" to something more generic. Since it would be a little strange to publish an Abiathar update to only make a fix that doesn't affect vanilla Abiathar, I instead used some reflection in Abiathar Live Studio to make the form say "One Moment" instead. When I next update Abiathar, I'll incorporate the real fix.

Saturday, December 2, 2017

Keen Modding Live - Level properties window

Today I implemented a way to alter a Keen Modding Live level's properties from the Abiathar Live Studio extension. Since only levels already published to Keen Modding Live have any properties, the option is only enabled after publication; the ADEPS stores the KML level ID.


This provides an easy way to mark a level as ready for public consumption from inside Abiathar - just change the zone from "developing" or "beta" to "showtime." One need not even visit the website.

There's not currently a way to view previous versions or manage the currently endorsed version, but that's one of the next things to do.