Sorry your browser is not supported!

You are using an outdated browser that does not support modern web technologies, in order to use this site please update to a new browser.

Browsers supported include Chrome, FireFox, Safari, Opera, Internet Explorer 10+ or Microsoft Edge.

DLL Talk / A class in a DLL

Author
Message
Prehistoric Gamer
12
Years of Service
User Offline
Joined: 30th Mar 2011
Location:
Posted: 18th Oct 2011 13:52
Having looked through the threads posted in this forum thus far, I'm guessing my question is original.

I successfully made a DLL for DBPro, in which I created one function that generates a new position based on first position, velocity, acceleration, and time. (The Physics classes I took in college weren't completely lost on me.)

Now, I'm trying my hand at writing a DLL with a simple container class that has two constructors, a destructor, an input function, and an output function. If I can get it working in DBPro, I plan to add more functions, and even instantiate it and other classes as objects in higher classes.

Thus far, I've been able to get the DLL compiled in C++, but I get errors, and the program stops working, when I try to use the commands I made in DBPro.

If there's something I can do to get the DLL working properly, I'll appreciate any advice you can give me.

I'm including the DLL project in this first post.

Attachments

Login to view attachments
WLGfx
16
Years of Service
User Offline
Joined: 1st Nov 2007
Location: NW United Kingdom
Posted: 18th Oct 2011 15:16
Yes, classes can be used just the same as in C++. I've not had any problems. Depending on how you pass the data for each class that's allocated back and forth to DBP is entirely upto you.

I assume DBP will handle any clean up of the dll with or without an error so your destructors should work just the same too.

I've allocated memory for classes, passed pointers back and forth without problems.

EDIT: Also to note if you use these:


EXPORT will decorate your exported function with all the stuff on the end, whereas EXPORTC you don't have to worry about adding the extra info.

ie. EXPORT function (string table = IDS_STRING1 "blah blah @@QAEHXZ")
and EXPORTC function (string table = IDS_STRING1 "blah blah" without the extras)

Saves having to load up the dll to get the decorated names...

Mental arithmetic? Me? (That's for computers) I can't subtract a fart from a plate of beans!
Warning! May contain Nuts!
IanM
Retired Moderator
21
Years of Service
User Offline
Joined: 11th Sep 2002
Location: In my moon base
Posted: 18th Oct 2011 15:16
DBPro can't deal directly with classes.

The way that I use to get around this is to use an std::map<int, class*> (or equivalent), and write wrapper functions that will use the ID passed in from DBPro to lookup the object address, then use that.

Prehistoric Gamer
12
Years of Service
User Offline
Joined: 30th Mar 2011
Location:
Posted: 19th Oct 2011 13:57
I took IanM's advice on using a map template class; and, when I finally got the DLL produced, the commands I made caused the program to crash even more.

I have the project zipped here, so maybe someone can take a look at it and find out what I'm doing wrong.

Attachments

Login to view attachments
WLGfx
16
Years of Service
User Offline
Joined: 1st Nov 2007
Location: NW United Kingdom
Posted: 19th Oct 2011 14:58
I just had a look at your code. When using the ".erase" you're deleteing an element, de-allocating memory. Whereas in your code you've not set anything up to ".push_back" anything onto the map stack. When I started using <vector> and <list> classes I found it awkward to understand at first. <map> is similar but using more than one typedef. I've not used <map> yet.

You might find it a tad easier to start with <vector> or <list> and use the ID as the element number as you can always check how many elements are stored by ".size".

Each time you ".push_back" it will add an element on the end. But be aware that if you ".erase" an element then the ID's will change, whereas ".pop_back" will remove the last element.

<vector> and <list> classes are handy for storing data for such things as particle data. If you assigned a string to each elements class data then you could search through the list for the name of the element.

Mental arithmetic? Me? (That's for computers) I can't subtract a fart from a plate of beans!
Warning! May contain Nuts!
IanM
Retired Moderator
21
Years of Service
User Offline
Joined: 11th Sep 2002
Location: In my moon base
Posted: 19th Oct 2011 15:25
I said:
Quote: "DBPro can't deal directly with classes."

The code quite clearly attempts to use classes directly with DBPro.

Assuming that it's the 'info' class you are interested in making available to DBPro, then the kind of code you need is (untested):


You can combine the above with the interface library that I posted in WLGfx's other thread (eg using DBPro::ReportError to raise a standard-looking DBPro error message).

Prehistoric Gamer
12
Years of Service
User Offline
Joined: 30th Mar 2011
Location:
Posted: 20th Oct 2011 13:56
IanM, is ".second" supposed to represent something in particular? I entered the code as you have it, and I noticed that Intellisense doesn't recognize it. So, I wasn't surprised by the error messages that came up when I tried to compile the project with the new code. The errors told me that "second" isn't a member of the iterator, and that what preceded the arrow and following code should be connected to a class, struct, union, or generic type.
IanM
Retired Moderator
21
Years of Service
User Offline
Joined: 11th Sep 2002
Location: In my moon base
Posted: 20th Oct 2011 15:51
std::map stores your key and your data in a single data structure (it's a std::pair). .first accesses the key, and .second accesses your data.

The problem is where I've put P.second->get() (and in equivalent areas) should be P->second.get() - I put the . and the -> the wrong way round.

Prehistoric Gamer
12
Years of Service
User Offline
Joined: 30th Mar 2011
Location:
Posted: 21st Oct 2011 13:22
I tried posting a message earlier, but it seems it didn't work. Still, I just thought I'd let you know that I finally got the dll working properly.

As it turns out, I needed an arrow between the P and the second, as well as between second and get or set. I'm guessing it's because, in the template call, I had put in "infoInt*" for the second input.

I'm now in the process of adding new member functions and related commands, but in the mean time, here's the successful project. I appreciate the help.

Attachments

Login to view attachments
Prehistoric Gamer
12
Years of Service
User Offline
Joined: 30th Mar 2011
Location:
Posted: 1st Nov 2011 11:04
Well, as it turns out, this thread isn't quite over yet.

Part of my project was to see of I could make a class with objects of another class as members, then make commands and expressions from that class, and see if I could get those commands to work in DBPro. When I got the commands to output from Visual C++ 2008 Express, the expressions came with warnings that "not all command paths return a value."

The commands are recognized in DBPro, but I get an error with the expressions, telling me that the expression and the description don't match up.

Included in this update is my latest unsuccessful attempt to correct the problem. I've also tried keeping everything in the coordinates class public, and having direct paths, with no success; and not even the switch from double to float was successful. Is there a solution to this problem that I haven't thought of?

Attachments

Login to view attachments
WLGfx
16
Years of Service
User Offline
Joined: 1st Nov 2007
Location: NW United Kingdom
Posted: 1st Nov 2011 14:28 Edited at: 1st Nov 2011 14:33


It looks like you've mistyped the last two lines in the above which will also invalidate the rest of the commands in DBP. Change those to GET and they won't be duplicated...

EDIT: You will also need [%FL% as your input is an int and you're returning a float.

Mental arithmetic? Me? (That's for computers) I can't subtract a fart from a plate of beans!
Warning! May contain Nuts!
Prehistoric Gamer
12
Years of Service
User Offline
Joined: 30th Mar 2011
Location:
Posted: 2nd Nov 2011 13:31
I guess I should've remembered that part about the input and output. Thanks.

After making those changes, I have gotten the expressions to be recognized in DBPro. Evidently the warnings in Visual C++ aren't so important after all.
Michael P
18
Years of Service
User Offline
Joined: 6th Mar 2006
Location: London (UK)
Posted: 17th Nov 2011 01:00 Edited at: 17th Nov 2011 01:05
IanM's way is probably better but I always just have a function to create the object which returns the pointer stored as an integer. Then whenever referring to that object you can pass in that pointer and cast it back to the appropriate type.

That way there's no need for a map, the error reporting in the event that what's passed in is wrong isn't going to be as nice but you can catch the access violations that occur.

IanM
Retired Moderator
21
Years of Service
User Offline
Joined: 11th Sep 2002
Location: In my moon base
Posted: 17th Nov 2011 13:41
Handles are fine, and if you look at my network plug-in, you'll see that I've used that technique there too.

The problem with your assumption of an access violation is that it might not actually occur - if the value you pass in doesn't hold an object of the correct class, but is owned by your process, then the address isn't invalid, but doesn't point to an instance of your class, and may not crash when you access it.

That's why even though my network plug-in uses that technique, it also holds a list of the valid pointers in a std::set so that they can at least be validated.

So that's why I disagree with:
Quote: "That way there's no need for a map"


Prehistoric Gamer
12
Years of Service
User Offline
Joined: 30th Mar 2011
Location:
Posted: 17th Nov 2011 22:44

I must admit, I'm pleasantly surprised this thread is still drawing attention.

In the mean time, when I tried the last DLL I posted here, I wondered why it wasn't working. Recently, as I was working on the code for the DLL to assist me with my Prehistoric Animator project, I noticed what I'd done wrong. Can anyone else pick out what I'd realized I'd done?

Beyond that, I plan to include the code I used in the folder with my finished product, both Visual C++ and DBPro. I'm sure you'll find the DLL I'm using for the Prehistoric Animator interesting, or the coding, particularly the hierarchy of classes and class objects. It's nice to know I can use a DLL from another programming language to make up for what I couldn't do strictly in DBPro.
Diggsey
17
Years of Service
User Offline
Joined: 24th Apr 2006
Location: On this web page.
Posted: 17th Nov 2011 23:18
There's a problem with just using a 'std::set' to store valid pointers, which is that you don't know if a given pointer is of the correct type, even if it is a valid pointer. There are a number of ways to solve this:

- Use a different set for each type of class.
This gets messy very quickly especially with inheritance.

- Derive all classes from a single base class and use either your own or the built in runtime type info to check that a pointer has the correct type.
This works well but has a performance penalty.

- Use a std::map rather than a std::set to store pointers. The second value indicates the type of the pointer.
This also has a performance penalty but it should be less than that for RTTI. It's possible to use bit patterns to represent types such that inheritance is supported, in this case the performance cost is only a single AND operation. This is the method I used in Box2D.

[b]
IanM
Retired Moderator
21
Years of Service
User Offline
Joined: 11th Sep 2002
Location: In my moon base
Posted: 18th Nov 2011 00:30
Well, at least we agree that the pointer should be validated rather than 'just hope for the best'

My network plug-in uses a virtual base class:
- It's faster than using if...then...elseif...etc
- It means I don't need to change existing code if I add a new class (eg, I don't need to know the class type to read a byte, I just tell it to read)

However, if I needed to put together a tight and fast loop, then I might recode to avoid the virtual calls - it depends upon circumstances - but in the first instance I'd nearly always go for the methods provided by the compiler.

Diggsey
17
Years of Service
User Offline
Joined: 24th Apr 2006
Location: On this web page.
Posted: 18th Nov 2011 00:55
Quote: "I'd nearly always go for the methods provided by the compiler."


Yes, the main reason I used a map with type info separate from the class is that I use box2d classes directly and I prefer not to modify the box2d library itself.

[b]
Prehistoric Gamer
12
Years of Service
User Offline
Joined: 30th Mar 2011
Location:
Posted: 24th Nov 2011 23:31
Alright, here's what I realized I'd done.

Here's a sample function from the ClassTestDll2 project I'd worked on:



You may notice the condition I had for the code being input successfully. It seems I had it programmed so that the input should only be successful if the object didn't already exist. So, it's no wonder the function wasn't working.

I'd done the same thing with most of the other functions as well. So, the only ones that were working were the constructor and destructor functions.

Garbage in, garbage out. I guess this shows the importance of comments.
Prehistoric Gamer
12
Years of Service
User Offline
Joined: 30th Mar 2011
Location:
Posted: 7th Jan 2012 09:01
Here's something more for advice to those who read this thread. This is something I found helpful when I was producing larger DLLs.

I found that it doesn't matter to the VC++ compiler where the commands come from. Just for an experiment, which proved successful, after finding the list of commands in the debug DLL, I copied the list and pasted it in a notepad document. From there, I separated the commands into separate lines so they'd be more accessible. I even enumerated the commands. After that, I just had to have the notepad document opened so I could copy from there and paste into the resource file. I have the resulting list included.

You will notice I did this with decorated functions, but I'm sure they'll work just as well with EXTERN "C" type functions. I haven't heard or read about others doing this, so I thought I'd mention it in this thread. Hope this helps someone.

Attachments

Login to view attachments
Robert The Robot
17
Years of Service
User Offline
Joined: 8th Jan 2007
Location: Fireball XL5
Posted: 7th Jan 2012 21:50
Actually, if you're generating string tables you might find this code snippet of mine quite helpful: VC++ String Table Generator

Just copy all your c++ source files into a folder with the DBP code file and a text file holding the decorated names (no need to modify). DBP then outputs a "StringTable.rc" file you can include straight back into your VC project - the input/output parameter lists and expressions are automatically taken care of.

I think there are a few others out there as well, might be worth searching around for.

We spend our lives chasing dreams. Dark Basic lets us catch some of them.
WLGfx
16
Years of Service
User Offline
Joined: 1st Nov 2007
Location: NW United Kingdom
Posted: 7th Jan 2012 22:26 Edited at: 7th Jan 2012 22:40
The normal website is down at the moment http://www.dependencywalker.com/ dunno what happened to it.

but attached is the one I use...

"depends.exe" lets you view the exports from a DLL in both decorated and undecorated.

There's another, it's a bigger file but doesn't undecorate the exports. http://www.ucware.com/apev/index.htm I've just tried it but it wasn't very good.

Apart from that google "dll dependency viewer"...

Also just found another one I've used - http://dll-export-viewer.software.informer.com/

Mental arithmetic? Me? (That's for computers) I can't subtract a fart from a plate of beans!
Warning! May contain Nuts!

Attachments

Login to view attachments
Prehistoric Gamer
12
Years of Service
User Offline
Joined: 30th Mar 2011
Location:
Posted: 10th Jan 2012 08:24
I actually find it interesting to look at the decorated form, particularly the decoration. I find that I get a better idea of what letters symbolize the data types I use by comparing the code I write with the decorated functions.

So far, this is what I've gathered from the comparisons:

X evidently represents a void input or output, aka a zero character.
PAD represents a character string.
H represents an integer.
N represents a double float.

While I don't understand all of it, this is what I've figured out from comparing the decorations to the letter symbols of the same data types for DarkBASIC.

I appreciate the advice, and I may look into the programs you've offered.
IanM
Retired Moderator
21
Years of Service
User Offline
Joined: 11th Sep 2002
Location: In my moon base
Posted: 10th Jan 2012 16:30
You don't need to try decoding that stuff yourself: http://en.wikipedia.org/wiki/Microsoft_Visual_C%2B%2B_Name_Mangling

Login to post a reply

Server time is: 2024-03-29 10:05:45
Your offset time is: 2024-03-29 10:05:45