【问题标题】:Is there STDCALL in Linux?Linux中有STDCALL吗?
【发布时间】:2023-06-22 11:10:01
【问题描述】:

我正在尝试将 Windows 应用程序移植到 Linux。这个应用程序用__stdcall 属性标记了一些功能。但是,有朋友告诉我stdcall 只在 Windows 上使用,在 Linux 中没有任何意义(但在 Windows GCC 中确实存在)。

搜索 Google - 一些结果表明在 Linux 中有 stdcall

Linux 中有stdcall 吗?

此外,GCC 表明: __attribute__((__stdcall__))__attribute__((stdcall))(stdcall 附近没有下划线)。

首选哪一个(如果适用于 Linux)?

【问题讨论】:

  • stdcall 只是一个调用 convention,stdcall 当然可以存在于 Linux 中但几乎从未使用过(可能除了 Wine 程序)。最好将您的程序转换为遵循本地约定。因此,要回答您的问题(首选哪一个),就是不使用 stdcall。 stdcall 在 Linux 程序中不合适。

标签: c++ linux porting stdcall


【解决方案1】:

最简单的解决方案是在 Linux 上将 __stdcall 有条件地定义为空。

【讨论】:

  • 这就是为什么我建议编写一个扩展为 "" 的条件(仅限 Linux)宏。
【解决方案2】:

这是 MSDN 上 __stdcall 描述的链接: http://msdn.microsoft.com/en-us/library/zxk0tw93(VS.80).aspx

仅用于调用 WinAPI 函数。要将这样的 Windows 应用程序移植到 Linux,您需要的不仅仅是将 __stdcall 定义为空:

#ifndef WIN32 // or something like that...
#define __stdcall
#endif

您还需要调用特定于 Linux 的 API 函数而不是 Win32 API 函数。根据 Win32 API 的特定部分和应用程序的大小(代码量),它可能介于中等难度和令人生畏之间。

应用程序将哪些特定函数标记为 __stdcall?

确实,GCC 的 Windows 端口必须有 __stdcall,因为它应该能够生成符合 Win32 平台的代码。但由于在 Linux 下只有一个标准调用约定,并且与默认的编译器输出一致,因此不需要此语句。

您的应用程序无法在 Linux 下编译的原因几乎可以肯定是因为它引用了未在 Linux 下定义的 Win32 API 函数——您需要找到合适的 Linux 对应项。 Win32 API 和 Linux GLibc API-s 非常不同,不能轻易替代。

将您的应用程序移植到 Linux 的最简单方法可能是使用 Wine,即以这样的方式修改 Windows 代码,使其在 Linux 中的 Wine 下顺利运行。即使是最复杂的应用程序,如现代计算机游戏,也是这样在 Linux 下运行的。

当然,如果你真的希望它在 Linux 下原生运行,那么移植是唯一的方法。

【讨论】:

    【解决方案3】:

    stdcall 不仅仅是一个调用约定;除了作为调用约定之外,它还允许 C 和 C++ 对象之间的同构。这是一个例子:

    #define _CRT_SECURE_NO_WARNINGS // disable marking use of strcpy as error.
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    class ICdeclGreeter {
    public:
        virtual ~ICdeclGreeter(){}
        virtual void setGreeting(const char *greeting) = 0;
        virtual void greet() = 0;
    };
    class IStdcallGreeter {
    public:
        virtual __stdcall ~IStdcallGreeter(){}
        virtual void __stdcall setGreeting(const char *greeting) = 0;
        virtual void __stdcall greet() = 0;
    };
    
    class CdeclGreeter : public ICdeclGreeter {
    public:
        char *greeting;
        ~CdeclGreeter() {
            if (greeting != nullptr) {
                free(greeting);
                puts("[CdeclGreeter] destroyed");
            }
        }
        void setGreeting(const char *greeting) {
            this->greeting = (char *)malloc(strlen(greeting) + 1);
            strcpy(this->greeting, greeting);
        }
        void greet() {
            puts(greeting);
        }
    };
    class StdcallGreeter : public IStdcallGreeter {
    public:
        char *greeting;
        __stdcall ~StdcallGreeter() {
            if (greeting != nullptr) {
                free(greeting);
                puts("[StdcallGreeter] destroyed");
            }
        }
        void __stdcall setGreeting(const char *greeting) {
            this->greeting = (char *)malloc(strlen(greeting) + 1);
            strcpy(this->greeting, greeting);
        }
        void __stdcall greet() {
            puts(greeting);
        }
    };
    typedef struct pureC_StdcallGreeter pureC_StdcallGreeter;
    
    typedef struct pureC_StdcallGreeterVtbl {
        void (__stdcall *dtor)(pureC_StdcallGreeter *This);
        void (__stdcall *setGreeting)(pureC_StdcallGreeter *This, const char *greeting);
        void (__stdcall *greet)(pureC_StdcallGreeter *This);
    } pureC_IStdcallGreeterVtbl;
    
    struct pureC_StdcallGreeter {
        pureC_IStdcallGreeterVtbl *lpVtbl;
        char *greeting;
        int length;
    };
    
    /* naive attempt at porting a c++ class to C; 
       on x86, thiscall passes This via ecx register rather than
       first argument; this register cannot be accessed in C without
       inline assembly or calling a reinterpretation of byte array
       as a function. there is no "This" argument in any of below. */
    typedef struct pureC_CdeclGreeter pureC_CdeclGreeter;
    
    typedef struct pureC_CdeclGreeterVtbl {
        void (*dtor)(pureC_CdeclGreeter *This);
        void (*setGreeting)(pureC_CdeclGreeter *This, const char *greeting);
        void (*greet)(pureC_CdeclGreeter *This);
    } pureC_CdeclGreeterVtbl;
    
    struct pureC_CdeclGreeter {
        pureC_CdeclGreeterVtbl *lpVtbl;
        char *greeting;
        int length;
    };
    
    
    void test() {
        ICdeclGreeter *g = new CdeclGreeter;
        g->setGreeting("hi");
        g->greet();
    
        IStdcallGreeter *g2 = new StdcallGreeter;
        g2->setGreeting("hi");
        g2->greet();
    
        // we can pass pointers to our object to pure C using this interface,
        // and it can still use it without doing anything to it.
        pureC_StdcallGreeter *g3 = (pureC_StdcallGreeter *)g2;
        g3->lpVtbl->setGreeting(g3, "hello, world!");
        g3->lpVtbl->greet(g3);
        g3->lpVtbl->dtor(g3);
        free(g2);
    
        /*
        // cdecl passes this via ecx in x86, and not as the first argument;
        // this means that this argument cannot be accessed in C without 
        // inline assembly or equivelent. Trying to run code below will cause a runtime error.
        pureC_CdeclGreeter *g4 = (pureC_CdeclGreeter *)g;
        g4->lpVtbl->setGreeting(g4, "hello, world!");
        g4->lpVtbl->greet(g4);
    
        g4->lpVtbl->dtor(g4);
        free(g);
        */
        delete g;
    }
    
    int main(int argc, char **argv)
    {
        test();
    
        system("pause");
    
        return 0;
    }
    

    TLDR;这与 cdecl 使 C++ 类在使用此约定的平台上无法从 C 中使用不同,因为为了将“This”发送到方法,您必须将 ecx 寄存器设置为“This”的地址,而不是仅仅推送它,同样如果如果您想在 C 中实现一个 C++ 可以识别的类,则该方法需要从 ecx 寄存器中获取 This 指针,如果没有内联汇编或等效项,C 将无法访问该指针。

    stdcall 有一个很好的特性,即使用 stdcall 的类可以轻松地同时在 C 或 C++ 中使用,而无需对它们做任何事情。

    所以只要不处理__thiscall,就只能#define __stdcall;尽管可能还有其他一些细微的区别。

    【讨论】:

    • is 只是一个调用约定,尽管它具有您(ab)在您的答案中使用的属性。此外,此代码与平台相关,除非您真的知道自己在做什么并且没有其他简单的方法,否则不应使用此代码。