我有一个使用 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_getCachedContext
与sws_scale
3 个 DLL 相关联:avcodec-58.dll、avutil-56.dll 和 swscale-5.dll
但是这些函数sws_getCachedContext
只sws_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_unref
和av_init_packet
应从 avcodec-58.dll 加载。
上述 C 源代码中的函数av_opt_set
、av_image_alloc
、av_free
、av_freep
和av_frame_free
应从 avutil-56.dll 加载。
上面的函数sws_getCachedContext
和sws_scale
C 源代码应该从 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