// // VBUTIL.CPP // // Source file for ArchiveLib 2.0 // // Copyright (c) Greenleaf Software, Inc. 1994-1996 // All Rights Reserved // // CONTENTS // // ALCreateVBString() // // REVISION HISTORY // // February 14, 1996 2.0A : New release #include "arclib.h" #if !defined( AL_IBM ) #pragma hdrstop #endif #include "memstore.h" #include "_vbutil.h" // // NAME // // ALCreateVBString() // // PLATFORMS/ENVIRONMENTS // // Windows // C++ // // SHORT DESCRIPTION // // Create a Visual Basic string object using the VB environment. // // C++ SYNOPSIS // // #include "arclib.h" // // extern "C" long _far _pascal // ALCreateVBString( const char _far *string, unsigned short int l ); // // C SYNOPSIS // // This function is only used internally by the library C++ code. // // VB SYNOPSIS // // This function is only used internally by the library C++ code. // // DELPHI SYNOPSIS // // This function is only used internally by the library C++ code. // // ARGUMENTS // // string : A standard C character array that is going to be returned to // a Visual Basic calling routine. // // l : The length of the array. Admittedly, it would be // pretty easy to figure that out here, but this routine // is replacing an assembly routine that didn't know // how to do it. And besides, I like being able to pass // binary data, which might not work with strlen(). // // DESCRIPTION // // If you poke around inside Microsoft's VB CDK, you will see that the // actual code to create a VB string can be replaced by a function // that does this: // // mov bx,50 // jmp dword ptr ss:[20] // // Originally, ArchiveLib created VB strings using an assembly language // routine that looked just like this. However, I didn't want everyone // to have to pay the price of having an assembler just to support two // lines of code. // // Replacing this with in line assembly looked doable. The big problem // is that the VB routine located at ss:[20] expects the stack to // be in just the right configuration when you call it, and that isn't // always easy to accomplish in the middle of a C routine. What makes // it really difficult is that the VB guy is going to return by way of // a far return. // // Writing the assembly code to accomplish this wasn't so bad. Using // Microsoft's inline assembly it was fairly easy to make it happen. // Unfortunately, Borland's assembler couldn't handle calls to local // labels, and Symantec's couldn't handle calls or jumps to local labels. // In both cases, you could work around this by inserting the correct // bytes. // // This was nothing compared to Watcom. They don't even support in // line assembly. When I asked them about this, one of their developers // got real snotty and pointed out that the C++ spec only requires that // the asm statements pass through the compiler without generating // any errors, they don't actually have to do anything. That attitude // might explain Watcom's current position in the C++ market. // // Anyway, to make Watcom work you have to create this strange pragma, // which I did, out of the goodness of my heart. It would have been // more satisfying to just not support them. // // RETURNS // // A Visual Basic string thingy. I'm not even sure what it is, a pointer, // a handle, and index or what. VB knows. // // EXAMPLE // // SEE ALSO // // REVISION HISTORY // // February 14, 1996 2.0A : New release // // // If watcom, we do it this way, everyone else is different. // #if defined( AL_WATCOM ) long call_vb( unsigned short int, unsigned short int, unsigned short int ); #pragma aux call_vb = \ 0x0cc \ "push ax" \ "push bx" \ "push cx" \ "mov bx,50h" \ "push cs" \ 0x0e8 0x02 0x00 \ 0x0eb 0x05 \ 0x36 0x0ff 0x2e 0x20 0x00 \ parm [ax] [bx] [cx] \ modify [ax bx cx dx si di] extern "C" long _far _pascal ALCreateVBString( char *string, unsigned short int l ) { long result = call_vb( (unsigned short int) (((long) string ) >> 16 ), (unsigned short int) (((long) string) & 0xffff), l ); return result; } #else // #if defined( AL_WATCOM ) // // I think this here just to make some error messages go away. // #if defined( AL_MICROSOFT ) #pragma optimize( "", off ) #endif extern "C" long _far _pascal ALCreateVBString( const char _far *string, /* Tag protected function */ unsigned short int l ) { unsigned short int string_seg = (unsigned short int) (((long) string ) >> 16 ); unsigned short int string_off = (unsigned short int) (((long) string) & 0xffff); unsigned short int result_seg; unsigned short int result_off; // // The VB routine we jump to is going to return to me with a far call, // so after setting the arguments up on the stack, I do a push CS before // a local call. At that point the stack is set up perfectly, and I // can jump to the VB guy. // _asm { push string_seg; push string_off; push l; mov bx, 0x50; push cs //This makes my near call look like a far call // // What you see next is three different ways of making the same // four lines of ASM code work. // #if defined( AL_SYMANTEC ) db 0e8H db 02H db 00H db 0ebh db 05H db 36H db 0ffH db 2eH db 20H db 00H #elif defined( AL_BORLAND ) db 0x0e8, 0x02, 0x00; db 0xeb, 0x05; jmp dword ptr ss:[0x20]; #else call do_vb jmp done do_vb: jmp dword ptr ss:[0x20]; done: #endif mov result_seg, dx; mov result_off, ax; } return ( (long) result_seg << 16 ) + result_off; } #endif // #if defined( AL_WATCOM )