创建跨进程函数指针

时间:2010-11-03 21:54:24

标签: c++ windows function-pointers

我有一个Windows Mobile 6的Visual Studio 2008 C ++项目,有两个进程。我想要访问process1中包含的同一个函数。

该功能是Buzz

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

typedef int( *pfn_Buzz )( TEST_STRUCT* );

int Buzz( TEST_STRUCT* info );

Process1包含Buzz的定义,并在内存映射文件中为它创建一个函数指针:

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从Process1接收Buzz的地址,将其强制转换为pfn_Buzz函数指针,并执行它。

// 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;
}

不幸的是,当我尝试执行PFNBuzz函数时,我在process2中收到了非法指令异常。

任何人都可以建议我可以改变以获得我追求的功能吗?

谢谢, PaulH

3 个答案:

答案 0 :(得分:6)

您的问题源于流程A(启动流程B)具有2个完全不同的虚拟地址空间。您可以将一个函数指针传递给进程A,但是当它们的地址空间不同时,您传递的任何指针都会减少到另一个进程。这是流程的巨大奖金之一。

如果您希望在流程之间进行通信,则需要使用某种形式的Inter-Process Communication (IPC)

有几种方法可以IPC under windows。在我看来,最通用的方法是使用套接字。套接字为您提供的优势是,您可以分离运行进程A和进程B的计算机,并仍然可以获得所需的功能。同样,没有什么能阻止在同一台机器上运行的两个进程。它提供的灵活性非常有用。

因此,假设您选择基于套接字的IPC方法,那么您实际所做的是Remote Procedure Call (RPC)

正如您可能想象的那样,有许多不同的RPC方法。您可以使用DCOMCorbaRaknet是支持RPC的第三方库。还有许多其他解决方案。我强烈建议对此事进行大量研究。我为您提供的链接和关键字应该为您提供一个很好的起点:)

答案 1 :(得分:3)

您只需使用__declspec(dllexport)导出该功能,然后使用GetProcAddress()使用其名称加载它。如果在C ++中,只需记住extern "C"声明以禁用名称修改。考虑:

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

然后,使用您选择的IPC方法传递函数名称。

答案 2 :(得分:2)

如果您希望两个进程都使用相同的函数,请将该函数放入DLL并将该DLL链接到这两个进程。每个进程都有自己的地址空间,因此一个进程中的地址可能在另一个进程中根本不起作用,即使它“有效”,它也会指向完全不同的地方。

虽然您正在将偏移量传递到父节点上的内存映射区域,但是当您尝试在该节点上访问它时,我看不到任何匹配的代码来映​​射相同的区域并将其基数添加到指针客户方。

Visual C ++有一个__based指针类型,可以简化这一点。