EDais - C++ DLLs in VB

download EDais - C++ DLLs in VB

of 28

Transcript of EDais - C++ DLLs in VB

  • 8/8/2019 EDais - C++ DLLs in VB

    1/28

    1

    EDais graphics programming tutorial

    Using C++ DLL's in VB

    Written by Mike D Sutton Of EDais

    Http://www.mvps.org/EDais/

    [email protected]

    - 29.08.2002 -

    Http://www.mvps.org/EDais/

    http://www.mvps.org/EDais/mailto:[email protected]:[email protected]://www.mvps.org/EDais/
  • 8/8/2019 EDais - C++ DLLs in VB

    2/28

    2

    EDais graphics programming tutorial

    IInnttrroo

    dduuccttiioonn Introduction

    VB is one of the nicest languages Ive ever written in which is why Ive decided to

    keep supporting it here with another tutorial on the subject which will hopefully give

    it a little extra lease of life to those developers feeling forced into other languages.I will however be the first to admit that VB does have some downfalls, which other

    languages are far more capable in - Knowing the strengths and weakness of a

    language can greatly enhance your final applications by exploiting the best parts of

    each development language and linking those parts together into the final application.

    One common way of linking bits of applications together is to use DLLs (Dynamic

    link libraries), which can be called upon from other applications to enhance their

    functionality, and they are what this tutorial covers. Ill be concentrating on VC++

    and VC++.NET in this tutorial however the same principles could most likely be

    applied in other similar languages.

    Since I dont want to assume that everyone has (or even wants to use) VC++.NET, Illbe writing this tutorial in tandem between that and VC++ 6 so you can pick and

    choose which sections you want to read. The majority of the code is exactly the same

    though since VC++ didnt receive the huge shake-up that VB did in the .NET

    conversion so only the first chapter will be written twice, the rest is coded the same in

    either development language.

    This tutorial assumes no prior knowledge of C++ and approaches the language from a

    VB developers perspective unlike most other C++ tutorials. Any prior knowledge of

    basic syntax would be preferable to ease the learning curve though, and a basic

    knowledge of the Win32 API would as always be useful but again not essential.

    Note: Anywhere in the tutorial when you see [DLLName], replace it with the name of

    your DLL and similarly [DLLPath] with the path of your DLL, this just means I can

    keep the majority of the tutorial generic under both VC++ and VC++.NET.

    Chapter 1a - Creating the DLL (VC++ 6)

    Chapter 1b - Creating the DLL (VC.NET)

    Chapter 2 - Using the DLL from VB

    Chapter 3 - Passing values into the DLL

    Chapter 4 - Passing Arrays to a DLL

    Chapter 5 - Using the Win32 API in a C++ DLLAppendix I - Variable types & compile configurations

    Appendix II - C++ Syntax

    Http://www.mvps.org/EDais/

  • 8/8/2019 EDais - C++ DLLs in VB

    3/28

    3

    EDais graphics programming tutorial

    Creating the DLL (VC++ 6)

    CChhaapptteerrIIaa

    Fire up VC++ and select File -> New to create a new project. Youll be greeted

    with a list of lots of different project templates that you can start from, dont worry

    this is nothing more different than when you fire up VB So far so good.Select Win32 Dynamic link-library from the list and type in a name for the DLL in

    the project name field. This name is important as it will define a lot of things later on

    so make sure you remember it and also the path that the files will be created in. For

    this demo Im calling the DLL VCDLL and its in the path C:\DLLDEV\VCDLL

    however you can call it whatever you want and put it wherever you want, just make

    sure you know where they are!

    Once youve passed that screen youll be presented with a second dialog asking you

    what you want the wizard to put in your sample DLL project, in this case well select

    A simple DLL project so theres a base to work from rather than coding everythingfrom scratch.

    Http://www.mvps.org/EDais/

  • 8/8/2019 EDais - C++ DLLs in VB

    4/28

    4

    EDais graphics programming tutorial

    After some chuntering from your machine as it goes and creates the directory

    structure and files youll be presented with a final dialogue that reports that the fileswere created successfully and what its done to your project:

    A simple Win32 DLL will be created for you.

    DllMain: VCDLL.cpp

    Pre Compiled Header: Stdafx.h and Stdafx.cpp.

    Great, weve got a DLL project set up and its all ready for us to add our own code.

    You can arrange the IDE as you wish now, if you dont see the workspace docked

    toolbar with two tabs at the bottom ClassView and FileView then hit Alt + 0

    (Zero, Not upper case o) to display it. Click the FileView tab and expand the treeuntil you find the [DLLName].cpp document and double click it to open in the IDE.

    This is the main code file for your project and is where youll add your custom code

    for the time being, for larger projects its better to create new .cpp and .h documents

    and use those rather than cluttering up the DLLs main file however in this case its

    ok.

    Youll see that theres only one function defined so far DllMain which is effectively

    the Form_Load of a DLL however for the time being were not interested in what it

    does and how it does it, just know that it has to be there for the DLL to function

    correctly.

    At the end of that file we can our new method, which will look like this:

    int_stdcallReportVersion(){

    Http://www.mvps.org/EDais/

  • 8/8/2019 EDais - C++ DLLs in VB

    5/28

    5

    EDais graphics programming tutorial

    return1;}

    Make sure you remember the semi-colon at the end of the middle line, this is a

    constant stumbling block for beginner C++ programmers and I can guarantee youll

    get stung with it more than a few times!If you have done some basic C++ before then you may be wondering about the

    _stdcall keyword, this is just to make sure that the function uses to the same way as

    VB for passing variables about and working with the stack. Since were not passing

    anything to this method, you could leave it out but for completeness well leave it in.

    If youve never written any C++ before then this is basically the same as the VB

    function:

    FunctionReportVersion()AsLongReportVersion=1EndFunction

    It will simply return 1 when you call it, nothing complicated but it will allow us tomake sure that we can call it properly.

    Before the DLL is ready for use in VB, we must make sure that the function(s) we

    create are viewable by outside applications and as such we must create a special kind

    of file called an Export definition list which is nothing more complicated than a list

    of function names to export so applications know what theyre looking for when

    querying the DLL for methods. To add a new file to the project, select Project ->

    Add to project -> New then select Text file from the list and type the name

    [DLLName].def in the name box.

    Once you hit ok, the file will be created, added to the project and opened for you in

    the IDE.

    Http://www.mvps.org/EDais/

  • 8/8/2019 EDais - C++ DLLs in VB

    6/28

    6

    EDais graphics programming tutorial

    The .def file starts with the keyword EXPORTS then a list of the function names so

    ours will look a little something like this:

    EXPORTSReportVersion

    You can add comments to this file by prefixing the comment text with a semi colon

    character so you could also write:

    EXPORTSReportVersion ; ReportsourversionnumberfortheDLLfile

    Hit F5 to run the project as you would in VB and youll be presented with the

    dialogue: One or more files are out of date or do not exist and a list of all the files in

    the project since nothing has been compiled as of yet, then asks Would you like to

    build them? Select yes and youll see the build log being generated as the files are

    compiled and linked, as long as everything went ok you should see something like

    this:

    --------------------Configuration: VCDLL - Win32 Debug--------------------

    Compiling...

    StdAfx.cpp

    Compiling...

    VCDLL.cpp

    Linking...

    Creating library Debug/VCDLL.lib and object Debug/VCDLL.exp

    VCDLL.dll - 0 error(s), 0 warning(s)

    If you get any errors or warnings then have a look back through your code and make

    sure youve not made any mistakes although theres very little to get wrong at thispoint since weve only added a few lines!

    Http://www.mvps.org/EDais/

  • 8/8/2019 EDais - C++ DLLs in VB

    7/28

    7

    EDais graphics programming tutorial

    CChhaapp

    tteerrIIbb Creating the DLL (VC++.NET)

    Fire up VC++.NET and select File->New to create a new project. Youll be

    presented with a large dialogue asking you which language you want to write in and

    what kind of project you want to create in that language, so select Visual C++Projects from the list on the left and Win32 Project from the list on the right (Yep,

    I do mean that and not MFC DLL - Ive not just lost the plot! All the base Win32

    projects are located in this tab under VC++.NET)

    Before you hit Ok, type in a name for the DLL in the name field, this name is

    important as it will define a lot of things later on so make sure you remember it and

    also the path that the files will be created in. For this demo Im calling the DLL

    VCNETDLL and its in the path C:\DLLdev\VCNETDLL however you can call

    it whatever you want and put it wherever you want, just make sure you know where

    they are!

    The next screen allows you to select what project type you want to create so click onthe Application settings tab and select DLL as the Application type.

    Http://www.mvps.org/EDais/

  • 8/8/2019 EDais - C++ DLLs in VB

    8/28

    8

    EDais graphics programming tutorial

    After some grinding of the hard disk the dialogue will disappear and drop you back

    into the IDE where you can arrange the panels to taste.

    If the Solution explorer isnt already visible, you can display it with Ctrl+R and

    double click on the [DLLName].cpp document to open it.

    This is the main code file for your project and is where youll add your custom code

    for the time being, for larger projects its better to create new .cpp and .h documents

    and use those rather than cluttering up the DLLs main file however in this case its

    ok.

    Youll see that theres only one function defined so far DllMain which is effectively

    the Form_Load of a DLL however for the time being were not interested in what it

    does and how it does it, just know that it has to be there for the DLL to function

    correctly.

    At the end of that file we can our new method, which will look like this:

    int_stdcallReportVersion(){return1;

    }

    Make sure you remember the semi-colon at the end of the middle line, this is a

    constant stumbling block for beginner C++ programmers and I can guarantee youll

    get stung with it more than a few times!

    If you have done some basic C++ before then you may be wondering about the

    _stdcall keyword, this is just to make sure that the function uses to the same way as

    VB for passing variables about and working with the stack. Since were not passing

    anything to this method, you could leave it out but for completeness well leave it in.

    If youve never written any C++ before then this is basically the same as the VB

    function:

    FunctionReportVersion()AsLong

    Http://www.mvps.org/EDais/

  • 8/8/2019 EDais - C++ DLLs in VB

    9/28

    9

    EDais graphics programming tutorial

    ReportVersion=1EndFunction

    It will simply return 1 when you call it, nothing complicated but it will allow up to

    make sure that we can call it properly.

    Before the DLL is ready for use in VB, we must make sure that the function(s) wecreate are viewable by outside applications and as such we must create a special kind

    of file called an Export definition list which is nothing more complicated than a list

    of function names to export so applications know what theyre looking for when

    querying the DLL for methods. To add a new file to the project, select Project ->

    Add new item -> Visual C++ -> C++ -> DEF file (.def) Then in the name field type

    [DLLName].def.

    Once you hit ok, the file will be created, added to the project and opened for you inthe IDE.

    Youll see that theres already a line in the file:

    LIBRARY[DLLName]

    This just reports the library name to other applications, we need to add the export list

    below that. This starts with the keyword EXPORTS then a list of the function

    names so ours will look a little something like this:

    LIBRARY[DLLName]EXPORTSReportVersion

    Http://www.mvps.org/EDais/

  • 8/8/2019 EDais - C++ DLLs in VB

    10/28

    10

    EDais graphics programming tutorial

    You can add comments to this file by prefixing the comment text with a semi colon

    character so you could also write:

    LIBRARY[DLLName]; TestVC++.NETDLLprojectEXPORTSReportVersion ; ReportsourversionnumberfortheDLLfile

    Hit F5 to run the project as you would in VB and youll be presented with the

    dialogue:

    These project configuration(s) are out of date

    [DLLName] Debug Win32

    Would you like to build them?

    Select yes and youll see the build log being generated as the files are compiled and

    linked, as long as everything went ok you should see something like this:

    ------ Build started: Project: VCNETDLL, Configuration: Debug Win32 ------

    Compiling...

    stdafx.cpp

    Compiling...

    VCNETDLL.cpp

    Linking...

    Creating library Debug/VCNETDLL.lib and object Debug/VCNETDLL.exp

    Build log was saved at "file://c:\DLLdev\VCNETDLL\Debug\BuildLog.htm"

    VCNETDLL - 0 error(s), 0 warning(s)

    ---------------------- Done ----------------------

    Build: 1 succeeded, 0 failed, 0 skipped

    If you get any errors or warnings then have a look back through your code and make

    sure youve not made any mistakes although theres very little to get wrong at this

    point since weve only added a few lines!

    Http://www.mvps.org/EDais/

  • 8/8/2019 EDais - C++ DLLs in VB

    11/28

    11

    EDais graphics programming tutorial

    CChhaapptteerrIIII Using the DLL from VB

    I expect that everyone reading this tutorial has used the API before but if not its not

    the end of the world, just follow the tutorial and it will walk you through everything

    that needs to go in. If you have however used the API then using the DLL is a breezesince that was all the API is Linking to DLLs!

    As with the API, we must first tell VB where the DLL and what within it we want to

    access - This is through method declarations, which go at the top of the code file. If

    you havent already, fire up VB (Leave your C++ project open in the background

    though since well be using it again later) and open up a new Standard EXE project,

    then open up the forms code view so we can add the declarations.

    Lets first have a look at a basic API declaration:

    PrivateDeclareSubSleepLib"Kernel32.dll"(ByValdwMillisecondsAsLong)

    Now, time to do some dissection:

    Private We know means that this method can only be used in this code

    block so thats no problem, its irrelevant as far as the declare

    goes, this is just for VBs sake.

    Declare This means that this is only a method header and not the entire

    method.

    Sub Again we know this means its a subroutine and doesnt return a

    value.

    Sleep This is the name of the method. It doesnt have to be the same as

    the method name in the DLL however if it differs then an Alias

    clause must also be added to the declaration, for simplicities sake

    though its best to just use the same name.

    Lib This means were going to give VB the library name (DLL file.)

    Kernel32.dll The name of the DLL file that contains the method.

    ( We know this indicates that were going to declare some

    parameters (If any) that need to be passed to this method.

    ByVal This means were passing the value of the variable passed to the

    method, not the address of its value in memory. This is important

    to get right in the function declarations since it can cause big

    problems later on.

    dwMilliseconds This is the name of the parameter, this is just for VBs sake, itdoesnt have to be the same as its declared in the DLL but it

    makes sense to do so, no alias needs setting if its not the same as

    in the DLL.

    As Long This means were setting the type and thus how much memory to

    send over to the DLL when we pass a value to it, again this is

    very important to get these matched up with the DLL correctly.

    ) Finally we know this means were done with parameters and as

    theres no return value since were dealing with a subroutine

    declaration then thats it.

    Ok, we know what makes up a method declaration now so lets see if we can createone of our own. When writing a DLL in parallel with your VB application its a pain

    Http://www.mvps.org/EDais/

  • 8/8/2019 EDais - C++ DLLs in VB

    12/28

    12

    EDais graphics programming tutorial

    to have to move the DLL file to the system or project directories every time you re-

    compile it so in these cases you can give it an explicit path to the DLL file rather than

    a relative path or file name as in the final declarations.

    As such we can construct the method declaration by going though the reverse of the

    dissection process above:

    Private Make this method only be accessible from this code module

    again this is just for VBs sake but well keep it in there for

    completeness.

    Declare This is a method declaration only; the method data does not

    follow.

    Function As this returns a value, it must be declared as a function.

    ReportVersion This is the name of the method as declared in the DLL.

    Lib Tell VB were going to give it the DLL (Library) path.

    [DLLPath]Debug\

    [DLLName]

    Enter the explicit DLL path and name here and remember to

    encapsulate with quotes. Also were compiling the DLL in

    debug mode so the DLL with be in a Debug sub-directory.

    ( Ready to enter parameters.

    ) No parameters so just close the parenthesis.

    As Long This method returns an int in C++, which maps to VBs

    Long data type.

    Put it together and you get something like this:

    PrivateDeclareFunction ReportVersionLib"C:\DLLdev\VCDLL\Debug\VCDLL.dll"()AsLong

    This is somewhat bad practice and you have to remember to change your declarations

    before you distribute the application to any remote systems, which leads to potential

    bugs both in development and distribution of the application. One common work-

    around to this is to use compiler directives to instruct the compiler which path you

    want to use when you compile and it does the hard work of working out which

    version to use.

    If youve never used them before then dont worry, theyre no different from standard

    If ... Else ... End If statements apart from theyre prefixed with #. To let the

    compiler know which version to include, we have to define the compiler directives in

    the Project -> Properties -> Make window where youll see Conditional

    compilation arguments at the bottom of the dialogue. So set an argument in, heresimply type the name of the conditional and give it a value or either 0 or 1 (Or any

    non-0 value, however many people prefer to use the true integer value of Boolean

    True which is 1), for instance:

    DevMode=1

    Then within your code, convert your declaration to:

    #If(DevMode)Then'ExplicitpathPrivateDeclareFunction ReportVersion_Lib"C:\DLLdev\VCDLL\Debug\VCDLL.dll"()AsLong#Else'RelativepathPrivateDeclareFunction ReportVersionLib"VCDLL.dll"()AsLong

    Http://www.mvps.org/EDais/

  • 8/8/2019 EDais - C++ DLLs in VB

    13/28

    13

    EDais graphics programming tutorial

    #EndIf

    When you do a final compile just knock the compiler directive out or set it to 0, which

    will use the relative rather than explicit path.

    After all that, lets actually test the routine to see if it does what we told it to, in

    Form_Load() simply display its return value with a MsgBox function:

    PrivateSubForm_Load()'DisplayDLLversionCallMsgBox(ReportVersion())EndSub

    As long as all went well, the message box should report 1 and your DLL is working

    correctly!

    If you get a blank message box then its probably something to do with the function

    name in the declaration and a run time error 53 means that the DLL name or path is

    incorrect so check those. If you get any other errors then congratulations for a start (!)

    and then go and check through your code to make sure youve put everything incorrectly, if you still cant find the cause of the bug then contact me and Ill take a

    look for you.

    Http://www.mvps.org/EDais/

  • 8/8/2019 EDais - C++ DLLs in VB

    14/28

    14

    EDais graphics programming tutorial

    CChhaapptteerrIIIIII Passing values into the DLL

    Its all very well having a DLL that only reports a number but its not really very

    much use! Lets get it to do something for us now so go back to your DLL project in

    either VC++6 or VC++.NET and lets get coding.Id expect that most of you will have at least seen C++ code before and probably

    converted some for use in VB and as such Im not going to focus too much on the

    C++ syntax (Dont worry, Ill keep it very simple for the time being) but on the

    interaction between it and VB.

    Lets add a second simply method to the C++ code now:

    //Returnthesumoftwonumbersint_stdcallSum(intinA,intinB){return(inA+inB);}

    This simply takes two numbers as input and returns the sum of them, youll notice the

    line prefixed // above the function, this is a comment in C++ in case you were

    wondering.

    As we want to use this in VB, we again have to add it to the .DEF file (Sans-

    parameters) so it would be entered as:

    Sum ; Reportsthesumoftwonumbers

    Finally Hit F5 to compile again and then its back to VB for the testing.

    This time the function declaration is a little more complicated since we have to pass

    parameters to the method but its not too tricky. We can simply re-use most of firstdeclaration so copy and paste that in the line below remembering to change the

    method name to Sum. When it comes to the parameters, we need to make sure that

    were passing them in the right order and in the right mode otherwise C++ will have

    problems when it tries to read the values from our application and cause a runtime

    error 49 - Bad DLL calling convention.

    In VB the default method of passing parameters is to pass the address of them in

    memory however in C++ weve declared the parameters as being direct values (Well

    see how to deal with pointers later) so we must force VB to pass the value of the

    parameters rather the address, this is accomplished by prefixing each parameter with

    the keyword ByVal:

    PrivateDeclareFunction SumLib"[DLLName]"(_ByValinAAsLong,ByValinBAsLong)AsLong

    Remember to change the DLL name to whatever youve called yours and add the

    version with the full path in the DevMode section.

    Now try running the function from VB:

    CallMsgBox(Sum(1,2))'Thisshouldreport"3"

    You shouldnt get any errors here as long as youve followed the tutorial however if

    you do get a bad DLL calling convention then make sure that youve declared the

    function correctly in C++ and in VB especially the parameter types and by-value

    passing method.

    Http://www.mvps.org/EDais/

  • 8/8/2019 EDais - C++ DLLs in VB

    15/28

    15

    EDais graphics programming tutorial

    If you get a run-time error 453 - Cant find entry point Sum in

    [DLLPath]Debug\[DLLName] error then make sure that youve declared the

    function properly in the C++ definition file then re-compile it.

    Finally if you get a stupidly high number being returned thats most definitely not the

    result of 1 and 2 then make sure that youre passing the parameters by value as its

    probably taking the sum of their locations in memory!

    Http://www.mvps.org/EDais/

  • 8/8/2019 EDais - C++ DLLs in VB

    16/28

    16

    EDais graphics programming tutorial

    CChhaapp

    tteerrIIVV

    Passing arrays to a DLL

    Arrays are dealt with slightly differently in C++ as they are in VB and as such they

    need a little special treatment when passing to and from a DLL.

    In VB an arrays data is prefixed in memory by a structure known as the Safe arraystructure which includes information about how big the array is, how many

    dimensions it has an so on. In C++ you simply have a pointer to a location in memory

    then this can be referred to as an array by offsetting the point from that location to the

    desired element of the array. This has to be taken into account when passing the array

    to a DLL since if we pass the wrong pointer it will try and read/write over the array

    descriptor, which would not be good and introduce all kinds of memory leaks and

    potential bugs!

    Ok, lets write a simple function in C++ which takes a pointer to an array and its size,

    then fills that array with ascending numbers. Go back into your C++ code and add the

    following function shell:

    int_stdcallFillArr(int*inArr,intArrSize){}

    Hang on a sec, where did that multiplication sign come from? It actually means that

    the parameter is treated as a pointer and effectively the same as ByRef in VB,

    notice that we dont declare it as an array in the same way an array would be passed to

    a VB function, in C++ we have far more flexibility over pointers and direct access to

    variables.

    First off well need a loop counter so go ahead and declare one:

    intLoopArr;//Loopvariable

    Now, before we go and do any work on the array we must first check to make sure

    that nothing silly has been passed to the routine (Within reason) so well add a quick

    check in here to validate that:

    //Makesuretheydidn'tpassanythingsillyif(ArrSize

  • 8/8/2019 EDais - C++ DLLs in VB

    17/28

  • 8/8/2019 EDais - C++ DLLs in VB

    18/28

    18

    EDais graphics programming tutorial

    Then hit F5 to compile it and make sure you dont get any errors. If all went well

    then its off to VB for the testing.

    Writing the function declaration is no different to before however since the first

    parameter the function expects to be a pointer, we have to tell VB to pass it as a

    pointer. We do this by using the ByRef keyword in place of the ByVal one, butfor that parameter only, the array size is still passed by value. With this in mind,

    heres the new declaration:

    PrivateDeclareFunction FillArrLib"[DLLName]"(_ByRefinArrAsLong,ByValArrSizeAsLong)AsLong

    Make sure to change DLLName to the name of your DLL and add the path in the

    DevMode section.

    Once youve got that done you can simply test the new method with the following

    code on your VB form:

    PrivateSubForm_Load()DimMyArr()AsLongDimArrSizeAsLongDimLoopArrAsLongDimMakeStringAsString'CreatethearrayArrSize=10ReDimMyArr(ArrSize-1)AsLong'GettheDLLtofillthearray

    'Noteherethatwepassthefirstentryofthearrayandnotthe'arraypointeritself,thisistomakesurewefillthearray'dataandnotoverwritethearraydescriptorbymistake.CallMsgBox(FillArr(MyArr(0),ArrSize))'LoopthroughthenewarraycontentsandmakeastringmessageForLoopArr=0ToArrSize-1MakeString=MakeString&MyArr(LoopArr)&vbCrLfNextLoopArr'ReportarraycontentsCallMsgBox(MakeString)EndSub

    Hopefully nothing went wrong and you saw first 1 as the output from the function

    then an ascending list from 0 to 9 which is the array contents. It if all crashed down

    around your ears then have a hack around in the code again and see if you find any

    problems, if not then let me know and Ill take a look for you.

    Its also worth mentioning that when youre doing this kind of development it makes

    sense to save and make backups reasonably frequently since VB is very flaky when

    you mention direct memory access around it - Just a warning.

    At this point, if youve never used C++ or the API much before then I suggest you

    bookmark this page and go and have a play for a few weeks until youre more

    comfortable with coding in C++, theres plenty of source code about and the help andexamples will help with most problems, if not then the guys over on the Microsoft

    Http://www.mvps.org/EDais/

  • 8/8/2019 EDais - C++ DLLs in VB

    19/28

    19

    EDais graphics programming tutorial

    public C++ newsgroups are usually full of good help and will get you through most

    basic problem in no time at all.

    Once you youre feeling more confident then you can if you wish continue with the

    rest of the tutorial which deals with using the Win32 GDI API in a C++ DLL from a

    VB EXE - Wow, what a mouthful..

    Http://www.mvps.org/EDais/

  • 8/8/2019 EDais - C++ DLLs in VB

    20/28

    20

    EDais graphics programming tutorial

    CChhaa

    pptteerrVV

    Using the Win32 API in a C++ DLL

    At this point Im assuming a little more knowledge of basic C++ syntax however Ill

    try to keep things as simple as possible so even without knowledge of C++ it can still

    be followed, a knowledge of the GDI API would be very useful though.Ok, what were going to do is write a routine to fill a device context will a checkered

    pattern, it may seem like a huge jump from the previous simple methods but its not

    too bad and Ill try and keep it simple.

    First up, well need to write the shell of the function, which will need to take a DC

    handle, the size of the checks to draw, the width and height to fill and the colour

    values. The first thing thats different in C++ from VB is that weve got lots more

    variables types to play about with which make code more readable by clearly defining

    exactly what a parameter should contain. In VB we pass all the parameters as Longs

    however in C++ we can use other types which more accurate describe the parameter

    but essentially map to the same thing. The first of these new parameters well use is

    one to hold a handle to a device context, HDC. The second is to describe a colour,COLORRREF. Both of these simply map to a 32-bit Long integer however they just

    make it a bit more obvious what were expecting them to be.

    With this new information we can now write the function shell in the C++ DLL:

    int_stdcallCheckerDC(HDCinDC,intinSize,intinWidth,intinHeight,COLORREFinColA,COLORREFinColB){}

    Again here were returning a long representing the functions outcome.Now we can start writing the routine, first off lets do some validation though. You

    could check to see if the DC handle youve been passed it a valid one or not with the

    GetObjectType() API call however Ill skip that here (It can always be added later).

    The first check is to make sure that the checker size makes sense so well make sure it

    isnt below 1 (A check size of 0 could cause stack overflow problems with the

    looping structure).

    if(inSize

  • 8/8/2019 EDais - C++ DLLs in VB

    21/28

    21

    EDais graphics programming tutorial

    If(Condiction)ThenDoSomething()DoSomethingElse()'etc...EndIf

    In C++ though this premise extends to most block structure such as loops however

    they must remain round method blocks.

    In the same way was we use the HDC type to hold a handle to a device context, we

    can use the HBRUSH type to hold a handle to a brush object, which well need to

    declare now. Another nice thing here is that we can assign the value of the variable

    when its declared so lets create the brush to fill with now:

    HBRUSHFillBrush=CreateSolidBrush(inColA) //Createbrushobject

    Now well need an API RECT type to hold the area to draw in. Now, unlike VB we

    dont need to add definitions to the API declarations to use them, instead C++ useswhats known as header files which contain all the declarations for us. By default our

    DLL project was created with some of these headers already included including all

    the common GDI APIs, however if you require use of a specific API then you can

    find the header that its contained within by searching on the MSDN for the

    Method/Type name and looking for the header requirement at the bottom of the page.

    This is the requirement for the FillRect() API which well be using in a second:

    Requirements

    Windows NT/2000/ XP : Included in Windows NT 3.1 and later.

    Windows 95/ 98/M e: Included in Windows 95 and later.

    Header: Declared in Winuser.h; include Windows.h.Library: Use User32.lib.

    Youll see there that the header we need is Windows.h however if you then look at

    the top of the .cpp file youve been writing your code in youll see the line:

    #include"stdafx.h"

    The #include means insert everything from the following file here so in this case

    its going to insert the contents of the file stdafx.h at the top of the project. If you

    have a look in file view for the project and open up stdafx.h then youll see amongst

    other things the lines:

    //WindowsHeaderFiles:#include

    So weve already got the header file declared and dont need to re-include it.

    Ok, back to the code. Well need to declare the RECT variable and we can do the

    same tick of filling it in the same line by including curly braces round the entry list:

    RECTFillArea={0,0,inWidth,inHeight};//Declarefillarea

    You must get the parameters in the correct order, you can find the ordering of the

    parameters either by looking in the MSDN or by opposite clicking on the RECT

    keyword and going to its definition where youll find:

    Http://www.mvps.org/EDais/

  • 8/8/2019 EDais - C++ DLLs in VB

    22/28

    22

    EDais graphics programming tutorial

    typedefstructtagRECT{LONGleft;LONGtop;LONGright;

    LONGbottom;}RECT,*PRECT,NEAR*NPRECT,FAR*LPRECT;

    It may look a bit nasty but you can pretty much ignore the last line, which just sets up

    some aliases for the type depending on how its being used. The important bit is the

    ordering of the elements within the type (In case you were unsure, this is the same as

    a UDT in VB) in this case left, top, right, bottom so this is how we must pass the

    elements to C++. You can also manually fill the type as follows if you choose as we

    would in VB:

    RECTFillArea;

    FillArea.left=0;FillArea.top=0;FillArea.right=inWidth;FillArea.bottom=inHeight;

    Weve already got the API declared so lets go ahead and call the FillRect() API to fill

    this DC with the selected colour. One thing to note here is that the second parameter

    of the call is actually a pointer to a rectangle structure so we must pass a pointer to our

    RECT structure rather than the structure itself, we do this by prefixing the variable

    name with an ampersand character:

    FillRect(inDC,&FillArea,FillBrush); //FilltheDC

    We must now kill the brush object to prevent memory leaks which is done in exactly

    the same was as in VB:

    DeleteObject(FillBrush); //Cleanup

    Finally well not need to bother with the rest of the routine so just return 1 now to let

    the user know it was a success:

    return1;//Indicatesuccess

    Lets have a look at the code so far:

    if(inSize

  • 8/8/2019 EDais - C++ DLLs in VB

    23/28

    23

    EDais graphics programming tutorial

    function with both colours the same and you should get the picture box being filled

    with the solid colour.

    Now we need to deal with the rest of the routine that will be run as long as both of the

    first tests were passed.

    First up well need two API brush objects to draw with rather than re-creating them

    every time so lets declare them now:

    HBRUSHBrushA=CreateSolidBrush(inColA); //CreateGDIbrushobjectsHBRUSHBrushB=CreateSolidBrush(inColB);

    We must remember to kill these when were done tough, I quite often add the clean-

    up code as soon as Ive created the objects so as to not forget but if youre following

    this tutorial then it shouldnt be a problem.

    Now well need a couple of variables to hold which brush we started the line on and

    which brush we should be drawing this checker in. There may be other ways of

    accomplishing this but this is how Ive done this in the past before so Ill stick with ithere too:

    intFirstBrush=0,ThisBrush=0;//Whichbrushtouse

    Finally well declare a RECT structure that well just re-use to set the area to fill:

    RECTFillArea;//Theareatofillforthischecker

    Ok, lets set up the base loop structure now that will handle the drawing. The outer

    loop will iterate horizontally and deal with the first brush for the line, while the inner

    loop will iterate vertically and deal with which brush to draw each checker with.As such the loop structure will look like this:

    for(intLoopX=0;LoopX

  • 8/8/2019 EDais - C++ DLLs in VB

    24/28

    24

    EDais graphics programming tutorial

    The last but one line also has some odd looking syntax, the % symbol in C++ is the

    operator for modulus division and is the same as the VB Mod statement. You could

    instead write:

    if(FirstBrush==1)FirstBrush=0;elseFirstBrush=1;

    However its a bit clunky so Ive stuck to the original method.

    The modulus version works as follows:

    0 Mod 2 = 0

    1 Mod 2 = 1

    2 Mod 2 = 0

    So at first we start off at 0, then add one and modulus divide by 2 to get 1, the next

    iteration adds 1 again to get 2 then modulus divides by 2 to get 0 so the number is

    swapped back and fourth.

    At this point, each time the inner loop runs, the ThisBrush variable is going to store

    the opposite brush than the first one used on the last line (Or 0 if its the first iteration

    of the loop) so well just have to deal with swapping the brushes on this line only and

    not what happens on the next.

    Before we do that thought well need to draw this checker in the current colour so

    assign the fill area some dimensions:

    //SetfillareaFillArea.left=LoopX;

    FillArea.top=LoopY;FillArea.right=LoopX+inSize;FillArea.bottom=LoopY+inSize;

    This simply sets the top left of the fill area to the current (X,Y) coordinate and sets

    the bottom left position to this coordinate offset by the size both horizontally and

    vertically. Now we must fill this checker with the current brush colour so well need

    to make a call to FillRect():

    //FillcheckerFillRect(inDC,&FillArea,(ThisBrush==1)?BrushB:BrushA);

    More strange syntax here again, this time theres a question mark in there too whichmeans the same as an Iif() (Immediate-if) statement in VB. The syntax is:

    Condition?TruePart:FalsePart

    However I usually put the condition in parenthesis to set it apart from the rest of the

    statement and adhere to C++s normal if syntax.

    This line would be the same as the following line in VB:

    CallFillRect(inDC,FillArea,IIf(ThisBrush=1,BrushB,BrushA))

    So weve filled this checker now, all we need to do to finish this inner loop off is toswap the checker colour:

    Http://www.mvps.org/EDais/

  • 8/8/2019 EDais - C++ DLLs in VB

    25/28

    25

    EDais graphics programming tutorial

    ThisBrush=(ThisBrush+1)%2;//SwitchBrushes

    This uses the same syntax as in the outer loop when swapping the first brush for the

    line.

    Now to finish the function off we must clean up the two brushes after the loops havefinished doing their stuff:

    //CleanupDeleteObject(BrushA);DeleteObject(BrushB);

    And return a value indicating success:

    //Returnsuccessreturn1;

    And thats all there is to it, after 5 and a half pages we have a finished checkerroutine, so lets see if it works in VB. First off add the function name to the function

    definition list so VB will be able to see it, then head over to VB and well add the new

    function header.

    The function header in C++ looks as follows:

    int_stdcallCheckerDC(HDCinDC,intinSize,intinWidth,intinHeight,COLORREFinColA,COLORREFinColB)

    So lets work our way through the header and write the corresponding VB version.int means it returns a Long as we know it ends in As Long and the function name

    is CheckerDC so we know it starts off with Private Declare Function CheckerDC

    The library name comes next (With the two different paths for development and

    release versions of the DLL) then comes the parameter list. You can see that none of

    the parameters have a multiplication sign next to them so none are pointers and will

    all be passed by-value. They also conveniently all map to Longs so the rest of the

    declaration is simply a matter of swapping around the syntax:

    PrivateDeclareFunction CheckerDCLib"[DLLName].dll"(_ByValinDCAsLong,ByValinSizeAsLong,_

    ByValinWidthAsLong,ByValinHeightAsLong,_ByValinColAAsLong,ByValinColBAsLong)AsLong

    To demonstrate the routine at work we can simply add a picture box to the form then

    add this code:

    PrivateSubForm_Load()'PersistdrawingPicture1.AutoRedraw=TruePicture1.ScaleMode=vbPixelsEndSubPrivateSubForm_Resize()

    CallPicture1.Move(0,0,Form1.Width-120,Form1.Height-400)CallCheckerDC(Picture1.hDC,12,Picture1.ScaleWidth,_Picture1.ScaleHeight,&H707070,&H909090)

    Http://www.mvps.org/EDais/

  • 8/8/2019 EDais - C++ DLLs in VB

    26/28

    26

    EDais graphics programming tutorial

    EndSub

    When you run the application, you should have a PhotoShop-style transparency grid

    being drawn for you, if not then go back and check your code to make sure

    everythings working, if you get an error then run through all the checks in the

    previous chapters which should iron out the problems and you can also download mycode and compare that to your own.

    Ive included a couple of appendices at the end as a quick reference to some things

    you may need, all of this and a lot more is in the help files though

    Http://www.mvps.org/EDais/

  • 8/8/2019 EDais - C++ DLLs in VB

    27/28

    27

    EDais graphics programming tutorial

    AApppp

    eennddiixxII Variable types

    Here are the basic types in VB and their equivalent in C++:

    VB Signed Size C++ByVal Byte No 8-bit unsigned char

    ByVal Integer Yes 16-bit short

    ByVal Long Yes 32-bit int / long

    ByVal Single Yes 32-bit float

    ByVal Double Yes 64-bit double

    ByVal String N/A Variable LPCSTR / char *

    Signed simply means that VBs type can support negative numbers too. Unlike VB,

    C++s integer data types (char, short, int and long) can all support either signed or

    unsigned values, which can be very useful in certain situations.

    To explicitly set if a variable can be signed or not you can prefix the type with either

    signed or unsigned. In this way, unsigned short would give you a 16-bit integer

    with a range of (0 to ~65,000) where as signed short would give you a 16-bit

    integer with a range of (~32,000 to ~32,000).

    By default all numeric data types are signed unless otherwise specified, and floating

    point values cannot be unsigned.

    About compile configurations in C++

    The default compile type in C++ is a Debug build which adds extra bullet proofing

    code to the compiled file however it also increases overhead so it generally runs

    slower that if that code were omitted. To do this you can change the compile type to

    Release which doesnt add this extra debug information but means you have to

    make sure your code is written properly as it will be less forgiving. To change the

    compile mode, simply use the drop down list at the top of the window, if youve never

    changed it, it will either say Debug or Win32 Debug with most likely only one

    other option on the list indicating a Release build.

    Once you re-compile, the new DLL will be put into a Release subdirectory off your

    main project directory, so if youre using absolute paths than make sure to changethis from the Debug subdirectory weve been using until now.

    Http://www.mvps.org/EDais/

  • 8/8/2019 EDais - C++ DLLs in VB

    28/28

    28

    EDais graphics programming tutorial

    CChhaapptteerrIIII C++ syntax

    Bit level operations:

    Name VB C++

    Bitwise AND And &Bitwise OR Or |

    Bitwise XOR Xor ^

    Bit-shift right N/A >>

    Bit-shift left N/A >

    Is less than or equal to >= >=

    Is greater then