2015-03-20 6 views
2

Я прочитал, что на окнах модули исполняемого файла отображаются в одном и том же адресном пространстве. я не понимаю, почемуНа окнах, почему существуют разные адреса для того же модуля?

typedef int (__stdcall *fptr)(); 

int  main(void) 
{ 

    HINSTANCE h; 
    fptr  f; 
    std::stringstream oss; 

    h = LoadLibrary("test.dll"); 
    if (! h) 
     return EXIT_FAILURE; 
    f = (fptr)GetProcAddress(h, "function"); 
    if (! f) 
     return EXIT_FAILURE; 

    oss << (DWORD *)f; 
    std::cout <<"main: "<< oss << std::endl; 

    _getch(); 
    return EXIT_SUCCESS; 
} 

и

extern "C" { 
    void __declspec(dllexport) function() { 
     return ; 
    } 
} 

int  main(HMODULE m) 
{ 
    std::stringstream oss; 

    oss << (DWORD *)function; 

    std::cout << "dll: " << oss << std::endl; 
} 

BOOL APIENTRY DllMain(HMODULE hModule, 
         DWORD ul_reason_for_call, 
         LPVOID lpReserved 
        ) 
{ 
    switch (ul_reason_for_call) 
    { 
    case DLL_PROCESS_ATTACH: 
     main(hModule); 
    case DLL_THREAD_ATTACH: 
    case DLL_THREAD_DETACH: 
    case DLL_PROCESS_DETACH: 
     break; 
    } 
    return TRUE; 
} 

дает следующий результат:

> "test.exe" 
dll: 007BFDB8 
main: 0039F944 

Кроме того, адрес 007BFD88 не могут быть доступны из основного процесса. Почему два адреса разные?

+1

Инкрементное связывание, реализованное компоновщиком MSVC++ и используемое в сборке Debug, является одним из объяснений. Адрес, который вы получаете в вашей DLL, является адресом инструкции JMP, которая перескакивает на реальную функцию. Повторите попытку с помощью сборки Release. –

+0

Большое спасибо, это именно то, что я искал. Вы решили мою проблему. – antoine

+0

@HansPassant, если точно такая же функция будет иметь разные адреса из-за некоторых соглашений генерации/компоновки кода, компилятор C++ будет несовместим со стандартом. – Christophe

ответ

0

Адрес 007BFD88 не должен быть адресом экспортируемой функции из DLL (который возвращает GetProcAddress()); это просто указатель на функцию. Указатели на функции в C/C++ имеют несколько интересных свойств, проверить это: Why do function pointer definitions work with any number of ampersands '&' or asterisks '*'?

+0

Верная интересная ссылка! Но все различные указатели функций этого примера содержат точно такой же адрес. Поэтому на самом деле он не отвечает на вопрос ... – Christophe

+0

это не адрес. это указатель на функцию. Теперь, что возвращает GetProcAddress(), действительно является адресом. –

+0

Разве это не значение, содержащееся в указателе на адрес? – Christophe

1

Основное правило в том, что DLL являются Allways загружается в адресное пространство процесса, который использует его. Поэтому ваш указатель на функцию должен быть одинаковым.

Это тот же адрес, с обеих сторон:

Я попробовал ваш фрагмент кода с небольшим вариантом. В DLL файлы основной(), я использовал:

std::cout << "f in dll: " << (DWORD *)function << std::endl; 

И в основной(), я использовал програмы:

std::cout << "f in main: " << (DWORD *)f << std::endl; 

Точно такой же адрес выходит! Все в порядке!

В чем проблема с кодом?

В фрагменте кода, вы не COUT адрес функции непосредственно, но вы используете oss:

std::stringstream oss; 
oss << (DWORD *)function; 
std::cout << "f in dll: " << oss << std::endl; // ??? 

На мой компилятор (MSVC13), я получаю ошибку компиляции для ОСС. Я не знаю, почему это работает для вас, но я подозреваю, что вы печатаете адрес вашего строкового потока, а не его контент. Поскольку oss - это локальная переменная, у нее есть другой адрес в обеих функциях, и поэтому причина вас беспокоит.

Попробуйте alternative:

std::cout << "f in dll: " << oss.str() << std::endl; 

и вы получите опять тот же результат (адрес распечатывается) с обеих сторон.

В GCC ваш код компилируется, но не дает результата, который вы ожидаете, так как это показывает online snippet.

Дополнительные замечания

Если же функция DLL будет иметь разные адреса, то это будет означать, что вы в присутствии двух различных процессов, каждый с собственным адресным пространством.

Когда вы используете необязательную точку ввода DllMain() и вызываете функцию (не экспортируется) main(), это может создать впечатление отдельного процесса, но это действительно не так.

Я также хотел бы добавить, что указатели функций являются функциями poitners. С ним нет черной магии. Два указателя точно такого же типа, указывая на точно такую ​​же функцию, будут иметь одинаковый адрес. Здесь нормативная ссылка в стандарте С ++:

5,10/1: Два указателя одного и того же типа сравнения равны тогда и только тогда, когда они оба нуль, указывают на один и тот же функции, или оба представляют один и тот же адрес.

+0

AFAIK, как только вы вводите динамическое связывание, вы находитесь за пределами покрытия стандарта C++. В частности, стандарт не требует, чтобы функция, которую вы получаете с помощью динамической компоновки, должна быть такой же, как и функция внутри DLL. Это может быть прокси-функция, которая вызывает фактическую функцию. –

+0

Как сказано, я сделал тест, он отлично работает с исправлением кода, который я документировал здесь: тот же адрес с обеих сторон. Я приглашаю вас проверить онлайн-фрагмент, в котором подчеркивается проблема op: в его коде он вообще не печатает указатель на функцию, а только адрес его локального stringsrtream! – Christophe

+0

Здесь требуется некоторая осторожность - если вы не можете воспроизвести проблему, вы не можете быть уверены в причинах. Обратите внимание, что OP сообщает, что проблема исчезла, как только он переключился с Debug на Release, что ваш ответ не может объяснить. Во всяком случае, даже если ваша гипотеза на самом деле правильная, это не меняет того факта, что стандарт C++ не требует такого поведения. –

Смежные вопросы