linux C语言GetModuleFileName(动态库获取自己的绝对路径)

在Linux系统中,与Windows的GetModuleFileNameAPI不同,没有内置方法直接获取动态库的绝对路径。文章介绍了如何利用<link.h>和<dlfcn.h>头文件中的dladdr函数来实现这一功能,通过示例代码展示了如何获取当前运行的动态库文件(.so)的路径。在编译时需要添加-D_GNU_SOURCE选项。

简介

由于linux系统应用程序加载动态库时,动态库(.so)文件可以放到系统的任何地方。而动态库内部并不知道自己所在的文件系统路径。如果需要获取动态库(.so)文件的系统绝对路径,需要采用一些方法。
在windows操作系统中,有微软实现好了的GetModuleFileName系统API, 可以直接调用。
windows下的定义如下:

DWORD GetModuleFileNameA(
  [in, optional] HMODULE hModule,
  [out]          LPSTR   lpFilename,
  [in]           DWORD   nSize
);

在linux操作系统中,并没有实现好的任何系统API直接获取动态库(.so)文件的路径,需要自己动手实现。

函数的定义及实现方法如下:

函数

#include <link.h>
#include <dlfcn.h>

int GetModuleFileName(char *pathbuf, int buflen)
{
	int ret;
	void *address;
    address = (void*)(&GetModuleFileName);

	if((pathbuf == NULL) || (buflen <=0))
		return -1;

    Dl_info dl_info;
    dl_info.dli_fname = 0;
    ret = dladdr(address, &dl_info);
    if (0 != ret && dl_info.dli_fname != NULL) {
        if(strlen(dl_info.dli_fname) >= buflen) {
			return -2;
		}

		strcpy(pathbuf, dl_info.dli_fname);
		return 0;
    }

    return -3;
}

使用方法

char *pos;
char szModulePath[260];
if(0 == GetModuleFileName(szModulePath, 260)) {
	//szModulePath 就是.so文件的绝对路径。
}

编译时,需要在gcc编译参数中加入:-D_GNU_SOURCE 选项。

<think>好的,我现在需要回答用户的问题:如何在Windows系统下使用C语言获取当前代码执行路径的父文件夹名称。首先,我需要回忆一下C语言中处理文件路径的相关函数,以及Windows API的相关知识。 用户的问题涉及两个主要部分:获取当前执行文件的路径,然后提取其父文件夹的名称。首先,我得确定如何获取当前执行文件的路径。在Windows中,常用的方法有使用GetModuleFileName函数。这个函数可以获取当前进程的可执行文件路径,包括文件名。我需要确认这个函数的使用方法,包括参数的正确传递。比如,第一个参数是模块的句柄,如果传入NULL,应该是指当前进程。第二个参数是接收路径的缓冲区,第三个参数是缓冲区的大小。返回值是实际写入的字符数,如果缓冲区不够大,返回值会是缓冲区的大小,这时需要处理错误情况。 接下来,获取到完整路径后,需要提取父文件夹的名称。这涉及到字符串处理。首先,我需要找到路径中的最后一个反斜杠'\',然后截断后面的部分,剩下的就是父文件夹的路径。然后再从这个路径中找到倒数第二个反斜杠,从而确定父文件夹的名称。例如,如果路径是C:\A\B\C\app.exe,那么父文件夹是C:\A\B\C,而父文件夹的名称是C。因此,需要找到最后一个反斜杠的位置,将其截断,得到父路径,然后在这个父路径中找到倒数第二个反斜杠的位置,中间的部分就是父文件夹的名称。 不过,这里可能存在一些问题。比如,路径可能有不同的分隔符,或者末尾是否有反斜杠。例如,如果路径是C:\A\B\C,那么父文件夹是C:\A\B,名称是B。所以处理时需要确保路径字符串的正确分割。此外,Windows路径可能使用正斜杠或反斜杠,但通常GetModuleFileName返回的是反斜杠。需要确保处理的是反斜杠。 另外,需要考虑路径可能的格式,比如根目录的情况。例如,路径是C:\,这时父路径可能不存在,需要处理这种情况,避免越界访问。 现在,我需要将这些步骤转化为C语言代码。首先,使用GetModuleFileName获取路径,然后处理字符串。对于字符串处理,可以使用strrchr函数来查找最后一个反斜杠的位置。找到之后,将其替换为字符串结束符'\0',从而得到父路径。然后再对这个父路径使用strrchr,找到上一个反斜杠的位置,之后的部分就是父文件夹的名称。 需要注意错误处理,比如路径是否有效,缓冲区是否足够大。例如,当GetModuleFileName返回的路径长度等于缓冲区大小时,说明缓冲区太小,需要扩大缓冲区或者处理错误。 还有一个问题,如果路径中有多个连续的斜杠,或者末尾有斜杠,如何处理?例如,路径可能是C:\A\B\C\\,这时候需要先规范化路径,去掉末尾的斜杠再处理。或者在使用strrchr时,找到最后一个非斜杠的位置,再进行处理。这可能需要一些额外的处理步骤,比如从后往前遍历字符串,找到最后一个非反斜杠的位置,然后再找前面的反斜杠。 另外,Windows API中是否有其他函数可以直接处理路径?比如PathRemoveFileSpec函数,可以去除文件名部分,得到目录路径。然后使用PathFindFileName函数获取最后的目录名。这些函数在shlwapi.h中,可能需要链接shlwapi.lib库。如果使用这些函数,代码会更简洁,但需要考虑可移植性和依赖问题。 不过,用户可能希望用标准C库函数和Windows API的基本函数,而不依赖额外的库。所以可能需要手动处理字符串。 现在,综合这些思路,我需要写出具体的代码步骤: 1. 使用GetModuleFileName获取当前执行文件的完整路径。 2. 找到最后一个反斜杠的位置,截断后面的文件名部分,得到父路径。 3. 再次在父路径中找到最后一个反斜杠的位置,截断后面的部分,得到父文件夹的名称。 4. 处理可能的错误情况,比如路径长度不足,或者路径已经是根目录。 例如,假设完整路径是C:\Projects\MyApp\bin\app.exe,第一次处理后得到C:\Projects\MyApp\bin,再次处理后找到bin,其父文件夹是MyApp所在的路径吗?或者用户需要的是父路径的父路径?不,用户的问题是要获取当前代码执行路径的父文件夹名称。例如,如果当前执行路径是C:\A\B\app.exe,那么父文件夹是C:\A\B,其父文件夹名称是B。或者,用户是否指的是app.exe所在目录的父目录?例如,如果app.exe在C:\A\B\,那么父目录是C:\A,父文件夹名称是A?这里可能存在歧义。需要明确用户的需求。 用户的问题中的“父文件夹名称”是指当前执行文件所在目录的父目录的名称。例如,如果执行文件路径是C:\A\B\C\app.exe,那么当前目录是C:\A\B\C,父目录是C:\A\B,父文件夹名称是B。或者用户是否指的是当前执行文件所在目录的名称?例如,如果当前目录是C:\A\B\C,那么父目录的父文件夹名称可能指的是B。或者可能用户的问题描述存在歧义,需要进一步确认。但根据问题描述,应该是当前执行路径的父文件夹,即执行文件所在目录的上一级目录的名称。 因此,正确的步骤应该是: 获取执行文件的完整路径,例如C:\A\B\app.exe,那么目录是C:\A\B,父目录是C:\A,父文件夹名称是A。或者如果路径是C:\A\B\(例如,执行文件在C:\A\B\目录下,并且路径是C:\A\B\app.exe),那么目录是C:\A\B,父目录是C:\A,名称是A。 所以需要从执行文件的路径中提取目录部分,然后提取该目录的父目录的名称。 例如,步骤: 1. 获取完整路径:C:\A\B\app.exe 2. 去除文件名,得到目录:C:\A\B 3. 找到该目录的父目录:C:\A 4. 提取父目录的名称:A 或者,如果用户指的是当前执行文件所在目录的父目录,即步骤3中的结果,然后取该目录的最后一级名称。 那么,具体实现可能需要两次截断: 第一次,去除文件名,得到当前目录的路径。 第二次,去除当前目录的最后一级,得到父目录路径,然后取最后一级作为名称。 例如: 完整路径:C:\A\B\C\app.exe 第一次处理后得到目录:C:\A\B\C 第二次处理后得到父目录路径:C:\A\B 然后,父文件夹名称是B。 这可能更符合用户的需求,即当前代码所在的目录的父文件夹名称。所以需要确认用户的需求到底是哪一级。 但用户的问题描述是“当前代码执行路径的父文件夹名称”,这里的执行路径可能指的是可执行文件所在的完整路径,包括文件名。因此,父文件夹应该是指该文件所在目录的父目录。例如,如果路径是C:\A\B\app.exe,那么文件所在目录是B,父目录是A,所以父文件夹名称是A。 或者用户可能认为执行路径的父文件夹是B,即所在目录的名称?这里需要进一步明确。但根据通常的理解,父文件夹应该是所在目录的上一级目录的名称。 不过,这可能取决于具体的应用场景。为了避免误解,应该在代码中明确处理步骤。 现在,假设用户需要的是当前执行文件所在目录的父目录的名称,例如,路径是C:\A\B\app.exe,那么目录是C:\A\B,父目录是C:\A,父文件夹名称是A。 所以步骤应该是: 1. 获取完整路径GetModuleFileName得到路径字符串。 2. 找到最后一个反斜杠,截断后面的文件名,得到目录路径。 3. 再找到这个目录路径的最后一个反斜杠,截断后面的部分,得到父目录路径。 4. 然后,父目录路径的最后一部分就是父文件夹名称。 例如,路径是C:\A\B\app.exe: 步骤2后得到C:\A\B\ 步骤3后得到C:\A\ 然后,步骤4中,父文件夹名称是A。 但可能需要处理路径末尾的反斜杠。比如,如果步骤2后的路径是C:\A\B\,则步骤3处理后得到C:\A\,然后父文件夹名称是A。或者如果路径是C:\A\B,则步骤3处理可能需要找到最后的反斜杠的位置。 所以,在代码中,需要确保处理后的目录路径末尾没有反斜杠,否则会影响后续处理。例如,在步骤2之后,如果路径是C:\A\B\,需要先将末尾的反斜杠去掉,然后再找最后一个反斜杠的位置。 因此,处理步骤可能需要: 步骤2之后,去掉路径末尾的反斜杠(如果有的话),然后找到最后一个反斜杠的位置,截断之后的部分,得到父目录路径,然后父文件夹名称是截断后的最后一个部分。 例如: 原始路径:C:\A\B\app.exe 步骤2后目录:C:\A\B 步骤3处理:去掉末尾反斜杠(如果有的话),然后找到最后一个反斜杠的位置,假设这里已经是C:\A\B,没有末尾反斜杠,那么最后一个反斜杠在B前面,截断后得到C:\A,父文件夹名称是A。 所以,代码中需要处理路径中的反斜杠,可能需要先规范化路径,去除末尾的反斜杠,然后再处理。 现在,将这些步骤转化为C代码: 首先,获取完整路径: char path[MAX_PATH]; GetModuleFileName(NULL, path, MAX_PATH); 然后,找到最后一个反斜杠,截断文件名: char *last_backslash = strrchr(path, '\\'); if (last_backslash != NULL) { *last_backslash = '\0'; // 现在path是目录路径,如C:\A\B } 此时,path是目录路径。接下来,需要找到这个目录路径的父目录: // 再次查找最后一个反斜杠 char *parent_last_backslash = strrchr(path, '\\'); if (parent_last_backslash != NULL) { // 父文件夹名称是parent_last_backslash + 1的位置到字符串结束 char *parent_folder = parent_last_backslash + 1; printf("父文件夹名称: %s\n", parent_folder); } else { // 没有找到,可能已经是根目录,如C:\ printf("已经是根目录\n"); } 例如,如果path现在是C:\A\B,那么parent_last_backslash指向第二个反斜杠(在C:\A\B中的位置是C:\A\B的索引5,假设字符串是"C:\A\B"),则parent_last_backslash +1是"B",所以父文件夹名称是B?这与之前的分析矛盾。这说明哪里出错了? 哦,这里的问题在于,如果原来的路径是C:\A\B\app.exe,经过第一次截断后,path变成C:\A\B。然后再次查找最后一个反斜杠,得到的位置是C:\A\B中的最后一个反斜杠,即分隔A和B的那个反斜杠。例如,假设path是"C:\\A\\B",则strrchr会找到最后一个\,位置在A和B之间,截断后,path变成C:\A,然后parent_folder是B?不,不是。例如,原path是C:\A\B,所以字符串是"C:\\A\\B"。strrchr找到最后一个\的位置是第三个字符(假设索引从0开始,具体取决于实际路径)。假设path是"C:\\A\\B",那么反斜杠的位置在索引5(假设每个反斜杠都是转义的,可能需要用双反斜杠表示,但实际上在C字符串中,每个反斜杠在内存中表示为一个字符)。因此,strrchr返回指向最后一个反斜杠的指针,比如在"C:\A\B"中,假设字符串是: 索引 0: C 1: : 2: \ 3: A 4: \ 5: B 6: \0 那么,strrchr(path, '\\')会找到索引4的位置,即指向第二个反斜杠的位置。此时,将*last_backslash设为'\0',则path变成"C:\A",然后再次调用strrchr(path, '\\'),会找到索引2的位置,即第一个反斜杠。此时,parent_last_backslash是索引2的位置,parent_folder是索引3的位置,即"A"。所以父文件夹名称是A,而原来的目录是C:\A\B,父目录是C:\A,名称是A。这符合预期。 因此,代码的逻辑是正确的。 但需要处理特殊情况,例如当路径是C:\(根目录),此时第一次截断后path是C:\,再次查找反斜杠,找到索引2的位置,截断后变为C:,此时parent_last_backslash为NULL,所以需要处理这种情况。 例如,如果路径是C:\app.exe,第一次截断后变成C:\,然后再次查找反斜杠,找到索引2的位置,将其截断,path变为C:,此时strrchr(path, '\\')返回NULL,因此需要处理这种情况,避免程序崩溃。 因此,在代码中需要添加判断,当parent_last_backslash为NULL时,说明已经是根目录,无法获取父文件夹名称。 另外,还需要考虑网络路径或其他特殊情况,例如UNC路径,但用户的问题可能仅涉及本地路径。 现在,综合这些情况,写出示例代码: #include <windows.h> #include <stdio.h> #include <string.h> int main() { char path[MAX_PATH]; DWORD result = GetModuleFileNameA(NULL, path, MAX_PATH); if (result == 0) { printf("获取路径失败,错误代码:%lu\n", GetLastError()); return 1; } else if (result == MAX_PATH) { printf("缓冲区不足,路径被截断\n"); // 这里可以动态分配更大的缓冲区 return 1; } // 去除文件名部分,得到目录路径 char *last_backslash = strrchr(path, '\\'); if (last_backslash == NULL) { printf("路径格式不正确\n"); return 1; } *last_backslash = '\0'; // 现在path是目录,如C:\A\B // 再次查找目录路径中的最后一个反斜杠,以获取父目录名称 char *parent_last_backslash = strrchr(path, '\\'); if (parent_last_backslash == NULL) { printf("当前目录已经是根目录,无父文件夹\n"); return 0; } char *parent_folder_name = parent_last_backslash + 1; printf("父文件夹名称:%s\n", parent_folder_name); return 0; } 测试这个代码: 情况1:路径是C:\A\B\app.exe 第一次截断后path是C:\A\B,然后查找最后一个\,找到在A和B之间,截断后path变为C:\A,parent_folder_name是A。 情况2:路径是C:\app.exe 第一次截断后path是C:\,然后查找\,找到在索引2的位置,截断后path变为C:(即字符串"C:"),再次查找\,返回NULL,提示已经是根目录。 情况3:路径是C:\A\B\C\app.exe 第一次截断得到C:\A\B\C,再次截断得到C:\A\B,父文件夹名称是B。 这似乎与用户的需求不符,因为用户可能需要的是当前目录的父目录,即C的父目录是B。因此,上述代码会输出B作为父文件夹名称。但用户的问题描述可能希望得到的是父目录的名称,即当前目录的上一级。例如,如果当前目录是C:\A\B,则父目录是A,名称是A。但根据代码逻辑,此时如果当前执行文件在C:\A\B\app.exe,则代码处理后得到的父文件夹名称是A,对吗? 让我再仔细分析一下: 假设执行文件路径是C:\A\B\app.exe: 1. GetModuleFileName得到"C:\A\B\app.exe" 2. 找到最后一个\,位置在app.exe前面,截断后path变成"C:\A\B" 3. 再次查找path中的最后一个\,位置在B前面,截断后path变成"C:\A",然后父文件夹名称是"A" 是的,这样得到的父文件夹名称是A,即B所在目录的父目录名称。所以代码是正确的,符合用户需求。 因此,该代码可以正确获取当前执行文件所在目录的父目录名称。 但是,如果用户希望获取的是当前执行文件所在目录的名称(例如,B),而不是其父目录的名称,那么代码需要调整。但根据问题描述,用户明确要求的是父文件夹名称,因此代码是正确的。 需要测试其他情况,例如路径中有空格、特殊字符等,但代码本身不涉及这些处理,只是字符串操作。 此外,可能需要注意路径中的斜杠方向,但Windows API通常返回反斜杠,所以使用strrchr查找'\'是正确的。 最后,用户可能需要动态分配缓冲区,以处理长路径(超过MAX_PATH的情况),但GetModuleFileName的文档指出,在Windows API中,MAX_PATH通常为260,但若路径超过这个长度且应用程序声明支持长路径,可以使用扩展长度的路径(以\\?\前缀)。但为了简单起见,示例代码使用MAX_PATH,可能不处理超过长度的路径,但作为示例已经足够。 综上,这个代码应该可以满足用户的需求。</think>在Windows系统下,使用C语言获取当前执行文件的父文件夹名称,可以通过以下步骤实现: --- ### **步骤 1:获取当前执行文件的完整路径** 使用Windows API函数 `GetModuleFileName` 获取当前可执行文件的完整路径: ```c #include <windows.h> #include <stdio.h> #include <string.h> int main() { char path[MAX_PATH]; DWORD result = GetModuleFileNameA(NULL, path, MAX_PATH); if (result == 0) { printf("获取路径失败,错误代码:%lu\n", GetLastError()); return 1; } else if (result == MAX_PATH) { printf("路径过长,需扩展缓冲区\n"); return 1; } // 后续步骤... } ``` --- ### **步骤 2:去除文件名,得到当前目录路径** 通过查找路径中最后一个反斜杠 `\`,截断文件名部分: ```c // 找到最后一个反斜杠并截断文件名 char* last_backslash = strrchr(path, '\\'); if (last_backslash == NULL) { printf("路径格式错误\n"); return 1; } *last_backslash = '\0'; // 此时path为当前目录路径(如C:\A\B) ``` --- ### **步骤 3:提取父文件夹名称** 再次查找当前目录路径的最后一个反斜杠,确定父文件夹名称: ```c // 查找父目录的反斜杠 char* parent_last_backslash = strrchr(path, '\\'); if (parent_last_backslash == NULL) { printf("当前目录已是根目录,无父文件夹\n"); return 0; } // 父文件夹名称是反斜杠后的部分 char* parent_folder = parent_last_backslash + 1; printf("父文件夹名称:%s\n", parent_folder); ``` --- ### **完整代码示例** ```c #include <windows.h> #include <stdio.h> #include <string.h> int main() { char path[MAX_PATH]; DWORD result = GetModuleFileNameA(NULL, path, MAX_PATH); if (result == 0) { printf("获取路径失败,错误代码:%lu\n", GetLastError()); return 1; } else if (result == MAX_PATH) { printf("路径过长,需扩展缓冲区\n"); return 1; } // 去除文件名 char* last_backslash = strrchr(path, '\\'); if (last_backslash == NULL) { printf("路径格式错误\n"); return 1; } *last_backslash = '\0'; // 提取父文件夹名称 char* parent_last_backslash = strrchr(path, '\\'); if (parent_last_backslash == NULL) { printf("当前目录已是根目录\n"); return 0; } printf("父文件夹名称:%s\n", parent_last_backslash + 1); return 0; } ``` --- ### **关键说明** 1. **路径长度限制**:`MAX_PATH` 默认为260字符,若路径超长需使用Unicode版本API并扩展缓冲区。 2. **根目录处理**:若路径为 `C:\`,则无父文件夹,需通过条件判断处理。 3. **跨平台差异**:此方法仅适用于Windows系统,Linux需使用 `readlink("/proc/self/exe", ...)`。 通过上述代码,可准确获取当前执行文件所在目录的父文件夹名称。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

化妖成魔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值