使用标准 C 获取 Mac OS X 中的操作系统版本

发布于 2024-11-06 22:57:51 字数 739 浏览 1 评论 0 原文

我试图在 C 中以编程方式获取 Mac OS X 的版本。搜索一段时间后,我尝试了以下代码:

#include <CoreServices/CoreServices.h>

int GetOS()
{
    SInt32 majorVersion,minorVersion,bugFixVersion;

    Gestalt(gestaltSystemVersionMajor, &majorVersion);
    Gestalt(gestaltSystemVersionMinor, &minorVersion);
    Gestalt(gestaltSystemVersionBugFix, &bugFixVersion);

    printf("Running on Mac OS X %d.%d.%d\n",majorVersion,minorVersion,bugFixVersion);    

    return 0;
}

XCode 返回 LD 错误:

架构 x86_64 的未定义符号: “_Gestalt”,引用自: _GetOS in main.o

我缺少什么?你如何做到这一点?

我还发现了这个片段

[[NSProcessInfo processInfo] operatingSystemVersionString]

,但我不知道如何用 C 语言编写它。

I'm trying to get the version of Mac OS X programmatically in C. After searching for a while I tried this code:

#include <CoreServices/CoreServices.h>

int GetOS()
{
    SInt32 majorVersion,minorVersion,bugFixVersion;

    Gestalt(gestaltSystemVersionMajor, &majorVersion);
    Gestalt(gestaltSystemVersionMinor, &minorVersion);
    Gestalt(gestaltSystemVersionBugFix, &bugFixVersion);

    printf("Running on Mac OS X %d.%d.%d\n",majorVersion,minorVersion,bugFixVersion);    

    return 0;
}

XCode returns an LD error:

Undefined symbols for architecture x86_64:
"_Gestalt", referenced from:
_GetOS in main.o

What am I missing? How do you do this?

I found also this snippet

[[NSProcessInfo processInfo] operatingSystemVersionString]

But I have no idea how to write that in C.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

︶葆Ⅱㄣ 2024-11-13 22:57:51

您是否将适当的框架传递给 GCC 以启用 CoreServices?

% gcc -framework CoreServices -o getos main.c

Did you pass the appropriate framework to GCC in order to enable CoreServices?

% gcc -framework CoreServices -o getos main.c
小耗子 2024-11-13 22:57:51

使用 @uchuugaka 在对 @McUsr 答案的评论中的提示,我编写了一个似乎有效的函数。我并不是说它比任何其他答案都好。

/*
 * Structure for MacOS version number
 */
typedef struct macos_version_str
{
    ushort major;
    ushort minor;
    ushort point;
} macos_type;

/****************************************************************************
 *
 * Determine the MacOS version.
 *
 * Parameters:
 *    version_struct:  (pointer to) macos_version structure to be filled in.
 *
 * Return value:
 *    0: no error.
 *
 ****************************************************************************/

static int get_macos_version ( macos_type *version_struct )
{
    char    os_temp [20] = "";
    char   *os_temp_ptr  = os_temp;
    size_t  os_temp_len  = sizeof(os_temp);
    size_t  os_temp_left = 0;
    int     rslt         = 0;

    
    version_struct->major = 0;
    version_struct->minor = 0;
    version_struct->point = 0;
    
    rslt = sysctlbyname ( "kern.osproductversion", os_temp, &os_temp_len, NULL, 0 );
    if ( rslt != 0 )
    {
        fprintf ( stderr,
                  "sysctlbyname() returned %d error (%d): %s",
                  rslt, errno, strerror(errno));
        return ( rslt );
    }
    
    os_temp_left = os_temp_len; /* length of string returned */
    int temp = atoi ( os_temp_ptr );
    version_struct->major = temp;
    version_struct->major = atoi ( os_temp_ptr );
    
    while ( os_temp_left > 0 && *os_temp_ptr != '.' )
    {
        os_temp_left--;
        os_temp_ptr++;
    }
    os_temp_left--;
    os_temp_ptr++;
    version_struct->minor = atoi ( os_temp_ptr );
    
    while ( os_temp_left > 0 && *os_temp_ptr != '.' )
    {
        os_temp_left--;
        os_temp_ptr++;
    }
    os_temp_left--;
    os_temp_ptr++;
    version_struct->point = atoi ( os_temp_ptr );
    
    fprintf ( stderr, "Calculated OS Version: %d.%d.%d", version_struct->major, version_struct->minor, version_struct->point );
    
    if ( version_struct->major == 0 ||
         version_struct->minor == 0 ||
         version_struct->point == 0   )
    {
        fprintf ( stderr, "Unable to parse MacOS version string %s", os_temp );
        return ( -2 );
    }
    
    return 0;
}

Using the hint from @uchuugaka in the comment on the answer by @McUsr, I wrote a function that seems to work. I'm not saying it's better than any other answer.

/*
 * Structure for MacOS version number
 */
typedef struct macos_version_str
{
    ushort major;
    ushort minor;
    ushort point;
} macos_type;

/****************************************************************************
 *
 * Determine the MacOS version.
 *
 * Parameters:
 *    version_struct:  (pointer to) macos_version structure to be filled in.
 *
 * Return value:
 *    0: no error.
 *
 ****************************************************************************/

static int get_macos_version ( macos_type *version_struct )
{
    char    os_temp [20] = "";
    char   *os_temp_ptr  = os_temp;
    size_t  os_temp_len  = sizeof(os_temp);
    size_t  os_temp_left = 0;
    int     rslt         = 0;

    
    version_struct->major = 0;
    version_struct->minor = 0;
    version_struct->point = 0;
    
    rslt = sysctlbyname ( "kern.osproductversion", os_temp, &os_temp_len, NULL, 0 );
    if ( rslt != 0 )
    {
        fprintf ( stderr,
                  "sysctlbyname() returned %d error (%d): %s",
                  rslt, errno, strerror(errno));
        return ( rslt );
    }
    
    os_temp_left = os_temp_len; /* length of string returned */
    int temp = atoi ( os_temp_ptr );
    version_struct->major = temp;
    version_struct->major = atoi ( os_temp_ptr );
    
    while ( os_temp_left > 0 && *os_temp_ptr != '.' )
    {
        os_temp_left--;
        os_temp_ptr++;
    }
    os_temp_left--;
    os_temp_ptr++;
    version_struct->minor = atoi ( os_temp_ptr );
    
    while ( os_temp_left > 0 && *os_temp_ptr != '.' )
    {
        os_temp_left--;
        os_temp_ptr++;
    }
    os_temp_left--;
    os_temp_ptr++;
    version_struct->point = atoi ( os_temp_ptr );
    
    fprintf ( stderr, "Calculated OS Version: %d.%d.%d", version_struct->major, version_struct->minor, version_struct->point );
    
    if ( version_struct->major == 0 ||
         version_struct->minor == 0 ||
         version_struct->point == 0   )
    {
        fprintf ( stderr, "Unable to parse MacOS version string %s", os_temp );
        return ( -2 );
    }
    
    return 0;
}
鸢与 2024-11-13 22:57:51

在可预见的将来,下面的代码应该可以用于确定 Mac OS X 的当前版本。

/*  McUsr put this together, and into public domain, 
    without any guarrantees about anything,
    but the statement that it works for me.
*/

#if 1 == 1
#define TESTING
#endif

#include <sys/param.h>
#include <sys/sysctl.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

struct osver {
    int minor;
    int sub;
} ;
typedef struct osver osxver ;
void macosx_ver(char *darwinversion, osxver *osxversion ) ;
char *osversionString(void) ;

#ifdef TESTING
int main( int argc, char *argv[] )
{
    osxver foundver;
    char *osverstr= NULL ;
    osverstr=osversionString() ;
    macosx_ver(osverstr, &foundver ) ;
    printf("Mac os x version = 10.%d.%d\n",foundver.minor,foundver.sub );
    free(osverstr);
    return 0;
}
#endif
char *osversionString(void) {
    int mib[2];
    size_t len;
    char *kernelVersion=NULL;
    mib[0] = CTL_KERN;
    mib[1] = KERN_OSRELEASE;

    if (sysctl(mib, 2, NULL, &len, NULL, 0) < 0 ) {
        fprintf(stderr,"%s: Error during sysctl probe call!\n",__PRETTY_FUNCTION__ );
        fflush(stdout);
        exit(4) ;
    }

    kernelVersion = malloc(len );
    if (kernelVersion == NULL ) {
        fprintf(stderr,"%s: Error during malloc!\n",__PRETTY_FUNCTION__ );
        fflush(stdout);
        exit(4) ;
    }
    if (sysctl(mib, 2, kernelVersion, &len, NULL, 0) < 0 ) {
        fprintf(stderr,"%s: Error during sysctl get verstring call!\n",__PRETTY_FUNCTION__ );
        fflush(stdout);
        exit(4) ;
    }

    return kernelVersion ;
}

void macosx_ver(char *darwinversion, osxver *osxversion ) {
/*
    From the book Mac Os X and IOS Internals:
    In version 10.1.1, Darwin (the core OS) was renumbered from v1.4.1 to 5.1,
    and since then has followed the OS X numbers consistently by being four
    numbers ahead of the minor version, and aligning its own minor with the
    sub-version.
*/
    char firstelm[2]= {0,0},secElm[2]={0,0};

    if (strlen(darwinversion) < 5 ) {
        fprintf(stderr,"%s: %s Can't possibly be a version string. Exiting\n",__PRETTY_FUNCTION__,darwinversion);
        fflush(stdout);
        exit(2);
    }
    char *s=darwinversion,*t=firstelm,*curdot=strchr(darwinversion,'.' );

    while ( s != curdot )
        *t++ = *s++;
    t=secElm ;
    curdot=strchr(++s,'.' );
    while ( s != curdot )
        *t++ = *s++;
    int maj=0, min=0;
    maj= (int)strtol(firstelm, (char **)NULL, 10);
    if ( maj == 0 && errno == EINVAL ) {
        fprintf(stderr,"%s Error during conversion of version string\n",__PRETTY_FUNCTION__);
        fflush(stdout);
        exit(4);
    }

    min=(int)strtol(secElm, (char **)NULL, 10);

    if ( min  == 0 && errno == EINVAL ) {
        fprintf(stderr,"%s: Error during conversion of version string\n",__PRETTY_FUNCTION__);
        fflush(stdout);
        exit(4);
    }
    osxversion->minor=maj-4;
    osxversion->sub=min;
}

The code below should work in the foreseeable future for figuring out the current version of Mac Os X.

/*  McUsr put this together, and into public domain, 
    without any guarrantees about anything,
    but the statement that it works for me.
*/

#if 1 == 1
#define TESTING
#endif

#include <sys/param.h>
#include <sys/sysctl.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

struct osver {
    int minor;
    int sub;
} ;
typedef struct osver osxver ;
void macosx_ver(char *darwinversion, osxver *osxversion ) ;
char *osversionString(void) ;

#ifdef TESTING
int main( int argc, char *argv[] )
{
    osxver foundver;
    char *osverstr= NULL ;
    osverstr=osversionString() ;
    macosx_ver(osverstr, &foundver ) ;
    printf("Mac os x version = 10.%d.%d\n",foundver.minor,foundver.sub );
    free(osverstr);
    return 0;
}
#endif
char *osversionString(void) {
    int mib[2];
    size_t len;
    char *kernelVersion=NULL;
    mib[0] = CTL_KERN;
    mib[1] = KERN_OSRELEASE;

    if (sysctl(mib, 2, NULL, &len, NULL, 0) < 0 ) {
        fprintf(stderr,"%s: Error during sysctl probe call!\n",__PRETTY_FUNCTION__ );
        fflush(stdout);
        exit(4) ;
    }

    kernelVersion = malloc(len );
    if (kernelVersion == NULL ) {
        fprintf(stderr,"%s: Error during malloc!\n",__PRETTY_FUNCTION__ );
        fflush(stdout);
        exit(4) ;
    }
    if (sysctl(mib, 2, kernelVersion, &len, NULL, 0) < 0 ) {
        fprintf(stderr,"%s: Error during sysctl get verstring call!\n",__PRETTY_FUNCTION__ );
        fflush(stdout);
        exit(4) ;
    }

    return kernelVersion ;
}

void macosx_ver(char *darwinversion, osxver *osxversion ) {
/*
    From the book Mac Os X and IOS Internals:
    In version 10.1.1, Darwin (the core OS) was renumbered from v1.4.1 to 5.1,
    and since then has followed the OS X numbers consistently by being four
    numbers ahead of the minor version, and aligning its own minor with the
    sub-version.
*/
    char firstelm[2]= {0,0},secElm[2]={0,0};

    if (strlen(darwinversion) < 5 ) {
        fprintf(stderr,"%s: %s Can't possibly be a version string. Exiting\n",__PRETTY_FUNCTION__,darwinversion);
        fflush(stdout);
        exit(2);
    }
    char *s=darwinversion,*t=firstelm,*curdot=strchr(darwinversion,'.' );

    while ( s != curdot )
        *t++ = *s++;
    t=secElm ;
    curdot=strchr(++s,'.' );
    while ( s != curdot )
        *t++ = *s++;
    int maj=0, min=0;
    maj= (int)strtol(firstelm, (char **)NULL, 10);
    if ( maj == 0 && errno == EINVAL ) {
        fprintf(stderr,"%s Error during conversion of version string\n",__PRETTY_FUNCTION__);
        fflush(stdout);
        exit(4);
    }

    min=(int)strtol(secElm, (char **)NULL, 10);

    if ( min  == 0 && errno == EINVAL ) {
        fprintf(stderr,"%s: Error during conversion of version string\n",__PRETTY_FUNCTION__);
        fflush(stdout);
        exit(4);
    }
    osxversion->minor=maj-4;
    osxversion->sub=min;
}
厌倦 2024-11-13 22:57:51

这是一个“工作量较少”的项目,对于家庭项目来说已经足够了(静态分配缓冲区,忽略错误)。适用于我的 OS X 10.11.1。

#include <stdio.h>

/*!
  @brief    Returns one component of the OS version
  @param    component  1=major, 2=minor, 3=bugfix
 */
int GetOSVersionComponent(int component) {
    char cmd[64] ;
    sprintf(
            cmd,
            "sw_vers -productVersion | awk -F '.' '{print $%d}'",
            component
    ) ;
    FILE* stdoutFile = popen(cmd, "r") ;

    int answer = 0 ;
    if (stdoutFile) {
        char buff[16] ;
        char *stdout = fgets(buff, sizeof(buff), stdoutFile) ;
        pclose(stdoutFile) ;
        sscanf(stdout, "%d", &answer) ;
    }

    return answer ;
}

int main(int argc, const char * argv[]) {
    printf(
           "Your OS version is: %d.%d.%d\n",
           GetOSVersionComponent(1),
           GetOSVersionComponent(2),
           GetOSVersionComponent(3)
           ) ;

    return 0 ;
}

Here is one with "less work", good enough for home projects (statically allocated buffers, ignoring errors). Works for me in OS X 10.11.1.

#include <stdio.h>

/*!
  @brief    Returns one component of the OS version
  @param    component  1=major, 2=minor, 3=bugfix
 */
int GetOSVersionComponent(int component) {
    char cmd[64] ;
    sprintf(
            cmd,
            "sw_vers -productVersion | awk -F '.' '{print $%d}'",
            component
    ) ;
    FILE* stdoutFile = popen(cmd, "r") ;

    int answer = 0 ;
    if (stdoutFile) {
        char buff[16] ;
        char *stdout = fgets(buff, sizeof(buff), stdoutFile) ;
        pclose(stdoutFile) ;
        sscanf(stdout, "%d", &answer) ;
    }

    return answer ;
}

int main(int argc, const char * argv[]) {
    printf(
           "Your OS version is: %d.%d.%d\n",
           GetOSVersionComponent(1),
           GetOSVersionComponent(2),
           GetOSVersionComponent(3)
           ) ;

    return 0 ;
}
回首观望 2024-11-13 22:57:51

如果出于某种原因您想避免使用 Gestalt API(它仍然可以正常工作,但已弃用),macosx_deployment_target.c 包含使用 CTL_KERN + KERN_OSRELEASE sysctl()< 的代码片段/code>,与此处的其他答案类似。

这是一个根据该代码改编的小程序,并考虑了 macOS 11 及更高版本(已在 macOS 12.6 上进行测试和验证,在更新本文时为最新的稳定版本):

#include <stdio.h>
#include <sys/sysctl.h>

int main()
{
    char osversion[32];
    size_t osversion_len = sizeof(osversion) - 1;
    int osversion_name[] = { CTL_KERN, KERN_OSRELEASE };

    if (sysctl(osversion_name, 2, osversion, &osversion_len, NULL, 0) == -1) {
        printf("sysctl() failed\n");
        return 1;
    }

    uint32_t major, minor;
    if (sscanf(osversion, "%u.%u", &major, &minor) != 2) {
        printf("sscanf() failed\n");
        return 1;
    }

    if (major >= 20) {
        major -= 9;

        // macOS 11 and newer
        printf("%u.%u\n", major, minor);
    } else {
        major -= 4;

        // macOS 10.1.1 and newer
        printf("10.%u.%u\n", major, minor);
    }

    return 0;
}

If for whatever reason you want to avoid the Gestalt API (which still works fine, but is deprecated), the macosx_deployment_target.c in cctools contains a code snippet that uses the CTL_KERN + KERN_OSRELEASE sysctl(), similar to other answers here.

Here's a small program adapted from that code and taking macOS 11 and newer (tested and verified with up to macOS 12.6, which was at time of updating this post the latest stable release) into account:

#include <stdio.h>
#include <sys/sysctl.h>

int main()
{
    char osversion[32];
    size_t osversion_len = sizeof(osversion) - 1;
    int osversion_name[] = { CTL_KERN, KERN_OSRELEASE };

    if (sysctl(osversion_name, 2, osversion, &osversion_len, NULL, 0) == -1) {
        printf("sysctl() failed\n");
        return 1;
    }

    uint32_t major, minor;
    if (sscanf(osversion, "%u.%u", &major, &minor) != 2) {
        printf("sscanf() failed\n");
        return 1;
    }

    if (major >= 20) {
        major -= 9;

        // macOS 11 and newer
        printf("%u.%u\n", major, minor);
    } else {
        major -= 4;

        // macOS 10.1.1 and newer
        printf("10.%u.%u\n", major, minor);
    }

    return 0;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文