2010-06-16 2 views
15

Я пытаюсь перенести приложение Windows в Linux. Это приложение отмечает некоторые функции с атрибутом __stdcall. Тем не менее, мне сказал друг, что stdcall используется только для окон и не имеет смысла в linux (но они существуют в Windows GCC). Я попытался найти Google об этом и получил некоторые результаты, заявив, что в Linux есть stdacll.Есть ли STDCALL в Linux?

Итак ... ??

Кроме того, для GCC я видел 2 варианта реализации для этого: __attribute__((__stdcall__)) и __attribute__((stdcall)) (без подчеркивания возле stdcall). Какой из них является предпочтительным (если применяется к Linux вообще)?

Спасибо!

+5

stdcall - это просто вызов * конвенция *, stdcall, безусловно, может существовать в Linux, но он почти никогда не используется (возможно, кроме программ Wine). Лучше конвертировать вашу программу, чтобы следовать локальному соглашению. Поэтому, чтобы ответить на ваш вопрос, какой из них предпочтительнее, не использовать stdcall. stdcall неуместен в Linux-программе. –

ответ

8

Простейшим решением является просто определить __stdcall ни к чему условно на Linux.

+0

Он не компилируется с GCC. –

+1

Вот почему я предложил написать условный (только для Linux) макрос, который расширяется до "". – dicroce

8

Вот ссылка на __stdcall описание на MSDN: 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 функции вместо них API Win32. В зависимости от конкретной части Win32 API и размера приложения (количество кода) он может быть где угодно между умеренно сложными и сложными.

Какие конкретные функции отмечены приложением как __stdcall?

Действительно, порт Windows GCC должен иметь __stdcall, поскольку он должен иметь возможность генерировать соответствующий код для платформы Win32. Но поскольку в Linux существует только одно стандартное соглашение о вызове и оно совпадает с выводом компилятора по умолчанию, это утверждение не требуется.

Причина, по которой ваше приложение не компилируется под Linux, почти наверняка связано с тем, что он ссылается на функции Win32 API, которые не определены в Linux - вам нужно найти соответствующие аналогия Linux. API Win32 API и Linux API GLibc очень разные и не могут быть легко заменены.

Возможно, самым простым способом переноса вашего приложения в Linux было бы использовать Wine, то есть модифицировать код Windows таким образом, чтобы он работал плавно под Wine в Linux. Таким образом, даже самые сложные приложения, такие как современные компьютерные игры, были запущены под Linux.

Конечно, если вы действительно хотите, чтобы он работал под Linux, то портирование - это единственный путь.

0

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 на платформах, используя это соглашение, потому что для того, чтобы отправить «Это» методу, вы должны установить регистр ecx на адрес «Это», а не просто нажимать его, а также, если вы хотите реализовать класс в C, который может распознать C++, нужно будет получить этот указатель из регистра ecx, который не доступен для C без встроенной сборки или эквивалентного.

У stdcall есть такое приятное свойство, что классы, которые используют stdcall, могут быть легко использованы одновременно с C или C++, не делая ничего с ними.

Таким образом, вы можете только #define __stdcall до тех пор, пока вы не имеете дело с __thiscall; хотя могут быть и другие тонкие различия.

Смежные вопросы