0

我有一个使用 3 个 DLL 的小型 C 程序。它使用第一个 DLL 中的 8 个函数。来自第二个 DLL 的 5 个函数。以及来自第 3 个 DLL 的 2 个函数。

问题 1) 当我使用 Dependency Walker 打开输出的可执行文件时,可执行文件仅尝试从每个 DLL 导入 2 个函数。这不可能。

问题 2) 更糟糕的是,它试图从每个 DLL导入相同的 2 个函数。但是只有一个 DLL 具有它甚至试图导入的这两个函数。所以可执行文件会抛出一个错误,说它找不到函数。但它当然不能,因为它正在查看显然没有该功能的 DLL。

问题)我该如何解决这个问题?我想用 Dependency Walker 打开可执行文件,并看到它尝试从第一个 DLL 导入 8 个函数。来自第二个 DLL 的 5 个函数。以及来自第 3 个 DLL 的 2 个函数。我希望看到它尝试从正确的 DLL 导入正确的函数。




希望有人可以根据上面的信息帮助我。但如果没有,希望我可以编辑这个问题,我将包括所有细节,包括程序的源代码(它是一个文件并且很短)。Dependency Walker 的屏幕截图。.h、.lib 和 .dll 文件的链接(它们是 libx264 的一部分)。以及 Visual Studio 中所有设置的屏幕截图,这些设置是让程序在没有任何错误的情况下构建。

附加信息...

抱歉,我是 C 编程新手,但我会尽量提供足够的信息。

以下是使用 libx264(或者可能是在幕后使用 libx264 的 ffmpeg API)的 C 程序,该程序手动绘制原始 RGB 图像(作为 uint8_t 数组),将它们提供给 libx264 编码器,从编码器接收单个 x264 帧,以及将 x264 帧写入文件(作为 uint8_t 数组)。然后可以将这些 x264 帧馈送到解码器。但是这个程序只是编码。

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

该行#pragma warning(disable : 4996)忽略了被视为错误的折旧警告。您可以看到我正在使用的一些函数在头文件中被标记为已弃用。稍后我会担心这个,现在我只需要一些可以运行的东西。

.h、.lib 和 .dll 文件可在此处找到:https ://ffmpeg.zeranoe.com/builds/

该站点上有一些下载选项:下载选项的屏幕截图

我正在使用 .h、.lib 和 .dll 文件的 64 位 beta 版本。您还必须选择是否要下载文件的“静态”、“共享”或“开发”版本。“共享”版本具有 .dll 文件。“Dev”版本具有 .h 和 .lib 文件。所以我需要这两个。

有人建议我不要在 Visual Studio 中发布设置的屏幕截图,但我会总结一下我在 Visual Studio 中为构建程序所做的工作。我必须确保项目设置为 64 位输出(默认为 32 位)。我不得不告诉 Visual Studio 编译和链接 C 而不是 C++ 代码(也许我通过将源代码文件扩展名重命名为 .c 而不是 .cpp 来做到这一点,我不记得了)。我将包含 .lib 文件和 .h 文件的文件夹放在我的项目文件夹内的一个逻辑位置,并将文件夹路径添加到 Visual Studio 将查找 .lib 文件和 .h 文件的位置。我还必须专门命名我正在使用的 .lib 文件。构建后,我将 .dll 文件与已编译的可执行文件放在同一文件夹中。可能还有别的东西,但我不记得了。

我还被建议不要制作 Dependency Walker 的屏幕截图,而是粘贴命令的输出:

dumpbin /imports LibLinkingTest.exe

LibLinkingTest.exe 是从上面的 C 源代码编译的可执行文件的名称,基于我给我的项目的名称。以下是上述命令的输出:

Microsoft (R) COFF/PE Dumper 版本 14.12.25835.0 版权所有 (C) Microsoft Corporation。版权所有。

文件 LibLinkingTest.exe 的转储

文件类型:可执行图像

部分包含以下导入:

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

概括

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

它将上述 C 源代码中使用的函数sws_getCachedContextsws_scale3 个 DLL 相关联:avcodec-58.dll、avutil-56.dll 和 swscale-5.dll

但是这些函数sws_getCachedContextsws_scale存在于 swscale-5.dll 中。

上面的 C 源代码中没有提到应该从这些 DLL 加载的 13 个其他函数。

上述 C 源代码中的函数avcodec_find_encoder, avcodec_alloc_context3, avcodec_encode_video2, avcodec_open2, avcodec_close, avcodec_register_all,av_packet_unrefav_init_packet应从 avcodec-58.dll 加载。

上述 C 源代码中的函数av_opt_setav_image_allocav_freeav_freepav_frame_free应从 avutil-56.dll 加载。

上面的函数sws_getCachedContextsws_scaleC 源代码应该从 swscale-5.dll 加载(所以看起来它实际上是正确的)。

这些函数肯定存在于这些 DLL 中,因为我可以使用 Dependency Walker 在 DLL 中看到它们。我很确定这些函数都没有静态嵌入到最终的可执行文件中。所有这些函数都应该从 DLL 中加载。

并建议我发布构建日志。这是构建日志:

Microsoft (R) C/C++ 优化编译器版本 19.12.25835 for x64
版权所有 (C) Microsoft Corporation。版权所有。

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) 增量链接器版本 14.12.25835.0
版权所有 (C) Microsoft Corporation。版权所有。

"/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

4

0 回答 0