Friday, June 30, 2017

Abiathar API - Configuration

Last time, we looked at the "lifecycle" events that come through the event bus. Some of those have to do with configuration storage in the project file.

All configuration objects need to inherit from FleexConfig. That class provides serialization and deserialization of the public fields. If the class has FleexConfHostAttribute, all public fields will be marked serializable, unless they have FleexConfExemptAttribute. Otherwise, all serializable fields must be marked with FleexConfEntryAttribute. If a field has FleexConfLoadOnlyAttribute, it will be loaded but not saved; this allows for smooth deprecation of old entries. If an entry has FleexConfSaveIfNonNullAttribute, it will only be written if it's not null; this saves space. If it has FleexConfSaveIfNotEqAttribute (which takes a parameter), it will only be written if it doesn't have that value; this also saves space. To have a comment placed before an entry, give it FleexConfCommentAttribute; this is how the guidance in editor.aconf is generated.

Supported types include strings, every value type with a Parse method, lists, and subclasses of FleexConfig.

If your config object should do some special logic after being loaded, override the OnLoad method.

Thursday, June 29, 2017

Abiathar API - Event bus

You might have noticed last time that Abiathar extension objects don't have a whole lot in terms of members. Events are received through the event bus. To make your extension handle an event, create a public function on the event object that receives one of the subclasses of AbiatharEvent, and add the AbiatharEventBusAttribute attribute to the method. This makes Abiathar see the method as an event handler. All event objects have a state accessor that you can use to interact with Abiathar.

Let's look at some "lifecycle" events.

After Abiathar loads all the extensions, it sends AbiatharExtensionsLoadedEvent. Extensions may use the RequestAnother method to ask for another round of these (say, it wants to talk to another extension after the other one has initialized). Therefore, you need to check whether you've already responded appropriately to one of these events so that you don't initialize things too many times.

Soon after, AbiatharSetupMenuStripEvent is sent. This provides the main menu of the window so you can fiddle around with it and add menu items if you please.

When a new project is created, AbiatharRegisterFileConfigEvent is sent. If your extension needs to store some bookkeeping in the project, add an entry to ConfigFiles. Configuration storage will be addressed next time.

When an existing project is loaded, AbiatharLoadFileConfigEvent is sent. Check the Name property and, if the configuration object belongs to your extension, set the ConfigTemplate to a default/empty instance of your configuration type. If a configuration section is not claimed, it will be deleted.

When the application is closing, AbiatharClosingEvent is sent. In response to this, clean up any external resources, like temporary files.

Wednesday, June 28, 2017

Abiathar API - Getting loaded

Welcome to Part 1 of a series on the Abiathar API. This will serve as API documentation so that other programmers can create extensions for Abiathar to add features. Without further ado, let's begin.

Abiathar extensions are .NET DLL files with a .aex extension. These require a reference to Abiathar's Interop.dll. To actually have your DLL recognized as an extension provider, include one public class that implements IAbiatharExtensionServer. That class must be constructable without parameters. Extensions servers have two required methods:

  • LoadExtensions returns a list of IAbiatharExtension. This list can be empty if your extension server determines it can't work on the platform, for instance. If you serve only one extension, it's fine to return a list containing only the extension server if the server also implements IAbiatharExtension.
  • ExtensionLoadFail is called if there's a problem getting the metadata of an extension. It includes the problematic extension and the exception.
The extension object itself only needs one property, Metadata, of type AbiatharExtensionMetadata, which has these fields:
  • Logo is the image that shows up next to your extension in the Extensions menu. It can be null if you don't want an icon.
  • Name is the short name of your extension, for showing the Extensions menu.
  • Version is the version of your extension. It shows up the extension info box.
  • Author is the author of your extension.
  • Description is the brief description of your extension that shows up in crash reports.
  • LongDescription is the more complete description that shows up in the extension info box. If this is empty, the Description is used instead.
  • Measurements is a function that takes a string and returns an object. You can do whatever you want in this function; Abiathar never uses it. It's to allow cooperating extensions to communicate among themselves. This can be null if you don't use that feature.
Drop the compiled AEX next to Abiathar and you'll see your extension in the Extensions list. It doesn't do anything yet, though. Next time, we'll look at adding some functionality.

Tuesday, June 27, 2017

Explorer has trouble with filenames ending in spaces

One user noticed some weird behavior of File Explorer when working with items whose names end with a space. Such items can't be deleted; Explorer claims it's no longer located in the parent folder. Renaming it produces an error that the source and destination names are the same. That probably indicates that Explorer trims the file name before doing anything with it.

The only way to get rid of such an item is to use the absolute path syntax \\?\ in a command prompt. The name should be passed in quotes so that the command knows to include the trailing space.

Monday, June 26, 2017

The real account name is not the display name

One user wanted to find the original name of an account after it "being changed from Control Panel." Based on existing answers to that question, I determined that there was some confusion about which name was changed. Accounts have a display name that shows up in most user-facing places. There's also a SAM name: the internal name that you can type into a domain login prompt. It's more challenging to change the SAM name - the only convenient way I know is to use the lusrmgr.msc snap-in - while the standard Control Panel allows the display name to be changed with ease.

While changes to the SAM name produce an event in the event log (4781, to be specific) with the old and new names, that's not the case for the display name. All account properties changes produce event 4738 with the new data, but the old value is not recorded. Therefore, the only way to find the old display name would be to find the previous instance of event 4738, which might have aged out of existence.

Sunday, June 25, 2017

Patch to fix 100% CPU usage and freezing in Clarion 6 applications

A little while back, I noted that Windows 10 build 1703 rewrote the window manager internals. Now, calling PeekMessage sometimes produces more window messages. If a program peeks for messages a few times every spin around its message loop, it can wedge itself into an infinite loop as GetMessage then returns immediately when it sees the internal message, letting the loop go around once more. It so happens that there's a framework that does just that. I worked with a third-party application built on version 6 of Clarion. When run on 1703, the program froze in certain places, pegging the CPU at 100%, but on older Windows versions it was fine.

SoftVelocity no longer supports Clarion 6, so Clarion users are on their own. Fortunately, I managed to create a patch that fixes the problem. The relevant code is in C60RUNX.DLL. At 0xA867B, write these bytes:

8B 44 24 28 ; mov eax, [esp+28h]
E8 D2 42 03 00 ; call fix_detour
83 F8 00 ; cmp eax, 0
75 F2 ; jne -Dh
90 ; nop

We need some extra space for a bit of new logic. Fortunately, there is plenty of padding at the end of the code segment, which is where the call goes. At 0xDC956, write:

50 ; push eax
6A 00 ; push 0
6A 00 ; push 0
6A 00 ; push 0
50 ; push eax
E8 25 46 F2 FF ; call GetMessageA
58 ; pop eax
8B 40 04 ; mov eax, [eax+4]
25 FF FF 00 00 ; and eax, FFFFh
3D 38 07 00 00 ; cmp eax, 738h
0F 94 C0 ; sete al
0F B6 C0 ; movzx eax, al
C3 ; ret

This latter part is a function that takes the address of the buffer for the message in eax and returns whether the message should be ignored (i.e. is the new 0x738 message). Experienced Win32 programmers may notice that this doesn't check the return value of GetMessage, but neither does the original code, so this is no worse. Back up in the first section, the patch replaces the call to GetMessage with a call to this function, and if the message needs to be skipped, it just jumps back and does the call again until it gets something else.

Saturday, June 24, 2017

X509Certificate2.Verify checks the entire chain

Someone was confused about why the Verify method on a self-signed certificate was returning False. More information than just a yes/no is available, though, if you know how to ask. Creating an X509Chain object, calling Build with the certificate, and examining the ChainStatus property shows why the certificate failed to verify. In this case, it was because the root was untrusted, which makes sense since it was a self-signed certificate.

To make it verify, the user needed to add the certificate to the Trusted Root Certification Authorities store. That makes the root trusted, and since everything else about the certificate is good, the certificate verifies.