Smalltalk MT Faq - General Development Issues

Home | Up | Search | X-Zone News | Services | Book Support | Links | Feedback | Smalltalk MT | The Scrapyard | FAQ | Technical Articles

 

General Development Issues

Deliverable Files

Questions about source code strategy

What is the purpose of the message #_asCinteger ?

How would I do it if I wanted to use runtime binding?

Is there a Notifier in Smalltalk MT?

What is the purpose of the window property 'STOBJ'?

ApplicationProcess allInstances is empty

Handling Windows Messages

I evaluated the following: [ ^self ] fork, and it raises an exception

Getting Started with Projects...

Building an Executable

Debugging an Executable

Miscellaneous Memory Allocation Questions

How do I stop a thread?

How do I exit Smalltalk MT by program?

MappedObjectStream only stores one object

Closing a memory-mapped file while keeping a reference generates an exception

Sample Code: Loading and unloading ActiveX Controls dynamically

Deliverable Files

What follows is a description of the files that you must or may ship with a Smalltalk MT executable:

File Description Status
strtdllx.DLL runtime routines for ST/MT (15K) required
xxx your executable (EXE or DLL) required
stmsg.DLL error messages (roll your own) should be integrated into executable
stsrl.DLL serialization routines (60K) optional

Note: strtdllx.DLL is named strtdll20.dll in Smalltalk MT 2.0 and strtdll25.dll in version 2.5/2.6. Strictly speaking, runtime Dlls are not version-dependant. We changed the functionality exposed by the runtime Dll. In addition, the latest Dlls have been compiled with VC 6, which resulted in an increase in code size.

Questions about source code strategy

Underscores Underscores identify private messages, classes, or pseudo-messages (inlined messages).
Class libraries Smalltalk MT only implements classes / methods that are also used by the development environment or samples. That way, all source code has been tested.
Back to Top

What is the purpose of #_asCinteger?

The message #_asCinteger is sent implicitly to API arguments, and returns a 32 bit representation of an object. If the object cannot be passed to an API (for example nil), it raises an exception.

You can bypass this message with one of the messages #basicAddress, #_asInteger, or more generally with an inline message that returns a known type (such as #_longAtOffset:).
 

Note that the address manipulation methods (#atAddress:put: etc.) do not send the message, so you may have to send it yourself if the parameter is not an integer or LONG type. Example: MemoryManager atAddress: ppEnumConnectionPoints put: ienum _asCinteger.

Back to Top

How would I do it if I wanted to use runtime binding?

It would be similar to what you do in C:

    module := HModule loadLibrary: 'XYZ.DLL'.
    module callProc: szAPI with: arg1 with: arg2

You can take a look at #callProc:with:with: and add more parameters as you
go. It is based on the pseudo-message #callAPI (which generates inline code). Use
#stdcallAPI for the C calling convention.

Back to Top

Is there a Notifier in Smalltalk MT?

Each window implements its own window procedure (although most windows inherit the default window procedure in Window), and exports it to the operating system.

A Smalltalk-created window other than a dialog box reserves private data in its window creation structure, and stores the address of the Smalltalk object in that slot (remember: in ST/MT, objects are never moved).

A DialogBox functions exactly like a Dialog Box in C/C++ ; each instance has its own dialog procedure that references the Smalltalk object.

Back to Top

What is the purpose of the window property 'STOBJ' that is associated with a window?

The purpose of this property is to be able to retrieve the Smalltalk object associated with a window, given the window handle. If the window is not maintained by Smalltalk (i.e., the window procedure is implemented elsewhere), the property will not be set.

Back to Top

I evaluated the following: [ ^self ] fork, and it raises an exception

The return statement in a block returns from the method that defines the block. If you create a thread that executes this block, the new thread would attempt to return from a method context in a parallel thread, therefore switching the stack and crashing. And your original thread would crash as well because the other thread corrupted its stack. Therefore, the code that actually returns verifies that the return is valid, and raises a software exception if it cannot return.

Back to Top

How do I stop a forked thread?

A forked block exits naturally. If you created the thread with one of the #fork messages, there is no need to worry about exiting the thread. For example:

[ | a | 1000 timesRepeat: [ a := a + 1 ] ] fork

exits when it is done.

In Win32, the only general way to kill a thread is to use TerminateThread. This works on Smalltalk MT created threads as well, but leaves some Win32 resources open (refer to the Win32 documentation).

So, terminating a thread in an orderly fashion requires some programming logic and synchronization. The Philosopher sample shows how to do this.

Back to Top

Getting Started with Projects...

A Windows Application requires at least the following components:

bulletsource code in project format (.SP)
bulletresources (in a DLL)

In order to open an application such as Generic, you must first install the project file (generic.sp). The next step is to copy the resources into your path (i.e., copy generic.dll to your image directory). You can then open the application (look at the readme.txt file that comes with the sample to see how to do this).

Some projects require additional components before they can be used. The Project Browser will take care of it automatically (linking with DLLs and installing prerequisite projects).

Back to Top

Building an Executable

You must first define a method ApplicationProcess>>winMain:with:with:with: that starts up your main window (or whatever you want to do in the process). Define this method in a file called winmain.sm and place the file in the project directory.

The Project Browser checks for a winmain.sm file in the current project directory before it enables the build EXE menu item.

Back to Top

Debugging an Executable

The generated executable raises an exception at runtime:

---------------------------------------------------------------------------
An instance of  "Undefined Object" does not understand the
message "Symbol(16r1000ACA)".  The message was sent
by an instance of "TestClient"
---------------------------------------------------------------------------
 

If you enable all error assertions you'll see the message box of the type "an instance of XXX did not understand Symbol(yyy), the message was sent by an instance of ZZZ".

In the development, evaluate

Symbol value: 16r1000ACA

and it returns #show:.

You can also run the executable in WinDbg or use DBMON on Windows NT to view debugging messages.

Back to Top

Miscellaneous Memory Allocation Questions

a) #basicAddress always returns an integer that is the object's address.

b) once you take an object's address, you must ensure that it is still referenced. The following is wrong:

| findStruct |
findStruct := FINDTEXT new.

findStruct lpstrText: 'Courier',' New'.

The reason is that 'Courier',' New' creates a new string which is stored in a structure. The structure is a byte object and stores the pointer as a 32-bit value, meaning that there are no more references to the string, which can be collected. So, use the code below instead:

| findStruct szText|
findStruct := FINDTEXT new.
szText := 'Courier',' New'.
findStruct lpstrText: szFont.
self doSomethingWith: findStruct.
^'and now szText gets out of scope'
 

c) to be sure an object is not discarded, you can:

- use #registerObject / #releaseObject

- allocate it on the external heap:

(String heapAlloc: szFont size) replaceFrom: 1 to: szFont size with: szFont; yourself

and free it using heapFree.

Back to Top

 

ApplicationProcess allInstances is empty

ApplicationProcess is instantiated with the message heapAlloc. The method allInstances only returns instances allocated on the Smalltalk heap.

Back to Top

Handling Window messages

To process a window message such as WM_MOUSEMOVE, just implement a method named WM_XXX:with: (for a WM_XXX message). Don't forget to return a value as specified by the documentation of the message.

If the window that receives the message is a Windows control, the method above will not directly work because the window procedure is implemented by the control. First, check whether the control send notification events to its parent. In this case, install an event handler for the notification (using #when:in:perform:). Otherwise, create a Smalltalk subclass of the control that implements the windows message in question, and send it the message #subclassWindow upon creation. The TwinEdit sample on the distribution set demonstrates this.

Back to Top

How to exit Smalltalk MT?

To exit the development environment programmatically, evaluate:

Processor close

Back to Top

MappedObjectStream only stores one object

In Smalltalk MT, the standard serialization methods use memory-mapping technology. MappedObjectStream implements an easy-to use interface. An instance of MappedObjectStream maps a set of objects into the address space of the process. The top-level object is called the root object. There is only one root object, but it can be arbitrary, and in particular it can be a collection.

If you open an existing file in RW mode, you can modify any object that resides in the file. When the file is saved, external references are automatically appended to the end of the file. If you wish to add more objects, you can add them to the root collection or to any other object in the file.

See also Object Serialization Example.

Back to Top

Closing a memory-mapped file while keeping a reference generates an exception

The garbage collector (GC) scans all object references. If you close the mapped file while keeping a reference, an otherwise valid reference will suddenly point to nowhere. The GC algorithm assumes that an object reference is always valid. Otherwise, object traversal would be much slower.

See also Object Serialization Example.

Back to Top

Loading and unloading ActiveX Controls dynamically

It is possible to load and unload ActiveX controls at runtime. One benefit is that an application can delay loading an ActiveX control until it is actually needed, which reduces the application startup time.

The following code fragment loads an ActiveX control (the Microsoft Web Browser Control) dynamically. The application must first create an OleContainerWindow.

| oleContainer if |
oleContainer := self childAt: IDC_CONTROL1.
(if := IClassFactory coCreateInstance: (GUID IIDFromString:
'{8856F961-340A-11D0-A96B-00C04FD705A2}' )
interface: IUnknown) notNil ifTrue: [

oleContainer insertObject: if.
oleContainer invoke: 500 with: 'D:\\index.htm'.

].

To unload the control:

oleContainer unload.

Back to Top

This site, created by DirectX MVP Robert Dunlop and aided by the work of other volunteers, provides a free on-line resource for DirectX programmers.

Special thanks to WWW.MVPS.ORG, for providing a permanent home for this site.

Visitors Since 1/1/2000: Hit Counter
Last updated: 07/26/05.