creating mixed-language applications (C, assembly language, and Basic).

by David Cary.

related pages:

So you have a Visual Basic program that works just fine, but there's this one little part that's kinda slow and you think you can write a far more efficient implementation of that part in C or assembly language ?

There's no need to translate *everything* to C; you can just speed up that one bottleneck by writing it in C or assembly language, and calling it from Visual Basic. Keep the user interface in Visual Basic, where it will be much easier to change (since this is the part that will likely change the most). (Can this DLL can also be called from a Excel spreadsheet using Visual Basic for Applications ?).

You generally only need to write only 1 DLL, since it can contain lots of independent functions, but this method also works for creating lots of DLLs.

Alas, "Calling DLL Functions from Visual Basic Applications" http://premium.microsoft.com/msdn/library/devprods/vc++/progsguide/f26/d2b/s25d9.htm is very scrambled, so I'm writing my own version. (Hopefully this will be easier to understand than the Microsoft "documentation").

My documentation is specific to Microsoft Visual C++ 5.0 Microsoft Visual Basic 5.0 .

I assume you are familiar with writing functions in C or assembly language, and with writing applications in Visual Basic.

Please replace the word "gleeptron" in the remainder of this document with the name you want to give the DLL file.

1. Create a new DLL
  1.1. Start Microsoft Developer Studio
  1.2  File | New
       Choose "MFC AppWizard (dll)"
	(I think "Win32 Dynamic-Link Library" will also work).

	I always enable "Create new workspace",
	and then type in the name I want to give the DLL:
	"gleeptron"
	(without the quotes, and without a ".dll" extension).
The AppWizard will create a new directory with that name and create lots of files inside it.

You might want to glance at the "ReadMe.txt" file it creates. The

gleeptron.h
gleeptron.cpp
gleeptron.def
are the only files that you really need to modify.

You also might want to update the version information by double-clicking on gleeptron.rc and filling in the blanks.

Write new functions in gleeptron.cpp, (copy their prototypes to gleeptron.h ), then copy just the name of each new function on a line by itself at the end of gleeptron.def (i.e., in the EXPORTS section), something like

	my_function

(see ? nothing before the function name, nothing after the function name, no parenthesis ... ... just a list of function names, one per line.)

LISTING ALL EXPORTED FUNCTIONS IN THE ".def" FILE IS VERY IMPORTANT. I've heard rumours of DLLs being created without a ".def" file, but I've never seen it actually work.

Make sure these new functions are wrapped with

extern "C" {
...
//new functions
...
}
in both the .cpp and the .h files.

(Alternatively, you could add the "WINAPI" or the "APIENTRY" to the proper location in the function definition,

int APIENTRY my_function(...){
	...
	}
the WINDEF.H include file defines these to
#define WINAPI      __stdcall
#define APIENTRY    WINAPI
)

If you're a hardcore assembly hacker, put your assembly language code inside

__asm{
...
// clever assembly-language code
...
}

which, in turn, can be placed inside a function. ...

Writing a Visual Basic program to call the DLL

More information can be found by running the "Books Online" application that came with Visual Basic, and look at "Component Tools Guide" / "Accessing DLLS and the Windows API" / "Converting C Declarations to Visual Basic".

It says "The procedures in DLLs are most commonly documented using C language syntax. To call these procedures from Visual Basic, you need to translate them into valid Declare statements and call them with the correct arguments."

My most commonly used variable types (and hence the values I use when I "Declare" my C functions in my Visual Basic programs) are
C function <==> BASIC Declare
(function inputs)
int variable ByVal variable As Long
uint variable ByVal variable As Long
char variable ByVal variable As Byte
short int variable ByVal variable As Integer
WORD variable ByVal variable As Integer
const char * variable ByVal variable As String (this only works passing strings *from* BASIC *to* C)
(function outputs)
int * variable ByRef variable As Long
float * variable ByRef variable As Single
double * variable ByRef variable As Double
double variable[], uint length ByRef variable As Double, ByVal variable As Long
(misc)
void procedure Sub procedure

(The ByRef keyword is optional, but I like the reminder).

Since the native Visual Basic array type is different from the native C array type, you must perform a conversion when you pass an array -- either on the C side (safer) or on the Visual Basic side (easier).

Bruce McKinney recommends always passing SafeArrays (the native Visual Basic array type), and doing the work necessary to parse them on the C side. He describes this in detail at http://www.microsoft.com/oledev/olecom/article5.htm Bruce McKinney's articles on writing C/C++ DLL's for VB. are in MSDN | Technical Articles | Visual Tools | Visual Basic | Creating DLLs for Visual Basic. Extending Visual Basic with C++ DLLs

If your C functions expect native C arrays, then conversion needs to take place on the Visual Basic side. Basically you pass by reference the first item in the array, and rely on the fact that arrays are contigous in memory. Because C cannot tell just by the array pointer how big an array is, every C function that accepts an array must also be told how big that array is, typically with a integer value that represents the "length" of the array (i.e., a number that is one more than the largest possible index). the the Visual Basic line that makes the call will need to mention UBOUND (and LBOUND if the array is not zero-based). for example, the function

extern "C" {

	int my_function( int list_length, double the_list[] )

}
can be called in your Visual Basic code with
	Dim list(200) AS Double
	x = my_function UBOUND(list)+1, list(0)
[untested -- is this right ?]

...

Annoyance: If you just select "run" from the Visual Basic menus, sometimes it won't be able to find "gleeptron.dll", even though when you create the executable, then exit the Visual Basic development environment and run that executable, it runs just fine. ... ...

If you do not specify a path for libname, Visual Basic will search for the file in the following order:

  1. Directory containing the .exe file
  2. Current directory
  3. Windows system directory (often but not necessarily \Windows\System)
  4. Windows directory (not necessarily \Windows)
  5. Path environment variable
-- http://premium.microsoft.com/msdn/library/devprods/vb/vb50docs/F1/D5/S1AF96.HTM ...

"For more information about creating type libraries, see Chapter 7 in Volume 2 of the OLE 2 Programmer's Reference."

In an intriguing case of synchronicity, one of the books I read was an extraordinary one titled The Chalice and the Blade, by Riane Eisler.1 The thesis of Eisler's book is that our culture is approaching a point in history at which we will choose between a dominator model (a few individuals and organizations ruling and commanding the masses) and a partnership model (everyone working together on an equal basis). The former promises a breakdown of cultural evolution; the latter, a breakthrough. In Eisler's words, "Human evolution is now at a crossroads. Stripped to its essentials, the central human task is how to organize society to promote the survival of our species and the development of our unique potentials…. Humans [as evolutionary theorist Erwin Laszlo points out] 'have the ability to act consciously, and collectively,' exercising foresight to 'choose their own evolutionary path.' "2

I see a similar crossroads in the state of the software industry today. Perhaps the choices we have in the software business are merely metaphorical aspects of humanity's overall cultural evolution. (Perhaps this is stage three of OLE nirvana.) Today we have a dominator model—millions of computer users are limited by a few applications created by a few large companies. Component software, however, is a computing environment in which diverse objects created by varied groups and individuals work together, in partnership, to empower all users to solve problems themselves and to create their own software solutions. The software industry can choose either to perpetuate its excessively competitive ways or to build a market in which winning does not have to come at the expense of everything else. Our current ways seek a homogeneous end—one company's products dominating the market. Instead, we can seek an end for which diversity is the most important factor.

In a component software environment, one's potential is enriched by the diversity of available components and the diversity of available tools. The greater the diversity, the greater our potential. This holds true whether we are discussing software or society.

-- from _Inside OLE 2_ by http://premium.microsoft.com/msdn/library/books/techlang/inole/D1/S10AA.HTM

Extending Visual Basic with C++ DLLs http://www.microsoft.com/devonly/tech/ole/olecom/cpp4vb.htm especially "Article 2. Libraries Made Too Easy" http://www.microsoft.com/oledev/olecom/article2.htm (has everything you need to know to make a DLL in Visual C++, with some notes on Borland C++) but his example didn't compile on my system -- "ambiguous =" error.

Writing a DLL in C++ http://netmar.com/~gdjones/wrappers.html has a tiny bit example of Visual Basic calling a DLL written in C++.

"Strings the OLE Way" by Bruce McKinney http://www.microsoft.com/oledev/olecom/article3.htm

"In normal C++ programming, you should use the generic versions of functions and types as much as possible, so that your strings will work in either Unicode or ANSI builds."

... "how is [BSTR] different from the null-terminated strings that C++ programmers know so well? Internally, ... The string length is maintained in a long variable just before the start address being pointed to, and the string always has an extra null character after the last character of the string. This null isn't part of the string, and you may have additional nulls embedded in the string.

That's the technical difference. The philosophical difference is that the contents of BSTRs are sacred. You're not allowed to modify the characters except according to very strict rules that we'll get to in a minute. OLE provides functions for allocating, reallocating, and destroying BSTRs. If you own an allocated BSTR, you may modify its contents as long as you don't change its size. Because every BSTR is, among other things, a pointer to a null-terminated string, you may pass one to any string function that expects a read-only (const) C string."

misc

Is this true ?

[[

I hear that the "WinExec( CommandLine, Show)" function will allow you to call DOS programs from Windows programs (you might want to set up th .PIF file for that DOS program ... foreground, full-screen, background ... execution priority ...).

If your windows program calculates parameters that it wants to pass to that DOS program, specify "%1 %2 %3 %4 ... %9" in the .PIF file (just like a batch file) and then WinExec() the .PIF file (not the DOS program it refers to) with the desired parameters.

]]

unsorted


Started 1998-08-11

Original Author: David Cary.

Send comments, suggestions, bug reports to

David Cary feedback.html
d.cary@ieee.org.

Return to index

end http://rdrop.com/~cary/program/mixed_language.html