Jump to content
Rpg²S Forum

Creare una dll da usare in RPGXP!


Keroro
 Share

Recommended Posts

E così avete una conoscenza universitaria di C ma non sapete ancora bene cosa farvene oltre a stupide applicazioni da console e calcoli matematici.

Ma il C nasconde una grande velocità di calcolo e l'accesso in modo semplice a funzioni di sistema, quindi perché non creare una dll da usare con rpgxp o con qualsidiavolo programma si voglia?

 

tutto nasce perché oggi per aiutare un mio amico sono andato a rispolverare una cartella con un backup di dei miei sorgenti C e mi trovo di fronte del codice vecchissimo di una dll che avevo creato per qualcuno per rpgmaker xp!

 

innanzitutto come è formata una dll in windows?

 

è IN TUTTO e PER TUTTO uguale ad un programma normale se non che al posto del main (o del WinMain) c'è questa funzione (in genere viene anche presentato lo scheletro in questo modo :D)

 

#include <windows.h>

BOOL APIENTRY DllMain (HINSTANCE hInst	 /* Library instance handle. */ ,
				   DWORD reason		/* Reason this function is being called. */ ,
				   LPVOID reserved	 /* Not used. */ )
{
switch (reason)
{
  case DLL_PROCESS_ATTACH:
	break;

  case DLL_PROCESS_DETACH:
	break;

  case DLL_THREAD_ATTACH:
	break;

  case DLL_THREAD_DETACH:
	break;
}
/* Returns TRUE on success, FALSE on failure */

return TRUE;

}

 

Così semplice?

Sì, così semplice! tutto quello che dovete fare è:

  • mettere il codice che va eseguito nel momento che la dll viene caricata dentro case DLL_PROCESS_ATTACH
  • tutto quello che va eseguito quando la dll viene disallocata con case DLL_PROCESS_DETACH:
  • definire funzioni con un PICCOLO PREFISSO ed un PICCOLO ACCORGIMENTO

(dei thread non ce ne occupiamo)

 

il piccolo prefisso è quello di mettere __declspec(dllexport) __cdecl prima del nome della funzione (esistono due modi per esportare funzioni ma solo cdecl funziona :) )

considerando quanto sia error prone scrivere tutti quei __ ho fatto un bel define all'inizio:

#define esporta __declspec(dllexport) __cdecl

 

quindi per creare una dll con una funzione stupida con la somma di due numeri mi basta mettere tra il define ed il DllMain

 

esporta int somma(int a,int b){
return a+b;
}

 

il piccolo accorgimento è per gli array, se restituisco una stringa ovvero un array di char non posso crearla come variabile automatica nella funzione (ovvero char stringa[dimensione]) perché verrebbe eliminata subito dopo il return (questo vale sia per le dll che per i programmi normali, avete mai provato a restituire una stringa e non funzionava :D?), quindi ci sono due modi:

1° uso una keyword static o una variabile globale

2° alloco dinamicamente l'array che eliminerò in Process DETACH

 

riprendo il codice che avevo scritto con ultima modifica sab 02 dic 2006 01:31:00 CET

#define _WIN32_WINNT 0x0501

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define esporta __declspec(dllexport) __cdecl


/*globali*/
HANDLE hFile;
WIN32_FIND_DATA fData;


esporta char * FindFirst(const char *path){
	static char ris[300];
	char *a=path;
	if (!strstr(a,"\\*")) strcat(a,"\\*");
	hFile = FindFirstFile(a,&fData);
	if (hFile==INVALID_HANDLE_VALUE) {
	   strcpy(ris,"*ERRORE*");
	}else {
	if(hFile) strcpy(ris,fData.cFileName);
	else strcpy(ris,"");
	}
	
	return ris;
}

esporta char * FindNext(){
	static char ris[300];
	if (hFile==INVALID_HANDLE_VALUE){
	   strcpy(ris,"");
	}else if (!FindNextFile(hFile,&fData)){
	   FindClose(hFile);
	   strcpy(ris,"");
	   hFile = INVALID_HANDLE_VALUE;
	}else strcpy(ris,fData.cFileName);

	return ris;
}


BOOL APIENTRY DllMain (HINSTANCE hInst	 /* Library instance handle. */ ,
				   DWORD reason		/* Reason this function is being called. */ ,
				   LPVOID reserved	 /* Not used. */ )
{

switch (reason)
{
  case DLL_PROCESS_ATTACH:
	   hFile = INVALID_HANDLE_VALUE;
	break;

  case DLL_PROCESS_DETACH:
	break;

  case DLL_THREAD_ATTACH:
	break;

  case DLL_THREAD_DETACH:
	break;
}

/* Returns TRUE on success, FALSE on failure */

return TRUE;
}

 

A cosa serve questa dll?

A trovare la lista di file fornendo una path sia locale sia assoluta tipo "C:\\Programmi\\Cartella\\*" (ricordo che la \ va raddoppiata per non essere malamente interpretata) o "C:\\Programmi\\Cartella" (il \\* lo aggiungo io) tramite funzioni di sistema.

Come vedete ho adottato la soluzione dello static inoltre poiché può esserci più di un file ho organizzato il tutto con più chiamate alla funzione

Mi sembra tutto abbastanza elementare, se avete dubbi sulle funzioni basta guardare la msdn :D

 

Una volta fatta la dll e compilata mettendo come tipo di progetto Windows dll (se usate dev c++) o tramite due semplici righe di codice in gcc:

gcc -c file.c
gcc -shared -o nomedll.dll file.o

 

ora veniamo a noi, come usare le dll in ruby?

ma... aveva scritto una bellissima guida tanti mesi fa un certo progm, RIGUARDATEVELA!

 

per i più pigri scrivo come dovrebbe essere usata la dll :)

@finder1 = Win32API.new("finder.dll", "FindFirst","P","P")
@finder2 = Win32API.new("finder.dll","FindNext","","P")
#metodo per chiamata
def cerca(percorso)
a = @finder1.call("screenshot") #cerca dentro la cartella screenshot
if (a[0] == "*") #gestisco l'errore come fossi in ruby e non in RGSS, non saprei come fare
	print "Errore, Cartella non valida o inesistente\n"
	return 1
end
if (a == "")
	print "Cartella vuota!"
	return 0
end
print "Elenco file:\n"+a
while (a = @finder2.call() && a != "") #non so se in ruby è concesso, io ci provo
	print a+"\n"
end
return 0
end
#chiamata
cerca("screenshot") #mostra la lista dei file

 

la dll è essenziale, potete aggiungerci le funzioni che volete come di ricerca per un determinato tipo di file, ecc...

 

Il tutorial finisce qui, forse sarò stato un po' confusionario perché molte cose o le ignoro o le do per scontato XD

se avete bisogno di chiarimenti/puntualizzazioni/segnalazioni errori o volete dare il vostro giudizio non avete che da postare

Edited by Keroro

I Miei Script:
Salva Schermata (3 Aprile 2012)
Attacco Personalizzabile (2 Aprile 2012)
Keyboard Input (Porting) (17 Marzo 2012)
Continua...

Link to comment
Share on other sites

non c'entra una cippa con rpgmaker xp comunque lo scrivo lo stesso :D, sarei tentato di aprire un altro topic per due motivi: 1° non si possono mettere in rpgxp 2° quando rispondo ad un tutorial non mi danno mai rens anche se dico cose interessanti :__________;

 

ma al diavolo lo scrivo qui perché me l'hai chiesto qui :D

 

COMPENDIO: Creazione di libreria .so (per sistemi *nix)

 

Le librerie .so, anche note come shared object, sono l'alternativa alle dll per le applicazioni linux (uso linux per semplicità e non dover stare a scrivere tutto).

Al contrario delle dll però non hanno bisogno di un entry point, ovvero di una funzione come DllMain ma basta mettere le funzioni.

 

 

Mi sento in vena di cavolate quindi presento la mia funzione h4x3d scritta al momento perché mi sento 1337

 

hax.c

#include <stdio.h>
#include <malloc.h>

int printh4x(char * stringa){
//strlen
int len = 0;
char *s = stringa;
while (*(s+len++)); //lunghezza incluso 'barra0'
if (len<2) return 1;
//h4x0r5
char * temp = (char *)malloc(sizeof(char)*len);
int i;
for (i=0; i< len;i++){
	switch (stringa[i]){
		case 'a':case 'A': temp[i] = '4'; break;
		case 's':case 'S': temp[i] = '5'; break;
		case 'o':case 'O': temp[i] = '0'; break;
		case 'l':case 'L': temp[i] = '1'; break;
		case 'e':case 'E': temp[i] = '3'; break;
		case 'b':case 'B': temp[i] = '8'; break;
		case 't':case 'T': temp[i] = '7'; break;
		case 'z':case 'Z': temp[i] = '2'; break;
		case 'c':case 'C': temp[i] = '{'; break;
		case 'i': 		   temp[i] = 'I'; break;
		case 'r':		   temp[i] = 'R'; break;
		default: temp[i] = stringa[i]; break; 
	}
}
temp[i] = '\'; //sarebbe '\ 0' attaccato ma il forum me lo toglie per protezione
printf("%s\n",temp);
free(temp);
return 0;
}

 

se non capite questo codice state messi male perché ora richiameremo il .so in un altro programma C e saranno dolori :D

 

Se però volessimo creare una funzione che viene eseguita prima che la dll venga messa a disposizione del programma e dopo essere stata sganciata dovremmo creare funzioni con prefissi questi attributi:

 

void __attribute__ ((constructor)) inizio(void) {

}
void __attribute__ ((destructor)) fine(void){

}

 

ma questa è un'altra storia...

Torniamo IT

 

Compilo il file in hax.so

gcc -c -fpic hax.c
gcc -shared -o hax.so hax.o

 

spiegazione: -c lo dovreste sapere, si usa solo per compilare senza linkare, genera il .

-fpic genera codice a posizione indipendente (richiesto perché la ,so può essere caricata in memoria anche più di una volta ;)

-o definisce il nome del file di output, in questo caso hax.so

-shared dice a gcc di creare una shared library :)

 

Ora che ho il mio bel .so, come faccio a caricarlo?

Per caricarlo in ruby ( non le chiamate di rgss, intendo il creare un'estensione, avete mai visto require 'libreria.so' in cima a qualche prorammino? ) bisogna creare una funzione ausiliaria in c chiamata Init_nomelibreria() e registrare all'interno eventuali classi e metodi, e dare tutto in pasto ad un file .rb creato ad hoc tramite la libreria mkmf che crea (come potete notare dalla sigla) un makefile per compilarci il .so :) fatto quello basta make.

Per caricarlo in C ci sono diversi modi, il modo più comodo è quello tramite la libreria dlfcn (che nome del cazzo XD) che contiene all'interno diverse funzioni di interesse:

  • dlopen() chiama l'eventuale costruttore allocando la dll
  • dlsym() permette di estrarre una funzione di cui già conoscevamo (in teoria ;) ) il prototipo
  • dlclose() chiude la dll che sarà deallocata dopo la chiamata all'eventuale distruttore
  • dlerror() restituisce la stringa di errore

ci vado leggero con l'error handling riportando solo se non trova la .so, non se non trova la funzione (che se non sbagliamo il prototipo o la versione della lib deve sempre trovare)

L'unica difficoltà qui è che si tratta di puntatori a funzione, un argomento molto trascurato in università ma a cui dovrete fare il callo perché capita quasi sempre in applicazioni "importanti"

#include <stdio.h>
#include <dlfcn.h>

int main(int argc,char * argv){
void * dll;
int (*print)(char *); //puntatore a funzione che prende come input una stringa e restituisce int

dll = dlopen("./hax.so", RTLD_LAZY);
if (!dll){
	fprintf(stderr,"Errore %s\n",dlerror());
	return 1;
}

print = dlsym(dll,"printh4x");

print("ciao mondo by keroro/cristiano87");

dlclose(dll);

return 0;
}

 

dllopen restituisce un puntatore che per semplicità ho chiamato dll, il secondo argomento dice di mappare la dll e tutte le sue reference solo su effettiva necessità, altrimenti si può usare RTLD_NOW (se la dll è molto grande e non puntiamo a prestazioni real time meglio essere pigri :) )

è importante la ./ prima del nome per dire di cercare nella stessa CARTELLA (la / da sola non funza), tramite le barre puoi specificare un percorso relativo o assoluto, altrimenti cerca dentro le cartelle standard o guarda in una determinata variabile dell'env

 

dlsym prende due argomenti, l'handle/puntatore della dll caricata e una stringa contente il nome della funzione da caricare, restituisce l'indirizzo della funzione caricata in memoria e per accoglierlo dovremmo aver già creato un puntatore a funzione idoneo (va bene anche ad argomento variabile se non facciamo cappellate di chiamata, ma è più rigoroso e errorfree definire tutto :) )

 

una volta inizializzato il puntatore possiamo benissimo chiamare la funzione ponendoci due parentesi tonde vicino :)

 

dlclose chiude l'handle

 

Come compilare il tester?

basta aggiungere dl alle lib

gcc -o tester tester.c -ldl

Nota: sono due ELLE MINUSCOLE

 

Eseguite

./tester

 

Output (per chi non ha linux XD)

{I40 m0nd0 8y k3R0R0/{RI57I4n087

 

Linuxiani ditemi che ne pensate :D

I Miei Script:
Salva Schermata (3 Aprile 2012)
Attacco Personalizzabile (2 Aprile 2012)
Keyboard Input (Porting) (17 Marzo 2012)
Continua...

Link to comment
Share on other sites

E così si dimostrò quanto le lib. di linux ruleggino e quanto le dll di win soccombino :D

Cmq mo vedo di farti mettere i rens perchè fai schifo ... =|

TPC Radio Site | Blog | Big-Bug

http://img102.imageshack.us/img102/4332/slackware2userbarok0.gif

http://img141.imageshack.us/img141/1571/nokappams1cf8.png

 

http://i29.tinypic.com/2vijdlh.jpg

Link to comment
Share on other sites

Mi serviva proprio questa tutorial.

Cercavo un modo di far interagire il mio gioco fatto con Rpg Maker XP con un ini per cambiarne un determinato valore, ora so come fare...

 

Sei impagabile Cri ;)

Davai

Link to comment
Share on other sites

  • 11 months later...

Scusate se riesumo questo topic, xò ho 1 domanda e mi sembra inutile aprire 1 altro topic per nulla..

Sapete se è possibile creare 1 dll per rmxp anche con il vb6? io ne ho fatto 1 xò non riesco a caricarlo, mi dà sempre:

RuntimeError Occured
GetProcAddress e GetProcAddressA: MiaFunzione o MiaFunzionaA

Ho provato invece a fare 1 la "somma" con il metodo riportato qui sopra e funziona.. Qualcn può aiutarmi? :sad:

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...