Thread: How to share imported DLL functions between source files in C?

  1. #1
    Registered User HelpfulPerson's Avatar
    Join Date
    Jun 2013
    Location
    Over the rainbow
    Posts
    288

    How to share imported DLL functions between source files in C?

    I've been trying not to post about my competition project for help since I felt it would be inappropriate, but this is one of the few problems I haven't been able to fix after trying for quite a while. I could fix the rest of the problems through debugging and through what people on this forum have told me before. I don't have enough experience in linking to fix my current problem.

    In general, my problem is that I've been trying to reorganize the project I and my group are working on into separate project files. Everything was working perfectly fine before, but now I'm facing the wrath of undefined reference errors when I try to call my DLL functions.

    I have them declared and included in the central header file here :

    Code:
    /* DLL functions */
    
    
    typedef void ( * t_wait )( int milliseconds, const int frames_per_seconds );
    typedef clock_t ( * ft_timer )( int command, t_timer * timer_object ); /* clock_t is usually defined as long */
    
    
    typedef void ( * t_SDL_errorexit )( const char * message );
    
    
    typedef SDL_Surface * ( * t_load_image )( char * image_path, unsigned is_transparent );
    
    
    typedef Mix_Music * ( * t_load_music )( const char * file_name );
    typedef void ( * t_play_music )( Mix_Music * game_music, int loops );
    typedef Mix_Music * ( * t_start_music )( const char * file_name, int loops );
    
    
    typedef Mix_Chunk * ( * t_load_sound )( const char * file_name );
    typedef void ( * t_play_sound )( int channel, Mix_Chunk * game_music, int loops );
    
    
    typedef int ( * t_character_to_digit )( char value );
    typedef int ( * t_number_of_value_places )( int value );
    typedef char * ( * t_convert_number_to_string )( int value, int * value_places );
    
    
    typedef projectile * ( * t_create ) ( );
    typedef void ( * t_print_list )( projectile * head );
    typedef projectile * ( * t_free_list )( projectile * head );
    typedef projectile * ( * t_add )( projectile * head, int h, int w, int x, int y, int speed );
    typedef projectile * ( * t_delete_node )( projectile * head, projectile * node );
    
    
    typedef maze_level * ( * t_create_level )( );
    typedef maze_level * ( * t_add_level )( maze_level * head, int * maze_data, const int w, const int h, const int array_size );
    typedef void ( * t_free_level_list )( maze_level * head );
    typedef void ( * t_print_maze_list )( maze_level * head );
    typedef maze_level * ( * t_delete_level )( maze_level * head, maze_level * node );
    
    
    typedef FILE * ( * t_loadfile_RW )( const char * file_name );
    typedef char * ( * t_read_file )( FILE * target_file, int * bytes_received );
    typedef maze_level * ( * t_load_level_data )( const char * file_name );
    
    
    /* Function pointers to imported DLL functions */
    
    
    extern t_wait wait;
    extern ft_timer timer;
    
    
    extern t_SDL_errorexit SDL_errorexit;
    
    
    extern t_load_image load_image;
    
    
    extern t_load_music load_music;
    extern t_play_music play_music;
    extern t_start_music start_music;
    
    
    extern t_load_sound load_sound;
    extern t_play_sound play_sound;
    
    
    extern t_character_to_digit character_to_digit;
    extern t_number_of_value_places number_of_value_places;
    extern t_convert_number_to_string convert_number_to_string;
    
    
    extern t_create create;
    extern t_print_list print_list;
    extern t_free_list free_list;
    extern t_add add;
    extern t_delete_node delete_node;
    
    
    extern t_create_level create_level;
    extern t_add_level add_level;
    extern t_free_level_list free_level_list;
    extern t_print_maze_list print_maze_list;
    extern t_delete_level delete_level;
    
    
    extern t_loadfile_RW loadfile_RW;
    extern t_read_file read_file;
    extern t_load_level_data load_level_data;
    Then I have them imported here :

    Code:
    /* ******************************************************** */
    /* *********       win_error( char *, bool )      ********* */
    /* ******************************************************** */
    /*  - Displays a GUI for a windows specific error message,  */
    /* pass true to it to have it exit the program, pass false  */
    /* to have it continue                                      */
    /* ******************************************************** */
    
    
    extern void win_error( char * message, bool is_exit )
    {
        /* Note : win_error uses the Win32 Api */
        /* ********************************** */
    
    
        char buffer[BUFSIZ] = { 0 }; /*     This is the buffer where the error message is stored from the FormatMessage( )
                                        function.
                                     */
        DWORD error_code = GetLastError( ); /* GetLastError( ) returns the last system error code */
    
    
        /* Formats an error message from the system */
        FormatMessage
        (
            FORMAT_MESSAGE_FROM_SYSTEM,
            NULL,
            error_code,
            MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),
            ( LPTSTR ) buffer,
            BUFSIZ,
            NULL
        );
    
    
        MessageBox( NULL, buffer, message, MB_ICONWARNING | MB_OK ); /* Shows the user a message box with the message */
    
    
        if ( is_exit ) exit( error_code );
    
    
        return;
    }
    
    
    /* ******************************************************** */
    /* *********   dll_functions_init( game_data * )  ********* */
    /* ******************************************************** */
    /*  - Imports any functions we need to use from our DLL     */
    /* ******************************************************** */
    
    
    static void dll_functions_init( game_data * game )
    {
        /* Note : DLL_functions_init uses the Win32 Api */
        /* ******************************************** */
    
    
        /* Load the DLL dynamically */
        if ( !( game->game_dll = LoadLibrary( "game_lib_dynamic.dll" ) ) )
            win_error( ( char * )"Loading DLL", true );
    
    
        /* Get the addresses of our functions for use in the game */
        if ( !( wait = ( t_wait )GetProcAddress( game->game_dll, "wait" ) ) )
            win_error( ( char * )"wait( )", true );
        if ( !( timer = ( ft_timer )GetProcAddress( game->game_dll, "timer" ) ) )
            win_error( ( char * )"timer( )", true );
    
    
        if ( !( SDL_errorexit = ( t_SDL_errorexit )GetProcAddress( game->game_dll, "SDL_errorexit" ) ) )
            win_error( ( char * )"SDL_errorexit( )", true );
    
    
        if ( !( load_image = ( t_load_image )GetProcAddress( game->game_dll, "load_image" ) ) )
            win_error( ( char * )"load_image( )", true );
    
    
        if ( !( load_music = ( t_load_music )GetProcAddress( game->game_dll, "load_music" ) ) )
        {
            win_error( ( char * )"load_music( ) ( warning : no music will be played )", false );
            game->sound_data.invalid_music_functions = true;
        }
        if ( !( play_music = ( t_play_music )GetProcAddress( game->game_dll, "play_music" ) ) )
        {
            win_error( ( char * )"play_image( ) ( warning : no music will be played )", false );
            game->sound_data.invalid_music_functions = true;
        }
        if ( !( start_music = ( t_start_music )GetProcAddress( game->game_dll, "start_music" ) ) )
        {
            win_error( ( char * )"start_music( ) ( warning : no music will be played )", true );
            game->sound_data.invalid_music_functions = true;
        }
    
    
        if ( !( load_sound = ( t_load_sound )GetProcAddress( game->game_dll, "load_sound" ) ) )
        {
            win_error( ( char * )"load_sound( ) ( warning : no sounds will be played )", false );
            game->sound_data.invalid_sound_functions = true;
        }
        if ( !( play_sound = ( t_play_sound )GetProcAddress( game->game_dll, "play_sound" ) ) )
        {
            win_error( ( char * )"play_sound( ) ( warning : no sounds will be played )", true );
            game->sound_data.invalid_sound_functions = true;
        }
    
    
        if ( !( character_to_digit = ( t_character_to_digit )GetProcAddress( game->game_dll, "character_to_digit" ) ) )
            win_error( ( char * )"character_to_digit( )", true );
        if ( !( number_of_value_places = ( t_number_of_value_places )GetProcAddress( game->game_dll, "number_of_value_places" ) ) )
            win_error( ( char * )"number_of_value_places( )", true );
        if ( !( convert_number_to_string = ( t_convert_number_to_string )GetProcAddress( game->game_dll, "convert_number_to_string" ) ) )
            win_error( ( char * )"convert_number_to_string( )", true );
    
    
        if ( !( create = ( t_create )GetProcAddress( game->game_dll, "create" ) ) )
            win_error( ( char * )"create( )", false );
        if ( !( print_list = ( t_print_list )GetProcAddress( game->game_dll, "print_list" ) ) )
            win_error( ( char * )"print_list( )", false );
        if ( !( free_list = ( t_free_list )GetProcAddress( game->game_dll, "free_list" ) ) )
            win_error( ( char * )"free_list( )", false );
        if ( !( add = ( t_add )GetProcAddress( game->game_dll, "add" ) ) )
            win_error( ( char * )"add( )", false );
        if ( !( delete_node = ( t_delete_node )GetProcAddress( game->game_dll, "delete_node" ) ) )
            win_error( ( char * )"delete_node( )", false );
    
    
        if ( !( create_level = ( t_create_level )GetProcAddress( game->game_dll, "create_level" ) ) )
            win_error( ( char * )"create_level( )", true );
        if ( !( add_level = ( t_add_level )GetProcAddress( game->game_dll, "add_level" ) ) )
            win_error( ( char * )"add_level( )", true );
        if ( !( free_level_list = ( t_free_level_list )GetProcAddress( game->game_dll, "free_level_list" ) ) )
            win_error( ( char * )"free_level_list( )", true );
        if ( !( print_maze_list = ( t_print_maze_list )GetProcAddress( game->game_dll, "print_maze_list" ) ) )
            win_error( ( char * )"print_maze_list( )", true );
        if ( !( delete_level = ( t_delete_level )GetProcAddress( game->game_dll, "delete_level" ) ) )
            win_error( ( char * )"delete_level( )", true );
    
    
        if ( !( loadfile_RW = ( t_loadfile_RW )GetProcAddress( game->game_dll, "loadfile_RW" ) ) )
            win_error( ( char * )"loadfile_RW( )", true );
        if ( !( read_file = ( t_read_file )GetProcAddress( game->game_dll, "read_file" ) ) )
            win_error( ( char * )"read_file( )", true );
        if ( !( load_level_data = ( t_load_level_data )GetProcAddress( game->game_dll, "load_level_data" ) ) )
            win_error( ( char * )"load_level_data( )", true );
    
    
        return;
    }
    This doesn't work, as my compiler sees it that I am trying to call invalid functions, whereas I have made sure to import the functions before calling. Obviously my compiler can't tell that, and is trying to protect me from calling them. I tried re-reading one of my C book's chapters on multiple source files, but it didn't help. It didn't talk about dynamic linking and sharing imported functions.

    Any help is appreciated.

    Extra notes :
    The source files are compiled as C++, but I'm using C-style code instead of true C++ code.

    Obviously I'm using Windows style DLL linking, but if the only ways to share imported functions are non-standard, please only post ways that will work on as low as Windows XP.

    I have tried to statically link them before, but that led to a problem that I posted a while back. Which led me to dynamically linking, which was problem-free until I separated my code into separate files.
    Last edited by HelpfulPerson; 11-24-2013 at 02:41 PM.
    "Some people think they can outsmart me, maybe. Maybe. I've yet to meet one that can outsmart bullet" - Meet the Heavy, Team Fortress 2

  2. #2
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Is it a compiler error or a linker error? If the first, make sure you actually #include the header file. If the latter, make sure you've already built the .dll and it is in a path where you can find it.

  3. #3
    Registered User HelpfulPerson's Avatar
    Join Date
    Jun 2013
    Location
    Over the rainbow
    Posts
    288
    Quote Originally Posted by tabstop View Post
    Is it a compiler error or a linker error? If the first, make sure you actually #include the header file. If the latter, make sure you've already built the .dll and it is in a path where you can find it.
    It's a compiler error, I didn't post them because I get 50 errors saying in general the same thing. Here is a sample of a few.

    Code:
    obj\Debug\character.o||In function `update_main_character':|
    F:\FBLA\Random game\character.cpp|35|undefined reference to `timer'|
    F:\FBLA\Random game\character.cpp|31|undefined reference to `timer'|
    F:\FBLA\Random game\character.cpp|47|undefined reference to `timer'|
    obj\Debug\game_data_utility.o||In function `free_game_data':|
    F:\FBLA\Random game\game_data_utility.cpp|68|undefined reference to `free_level_list'|
    obj\Debug\game_data_utility.o||In function `level_win':|
    F:\FBLA\Random game\game_data_utility.cpp|114|undefined reference to `timer'|
    It's only undefined reference issues.

    And yes, I include the header in every source file. Otherwise I would probably be getting declaration errors too.
    "Some people think they can outsmart me, maybe. Maybe. I've yet to meet one that can outsmart bullet" - Meet the Heavy, Team Fortress 2

  4. #4
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Yeah so those are linker errors, not compiler errors. (Anything from an .o file has already passed compilation and is being linked.) I'm assuming you've got some sort of build process/makefile -- what does it look like?

  5. #5
    Registered User HelpfulPerson's Avatar
    Join Date
    Jun 2013
    Location
    Over the rainbow
    Posts
    288
    Quote Originally Posted by tabstop View Post
    Yeah so those are linker errors, not compiler errors. (Anything from an .o file has already passed compilation and is being linked.) I'm assuming you've got some sort of build process/makefile -- what does it look like?
    Well, it doesn't auto-generate a makefile as far as I can find. It has a dependants file, but it's super long and full of useless information in this case. Looks like to me the relevant information will be the build process. Here it is.

    Code:
    -------------- Clean: Debug in Random game (compiler: GNU GCC Compiler)---------------
    
    
    Cleaned "Random game - Debug"
    
    
    -------------- Build: Debug in Random game (compiler: GNU GCC Compiler)---------------
    
    
    mingw32-g++.exe -Wall  -g  -O3 -Winline -Wall   -IE:\SDL\SDL-1.2.15\include -I"F:\FBLA\Random game"  -c "F:\FBLA\Random game\character.cpp" -o obj\Debug\character.o
    mingw32-g++.exe -Wall  -g  -O3 -Winline -Wall   -IE:\SDL\SDL-1.2.15\include -I"F:\FBLA\Random game"  -c "F:\FBLA\Random game\character_init.cpp" -o obj\Debug\character_init.o
    mingw32-g++.exe -Wall  -g  -O3 -Winline -Wall   -IE:\SDL\SDL-1.2.15\include -I"F:\FBLA\Random game"  -c "F:\FBLA\Random game\draw_game.cpp" -o obj\Debug\draw_game.o
    mingw32-g++.exe -Wall  -g  -O3 -Winline -Wall   -IE:\SDL\SDL-1.2.15\include -I"F:\FBLA\Random game"  -c "F:\FBLA\Random game\game_data_utility.cpp" -o obj\Debug\game_data_utility.o
    mingw32-g++.exe -Wall  -g  -O3 -Winline -Wall   -IE:\SDL\SDL-1.2.15\include -I"F:\FBLA\Random game"  -c "F:\FBLA\Random game\game_init.cpp" -o obj\Debug\game_init.o
    mingw32-g++.exe -Wall  -g  -O3 -Winline -Wall   -IE:\SDL\SDL-1.2.15\include -I"F:\FBLA\Random game"  -c "F:\FBLA\Random game\game_utility.cpp" -o obj\Debug\game_utility.o
    mingw32-g++.exe -Wall  -g  -O3 -Winline -Wall   -IE:\SDL\SDL-1.2.15\include -I"F:\FBLA\Random game"  -c "F:\FBLA\Random game\main.cpp" -o obj\Debug\main.o
    mingw32-g++.exe -Wall  -g  -O3 -Winline -Wall   -IE:\SDL\SDL-1.2.15\include -I"F:\FBLA\Random game"  -c "F:\FBLA\Random game\text.cpp" -o obj\Debug\text.o
    mingw32-g++.exe -LE:\SDL\SDL-1.2.15\lib -L"F:\FBLA\Random game" -L"F:\FBLA\Random game\libraries"  -o "bin\Debug\Random game.exe" obj\Debug\character.o obj\Debug\character_init.o obj\Debug\draw_game.o obj\Debug\game_data_utility.o obj\Debug\game_init.o obj\Debug\game_utility.o obj\Debug\main.o obj\Debug\text.o  
     
    -mwindows -lSDL -lSDL_mixer  -lmingw32 -lSDLmain -lSDL.dll -luser32 -lgdi32 -lwinmm -ldxguid
    "Some people think they can outsmart me, maybe. Maybe. I've yet to meet one that can outsmart bullet" - Meet the Heavy, Team Fortress 2

  6. #6
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    So you're including
    • character.o, character_init.o, draw_game.o, game_data_utility.o, game_init.o, game_utility.o, main.o, text.o directly from .cpp files
    • the standard windows GUI functions via -mwindows, -luser32, -lgdi32, -lwinmm, and -ldxguid
    • the SDL, SDL_mixer, SDLmain, and SDL.dll libraries which is for your graphics
    • the mingw32 library (I don't think I've ever used that on purpose, somehow, so not sure what's in it)
    • and that's it.

    Which of those is supposed to be your dll?

  7. #7
    Registered User
    Join Date
    May 2009
    Posts
    4,183
    FYI: I am guessing you are using Code::Blocks as the IDE; if not, ignore this advice.

    To avoid some linking errors; you need to adjust the build order; the order it compilers files is the same as the order it links the object files.
    Look up how to adjust the file weights; IIRC this is under right click on file select properties.

    Tim S.
    "...a computer is a stupid machine with the ability to do incredibly smart things, while computer programmers are smart people with the ability to do incredibly stupid things. They are,in short, a perfect match.." Bill Bryson

  8. #8
    Registered User HelpfulPerson's Avatar
    Join Date
    Jun 2013
    Location
    Over the rainbow
    Posts
    288
    Quote Originally Posted by tabstop View Post
    So you're including
    • character.o, character_init.o, draw_game.o, game_data_utility.o, game_init.o, game_utility.o, main.o, text.o directly from .cpp files
    • the standard windows GUI functions via -mwindows, -luser32, -lgdi32, -lwinmm, and -ldxguid
    • the SDL, SDL_mixer, SDLmain, and SDL.dll libraries which is for your graphics
    • the mingw32 library (I don't think I've ever used that on purpose, somehow, so not sure what's in it)
    • and that's it.

    Which of those is supposed to be your dll?
    I don't link it statically, I link it dynamically.

    It was this line here that included it :

    Code:
        /* Load the DLL dynamically */
        if ( !( game->game_dll = LoadLibrary( "game_lib_dynamic.dll" ) ) )
            win_error( ( char * )"Loading DLL", true );
    Quote Originally Posted by stahta01 View Post
    FYI: I am guessing you are using Code::Blocks as the IDE; if not, ignore this advice.

    To avoid some linking errors; you need to adjust the build order; the order it compilers files is the same as the order it links the object files.
    Look up how to adjust the file weights; IIRC this is under right click on file select properties.

    Tim S.
    Yes I'm using the Code::Blocks IDE with GCC( forgot to mention that in the OP ). I saw that earlier, but I didn't know how to adjust it. I'll see if I can maybe find something.

    Edit : Looks like the official wiki for code::blocks doesn't have a section on priority weight... Hm.
    Last edited by HelpfulPerson; 11-24-2013 at 03:42 PM.
    "Some people think they can outsmart me, maybe. Maybe. I've yet to meet one that can outsmart bullet" - Meet the Heavy, Team Fortress 2

  9. #9
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Quote Originally Posted by HelpfulPerson View Post
    I don't link it statically, I link it dynamically.
    Ha! I've been reading the errors, not the source code -- that's not what I would expect from function pointers. gcc will allow you to not specify the function behind a function pointer no problem, so it must not believe those are function pointers. I can't quite tell why, though.

  10. #10
    Registered User HelpfulPerson's Avatar
    Join Date
    Jun 2013
    Location
    Over the rainbow
    Posts
    288
    Quote Originally Posted by tabstop View Post
    Ha! I've been reading the errors, not the source code -- that's not what I would expect from function pointers. gcc will allow you to not specify the function behind a function pointer no problem, so it must not believe those are function pointers. I can't quite tell why, though.
    I don't know either. I've never had it say that about my function pointers before...
    "Some people think they can outsmart me, maybe. Maybe. I've yet to meet one that can outsmart bullet" - Meet the Heavy, Team Fortress 2

  11. #11
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Quote Originally Posted by HelpfulPerson View Post
    I don't know either. I've never had it say that about my function pointers before...
    You do have one source code file that defines these variables globally (ie without the externs) right? Extern variables do need to actually be defined somewhere.

  12. #12
    Registered User HelpfulPerson's Avatar
    Join Date
    Jun 2013
    Location
    Over the rainbow
    Posts
    288
    Quote Originally Posted by tabstop View Post
    You do have one source code file that defines these variables globally (ie without the externs) right? Extern variables do need to actually be defined somewhere.
    They do? Ugh, I did that at first, but I thought that it allocates space in that variable for storage in the header. I thought it would be a naming violation too, so I removed them. It works perfectly fine again now. Can you explain to me why that is?
    "Some people think they can outsmart me, maybe. Maybe. I've yet to meet one that can outsmart bullet" - Meet the Heavy, Team Fortress 2

  13. #13
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    "extern" means "this variable is somebody else's problem" -- and when you have ten source files in a project, you definitely want nine of them to say that (otherwise you'll have ten separate copies running around). But somebody has to take responsibility for these things, so one of your source files (generally main) has to actually set aside space for the variables to exist in.

    You definitely don't want the header to allocate storage, because the header is not a separate entity -- it would get included in each of the source files and you're back to ten separate copies again.

  14. #14
    Registered User HelpfulPerson's Avatar
    Join Date
    Jun 2013
    Location
    Over the rainbow
    Posts
    288
    Quote Originally Posted by tabstop View Post
    "extern" means "this variable is somebody else's problem" -- and when you have ten source files in a project, you definitely want nine of them to say that (otherwise you'll have ten separate copies running around). But somebody has to take responsibility for these things, so one of your source files (generally main) has to actually set aside space for the variables to exist in.

    You definitely don't want the header to allocate storage, because the header is not a separate entity -- it would get included in each of the source files and you're back to ten separate copies again.
    Thanks, that makes more sense.
    "Some people think they can outsmart me, maybe. Maybe. I've yet to meet one that can outsmart bullet" - Meet the Heavy, Team Fortress 2

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Share values between functions in same class?
    By Meerul264 in forum C++ Programming
    Replies: 4
    Last Post: 03-23-2013, 09:34 AM
  2. How to share a private function between multiple .c files?
    By purestr999 in forum C Programming
    Replies: 5
    Last Post: 04-17-2011, 03:40 PM
  3. Passing pointers to functions in seperate source files
    By derekeverett in forum C Programming
    Replies: 12
    Last Post: 10-08-2009, 07:15 AM
  4. Checking for commas in imported files
    By Ness757 in forum C Programming
    Replies: 2
    Last Post: 03-23-2006, 01:49 PM
  5. Share files between computers
    By Raihana in forum Networking/Device Communication
    Replies: 3
    Last Post: 12-08-2003, 08:05 AM