SRL Archive - C/C++ Help and Tutorials

DLL Help needed (scar++ related)


boberman

2008-09-30 18:46:35

Ok, I have ran into a snag with Scar++, I want to put a alright plugin loader into it, but the problem is that data types in delphi plugins don't always convert straight over.

So here is where I am at, if you can figure out how to do this I would be much appreciative.

What you need is Wizzup's Plugin from the SRL SVN. Put it in the same folder. Here is the program I have so far.

#include <iostream>
#include <windows.h>

using namespace std;

typedef long (*fcount)() __attribute((stdcall));
typedef long (*finfo)(int, void**, char**) __attribute((stdcall));
typedef POINT*& (*tpa)(HDC) __attribute((stdcall));

fcount GetFunctionCount;
finfo GetFunctionInfo;

int main()
{
long count;
HINSTANCE inst = LoadLibrary("WizzyPlugin.dll");
GetFunctionCount = (fcount)GetProcAddress(inst,"GetFunctionCount");
GetFunctionInfo = (finfo)GetProcAddress(inst,"GetFunctionInfo");

tpa CreateTPAFromBMP;
void* funcAddr;
char* junk = new char[512];
POINT* points;

GetFunctionInfo(33, &funcAddr, &junk);
CreateTPAFromBMP = (tpa)funcAddr;

cerr << "Failed after here!\n";
cerr << notJunk << endl;

HDC dc = GetDC(GetDesktopWindow());

cerr << "got the correct dc\n";

points = CreateTPAFromBMP(dc);

return 0;
}

Well, thats what I have so far. If you know how to get further I would be very grateful.

R0b0t1

2008-09-30 21:13:10

All types will convert over evenly. Delphi has no different types than any other language, unless a user defines them.

However, what is most likely the problem is that you don't declare the function pointer right. It should be something like:


int (*get_procnum)(void)
int (*get_procaddr)(long, void*, char*)

The functions inside the DLL should be 'stdcall', not 'pascal' or 'cdecl' (which C++ happens to be). So they should be found. ALSO, when in windows, stdcall decorates the functions like:


_name@x

Where x = number of bytes in arguments. (On 32bit, void == sizeof(int)) So the symbols in the Delphi DLL would be (I think):


_GetFunctionCount@0
_GetFunctionInfo@12


EDIT: Wait, I saw what was at the top with the function pointers… But… WHAT THE FUCK is going on with those typedefs?

boberman

2008-09-30 23:10:52

From: R0b0t1
All types will convert over evenly. Delphi has no different types than any other language, unless a user defines them.

However, what is most likely the problem is that you don't declare the function pointer right. It should be something like:


int (*get_procnum)(void)
int (*get_procaddr)(long, void*, char*)

The functions inside the DLL should be 'stdcall', not 'pascal' or 'cdecl' (which C++ happens to be). So they should be found. ALSO, when in windows, stdcall decorates the functions like:


_name@x

Where x = number of bytes in arguments. (On 32bit, void == sizeof(int)) So the symbols in the Delphi DLL would be (I think):


_GetFunctionCount@0
_GetFunctionInfo@12


EDIT: Wait, I saw what was at the top with the function pointers… But… WHAT THE FUCK is going on with those typedefs?


:P Yep I was a couple a steps ahead of you. using typedefs makes it much easier to convert from a void* (which is returned by GetFunctionInfo) to a function pointer of your choice. It just makes it more readable. As for the stdcall. Well, g++ has a weird way to make functions stdcall it is __attribute((stdcall)); Yes, it is different from most other compilers, but it's what I got.

However, I don't have a perfect knowledge of C++, pascal, and windows DLLs. This much I know for certain.

1. The returned pointer is pointing to the correct location. This method that I just used works for other functions (like smart)

2. stdcall is working correctly, again, smart was used to test this method and It came out fine.

What I don't know.

1. Why it crashes, perhaps c++ was supposed to handle creating and allocating the array rather then the delphi dll. That is one of my suspicions.

Anyways, if you can get a working declaration that runs CreateTPAfromBMP I would be more then happy to see the implementation.

R0b0t1

2008-10-03 19:50:43

No, I understand what typedef's are for. But you didn't specify a replacement type (because the space makes the type "long" whether it be anywhere in your code, refer to the function pointers you made up).

boberman

2008-10-03 20:44:10

From: R0b0t1
No, I understand what typedef's are for. But you didn't specify a replacement type (because the space makes the type "long" whether it be anywhere in your code, refer to the function pointers you made up).


Trust me, that is correct, it wouldn't compile if it wasn't. The way you declare a function pointer is long (*NameOfFunctionPointer)(int arguments); Don't blame me for the strange syntax, that is just how it is. The replacement type is in the parenthesis

If you still don't believe me, take out the call to the new function, do a loop on Get function info (make sure you allocate memory for the new functions name) and print out all the names of the functions. It works, trust me.

BenLand100

2008-10-04 06:45:31

I wrote it in a class a while back, shouldn't be too hard to modify. The class pretty much works like SCAR, e.g. loads all plugins from a folder. Then the user must get method addresses out of the class via their name and assign that value to the proper pointer definition.

#ifndef PLUGINS_H
#define PLUGINS_H

#include "Utils.h"
#include <windows.h>
#include <string>
#include <vector>
#include <map>

#define EXPORT __declspec(dllexport)

class PluginLoader {

public:
EXPORT PluginLoader(const char* pluginDir);
EXPORT ~PluginLoader();
EXPORT void* getMethodAddress(const char* name);

private:
std::vector<HINSTANCE> handles;
std::map<const char*, void*, strCmp> addrs;

};

#endif

#include "Plugins.h"
#include <iostream>

using namespace std;

typedef long (*GetFunctionInfo)(int index, void** address, char** def) __attribute__((stdcall));
typedef long (*GetFunctionCount)() __attribute__((stdcall));

void lower(char* str) {
while (*str != 0) {
*str = (char) tolower(*str);
*str++;
}
}

PluginLoader::PluginLoader(const char* pluginDir) {
cout << "Loading plugins from " << pluginDir << "\n";
int dirlen = strlen(pluginDir);
char search[MAX_PATH];
char temp[MAX_PATH];
strcpy(search, pluginDir);
strcat(search, "/*.dll");
HANDLE find;
WIN32_FIND_DATA file;
find = FindFirstFile(search, &file);
if (find != INVALID_HANDLE_VALUE) {
do {
strcpy(temp, pluginDir);
strcat(temp, "/");
strcat(temp, file.cFileName);
cout << "Loading " << file.cFileName << "\n";
HINSTANCE inst = LoadLibrary(temp);
if (inst) {
handles.push_back(inst);
GetFunctionCount count = (GetFunctionCount)GetProcAddress(inst,"GetFunctionCount");
GetFunctionInfo info = (GetFunctionInfo)GetProcAddress(inst,"GetFunctionInfo");
if (count != 0 && info != 0) {
long functions = count();
for (int i = 0; i < functions; i++) {
try {
void* address;
char* def = (char*)malloc(512);
if (info(i,&address,&def) == -1) break;
char* ref = def;
lower(def);
while (*def == ' ') def++;
while (*def != ' ') def++;
while (*def == ' ') def++;
char* start = def;
while ((*def != ' ') && (*def != '(') && (*def != ':') && (*def != ';')) def++;
*def = 0;
char* name = (char*)malloc(def - start + 1);
strcpy(name, start);
addrs[name] = address;
cout << "\t" << name << "\n";
free(ref);
} catch (...) {
cout << "Error loading function index " << i << " from " << file.cFileName << "\n";
}
}
}
}
} while (FindNextFile(find, &file));
cout << "Done loading plugins!\n";
FindClose(find);
}
}

PluginLoader::~PluginLoader() {
while (!handles.empty()) {
FreeLibrary(handles.back());
handles.pop_back();
}
map<const char*, void*>::iterator it;
for (it = addrs.begin(); it != addrs.end(); it++) {
free((void*)(it->first));
}
addrs.clear();
}

void* PluginLoader::getMethodAddress(const char* name) {
char* lowerName;
lowerName = (char*)malloc(strlen(name)+1);
strcpy(lowerName, name);
lower(lowerName);
if (addrs.end() != addrs.find(lowerName))
return addrs[lowerName];
return 0;
}

boberman

2008-10-04 09:25:24

From: BenLand100
I wrote it in a class a while back, shouldn't be too hard to modify. The class pretty much works like SCAR, e.g. loads all plugins from a folder. Then the user must get method addresses out of the class via their name and assign that value to the proper pointer definition.

#ifndef PLUGINS_H
#define PLUGINS_H

#include "Utils.h"
#include <windows.h>
#include <string>
#include <vector>
#include <map>

#define EXPORT __declspec(dllexport)

class PluginLoader {

public:
EXPORT PluginLoader(const char* pluginDir);
EXPORT ~PluginLoader();
EXPORT void* getMethodAddress(const char* name);

private:
std::vector<HINSTANCE> handles;
std::map<const char*, void*, strCmp> addrs;

};

#endif

#include "Plugins.h"
#include <iostream>

using namespace std;

typedef long (*GetFunctionInfo)(int index, void** address, char** def) __attribute__((stdcall));
typedef long (*GetFunctionCount)() __attribute__((stdcall));

void lower(char* str) {
while (*str != 0) {
*str = (char) tolower(*str);
*str++;
}
}

PluginLoader::PluginLoader(const char* pluginDir) {
cout << "Loading plugins from " << pluginDir << "\n";
int dirlen = strlen(pluginDir);
char search[MAX_PATH];
char temp[MAX_PATH];
strcpy(search, pluginDir);
strcat(search, "/*.dll");
HANDLE find;
WIN32_FIND_DATA file;
find = FindFirstFile(search, &file);
if (find != INVALID_HANDLE_VALUE) {
do {
strcpy(temp, pluginDir);
strcat(temp, "/");
strcat(temp, file.cFileName);
cout << "Loading " << file.cFileName << "\n";
HINSTANCE inst = LoadLibrary(temp);
if (inst) {
handles.push_back(inst);
GetFunctionCount count = (GetFunctionCount)GetProcAddress(inst,"GetFunctionCount");
GetFunctionInfo info = (GetFunctionInfo)GetProcAddress(inst,"GetFunctionInfo");
if (count != 0 && info != 0) {
long functions = count();
for (int i = 0; i < functions; i++) {
try {
void* address;
char* def = (char*)malloc(512);
if (info(i,&address,&def) == -1) break;
char* ref = def;
lower(def);
while (*def == ' ') def++;
while (*def != ' ') def++;
while (*def == ' ') def++;
char* start = def;
while ((*def != ' ') && (*def != '(') && (*def != ':') && (*def != ';')) def++;
*def = 0;
char* name = (char*)malloc(def - start + 1);
strcpy(name, start);
addrs[name] = address;
cout << "\t" << name << "\n";
free(ref);
} catch (...) {
cout << "Error loading function index " << i << " from " << file.cFileName << "\n";
}
}
}
}
} while (FindNextFile(find, &file));
cout << "Done loading plugins!\n";
FindClose(find);
}
}

PluginLoader::~PluginLoader() {
while (!handles.empty()) {
FreeLibrary(handles.back());
handles.pop_back();
}
map<const char*, void*>::iterator it;
for (it = addrs.begin(); it != addrs.end(); it++) {
free((void*)(it->first));
}
addrs.clear();
}

void* PluginLoader::getMethodAddress(const char* name) {
char* lowerName;
lowerName = (char*)malloc(strlen(name)+1);
strcpy(lowerName, name);
lower(lowerName);
if (addrs.end() != addrs.find(lowerName))
return addrs[lowerName];
return 0;
}


Aye, Indeed this program is loosely (very loosely) based on that class (it was a big help for me to understand how pascal dlls works). It is just that, for some reason, I can't write a function pointer definition to get the TPoint arrays to be returned from the function. I suspect that the function tries to write where it has no rights to write, but I am not sure.

R0b0t1

2008-10-05 02:32:42

What you say? All DLL same.

And I assume what a point would be is what it is already declared as in C++. Its just two integers glued together (So you can probably assume that whatever you declare, as long as it is two integers together in a struct, is right.)

boberman

2008-10-05 10:30:09

From: R0b0t1
What you say? All DLL same.

And I assume what a point would be is what it is already declared as in C++. Its just two integers glued together (So you can probably assume that whatever you declare, as long as it is two integers together in a struct, is right.)


Ok, try it and see if it works. It didn't work for me, but maybe I was somehow doing it wrong.

mastaraymond

2008-10-13 15:06:57

Not quite sure, but if you want to pass a TPointArray to a C++ dll you probably need to do it like em.. Make one of the parameters a var to a TPoint, have a length parameter and then..
You do like "RandomMethod(TPA[0], Length(TPA));

Now you should have the pointer to the first element..

Not quite sure if this is what you mean xD..

EDIT: I see your problem, a returning TPA.. Well now you have to find out how Delphi stores Array's.. >.<