0

I'm trying to build a windows dll using mingw-64 that once loaded starts printing "Hello World" indefinetly.

Here's my dll.c

#include <stdio.h>
#include <windows.h>
#include "dll.h"
#include "main.h"

HINSTANCE hThisModule;


DWORD mainThread() {
    while(1) {
        printf("Hello world!");
    }
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
    static HANDLE hThread;
    hThisModule = hinstDLL;

    switch (fdwReason)
    {
        case DLL_PROCESS_ATTACH:
            hThread = CreateThread(0, 0, mainThread, 0, 0, 0);
            break;

        case DLL_PROCESS_DETACH:
            break;

        case DLL_THREAD_ATTACH:
            break;

        case DLL_THREAD_DETACH:
            break;
    }

    return TRUE;
}

void dummy() {
    Hello();
}

and here's my dll.h:

#ifndef DLL_H_
#define DLL_H_

#ifdef BUILD_DLL
/* DLL export */
#define EXPORT __declspec(dllexport)
#else
/* EXE import */
#define EXPORT __declspec(dllimport)
#endif

#endif /* DLL_H_ */

so I've built a simple program that loads my DLL to see if it's working correctly, here it is: hello.cpp

#include <windows.h>
#include <iostream>

typedef int (__stdcall *f_funci)();

int main()
{
  HINSTANCE hGetProcIDDLL = LoadLibrary("./wow.dll");

  if (!hGetProcIDDLL) {
    std::cout << "could not load the dynamic library" << std::endl;
    return EXIT_FAILURE;
  }

  return EXIT_SUCCESS;
}

Now, when I compile hello.cpp into hello.exe and dll.c into wow.dll, I get nothing on my console. What's wrong?

too honest for this site
  • 12,050
  • 4
  • 30
  • 52
Micheal N.
  • 37
  • 5
  • 2
    The program terminates before the thread has a chance to output anything. Consider the proverbial "hit any key to continue" code. – Hans Passant Jul 27 '18 at 16:43
  • 2
    There are a lot of things you should not do in DllMain - see https://learn.microsoft.com/en-us/windows/desktop/dlls/dynamic-link-library-best-practices - one of them is creating threads. –  Jul 27 '18 at 16:43
  • 3
    CreateThread is just fine. – Hans Passant Jul 27 '18 at 16:44
  • 1
    @Hans Not according to Microsoft in the link I posted. –  Jul 27 '18 at 16:46
  • @HansPassant so I there anything I can do to let the thread having a chance to output Hello World / executing my while loop? – Micheal N. Jul 27 '18 at 17:00
  • @MichealN.(1) call `CreateThread` from a normal function exported from your DLL and called from your `main` function and (2) pause your main program before it exits so that the output has a chance to happen. – Richard Critten Jul 27 '18 at 17:03
  • https://stackoverflow.com/questions/21257544/c-wait-for-user-input – Hans Passant Jul 27 '18 at 17:04
  • calling `getchar()` before `return EXIT_SUCCESS;` seems to be fixing my issue - thanks! – Micheal N. Jul 27 '18 at 17:12
  • 2
    @NeilButterworth: As that document explains, it isn't creating a thread that is a problem, but synchronizing with it. – Ben Voigt Jul 27 '18 at 17:16
  • 4
    @NeilButterworth see Raymond Chen's blog: [Does creating a thread from DllMain deadlock or doesn’t it?](https://blogs.msdn.microsoft.com/oldnewthing/20070904-00/?p=25283). Creating a thread in `DllMain` is not **recommended**, but it does work **but only** if you don't make `DllMain` block on something after creating the thread. As long as `DllMain` is allowed to exit normally, creating a thread in `DllMain` works. – Remy Lebeau Jul 27 '18 at 17:17
  • 1
    @Ben From that document "Creating a thread can work if you do not synchronize with other threads, **but it is risky**` IMHO, doing anything in DllMain is a bad design- the DLL should provide a separate initilisation function. –  Jul 27 '18 at 17:18
  • 4
    @MichealN. FYI, in your DLL code, the signature of `mainThread()` is wrong for `CreateThread()`. It needs to be declared as `DWORD WINAPI mainThread(LPVOID)` instead. Your compiler should have complained about that. – Remy Lebeau Jul 27 '18 at 17:24
  • 1
    @NeilButterworth - *but it is risky* - this is only bad documentation. here (on this page) exist many mistakes. call `CreateThread` from dll entry not risky – RbMm Jul 27 '18 at 17:38
  • 1
    @NeilButterworth: If you don't know, why doing things in `DllMain` is dangerous, then yes, *you* shouldn't be creating threads. For the rest of us, that understand the implications of the loader lock, creating a thread is entirely safe. Whether that is indeed bad design or not depends on your, well, design. – IInspectable Jul 28 '18 at 11:11

2 Answers2

0

First, I'd like to mention that it is not advisable to implement such a busy loop in your thread.

As for the issue you are experiencing, there are several potential issues here:

  1. printf is a CRT function, however you are calling CreateThread() instead of beginthread(ex), so the CRT is not initialized properly.
  2. Dll entry point is a notoriously problematic place. You can hardly call any kernel32 function from there, let alone CRT ones (see DllMain entry point and Dynamic Link Library Best Practices).

In most cases, it is advisable to implement separate Init and Exit functions that the client will need to call when using your library.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
hedgehog81
  • 134
  • 4
  • 1
    It's fine to use CreateThread. That limitation was removed years and years ago. And CreateThread is one of the functions that you can call from DllMain. – David Heffernan Jul 27 '18 at 17:51
  • First you do not reference anything to back your claim. Secondly, the only guidance at the moment is "Call CreateThread. Creating a thread can work if you do not synchronize with other threads, but it is risky." – hedgehog81 Jul 27 '18 at 18:03
  • 1
    The docs are out of date, but the vista thread pool would be useless if beginthreadex was necessary. That's proof enough. Also, you didn't answer the question. Hans' comment explains the problem. – David Heffernan Jul 27 '18 at 18:08
  • 1) As I said you did not privide any other document which is up to date. 2) Thread pool is totally unrelated to the CreateThread issue You probably refer to the crt/beginthread. In any case, If the idea is just to nitpick I am ok with that. I missed the absence of wait/getch in the main but the rest of my points were discussed and added to the answer. Let's end this discussion and go on with our lives:) – hedgehog81 Jul 27 '18 at 18:14
  • 1
    Point 1 is wrong. It used to be correct. But not for the past 10 years. Point 2 is also wrong. This isn't nit picking. If you don't want to learn, that's up to you. Readers can make there own minds up. – David Heffernan Jul 27 '18 at 18:16
  • @DavidHeffernan: Pretty sure that #1 being right or wrong has nothing to do with what year it is, and everything to do with whether the CRT is linked statically or as a DLL. Static libraries don't receive per-thread callbacks like DLLs do. – Ben Voigt Jul 27 '18 at 22:44
  • @BenVoigt Way back when, even for dynamic linked RTL, you have to use beginthreadex – David Heffernan Jul 28 '18 at 13:18
  • @DavidHeffernan: Anachronism? (because I think those versions predate the "ex" function) But what's more important today is whether a statically linked CRT still imposes the same old requirement, and I seem to recall that things do go mildly wrong (resource leaks) when you neither call the setup/cleanup code yourself nor can the CRT catch thread exits through its DllMain. – Ben Voigt Jul 28 '18 at 15:10
0

As has been already mentioned, your mainThread function has wrong signature. Try something like this:

DWORD WINAPI mainThread(LPVOID lpParam)
{
    UNREFERENCED_PARAMETER(lpParam);
    while (1)
    {
        printf("Hello world!\n");
        Sleep(1000);
    }
    return 0;
}

This works just fine for me. I modified your .exe so that you could drag and drop .dll onto it to test:

#include <windows.h>
#include <iostream>

int main(int argc, char *argv[])
{
    if (argc < 2)
    {
        std::cout << "drag drop dll over exe" << std::endl;
        std::cin.get();
        return EXIT_FAILURE;
    }

    HINSTANCE hGetProcIDDLL = LoadLibraryA(argv[1]);

    if (!hGetProcIDDLL)
    {
        std::cout << "could not load the dynamic library" << std::endl;
        std::cin.get();
        return EXIT_FAILURE;
    }

    std::cin.get();
    return EXIT_SUCCESS;
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Killzone Kid
  • 6,171
  • 3
  • 17
  • 37