22

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

搜索 Google - 一些结果表明stdcallLinux 中存在。

stdcallLinux中有一个吗?

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

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

4

3 回答 3

15

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

于 2010-06-16T14:36:41.800 回答
11

这是 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 下本地运行,那么移植是唯一的方法。

于 2010-06-16T15:31:23.057 回答
1

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++ 中使用,而无需对它们做任何事情。

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

于 2018-02-23T04:03:49.290 回答