Debugging Microsoft Transaction Server Components

Microsoft Corporation

June 1997

Abstract

This article suggests techniques for debugging Microsoft® Transaction Server (MTS) components written with Microsoft Visual Basic®, Microsoft Visual C++®, and Microsoft Visual J++™ development tools. These suggestions are not mandatory for successfully debugging MTS components; you can choose your debugging environment and techniques according to your application needs.

If you are using MTS components in a distributed environment, it is recommended that you debug your components on a single computer before deploying to multiple servers. Components that function without error in a package on a local computer should generally run successfully over a distributed network. If you do encounter problems with distributed components, you will have to test and debug both the client and server machines to determine the problem. It is also recommended that you stress test your application with as many clients as possible. You can build a test client that simulates multiple clients to perform the stress test on your application.

Debugging Visual Basic MTS Components

MTS components that are written in Visual Basic version 5.0 or Visual C++ version 5.0 can be debugged in the Microsoft Visual Studio™ 97 Integrated Development Environment (IDE).

If you want to debug your components after they are compiled, you cannot use the Visual Basic 5.0 debugger, which only debugs at design time. To debug a compiled Visual Basic component, you will need to use the Visual Studio 97 IDE debugger.

Follow these steps to configure the Visual Studio IDE to debug MTS components built with Visual Basic 5.0:

  1. In the Visual Basic IDE, click Properties on the Project menu and then click the Compile tab to select the Compile to Native Code and the Create Symbolic Debug Info check box. You should also select the No Optimization check box while debugging.

  2. In the MTS Explorer, right-click on the package in which your component is installed and select the Properties option. Place your cursor over the Package ID and select and copy the GUID to the clipboard.

  3. Open the Visual Studio IDE. On the File menu, click Open and select the DLL containing the component that you want to debug.

  4. Select Project Settings, then click the Debug tab. Select the MTS executable for the debug session (\mtx\mtx.exe). Enter the program arguments as /p:{<package GUID>} for the package GUID that you copied from the package properties. MTS 2.0 allows for the package name to be used in place of the GUID. Open the .cls files containing the code that you want to debug and then set your breakpoints. You may also want to display variable information in the debug environment by going to the Visual Studio Tools menu, selecting Options, and then selecting the Debug tab. In the Debug tab, place a check next to Display Unicode Strings.

  5. In the MTS Explorer, shut down all server processes.

  6. In the Visual Studio IDE, select Build and then Start Debug. Next, select Go to run the server process that will host your component(s), and set breakpoints to step through your code.

  7. Run your client application to access and debug your components in the Visual Studio IDE.

  8. Before you deploy your application, remember to select one of the optimizing options in the Compile tab on the Project menu of the Visual Basic IDE (set to No Optimization in Step 1), and clear the Create Symbolic Debug Info check box and recompile the project.

In order to facilitate application debugging using Visual Basic 5.0, a component that uses ObjectContext can be debugged by enabling a special version of the object context. This debug-only version is enabled by creating the following registry key:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Transaction Server\Debug\RunWithoutContext

Note that when running in debug mode, none of the functionality of MTS is actually enabled. GetObjectContext will return the debug ObjectContext rather than returning Nothing. See the MTS Readme file for more information.

You can also develop your own testing message box functions to generate an assert in an MTS Visual Basic component. The following sample code can be used to display error messages while debugging Visual Basic code. You can also use this in conjunction with the Microsoft Windows NT® debugger (WinDbg), a 32-bit application that, along with a collection of DLLs, is used for debugging the Kernel, device drivers, and applications. Note that you must enter DEBUGGING = -1 in the Conditional Compilation dialog box (located on the Make tab of the Project Properties dialog box) to enable the assert.

Example

#If DEBUGGING Then
    'API Functions
    Private Declare Sub OutputDebugStringA _
        Lib "KERNEL32" (ByVal strError As String)
    Private Declare Function MessageBoxA _
        Lib "USER32" (ByVal hwnd As Long, _
        ByVal lpText As String, _
        ByVal lpCaption As String, _
        ByVal uType As Long) As Long
    'API Constants
    Private Const API_NULL                  As Long = 0
    Private Const MB_ICONERROR              As Long = &H10
    Private Const MB_SERVICE_NOTIFICATION As Long = &H200000

Public Sub DebugPrint(ByVal strError As String)
    Call OutputDebugStringA(strError)
End Sub

Public Sub DebugMessage(ByVal strError As String)
    Dim lngReturn As Long
        lngReturn = MessageBoxA(API_NULL, strError, "Error In Component", _
            MB_ICONERROR Or MB_SERVICE_NOTIFICATION)
End Sub
#End If

You can then run checks through your code to aid stress debugging.

Example

SetobjObjectContext=GetObjectContext()
#If DEBUGGING Then
If objObjectContext Is Nothing Then Call DebugMessage(“Context is Not Available”)
#End If

Debugging Visual C++ Microsoft Transaction Server Components

You can use Visual Studio 97 to debug MTS components written in Visual C++, including components that call Microsoft SQL Server™ functions or stored procedures. (See the section on debugging Visual Basic MTS components above for more information.)

If you are using Visual Studio and Microsoft Foundation Classes (MFC) to debug, the TRACE macro can facilitate your debugging. The TRACE macro is an output debug function that traces debugging output to evaluate argument validity. The TRACE macro expressions specify a variable number of arguments that are used in exactly the same way that a variable number of arguments are used in the run-time function printf. The TRACE macro provides similar functionality to the printf function by sending a formatted string to a dump device such as a file or debug monitor. Like printf for C programs under MS-DOS, the TRACE macro is a convenient way to track the value of variables as your program executes. In the Debug environment, the TRACE macro output goes to afxDump. In the Release environment, the TRACE macro output does nothing.

Example

// example for TRACE
int i = 1;
char sz[] = "one";
TRACE( "Integer = %d, String = %s\n", i, sz );
// Output: 'Integer = 1, String = one'

The TRACE macro is available only in the debug version of MFC, but a similar function could be written for use without MFC. For more information on using the TRACE macro, see the "MFC Debugging Support" section in the Microsoft Visual C++ Programmer's Guide (on the Library at Developer Products, Visual C++).

Note that you should avoid using standard ASSERT code in Visual C++. Instead, it is recommended that you write assert macros like a MessageBox using the MB_SERVICE_NOTIFICATION flag, and TRACE macro statements using the OutputDebugString function call.

Debugging Java Classes

Debug your Java classes as thoroughly as possible before converting the Java classes into MTS components. Note that once your Java class is converted into an MTS component, it is not possible to step through the code in the Visual J++ debugger (or any current debugging tool).

Using Visual J++ to Debug Java Classes

Microsoft Visual J++ provides a Java debugger that you can use to set breakpoints in your code. Note that when you are using Visual J++ to debug, if you set a breakpoint in a Java source file before starting the debugging session, Visual J++ sometimes will not stop on the breakpoint. For performance reasons, the debugger pre-loads only the main class of your project. The main class is either the class with the same name as the project or the class you specify in Visual J++. If you set breakpoints in other classes via the editor before the classes are loaded, the breakpoints are disabled.

You can do one of the following to load the correct class so that the debugger stops at breakpoints:

Also, when a method has one or more overloaded versions and shows up as a called method in the Call Stack window, the type and value for the parameters are not displayed in some cases. It appears as though the method takes no parameters. This occurs when the method being called is not defined as the first version of the overloaded method in the class definition. For example, see the following class definition.

public class Test
   {
       int method(short s)
       {
           return s;
       }

       int method(int i)
       {
           return i;
       }
   }

If you were looking at a call to the second version of the method in the Call Stack window, it would appear without the type and value for the method:

method()

To view the method’s parameters, change the order of the method overloads so that the method you are currently debugging is first in the class definition.

printf-style Debugging

You can use printf-style debugging to debug your Java classes without using a debugger. printf-style debugging involves including status text messages into your code, allowing you to "step through" your code without a debugger. You can also use printf-style debugging to return error information. The following code shows how you can add a System.out.println call to the try clause of the Hellojtx.HelloObj.SayHello sample.

try
{    
System.out.println("This message is from the HelloObj implementation");
    result[0] = "Hello from simple MTS Java sample";
    MTx.GetObjectContext().SetComplete();
    return 0;
}

The client must be a Java client class, and you must use the JVIEW console window to run that class. Note that you should configure your component to run in the process of its caller (in this case, the JVIEW). Otherwise, this debugging technique results in your component running in the MTS server process (mtx.exe), which would put the println output in the bit bucket rather than the JVIEW console window.

Use the MTS Explorer to configure your component to run in the caller's process by following these steps.

  1. Right-click on the component.

  2. Click the Properties option.

  3. Click the Activation tab and clear the "In a server process on this computer" check box.

  4. Select the "In the creator’s process..." check box.

  5. Reload the Client class. Your component's println calls will be visible in the JVIEW console window.

Using the AWT Classes

You can also use the Abstract Window Toolkit (AWT) classes to display intermediate results, even if your component is running in a server process. The java.awt package provides an integrated set of classes to manage user interface components such as windows, dialog boxes, buttons, checkboxes, lists, menus, scrollbars, and text fields.

The following example demonstrates how to use the AWT classes to display intermediate results in a dialog box:

import java.awt.*;

public final class MyMessage extends Frame
{

    private Button closeButton;
    private Label textLabel;

    // constructor
    public MyMessage(String msg)
    {
        super("Debug Window");

        Panel panel;

        textLabel = new Label (msg, Label.CENTER);
        closeButton = new Button ("Close");

        setLayout (new BorderLayout (15, 15));
        add ("Center", textLabel);

        add ("South", closeButton);

        pack();
        show();
    }

    public boolean action (Event e, Object arg)
    {

        if (e.target == closeButton)
        {
            hide();
            dispose();
            return true;
        }

        return false;
    }

}

Asynchronous Java Garbage Collection

Note that garbage collection for Java components is asynchronous to program execution and can cause unexpected behavior. This behavior especially affects MTS components that perform functions like enumerating through the collections in the catalog, because the collection count will be too high (since garbage collection of pointers is not synchronized). To force synchronous release of references to COM or MTS objects, you can use the release method defined in class com.ms.com.ComLib.

Example

Import com.ms.com.ComLib
…
ComLib.release(someMTSObject);

This method releases the reference to the object when the call is executed. Release the object reference when you are sure that the reference will not be used again. Note that if you fail to release the reference, you will not be returned an application error. However, your collection count will be incorrect because the object reference will be released asynchronously when the garbage collector eventually runs.

You can also force the release of your pointer and not call that released pointer again.

Example

myHello = null;
    System.gc();

Note that forcing the release of a pointer consumes extensive system resources. It is recommended that you use the release method defined in class com.ms.com.ComLib to enable synchronous release of references to MTS objects.