| 
 General: 
  C/C++ DLL's 
By: Jack Hoxley (with help from Torsten 
  Damberg and David Goodlad) 
Written: June 2000 
                                Download: 
                                GM_CppDll.Zip 
                                (20kb) GM_DLLDemo 
                                (363kb)   
 
I've been programming in VB for 
  several years and am a firm supporter of the language; however, every so often 
  you get a really good idea in your head that needs raw power to be done. Then 
  you hit the boundaries of the VB language - you've gone so far and it wont take 
  you any faster. In my experience this comes when you're doing maths, algorithms 
  and array manipulation (including memory arrays). You just need that extra speed. 
Visual Basic is and always will 
  be the easier and more fun language to write; the equivilant game written in 
  C++ is much harder, much less friendly to read and will take longer. VB with 
  the power of directX behind it has become a serious contender for making games; 
  It's just several small things that limit your creativity, and these are the 
  things that C++ developers enjoy the ability of using. 
Without a doubt C/C++ and assembler 
  can wipe the floor with VB's array and mathematics functions. There is a way 
  however to keep the bulk of your code in VB and use another (faster) language 
  to the time-critical maths functions. The easiest way is to write a simple C/C++ 
  DLL and link your VB project to it. 
This is what this tutorial will 
  be discussing. There are two points you will need to bare in mind first though: 
  - You will need a basic knowledge 
    of C/C++. I'm no expert of the language, but I can just about pull off most 
    of the things I try. This tutorial can't and won't attempt to teach you C/C++, 
    It will demonstrate how to get started - the rest is up to you.
 
  - You will need a C++ compiler. 
    All the examples here will be saved in Visual C++ format, I do believe that 
    the code will work in other compilers, but I have no way of checking. For 
    the record, I will be using Visual C++ 6.0 professional. Everything will work 
    fine in this version and the enterprise edition, but I'm not sure how it will 
    in the learning edition.
 
 
If you do some simple tests with 
  equivelant VB and C++ code with full optimizations you will find that C++ can 
  sometimes double VB's speed: 
  C++ DLL; 307,200 calculations: <=1ms 
  VB DLL; 307,200 calculations: 170ms 
   
  C++ DLL; counting 0 to 1000: <1ms 
  VB DLL; counting 0 to 1000: 28ms 
   
  For the record, these tests were done on an AMD 500mhz computer. 
  As you can see, in the first test the C++ DLL was an amazing 170x faster. In 
  the second test it was 28x faster - still a substantial improvement. These tests 
  cannot be taken as standards as they were done once on my computer - and are 
  likely to be different from computer to computer. Even if these differences 
  aren't entirely accurate, with even a wide margin you can see that C++ out-performs 
  VB quite dramatically. 
 
Now we can get onto some code. 
  The first part is understanding the differences in variables between C/C++ and 
  Visual Basic. You will need to bare this in mind when it comes to writing your 
  DLL. 
 
  
     
      | C/C++ 
        Variable name | 
      Visual Basic 
        Variable Name | 
     
     
      | Short | 
      Integer | 
     
     
      | Long | 
      Long | 
     
     
      | LPLong | 
      Long | 
     
     
      | Float | 
      Single | 
     
     
      | Double | 
      Double | 
     
     
      | unsigned 
        __int8 | 
      Byte | 
     
     
      | BStr | 
      String | 
     
   
 
This will be needed when you 
  write the entry point for your DLL and when you declare you're DLL in VB. For 
  example: 
void _stdcall DoSomething(short 
  Val); In C++ becomes: 
  Private Declare Sub DoSomething Lib "dllname.dll" (ByVal Val as Integer) 
    
Note the ByVal part - this is 
  important. If you didn't have it here, VB would pass "0", or Nothing. 
  As a general rule you'll need byVal for everything, except pointers that need 
  ByRef. 
You can then use the table above 
  to create the function that you need. The next part of this tutorial will deal 
  with the C++ side of things; If you don't understand this you're best not going 
  any further. 
  - Launch Visual C++ developer 
    Studio - This should be easy
 
  - Create a new "Win32 Dynamic 
    Link Library" project from the new project library
 
  - When the AppWizard screen 
    appears, select "Empty Application" - We want to add the files ourself.
 
  - Visual C++ will then present 
    you with an empty project. On the project menu, select add file.
 
  - In the dialog select "C++ 
    source File" and give it a name
 
  - You will now have a blank 
    document to play with.
 
  - At the same time create another 
    blank document and save it as "projectName.Def" - replace ProjectName 
    with the real project name. This will be discussed later
 
 
Now open up the blank C++ source 
  file and add this code: 
                                        
                                          
                                          
                                            
                                              
                                                
                                                  
                                                    
                                                      
                                                        
                                                          
                                                            
                                                              
                                                                
                                                                  
                                                                    
                                                                      
                                                                        #include "Windows.h" 
        //Any other files that you want can go here... 
         
        //We must prototype all the functions that we wish 
        to use. 
        short _stdcall Multiply(short Num1, short Num2); 
         
        //Now we can create the function 
        short _stdcall Multiply(short Num1, short Num2) 
        { 
        //This code here is executed when VB calls this 
        function 
        return Num1*Num2; //Multiply the numbers and return 
        them 
        }  | 
                                                                       
                                                                    
                                                                   
                                                                 | 
                                                               
                                                            
                                                           
                                                         | 
                                                       
                                                    
                                                   
                                                 | 
                                               
                                            
                                           
                                          
                                         
Okay; so this DLL would do nothing 
  useful whatsoever - VB can do multiplications on it's own. Although C++ may 
  well do it faster; when we only want it done once we'll probably slow the program 
  down as we have to call the DLL, and wait for it to return information. 
There is one last part though: 
  The .Def file. This is important, and can be thought of as a DEFinition file. 
                                        
                                          
                                          
                                            
                                              
                                                
                                                  
                                                    
                                                      
                                                        
                                                          
                                                            
                                                              
                                                                
                                                                  
                                                                    
                                                                      
                                                                        LIBRARY dllname 
       
      EXPORTS 
      Multiply | 
                                                                       
                                                                    
                                                                   
                                                                 | 
                                                               
                                                            
                                                           
                                                         | 
                                                       
                                                    
                                                   
                                                 | 
                                               
                                            
                                           
                                          
                                         
This is very simple and very 
  small; but it will get bigger as you add more procedures. You will have to change 
  the part after LIBRARY to suit your project's name, but that;s it. After EXPORTS 
  you need to name all of the procedures in your project that you want other applications 
  to use, currently for us that only includes "Multiply". 
Compile you're DLL and copy it 
  from the folder to the C:\windows\System folder. Note: you may as well do a 
  straight release version, you're not going to get any Debug data when using 
  it in VB. The Release version can be 4-7x faster as well. 
Now open up VB and start a new 
  Standard EXE project. One form will appear, open it up in the code window and 
  add these lines of code. 
                                        
                                          
                                          
                                            
                                              
                                                
                                                  
                                                    
                                                      
                                                        
                                                          
                                                            
                                                              
                                                                
                                                                  
                                                                    
                                                                      
                                                                        'This goes in 
      the declarations section. 
      Private Declare Function Multiply Lib "dllname.dll" (ByVal Num1 as Integer, 
      ByVal Num2 as Integer) as integer 
       
      Private Sub Form_Load() 
      'We need a value to hold it. 
      dim RetVal as integer 
      RetVal = Multiply(10,170) 
      'RetVal now holds the number 1700 = 10*170 
      'We now convert it to a string for output as a message box. 
      msgbox CStr(RetVal) 
      End Sub | 
                                                                       
                                                                    
                                                                   
                                                                 | 
                                                               
                                                            
                                                           
                                                         | 
                                                       
                                                    
                                                   
                                                 | 
                                               
                                            
                                           
                                          
                                         
Done! You now have a working 
  DLL. It is quite easy to modify this to suit your needs - but you'll need a 
  little bit of C++ knowledge. A word of warning; if you generate an error it 
  may well crash VB - losing everything. Save your project first. 
Using Strings 
  Having a function that returns strings has two main uses as far as I can 
  see: 
  1. Encryption - You could write a DLL that encrypts a string or decrypts a string. 
  This encryption could be quite complicated, as C++ will be able to run through 
  it very quickly 
  2. File access; you could pass it a Filename and it could return an entry in 
  the file; or you could use it with the first option and make it decrypt an encrypted 
  level file and give you're program the data it wants. 
Open the DLL project that you 
  should just have created - and open the source file. 
                                        
                                          
                                          
                                            
                                              
                                                
                                                  
                                                    
                                                      
                                                        
                                                          
                                                            
                                                              
                                                                
                                                                  
                                                                    
                                                                      
                                                                        //Remember to 
      prototype this function 
       
      LONG _stdcall GetStringFromDLL ( BSTR S )  
      {  
      int i;  
      LPSTR pS;  
      pS = (LPSTR) S;  
      for (i=0; i<26; i++)  
      { 
      *pS++='A'+i;  
      }  
      return 26; 
      }  | 
                                                                       
                                                                    
                                                                   
                                                                 | 
                                                               
                                                            
                                                           
                                                         | 
                                                       
                                                    
                                                   
                                                 | 
                                               
                                            
                                           
                                          
                                         
Add an entry in the .Def File 
  to allow VB to access this function. Then open up you're VB project and add 
  this code in: 
                                        
                                          
                                          
                                            
                                              
                                                
                                                  
                                                    
                                                      
                                                        
                                                          
                                                            
                                                              
                                                                
                                                                  
                                                                    
                                                                      
                                                                        'You'll have 
      to declare the DLL in the declaration section - You should be able to do 
      this. 
       
      'The VB string must be big enough to hold the data that the C++ DLL will 
      put in this. The only way of 
      'doing this is to fill it with stuff - the C++ DLL will then overwrite whatever 
      rubbish we put in it. 
      Dim S as String  
      Dim length as Long  
      S = Space(200) 'you should be more precise; but we'll 
      give ourselves a 200 character limit 
      Length = GetStringFromDLL(S)  
      'Output what the DLL returned... 
      MsgBox (Left$(S,Length))  | 
                                                                       
                                                                    
                                                                   
                                                                 | 
                                                               
                                                            
                                                           
                                                         | 
                                                       
                                                    
                                                   
                                                 | 
                                               
                                            
                                           
                                          
                                         
Using Arrays 
  This is the important part, and also where it gets fun. The example project 
  above is fairly useless; it does nothing new, and it wouldn't give us any speed 
  advantages. Array manipulation is where this method will shine. Through arrays 
  you could pass hundreds of thouands of values, even millions. Arrays can also 
  represent picture data - in particular, you could pass a C++ DLL the array of 
  data behind a DirectDraw surface (GetLockedArray) - and allow C++ to modify 
  your surface. This suddenly opens up the door to an amazing amount of possibilities. 
  This example will show you how to invert the colours of A DirectDraw surface 
  (in theory). It also doubles up as a benchmarking program. This First part is 
  the C++ side of things: 
                                        
                                          
                                          
                                            
                                              
                                                
                                                  
                                                    
                                                      
                                                        
                                                          
                                                            
                                                              
                                                                
                                                                  
                                                                    
                                                                      
                                                                        #include "windows.h" 
      //Had to change this to stop it 
      //messing up the HTML!!  
       
      //This is our Prototype 
      //Note the use of pointers  
      void _stdcall BltNot(unsigned __int8 *myarray, short width, short height); 
       
       
      //This is our actual procedure.  
      void _stdcall BltNot(unsigned __int8 *myarray, short width, short height) 
       
      {  
      int x=0; //Create us a variable  
      x= width * height; //This is the total number of entries 
      in the array.  
      unsigned __int8 Val; //Temporary Value  
      for(int i = 0; i x; i++) //Run a loop through the 
      data. There should  
      //be a less-than sign in the prev. line - but it messed 
      up the HTML...  
      {  
      Val = *myarray; //Copy our variable  
      Val = 255 - Val; //Invert our variable  
      *myarray = Val; //Put our variable back  
      myarray++; //Increment the address by one. 
      } | 
                                                                       
                                                                    
                                                                   
                                                                 | 
                                                               
                                                            
                                                           
                                                         | 
                                                       
                                                    
                                                   
                                                 | 
                                               
                                            
                                           
                                          
                                         
Now; accessing the array probably 
  isn't quite what you expected. So I'll explain. In memory, the 2D array: 
  0 1 2 
  0 1 2 
  0 1 2 
  is represented like this: 
012012012 
  or, -FirstRow-SecondRow-Thirdrow- 
  This makes it slightly difficult to access it. You could, if you knew C++, write 
  a loop that did it as a 
for(x) 
for(y) 
  loop, but we're not going to do that here - just for simplicity. We'll just 
  change the whole lot in order - which is fine if you don't mind what the X/Y 
  position the entry represents. 
You cant actually pass an array 
  in a DLL call. Mostly on the grounds that VB will include lots of junk other 
  languages wont like. Instead we use a pointer, this points to where the array 
  starts - it says "This is where the array starts, keep reading from this 
  point onwards". This means that we rely on the VB programmer to give us 
  the correct size - overwise we may screw up. 
When we get into our loop it's 
  fairly simple. On the first run we'll get the value behind the first value - 
  the one that the function is given; then we just keep on going through incrementing 
  the memory address by one each time. 
Now onto VB. This is fairly easy. 
  When it comes to declaring the the DLL use this: 
                                        
                                          
                                          
                                            
                                              
                                                
                                                  
                                                    
                                                      
                                                        
                                                          
                                                            
                                                              
                                                                
                                                                  
                                                                    
                                                                      
                                                                        | private declare sub BltNot 
      lib "Bltnot.Dll" (ByRef myarray as byte, byVal width as integer, 
      ByVal height as integer) | 
                                                                       
                                                                    
                                                                   
                                                                 | 
                                                               
                                                            
                                                           
                                                         | 
                                                       
                                                    
                                                   
                                                 | 
                                               
                                            
                                           
                                          
                                         
It is important that you pass 
  the "myarray" as ByRef - otherwise it wont work. ByRef simply means 
  By Reference - ie, give the DLL it's address. As far as both parts are concerned 
  we are only passing one variable - that's why at no point is there an array 
  declaration. 
When it comes to using the DLL, 
  use this: 
                                        
                                          
                                          
                                            
                                              
                                                
                                                  
                                                    
                                                      
                                                        
                                                          
                                                            
                                                              
                                                                
                                                                  
                                                                    
                                                                      
                                                                        | BltNot Array(0,0),640,480 | 
                                                                       
                                                                    
                                                                   
                                                                 | 
                                                               
                                                            
                                                           
                                                         | 
                                                       
                                                    
                                                   
                                                 | 
                                               
                                            
                                           
                                          
                                         
We want the myarray variable 
  to be where you start from; most cases this will be the first variable in the 
  array (0,0). You could set it so it started elsewhere, but for now we'll let 
  it start at the beginning. Then we specify the height and width. These are 1-x 
  entries, not 0-x. Basically, start counting as normal (we count from 1), not 
  like computers which count starting from 0. Be careful here - if you specify 
  a number greater than the size of the array you'll crash everything. 
You could quite easily modify 
  this to take a 3 dimensional array, or do many other things - it's up to you 
  to experiment; but be careful - one error and you could say goodbye to your 
  computer (until you restart). 
Download the example from the 
  top of this page, or get it from the downloads page. 
  Make sure you read the Info.txt file included. Note, the C++ code is not included, 
  but it is all on this page here. NB: there is a demo application to show you 
  how to use the DLL with DirectDraw surfaces. 
                                       |