OVH Cloud OVH Cloud

dll et variable globale

10 réponses
Avatar
Fred
Bonjour à tous et bonne année!

J'ai une petite question à vous soumettre...

soit une dll toto.dll
soit une variable globale de la dll : int ma_variable_globale
soit une fonction de la dll (ma fonction) qui permet d'accéder en
lecture/écriture à la variable ma_variable_globale.
Et enfin, soient deux applications app1 et app2 qui chargent toutes les
deux la dll toto.dll

Ma question est la suivante:
On lance app1 qui initialise ma_variable_globale à 1 (via ma_fonction).
On lance app2 et on lit ma_variable_globale (via ma_fonction)... quelle
sera la valeur de ma_variable_globale?
Les variables globales d'une dll sont-elles partagées par toutes les
instances de cette dll?

Merci d'avance.

Fred

10 réponses

Avatar
Patrick Philippot
Bonjour,

Les variables globales d'une dll sont-elles partagées par toutes les
instances de cette dll?



Non. C'était vrai en 16-bit mais pas en Win32 (en tous cas, par par
défaut). Cependant, on peut forcer ce comportement en déclarant la
variable de manière adéquate. Il suffit de compiler la partie des
données à partager en utilisant la directive pragma suivante (en C/C++):

#pragma data_seg (".port")
int monentier ;
char tableau[25] ;
#pragma data_seg ()

et ensuite d'indiquer au linker que ce segment doit être partagé
(monentier et tableau seront partagés entre toutes les instances de la
DLL)

-SECTION:.port,RWS

ce qui signifie que .port est READ, WRITE et SHARED. Le nom .port n'est
pas obligatoire mais correspond à une convention. On pourrait également
utiliser le fichier .DEF et insérer l'instruction

DATA READ WRITE SHARED

Mais cela aurait l'inconvénient de rendre toutes les données
partageables. Un variante plus fine est possible:

SECTIONS .port READ WRITE SHARED

Il y a des procédures équivalentes pour d'autres outils de développement
que le coimpilateur Microsoft.

A NOTER:
On ne peut malgré tout pas partager n'importe quoi. En 16-bit, partager
un handle ou un pointeur était possible puisqu'il n'y avait qu'un seul
espace adresse. Sous Win32, partager handles ou pointeurs ne sert à rien
puisqu'il ne sont valides que dans un processus donné.

--
Patrick Philippot - Microsoft MVP
MainSoft Consulting Services
www.mainsoft.fr
Avatar
Fred
Patrick Philippot wrote:
Bonjour,


Les variables globales d'une dll sont-elles partagées par toutes les
instances de cette dll?




Non. C'était vrai en 16-bit mais pas en Win32 (en tous cas, par par
défaut). Cependant, on peut forcer ce comportement en déclarant la
variable de manière adéquate. Il suffit de compiler la partie des
données à partager en utilisant la directive pragma suivante (en C/C++):

#pragma data_seg (".port")
int monentier ;
char tableau[25] ;
#pragma data_seg ()

et ensuite d'indiquer au linker que ce segment doit être partagé
(monentier et tableau seront partagés entre toutes les instances de la
DLL)

-SECTION:.port,RWS

ce qui signifie que .port est READ, WRITE et SHARED. Le nom .port n'est
pas obligatoire mais correspond à une convention. On pourrait également
utiliser le fichier .DEF et insérer l'instruction

DATA READ WRITE SHARED

Mais cela aurait l'inconvénient de rendre toutes les données
partageables. Un variante plus fine est possible:

SECTIONS .port READ WRITE SHARED

Il y a des procédures équivalentes pour d'autres outils de développement
que le coimpilateur Microsoft.

A NOTER:
On ne peut malgré tout pas partager n'importe quoi. En 16-bit, partager
un handle ou un pointeur était possible puisqu'il n'y avait qu'un seul
espace adresse. Sous Win32, partager handles ou pointeurs ne sert à rien
puisqu'il ne sont valides que dans un processus donné.




Ah...
Bon, j'aurais dû étre plus précis dès le début alors.
En fait, il s'agit bien de partager un pointeur... donc j'imagine que
tout ceci n'est pas applicable...
Le but du jeu était qu'une adresse mémoire, initialisée par
l'application app1, soit accessible par l'application app2. L'adresse
n'étant pas nécessairement la même à chaque fois, je me demandais
comment la faire connaitre à app2. La variable globale partagée me
paraissait être une idée... mais apparement, ce n'est pas la bonne. :(

Merci en tout cas pour votre explication...

Si vous avez des idées, je suis preneur :)

Merci encore,

Fred
Avatar
adebaene
Fred a écrit :


Ah...
Bon, j'aurais dû étre plus précis dès le début alors.
En fait, il s'agit bien de partager un pointeur... donc j'imagine que
tout ceci n'est pas applicable...
Le but du jeu était qu'une adresse mémoire, initialisée par
l'application app1, soit accessible par l'application app2. L'adresse
n'étant pas nécessairement la même à chaque fois, je me demandais
comment la faire connaitre à app2. La variable globale partagée me
paraissait être une idée... mais apparement, ce n'est pas la bonne. :(


Non : toute l'idée du mode protégé est que chaque processus a son
propre espace d'adresage privé, et que personne d'autre que lui ne
peut y arriver, justement pour arriver que les différents processus se
marchent sur les pieds les un des autres en faisant des manipulations
foireuses de pointeurs.

Si vous avez des idées, je suis preneur :)



Pour partager une zone de mémoire sous Win32 entre différents
processus, il faut utiliser le mécanisme spécfiquement prévu pour
cela : les Memory Mapped Files (MMF). cf. MSDN pour tous les détails.

A noter que si pars sur cette voie, il faut prévoire un mécanisme de
synchronisation approprié pour protéger la mémoire partagée des
accès concurrents par les différents processus (un mutex nommé
générallment).

Arnaud
Avatar
Patrick Philippot
> Si vous avez des idées, je suis preneur :)



Utilisez un file mapping nommé (MMF) ouvert sur le pseudo-handle
0xFFFFFFFF (au lieu de l'ouvrir sur un fichier physique réel), ce qui
revient en fait à partager une zone de mémoire virtuelle.

C'est la méthode "officielle" de partage de blocs mémoire sous Win32.

Comme le précise Arnaud, il faudra protéger l"accès à la zone pointée
par le MMF par un mutex.

--
Patrick Philippot - Microsoft MVP
MainSoft Consulting Services
www.mainsoft.fr
Avatar
Fred
Patrick Philippot wrote:
Si vous avez des idées, je suis preneur :)




Utilisez un file mapping nommé (MMF) ouvert sur le pseudo-handle
0xFFFFFFFF (au lieu de l'ouvrir sur un fichier physique réel), ce qui
revient en fait à partager une zone de mémoire virtuelle.

C'est la méthode "officielle" de partage de blocs mémoire sous Win32.

Comme le précise Arnaud, il faudra protéger l"accès à la zone pointée
par le MMF par un mutex.




Ok.
Merci à vous tous... je vais regarder de ce côté là alors.

à+

Fred
Avatar
Fred
Fred wrote:
Patrick Philippot wrote:

Si vous avez des idées, je suis preneur :)





Utilisez un file mapping nommé (MMF) ouvert sur le pseudo-handle
0xFFFFFFFF (au lieu de l'ouvrir sur un fichier physique réel), ce qui
revient en fait à partager une zone de mémoire virtuelle.

C'est la méthode "officielle" de partage de blocs mémoire sous Win32.

Comme le précise Arnaud, il faudra protéger l"accès à la zone pointée
par le MMF par un mutex.




Ok.
Merci à vous tous... je vais regarder de ce côté là alors.

à+

Fred



Voilà les nouvelles...

Selon vos conseils, j'utilse donc CreateFileMapping pour partager de la
mémoire entre processus.
Le problème est que le deuxième processus qui tente d'accéder à la
mémoire via un OpenFileMapping se prend un ERROR_ACCESS_DENIED.
En effet, le CreateFileMapping est fait par un process tournant en tant
que SYSTEM. Le deuxième process n'a pas ces privilèges et ne peut donc
pas y accéder.
Sachant que le CreateFileMapping peut, dans mon cas, être fait par
SYSTEM ou par un user lambda (selon les cas de figure), pourriez-vous me
dire comment faire pour que le FileMapping puisse être accésible par
tout le monde en écriture et en lecture?

Merci d'avance

Fred
Avatar
Patrick Philippot
Fred wrote:
En effet, le CreateFileMapping est fait par un process tournant en
tant que SYSTEM. Le deuxième process n'a pas ces privilèges et ne
peut donc pas y accéder.



Dans ce cas, il faut créer un security descriptor donnant accès à
Everyone et passer son addresse à l'argument lpAttributes de
CreateFileMapping au lieu de NULL.

Voici un extrait du code d'une de mes applis créant un security
descriptor pour un event (c'est pareil) de manière à ce que Everyone ait
accès à cet event . Je ne dis pas que c'est le même code qu'il faut
utiliser dans votre cas (notamment au niveau des permissions qui seront
différentes) mais vous pourrez vous en inspirer:

PSID pEveryoneSID = NULL, pAdminSID = NULL;
PACL pACL = NULL;
EXPLICIT_ACCESS ea[1];
SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;

pSD = NULL;

// Create a well-known SID for the Everyone group.
AllocateAndInitializeSid(&SIDAuthWorld, 1,
SECURITY_WORLD_RID,
0, 0, 0, 0, 0, 0, 0,
&pEveryoneSID);

// Initialize an EXPLICIT_ACCESS structure for an ACE.
// The ACE will allow Everyone read access to the key.
ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS));
ea[0].grfAccessPermissions = GENERIC_ALL; // à affiner
ea[0].grfAccessMode = SET_ACCESS;
ea[0].grfInheritance= NO_INHERITANCE;
ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
ea[0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
ea[0].Trustee.ptstrName = (LPTSTR) pEveryoneSID;

// Create a new ACL that contains the new ACEs.
SetEntriesInAcl(1, ea, NULL, &pACL);

// Initialize a security descriptor
pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR,
SECURITY_DESCRIPTOR_MIN_LENGTH);
InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION);

// Add the ACL to the security descriptor.
SetSecurityDescriptorDacl(pSD, TRUE, pACL, FALSE);
sa.nLength = sizeof (SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = pSD;
sa.bInheritHandle = FALSE;

Ensuite vous passez l'adresse de sa à CreateFileMapping.

--
Patrick Philippot - Microsoft MVP
MainSoft Consulting Services
www.mainsoft.fr
Avatar
Fred
Patrick Philippot wrote:
Fred wrote:

En effet, le CreateFileMapping est fait par un process tournant en
tant que SYSTEM. Le deuxième process n'a pas ces privilèges et ne
peut donc pas y accéder.




Dans ce cas, il faut créer un security descriptor donnant accès à
Everyone et passer son addresse à l'argument lpAttributes de
CreateFileMapping au lieu de NULL.

Voici un extrait du code d'une de mes applis créant un security
descriptor pour un event (c'est pareil) de manière à ce que Everyone ait
accès à cet event . Je ne dis pas que c'est le même code qu'il faut
utiliser dans votre cas (notamment au niveau des permissions qui seront
différentes) mais vous pourrez vous en inspirer:




Merci beaucoup...
Ca marche impecc! :)

Fred
Avatar
Fred
Re-bonjour à tous

Je reviens à la charge car j'ai encore une petite question à vous poser.

Lorsqu'une application "A" qui crée un FileMapping via
CreateFileMapping(INVALID_HANDLE_VALUE,...) se termine, tout autre appel
à OpenFileMapping via une autre application échoue avec le code 2
(ERROR_FILE_NOT_FOUND). Est ce normal?
La durée de vie d'un FileMapping est-elle égale à celle de l'application
qui l'a créée?
Si oui, y'a t-il un moyen de forcer la persistance du FileMapping créé?

Merci encore pour votre aide.

Fred
Avatar
Aurelien Regat-Barrel
Patrick Philippot a écrit :
Bonjour,


Bonjour,

A NOTER:
On ne peut malgré tout pas partager n'importe quoi. En 16-bit, partager
un handle ou un pointeur était possible puisqu'il n'y avait qu'un seul
espace adresse. Sous Win32, partager handles ou pointeurs ne sert à rien
puisqu'il ne sont valides que dans un processus donné.



On peut un peu pinailler : ça dépend du type de handle. Un handle user
est utilisable par n'importe quel process. D'ailleurs la technique que
tu décris est courament employée dans une dll hook pour "partager" le
handle de la fenêtre vers qui rediriger (ou autre...) les messages.

Un handle de type kernel peut être dupliqué/hérité...

--
Aurélien Regat-Barrel