Smalltalk MT Knowledgebase

Overview

General

Tools

Windows

OLE

Memory-Mapped Files

Samples


General

Inspecting Registered Objects

Objects that are referenced by external (e.g., not garbage collected) code are registered with #registerObject and freed using #releaseObject.

Processor currentThread

returns the instance of _StThread for the current thread.

The instance variable m_extRefs contains the references. The first element can be ignored; it is the collection of recursive references used by Collection>>printOn:.

To determine the number of occurrences of a given object:

Processor currentThread referenceCount: anObject

Back To Top


Binary Operands * and + are commutative

The compiler treats * and + as being commutative; so (a * b) is the same as (b * a). In some cases, it is more efficient to evaluate the expression in reverse order.


Automatically Allocating a Console

To mark an executable as a Win32 console application, you must click on the Console button in the build properties of the Project
Browser. This marks the executable as being a console app and Windows automatically allocates a console for the process.

If you don't mark the executable and do not allocate a console explicitly, GetStdHandle returns NULL (an error would be INVALID_HANDLE_VALUE).

Back To Top


Tools

GUI Builder: Control Subclasses not used in Test Mode

Testing a window (Layout|Test) always uses the standard controls (e.g., Button, ListBox, ...), even if a control is attached to a particular Control class.

This is to guarantee that the window is created successfully. Using a custom Control implementation could result in exceptions when the control tries to interact with its parent.

Transcript: Missing Toolbar Buttons

Some development windows (Transcript, CHB) save the toolbar state to the registry. In some rare instances, it is possible that the registry settings are corrupted, usually with the result that toolbar buttons are not displayed correctly.

Rather than reconfiguring the toolbar, you can delete the registry value "Toolbar" under ObjectConnect/Smalltalk MT/version/Transcript or .../CHB.

Back To Top


Windows

Handle of Control not set to Zero when Window closes

Windows implemented by Smalltalk automatically set their handle to NULL when the window is destroyed. This can be useful for debugging and diagnostic purposes.

Controls do not receive the WM_NCDESTROY message, unless they are subclassed. Therefore, the handle is not set to NULL.

Back To Top


Cannot Access Static Control (IDC_STATIC)

IDC_STATIC is defined as -1, which means that you don't care about the identifier. The GUI builder uses this id by default when creating a static control. There can be multiple controls with the IDC_STATIC identifier. The caveat of IDC_STATIC is that you can't do anything with the control. In particular, this includes resizing.

Note: The behavior is the same as in MSVC++.

Back To Top


BN_CLICKED Event not propagated to Button Subclass

Controls can implement behavior by responding to events of interest. The event framework dispatches control events to the respective controls. An exception to this rule is the BN_CLICKED event because BN_CLICKED == 0.

When received from Windows, the BN_CLICKED event is identical to a command event whose command identifier equals the button id. If the button has the focus, it will receive an event with its identifier.

Workarounds:

1.) Use an instance handler instead of a static handler:

attachTo: parentWindow id: anID
super attachTo: parentWindow id: anID.
parentWindow when: self id perform: [ self onClicked ].

2.) Instead of using BN_CLICKED, use WM_LBUTTONDOWN as below:

self addHandler: WM_LBUTTONDOWN selector: #onClicked

WM_LBUTTONDOWN is generated in FrameWindow from a WM_PARENTNOTIFY message.

Unfortunately, the method above does not work in dialog boxes because Windows automatically prevents WM_PARENTNOTIFY messages by specifying the WS_EX_NOPARENTNOTIFY style when creating the controls. An application cannot override this default behavior unless it creates its own controls for the dialog box.

Back To Top


OLE

Alternatives to using OleControlContainer

OLE events are sent by IDispatchEvents. The owner must implement oleEvent:with:. The implementation in OleControlContainer simply feeds them into the normal event mechanism, using the id of the child control. Therefore, an OLE event OLE_EVENT1 (as an example) appears to have been raised by the OleControlContainer control, and you can define handlers such as:

when: OLE_EVENT1 in: id perform: #xyz

IDispatchEvents is a specialization of IDispatch that handles custom events; the user can QueryInterface on IDispatch or a predefined event IID (as defined in an IDL file), and the interface will return itself.

You can directly use IDispatchEvents without using OleControlContainer. You must first create the instance, specify an owner (which must implement oleEvent:with:), set the event IID, and connect the IDispatchEvents to the event sink. The method OleControlContainer>>interfaceConnect:on: shows how to do that, and it isn't too difficult. Next, you must keep track of the cookie (the connection identifier), because you'll need it when terminating the connection. The code in OleControlContainer>>interfaceDisconnect:dwConn: shows how to disconnect the event sink.

If you don't know the event IIDs in advance, you have to replicate the code in OleControlContainer. It is quite tricky, so you might as well consider using OleControlContainer directly. On the other hand, if you know the IIDs or can easily gain access to them at runtime, implementing your own container is a viable alternative.

To implement simple OLE event handling, you just need to create an instance variable for the dispatch interface and another for the connection id. Then, add the connect / disconnect code.

Note: OleControlContainer supports loading and unloading of clients. If you don't need this functionality, consider implementing a subclass of IDispatchEvents (or directly IDispatch) that implements connecting / disconnecting from the oleObject.

Back To Top


Files Used by OLE Components

DUMMY.DEF Is used to build a resource-only DLL. The resource section of this DLL is
later incorporated into the target image.
MAKE.BAT A batch file that calls the MIDL compiler and NMAKE.
MAKEFILE A makefile for the resource-only DLL.
project.ODL The ODL source file that describes the interface. The MIDL compiler
generates the type library from this file.
project.RC The resource file for the target image. A COM component must at least
contain a version resource that marks the image as supporting
self-registration.
project.TLB The type library, as generated from project.ODL by the MIDL compiler.
STMSG.BIN Smalltalk error messages. The source is in SUPPORT\MESSAGES\STMSG.MC. Due to
limitations of the resource compiler, the compiled file (.BIN) must be
placed in the same directory as the other resource files.

To compile the resources, you must:

  1. Add <smalltalk root>\SUPPORT\INCLUDE to the include path if not already
    done.
  2. Run make.bat.

Back To Top


Memory-Mapped Files

Writing to a Memory-Mapped File

Passing a zero size automatically opens the file in read-only mode. On NT, it is protected (in virtualProtect:), so a write access causes a protection violation. On Windows 95, changing the memory protection of a mapped file doesn't work after it has been mapped (and we need R/W access while loading to be able to fix up object references), so the method defaults to a nop. This means that, under Windows 95, it is possible to write to memory-mapped file that you're not supposed to write to, with the consequence that objects that have been written are not garbage collected.

MMF cannot grow, so it is necessary to specify a maximum size for the file before it is mapped (if you have MSDN, you can find an article about growable MMF; but we have doubts about the robustness). Writing to a MMF that cannot grow (if the specified size is the one of the file) does not generally make sense.

Back To Top


Serializing a SortedCollection to a Memory-Mapped File

In general, you cannot serialize SortedCollection or any object that references a block. Although it may work under certain circumstances, it is not recommended.

Workarounds:

1) Set the sortBlock explicitely after you load the collection.

2) Create a subclass of SortedCollection that sets the sort block to nil and implements sorting; for example:

!MySortedCollection Operations methods !
bsearch: anObject
^self bsearch: anObject sortBlock: [ :a :b | a < b ] ! !

Back To Top


Samples

Philosopher Sample

The Smalltalk expression in the readme.txt file is obsolete. To start the simulation in the development environment, evaluate:

PhiloWindow registerClass.
PhiloWindow new open.

TestWindow

TestWindow requires testwindow.dll at runtime.

Alternatively, you can change the category of #initialize in TestWindow and ResourceSampleBox to .INTERNALDEV. In this case, the DLL is not needed.

You can also change the category of #initialize in TestWindow and ResourceSampleBox to .INTERNALDEV. In this case, the DLL is not needed.

Note: To find out about the problem in the first place, check the debugging options in the image properties, check <recompile all methods>, and build the image.

ActiveX Container Sample

The readme.txt file is modified as below:

To open the sample without using the Resource DLL, remove the methods SampleCalendarDialog>>openOn: and SampleCalendarDialog>>initialize.

Back To Top