0

I have a small C program that uses 3 DLLs. It uses 8 functions from the first DLL. 5 functions from the second DLL. And 2 functions from the 3rd DLL.

Problem 1) When I open the outputed executable with Dependency Walker, the executable only tries to import 2 functions from each DLL. That can't be right.

Problem 2) What's worse, it tries to import the same 2 functions from each DLL. But only one of the DLLs has these 2 functions it's even trying to import. So the executable throws an error saying it can't find a function. But of course it can't, because it's looking inside DLLs that clearly don't have the function.

Question) How do I fix this? I want to open the executable with Dependency Walker and see that it tries to import 8 functions from the first DLL. 5 functions from the second DLL. And 2 functions from the 3rd DLL. And I want to see that it tries to import the right functions from the right DLLs.




Hopefully someone can help me based on just the information above. But if not, hopefully I can edit this question, and I will include all the details including the source code of the program (it's one file and short). Screenshots of Dependency Walker. Links to the .h, .lib, and .dll files (they are a part of libx264). And screenshots of all the settings in Visual Studio that are needed to get the program to build without any errors.

Aditional Information...

Sorry I'm new to C programming but I will try to provide enough information.

The following is a C program that uses libx264 (or maybe an ffmpeg API that uses libx264 behind the scenes) that manually draws raw RGB images (as uint8_t arrays), feeds them to the libx264 encoder, receives individual x264 frames from the encoder, and writes the x264 frames to a file (as uint8_t arrays). These x264 frames can then be fed to a decoder. But this program just encodes.

#include <libavcodec/avcodec.h>
#include <libavutil/imgutils.h>
#include <libavutil/opt.h>
#include <libswscale/swscale.h>

#pragma warning(disable : 4996)

static AVCodecContext *c = NULL;
static AVFrame *frame;
static AVPacket pkt;
static FILE *file;
struct SwsContext *sws_context = NULL; 

static void ffmpeg_encoder_set_frame_yuv_from_rgb(uint8_t *rgb) {
    const int in_linesize[1] = { 3 * c->width };
    sws_context = sws_getCachedContext(sws_context,
        c->width, c->height, AV_PIX_FMT_RGB24,
        c->width, c->height, AV_PIX_FMT_YUV420P,
        0, 0, 0, 0);
    sws_scale(sws_context, (const uint8_t * const *)&rgb, in_linesize, 0,
        c->height, frame->data, frame->linesize);
}

uint8_t* generate_rgb(int width, int height, int pts, uint8_t *rgb) {
    int x, y, cur;
    rgb = (uint8_t*)realloc(rgb, 3 * sizeof(uint8_t) * height * width);
    for (y = 0; y < height; y++) {
        for (x = 0; x < width; x++) {
            cur = 3 * (y * width + x);
            rgb[cur + 0] = 0;
            rgb[cur + 1] = 0;
            rgb[cur + 2] = 0;
            if ((frame->pts / 25) % 2 == 0) {
                if (y < height / 2) {
                    if (x < width / 2) {
                        /* Black. */
                    }
                    else {
                        rgb[cur + 0] = 255;
                    }
                }
                else {
                    if (x < width / 2) {
                        rgb[cur + 1] = 255;
                    }
                    else {
                        rgb[cur + 2] = 255;
                    }
                }
            }
            else {
                if (y < height / 2) {
                    rgb[cur + 0] = 255;
                    if (x < width / 2) {
                        rgb[cur + 1] = 255;
                    }
                    else {
                        rgb[cur + 2] = 255;
                    }
                }
                else {
                    if (x < width / 2) {
                        rgb[cur + 1] = 255;
                        rgb[cur + 2] = 255;
                    }
                    else {
                        rgb[cur + 0] = 255;
                        rgb[cur + 1] = 255;
                        rgb[cur + 2] = 255;
                    }
                }
            }
        }
    }
    return rgb;
}

/* Allocate resources and write header data to the output file. */
void ffmpeg_encoder_start(const char *filename, int codec_id, int fps, int width, int height) {
    AVCodec *codec;
    int ret;

    codec = avcodec_find_encoder(codec_id);
    if (!codec) {
        fprintf(stderr, "Codec not found\n");
        exit(1);
    }
    c = avcodec_alloc_context3(codec);
    if (!c) {
        fprintf(stderr, "Could not allocate video codec context\n");
        exit(1);
    }
    c->bit_rate = 400000;
    c->width = width;
    c->height = height;
    c->time_base.num = 1;
    c->time_base.den = fps;
    c->keyint_min = 600;
    c->pix_fmt = AV_PIX_FMT_YUV420P;
    if (codec_id == AV_CODEC_ID_H264)
        av_opt_set(c->priv_data, "preset", "slow", 0);
    if (avcodec_open2(c, codec, NULL) < 0) {
        fprintf(stderr, "Could not open codec\n");
        exit(1);
    }
    file = fopen(filename, "wb");
    if (!file) {
        fprintf(stderr, "Could not open %s\n", filename);
        exit(1);
    }
    frame = av_frame_alloc();
    if (!frame) {
        fprintf(stderr, "Could not allocate video frame\n");
        exit(1);
    }
    frame->format = c->pix_fmt;
    frame->width = c->width;
    frame->height = c->height;
    ret = av_image_alloc(frame->data, frame->linesize, c->width, c->height, c->pix_fmt, 32);
    if (ret < 0) {
        fprintf(stderr, "Could not allocate raw picture buffer\n");
        exit(1);
    }
}

/*
Write trailing data to the output file
and free resources allocated by ffmpeg_encoder_start.
*/
void ffmpeg_encoder_finish(void) {
    uint8_t endcode[] = { 0, 0, 1, 0xb7 };
    int got_output, ret;
    do {
        fflush(stdout);
        ret = avcodec_encode_video2(c, &pkt, NULL, &got_output);
        if (ret < 0) {
            fprintf(stderr, "Error encoding frame\n");
            exit(1);
        }
        if (got_output) {
            fwrite(pkt.data, 1, pkt.size, file);
            av_packet_unref(&pkt);
        }
    } while (got_output);
    fwrite(endcode, 1, sizeof(endcode), file);
    fclose(file);
    avcodec_close(c);
    av_free(c);
    av_freep(&frame->data[0]);
    av_frame_free(&frame);
}

/*
Encode one frame from an RGB24 input and save it to the output file.
Must be called after ffmpeg_encoder_start, and ffmpeg_encoder_finish
must be called after the last call to this function.
*/
void ffmpeg_encoder_encode_frame(uint8_t *rgb) {
    int ret, got_output;
    ffmpeg_encoder_set_frame_yuv_from_rgb(rgb);
    av_init_packet(&pkt);
    pkt.data = NULL;
    pkt.size = 0;
    if (frame->pts == 1) {
        frame->key_frame = 1;
        frame->pict_type = AV_PICTURE_TYPE_I;
    }
    else {
        frame->key_frame = 0;
        frame->pict_type = AV_PICTURE_TYPE_P;
    }
    ret = avcodec_encode_video2(c, &pkt, frame, &got_output);
    if (ret < 0) {
        fprintf(stderr, "Error encoding frame\n");
        exit(1);
    }
    if (got_output) {
        fwrite(pkt.data, 1, pkt.size, file);
        av_packet_unref(&pkt);
    }
}

/* Represents the main loop of an application which generates one frame per loop. */
static void encode_example(const char *filename, int codec_id) {
    int pts;
    int width = 320;
    int height = 240;
    uint8_t *rgb = NULL;
    ffmpeg_encoder_start(filename, codec_id, 25, width, height);
    for (pts = 0; pts < 100; pts++) {
        frame->pts = pts;
        rgb = generate_rgb(width, height, pts, rgb);
        ffmpeg_encoder_encode_frame(rgb);
    }
    ffmpeg_encoder_finish();
}

int main(void) {
    avcodec_register_all();
    encode_example("tmp.h264", AV_CODEC_ID_H264);
    encode_example("tmp.mpg", AV_CODEC_ID_MPEG1VIDEO);
    return 0;
}

The line #pragma warning(disable : 4996) ignores depreciation warnings that are treated as errors. You can see that some of the functions I'm using are marked as depreciated in the header files. I'll worry about that later, right now I just need something that runs.

The .h, .lib, and .dll files can be found here: https://ffmpeg.zeranoe.com/builds/

There are some download options on that site: Screenshot of Download Options

I'm using that 64-bit beta version of the .h, .lib, and .dll files. You also have to select if you want to download the "Static", "Shared", or "Dev" version of the files. The "Shared" version has the .dll files. The "Dev" version has the .h and .lib files. So I needed both of those.

I was advised to not post screenshots of settings in Visual Studio but I'll summary of what I did in Visual Studio to get the program to build. I had to make sure the project was set to 64-bit output (it was 32-bit by default). I had to tell Visual Studio to compile and link C not C++ code (maybe I did this by renaming the source code file extension to .c instead of .cpp, I can't remember). I put the folders with the .lib files and .h files in a logical place inside my project folder and added the folder paths to the places Visual Studio will look for .lib files and .h files. I also had to specifically name the .lib files I was using. After building I put the .dll files in the same folder as the compiled executable. There might have been something else but I can't remember.

I was also advised not to make screenshots of Dependency Walker, and instead paste output from the command:

dumpbin /imports LibLinkingTest.exe

LibLinkingTest.exe is the name of the executable that was compiled from the C source code above, based on the name I gave to my project. Here is the output from that command above:

Microsoft (R) COFF/PE Dumper Version 14.12.25835.0 Copyright (C) Microsoft Corporation. All rights reserved.

Dump of file LibLinkingTest.exe

File Type: EXECUTABLE IMAGE

Section contains the following imports:

avcodec-58.dll
         1400221D8 Import Address Table
         1400226D8 Import Name Table
                 0 time date stamp
                 0 Index of first forwarder reference

                       C sws_getCachedContext
                      1B sws_scale

avutil-56.dll
         1400221D8 Import Address Table
         1400226D8 Import Name Table
                 0 time date stamp
                 0 Index of first forwarder reference

                       C sws_getCachedContext
                      1B sws_scale

swscale-5.dll
         1400221D8 Import Address Table
         1400226D8 Import Name Table
                 0 time date stamp
                 0 Index of first forwarder reference

                       C sws_getCachedContext
                      1B sws_scale

VCRUNTIME140D.dll
         140022150 Import Address Table
         140022650 Import Name Table
                 0 time date stamp
                 0 Index of first forwarder reference

                      31 __vcrt_LoadLibraryExW
                      2F __vcrt_GetModuleHandleW
                      25 __std_type_info_destroy_list
                       8 __C_specific_handler
                      2E __vcrt_GetModuleFileNameW

ucrtbased.dll
         140022270 Import Address Table
         140022770 Import Name Table
                 0 time date stamp
                 0 Index of first forwarder reference

                      4D __p__commode
                     52B strcpy_s
                     527 strcat_s
                      68 __stdio_common_vsprintf_s
                     2C1 _seh_filter_dll
                     172 _initialize_onexit_table
                     2B4 _register_onexit_function
                      E5 _execute_onexit_table
                      C2 _crt_atexit
                      C1 _crt_at_quick_exit
                     54A terminate
                     39B _wmakepath_s
                     3B7 _wsplitpath_s
                     563 wcscpy_s
                      B5 _configthreadlocale
                     2CA _set_fmode
                     2B5 _register_thread_local_exe_atexit_callback
                     175 _initterm_e
                     174 _initterm
                     13D _get_initial_narrow_environment
                     171 _initialize_narrow_environment
                      B6 _configure_narrow_argv
                      5B __setusermatherr
                     2C5 _set_app_type
                     2C2 _seh_filter_exe
                       5 _CrtDbgReportW
                       4 _CrtDbgReport
                     44F exit
                     504 realloc
                      5C __stdio_common_vfprintf
                     48A fwrite
                     47B fopen
                     468 fflush
                     459 fclose
                      35 __acrt_iob_func
                     2CD _set_new_mode
                      A4 _cexit
                      9F _c_exit
                      EA _exit
                      4A __p___argv
                      49 __p___argc

KERNEL32.dll
         140022000 Import Address Table
         140022500 Import Name Table
                 0 time date stamp
                 0 Index of first forwarder reference

                     37D IsDebuggerPresent
                     45F RaiseException
                     3EB MultiByteToWideChar
                     605 WideCharToMultiByte
                     4CB RtlCaptureContext
                     4D2 RtlLookupFunctionEntry
                     4D9 RtlVirtualUnwind
                     5B4 UnhandledExceptionFilter
                     573 SetUnhandledExceptionFilter
                     2B1 GetProcAddress
                     1AF FreeLibrary
                     5D5 VirtualQuery
                     21B GetCurrentProcess
                     592 TerminateProcess
                     2B7 GetProcessHeap
                     34E HeapFree
                     384 IsProcessorFeaturePresent
                     34A HeapAlloc
                     449 QueryPerformanceCounter
                     21C GetCurrentProcessId
                     220 GetCurrentThreadId
                     2EC GetSystemTimeAsFileTime
                     367 InitializeSListHead
                     2D3 GetStartupInfoW
                     27A GetModuleHandleW
                     263 GetLastError

Summary

    1000 .00cfg
    1000 .data
    2000 .idata
    3000 .pdata
    3000 .rdata
    1000 .reloc
    1000 .rsrc
    A000 .text
   10000 .textbss

It's associating the functions sws_getCachedContext and sws_scale which are used in the C source code above with 3 DLLs: avcodec-58.dll, avutil-56.dll, and swscale-5.dll

But the functions sws_getCachedContext and sws_scale are only present in swscale-5.dll.

And there is no mention of the 13 other functions in the C source code above that should be loaded from these DLLs.

The functions avcodec_find_encoder, avcodec_alloc_context3, avcodec_encode_video2, avcodec_open2, avcodec_close, avcodec_register_all, av_packet_unref, and av_init_packet from the C source code above should be loaded from avcodec-58.dll.

The functions av_opt_set, av_image_alloc, av_free, av_freep, and av_frame_free from the C source code above should be loaded from avutil-56.dll.

The functions sws_getCachedContext and sws_scale from the C source code above should be loaded from swscale-5.dll (so it looks like it actually got that right).

These function are definitely present in these DLLs because I can see them inside the DLLs using Dependency Walker. I'm pretty sure that none of these functions are statically embedded inside the final executable. All of these functions should be loaded from the DLLs.

And I was advised to post the build log. Here is the build log:

Microsoft (R) C/C++ Optimizing Compiler Version 19.12.25835 for x64
Copyright (C) Microsoft Corporation. All rights reserved.

cl /c /ZI /W3 /WX- /diagnostics:classic /sdl /Od /D _DEBUG /D _CONSOLE /D _UNICODE /D UNICODE /Gm /EHsc /RTC1 /MDd /GS /fp:precise /permissive- /Zc:wchar_t /Zc:forScope /Zc:inline /Fo"x64\Debug\" /Fd"x64\Debug\vc141.pdb" /Gd /TC /errorReport:prompt LibLinkingTest.c

LibLinkingTest.c
Microsoft (R) Incremental Linker Version 14.12.25835.0
Copyright (C) Microsoft Corporation. All rights reserved.

"/OUT:C:\Users\me\Desktop\Programming\Windows\VS2017\LibLinkingTest\x64\Debug\LibLinkingTest.exe" /INCREMENTAL avcodec.lib avdevice.lib avfilter.lib avformat.lib avutil.lib postproc.lib swresample.lib swscale.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /MANIFEST "/MANIFESTUAC:level='asInvoker' uiAccess='false'" /manifest:embed /DEBUG:FASTLINK "/PDB:C:\Users\me\Desktop\Programming\Windows\VS2017\LibLinkingTest\x64\Debug\LibLinkingTest.pdb" /SUBSYSTEM:CONSOLE /TLBID:1 /DYNAMICBASE /NXCOMPAT "/IMPLIB:C:\Users\me\Desktop\Programming\Windows\VS2017\LibLinkingTest\x64\Debug\LibLinkingTest.lib" /MACHINE:X64 x64\Debug\LibLinkingTest.obj x64\Debug\stdafx.obj
LibLinkingTest.vcxproj -> C:\Users\me\Desktop\Programming\Windows\VS2017\LibLinkingTest\x64\Debug\LibLinkingTest.exe

JDO
  • 11
  • 2
  • 3
    The only way for what you describe to happen is for the Import Library(s) not to match the DLLs. – Richard Critten Feb 17 '18 at 18:01
  • 1
    "*Question) How do I fix this?*", is the problem really just dependency walker's behavior? Is that behavior correct? Also, could you post an example of 1. How your program "*imports*" those functions? 2. How do you compile and link your program? – Iharob Al Asimi Feb 17 '18 at 18:06
  • Instead of screenshots of dependency walker, use `dumpbin /imports` to get the information neatly in text form. Instead of screenshots of Visual Studio project properties, paste the build log where the compiler and linker command lines are shown. Information included as quoted text in the question is generally significantly more useful than pictures, unless you're talking about a graphics bug. – Ben Voigt Feb 17 '18 at 18:18
  • Did you use the same "module handle" for the same dlls? – Raindrop7 Feb 17 '18 at 18:19
  • 2
    @Raindrop7: Module handles are involved in the runtime loading process, but don't apply to imports. – Ben Voigt Feb 17 '18 at 18:20
  • @BenVoigt: I know the import time is right before compilation. But don't you think that the OP has a runtime problem instead of import-time one? – Raindrop7 Feb 17 '18 at 18:22
  • @Raindrop7: It could be a runtime, problem, if he's using Dependency Walker "profiling" mode to observe `GetProcAddress` calls. But from the question it seems that he's looking at the static import table. – Ben Voigt Feb 17 '18 at 18:49
  • @BenVoigt: Ok get it. Thanx. – Raindrop7 Feb 17 '18 at 19:00
  • Did you copy all DLLs to the same folder which contains the executable? – Mahmoud Fayez Feb 17 '18 at 21:17
  • @MahmoudFayez Yes, all DLLs are in the same folder as the executable. If I remove one of them I get a different error about a missing DLL. – JDO Feb 17 '18 at 21:23
  • Are you using the `.def` files that are included in the "dev" download? Module definition files are usually required for correct exports in Windows builds. The property pages of your VS project will have a place to include one or more module definition files. – Mark Benningfield Feb 17 '18 at 21:26
  • @MarkBenningfield I don't think I'm using the .def files because I don't know what those are. But I will try to use them. – JDO Feb 17 '18 at 21:32
  • when compiling, always enable the warnings, then fix those warnings (for `gcc`, at a minimum use: `-Wall -Wextra -Wconversion -pedantic -std=gnu11` ) The posted code causes the compiler to output about a dozen warning, some of which are critical. – user3629249 Feb 17 '18 at 21:36
  • defining a function inside another function is not allowed (although the `gcc`, with extensions, will allow it. – user3629249 Feb 17 '18 at 21:42
  • for ease of readability and understanding: 1) consistently indent the code. indent after every opening brace '{'. unindent before every closing brace '}'. Suggest each indent level be 4 spaces. 2) separate code blocks ( `for` `if` `else` `while` `do...while` `switch` `case` `default` ) via a single blank line. 3) separate functions via 2 or 3 blank lines (be consistent) – user3629249 Feb 17 '18 at 21:47
  • the function: `ate_rgb()` is missing a return type – user3629249 Feb 17 '18 at 21:49
  • since there is only a single file, there is no need to add the `static` modifier to the functions and global variables – user3629249 Feb 17 '18 at 21:50
  • @MarkBenningfield Visual Studio will only let me set one .def file for the project. But there are several of them included with the .lib files. What can I do? – JDO Feb 17 '18 at 21:54
  • this statement: `uint8_t* generstatic void encode_example( const char *filename, int codec_id)` is missing a trailing '{' – user3629249 Feb 17 '18 at 21:54
  • regarding: `fprintf(stderr, "Could not open %s\n", filename);` This fails to tell the user WHY the call to `fopen()` failed. Either include a call to the function: `strerror()` or (better) call `perror()` – user3629249 Feb 17 '18 at 21:57
  • on linux 16.04, this type: `SwsContext` is not defined within the included header files. – user3629249 Feb 17 '18 at 22:07
  • @user3629249 I don't understand your comments. The C source code I provided was copied and pasted verbatim from another stackoverflow post with the exception of `#pragma warning(disable : 4996)` because at the time of that post none of these functions were declared depreciated. No one commented on their code. And there is no function `ate_rgb()`, there are no functions declared inside functions, there is no statement `uint8_t* generstatic void encode_example( const char *filename, int codec_id)`. I don't care why `fopen()` failed. This is not a finished product it is a test. – JDO Feb 17 '18 at 22:07
  • Oh, right, sorry, you're building an .exe, not a .dll. The .def files aren't needed for that. This could go back and forth for quite a long time -- this sort of build scenario in VS is not one for a novice. – Mark Benningfield Feb 17 '18 at 22:11
  • I copy/paste the posted code, and passed it to my compiler. It does not compile. Regardless of what some other stackoverflow post may or may not have shown. Suggest posting a link to that other stackoverflow question. – user3629249 Feb 17 '18 at 22:14
  • @user3629249 You tell me. It doesn't exist in the C source code I posted. Look at the C source code on this webpage, not what you think you copied and pasted correctly. But what is actually on this page. – JDO Feb 17 '18 at 22:16
  • @user3629249 With the exception of `#pragma warning(disable : 4996)` this code is a verbatim copy from this post: https://stackoverflow.com/questions/2940671/how-does-one-encode-a-series-of-images-into-h264-using-the-x264-c-api – JDO Feb 17 '18 at 22:19
  • It would be interesting to see the results of `dumpbin /symbols` on the .obj file made by the compiler. We could see if the functions you're expecting to use from a DLL are indeed undefined (the linker will fill them in using the import library) or if somehow the compiler got rid of them (perhaps they are defined in the header files, either as inline functions or macros) – Ben Voigt Feb 17 '18 at 23:02
  • Your problem is that the .lib files don't match the .dll's. I downloaded the stable versions of "dev" and "shared", and the `avcodec.lib` file was calling out "avcodec-58.dll", but the .dll in the package was "avcodec-57.dll". You need to talk to the folks at ffmpeg and yank their chain. – Mark Benningfield Feb 17 '18 at 23:06
  • Thank you very much everyone. The page where I downloaded the .h, .lib, and .dll files also had older versions available. I downloaded version 3.2 of the Dev and Shared files and everything works. The people at ffmpeg that prepare the files have made a mistake on the latest version. – JDO Feb 18 '18 at 02:38
  • only the last answer (starting with: FFmpeg 2.8.6 runnable example) has a complete application, so I guess that is the code your referring to). That code does not cleanly compile. Amongst (many) other problems, it is missing the header file: `#include ` for the `realloc()` function – user3629249 Feb 18 '18 at 23:28
  • regarding: `rgb = realloc(rgb, 3 * sizeof(uint8_t) * height * width);` Here it is multiplying a 'size_t` (which is unsigned long) by two `int` types (height and width) – user3629249 Feb 18 '18 at 23:40
  • regarding: `uint8_t* generate_rgb(int width, int height, int pts, uint8_t *rgb)` this function has the unused parameter: `int ptr`, – user3629249 Feb 18 '18 at 23:41
  • regarding: `fwrite(pkt.data, 1, pkt.size, file);` the third parameter, `pkt.size`, the function is expecting a 'size_t' (which is unsigned) but the passed variable is a `int` (which is signed) – user3629249 Feb 18 '18 at 23:46
  • in general, if your code is calling some system function,do not depend on some header file over which you do not have control to actually include some header file for functions you directly call. so the posted code should have: `#include ` (for `fprintf(), etc) and `#include ` (for `uint8_t`, etc) and `#include ` (for `exit()`, etc) – user3629249 Feb 18 '18 at 23:51

0 Answers0