2010-11-03 5 views
3

J'ai un projet Visual Studio 2008 C++ pour Windows Mobile 6 avec deux processus. Les deux dont je voudrais avoir accès à la même fonction qui est contenue dans le processus1.Création d'un pointeur de fonction inter-processus

Cette fonction est Buzz:

struct TEST_STRUCT 
{ 
    int bar; 
    WCHAR foo[ 20 ]; 
}; 

typedef int(*pfn_Buzz)(TEST_STRUCT*); 

int Buzz(TEST_STRUCT* info); 

Process1 contient la définition de Buzz et crée un pointeur de fonction pour dans un fichier mappé en mémoire:

int Buzz(TEST_STRUCT* info) 
{ 
    info->bar = 1; 
    wsprintf(info->foo, L"Hello!"); 
    return 100; 
} 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    // create a memory-mapped file shared memory space that can be read by any process 
    HANDLE mapping = ::CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(pfn_Buzz) , NULL); 
    LPVOID buzz_addr = ::MapViewOfFile(mapping, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(pfn_Buzz)); 

    // find our process' memory offset 
    DWORD offset = ::GetCurrentProcessIndex() + 0x02000000; 

    // copy the complete function address to the shared memory space 
    buzz_addr = (LPVOID)((DWORD)&Buzz + offset); 

    // convert the function address to a string to send to process2.exe 
    WCHAR address[ 9 ] = { 0 }; 
    wsprintf(address, L"%x", ((DWORD)buzz_addr)); 

    // start process2.exe and wait for it to finish 
    PROCESS_INFORMATION pi = { 0 }; 
    ::CreateProcess(L"\\test_user.exe", address, NULL, NULL, FALSE, 0, NULL, NULL, NULL, &pi); 
    ::WaitForSingleObject(pi.hProcess, INFINITE); 

    ::UnmapViewOfFile(buzz_addr); 
    ::CloseHandle(mapping); 

    return 0; 
} 

Process2 reçoit l'adresse de Buzz de Process1, le projette sur le pointeur de fonction pfn_Buzz et l'exécute.

// process2.exe 
int _tmain(int argc, _TCHAR* argv[]) 
{ 
    // get the address of the Buzz() function pointer 
    WCHAR* wszAddr = argv[ 1 ]; 
    WCHAR* wszAddrEnd = &argv[ 1 ][ 8 ]; 
    DWORD address = wcstol(wszAddr, &wszAddrEnd, 16); 
    pfn_Buzz PFNBuzz = (pfn_Buzz)address; 

    // execute buzz 
    TEST_STRUCT test = { 0 }; 
    PFNBuzz(&test); 

    return 0; 
} 

Malheureusement, je reçois une exception d'instruction illégale dans process2 lorsque je tente d'exécuter la fonction PFNBuzz.

Quelqu'un peut-il suggérer ce que je pourrais changer pour obtenir la fonctionnalité que je suis après?

Merci, PaulH

Répondre

2

Si vous voulez que les deux processus utilisent la même fonction, placez la fonction dans une DLL et liez cette DLL aux deux processus. Chaque processus a son propre espace d'adressage, donc une adresse dans un processus peut ne pas fonctionner du tout dans un autre processus, et même si cela "fonctionne", cela pointera vers quelque chose de complètement différent.

Bien que vous utilisiez/transmettiez le décalage dans la zone mappée en mémoire du côté parent, je ne vois aucun code correspondant pour mapper la même région et ajouter sa base au pointeur lorsque vous essayez d'y accéder le côté client.

Visual C++ a un type de pointeur __based qui peut simplifier un peu.

+0

N'existe-t-il aucun moyen de copier le code de fonction dans un espace d'adressage partagé? – PaulH

+0

@PaulH: il est * possible * de le faire par vous-même, mais une DLL le fait automatiquement. À moins que la DLL n'ait besoin d'accéder aux données dans un processus, c'est beaucoup plus simple (et si elle a besoin d'accéder aux données, il est généralement préférable de les mettre dans la DLL et de les partager - voir '#pragma data_set' pour plus de détails sur comment faire ça). –

+0

vouliez-vous dire '#pragma data_seg'? Je ne vois pas de 'data_set' sur MSDN. – PaulH

5

Votre problème vient du fait que le procédé A (qui lance un processus B) ont 2 espaces d'adressage virtuel totalement différentes. Vous pouvez passer un pointeur de fonction au Processus A, mais comme les espaces d'adressage sont différents, tout pointeur que vous passerez sera moins affecté à un autre processus. C'est l'un des énormes avantages des processus.

Si vous souhaitez communiquer entre les processus, vous devrez utiliser une forme de Inter-Process Communication (IPC).

Il existe plusieurs façons de faire IPC under windows. À mon avis, la méthode la plus polyvalente consiste à utiliser des douilles. Les sockets vous offrent l'avantage de pouvoir séparer les machines exécutant le processus A et le processus B tout en conservant les fonctionnalités souhaitées. De même, rien n'interrompt les deux processus s'exécutant sur la même machine. La flexibilité qu'il fournit est très utile cependant.

Donc, en supposant que vous choisissiez une méthode basée sur socket de IPC, ce que vous faites est Remote Procedure Call (RPC). Il ya, comme vous pouvez l'imaginer, des tas de façons différentes de faire du RPC. Vous pouvez utiliser DCOM ou Corba. Raknet est une bibliothèque tierce prenant en charge RPC. Il existe également de nombreuses autres solutions. Je recommande fortement de faire beaucoup de recherches sur le sujet. Les liens et mots-clés que je vous ai fournis devraient vous donner un bon point de départ :)

+0

Les fichiers mappés en mémoire sont une méthode IPC dans Windows CE. En outre, j'ai fourni l'adresse de mémoire virtuelle corrigée de l'intervalle de traitement de la fonction pour tenir compte des différences dans l'espace d'adressage virtuel. – PaulH

+0

@PaulH: Vous ne mappez que dans un processus (et même pas nommé mappage à cela!) Donc, bien sûr, il peut être utilisé mais je vous suggère de lire ce lien: http://msdn.microsoft.com/fr-fr /library/aa366878(v=VS.85).aspx Notez également que vous ne prenez pas en compte la randomisation de l'espace adresse. Soit suivez attentivement le lien ci-dessus ou utilisez une méthode différente ... – Goz

+0

Je savais que Win 7 et Vista utilisaient la randomisation d'adresse. Je ne savais pas Windows Mobile 6 fait. Avez-vous un lien MSDN le décrivant? – PaulH

2

Vous pouvez simplement exporter la fonction à l'aide de __declspec(dllexport), puis la charger en utilisant GetProcAddress() en utilisant son nom. Si en C++, rappelez-vous simplement extern "C" la déclaration pour désactiver la gestion des noms. Considérez:

extern "C" int __declspec(dllexport) Buzz(TEST_STRUCT* info) { ... } 
int main (int, char **) 
{ 
    pfn_Buzz buzz = GetProcAddress(GetModuleHandle(NULL), "Buzz"); 
    // ... 
} 

Ensuite, transmettez le nom de la fonction en utilisant la méthode IPC de votre choix.

+0

En supposant que Buzz soit présent dans les deux processus. par exemple. grâce à l'utilisation d'une DLL commune comme suggéré par Jerry Coffin. –

+0

Je supposais que c'était une chose de type fourche où le processus ré-engendré le même exécutable. Si vous chargez via une DLL commune, le handle de module sera bien sûr celui obtenu par un appel à 'LoadLibrary()'. –