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.


  1. For which subversion of C6 is this patch?

    1. Sorry for the long delay! The product name of the DLL that this patch works on says that it's from build 6.200.00. It's 1169144 bytes long.