天天看點

移植 libuv 到 Visual C++ 6.0 并支援在 Windows XP 系統下編譯

移植版的 libuv:https://github.com/liigo/libuv-vc6 (支援VC6和XP,作者Liigo)。

我從一年前(大概2013年6,7月份)開始在業餘時間做這項移植工作,走走停停,陸續用了一兩個月的時間,才基本完成。這期間做了詳細的移植記錄,現在釋出出來,希望對某些人有用。就在昨天(2014年7月12日),我又把移植的代碼同步到最新的libuv(https://github.com/joyent/libuv)并釋出到Github上;但是之前的移植記錄沒有變更,或許在一定程度上已經部分失效了。

我(Liigo)當初做這項移植工作的原始意圖,是打算做一個封裝 libuv 的易語言支援庫。然而終究還沒有做。理由是沒有時間呢,缺乏需求呢,還是懶呢?我暫時不想下結論。

此移植版 libuv-vc6 的完整性和準确性尚未得到證明。但是我的tinyweb 已經基于該移植版編譯,并且在Windows XP系統下運作正常。我想這能證明至少它是“基本可用”的。

VC6系統檔案 MSWSOCK.H 編譯時報錯,說不認識“SOCKET”類型:

解決辦法,在前面增加 typedef unsigned int  SOCKET;

uv-win.h 報錯說 SRWLOCK 未定義,解決辦法是在前面增加:

//http://blog.csdn.net/xiewneqi/article/details/4787156

typedef struct SRWLOCK {

void* ptr;

} SRWLOCK, *PSRWLOCK;

uv.h 未定義 sockaddr_storage,解決辦法是在前面增加:

//http://stackoverflow.com/questions/1345109/why-sockaddr-storage-structure-defined-as-the-way-it-is-defined

struct sockaddr_storage {

short   ss_family;

char    __ss_pad1[6];

int64_t __ss_align;

char    __ss_pad2[112];

};

uv.h 報錯說辨別符 LPFN_CONNECTEX 文法錯誤,解決辦法:

将 uv-win.h 内所有 " PASCAL (*" 替換為 " (PASCAL *" 。

把 LPFN_CONNECTEX 的定義從 uv-win.h 複制到 uv.h 報錯行之前。

uv\src\winapi.h '_REPARSE_DATA_BUFFER'重定義,解決辦法:删除定義,或注釋掉

ULONG_PTR 未定義,加入 typedef unsigned int* ULONG_PTR;

uv\src\winapi.h 未定義LPOVERLAPPED_ENTRY?加入以下代碼:

typedef struct _OVERLAPPED_ENTRY {

  ULONG_PTR    lpCompletionKey;

  LPOVERLAPPED lpOverlapped;

  ULONG_PTR    Internal;

  DWORD        dwNumberOfBytesTransferred;

} OVERLAPPED_ENTRY, *LPOVERLAPPED_ENTRY;

缺少檔案iptypes.h? 在下面連結下載下傳後放到uv\src\win

http://stuff.mit.edu/afs/athena/astaff/project/opssrc/nmap-2.54BETA34/mswin32/IPTypes.h

ERROR_INVALID_REPARSE_DATA未定義?修改為 0x00001128

INVALID_FILE_ATTRIBUTES未定義?修改為 (DWORD)-1

'bad suffix on number'?把數值後面的ULL删除即可(10000000ULL -> 10000000)

EAI_BADFLAGS等EAI_*未定義?加入以下代碼:

#define EAI_AGAIN           WSATRY_AGAIN

#define EAI_BADFLAGS        WSAEINVAL

#define EAI_FAIL            WSANO_RECOVERY

#define EAI_FAMILY          WSAEAFNOSUPPORT

#define EAI_MEMORY          WSA_NOT_ENOUGH_MEMORY

#define EAI_NOSECURENAME    WSA_SECURE_HOST_NOT_FOUND

//#define EAI_NODATA        WSANO_DATA

#define EAI_NONAME          WSAHOST_NOT_FOUND

#define EAI_SERVICE         WSATYPE_NOT_FOUND

#define EAI_SOCKTYPE        WSAESOCKTNOSUPPORT

#define EAI_IPSECPOLICY     WSA_IPSEC_NAME_POLICY_ERROR

addrinfo/addrinfoW未定義?在uv.h前面加入以下代碼:

//http://msdn.microsoft.com/en-us/library/windows/desktop/ms737530(v=vs.85).aspx

typedef struct addrinfo {

  int             ai_flags;

  int             ai_family;

  int             ai_socktype;

  int             ai_protocol;

  size_t          ai_addrlen;

  char            *ai_canonname;

  struct sockaddr  *ai_addr;

  struct addrinfo  *ai_next;

} ADDRINFOA, *PADDRINFOA;

//http://msdn.microsoft.com/en-us/library/windows/desktop/ms737529(v=vs.85).aspx

typedef struct addrinfoW {

  int              ai_flags;

  int              ai_family;

  int              ai_socktype;

  int              ai_protocol;

  size_t           ai_addrlen;

  PWSTR            ai_canonname;

  struct sockaddr  *ai_addr;

  struct addrinfoW  *ai_next;

} ADDRINFOW, *PADDRINFOW;

WT_EXECUTELONGFUNCTION未定義?替換為 0x00000010

WT_EXECUTEONLYONCE未定義?替換為 0x00000008

in6_addr未定義?在錯誤提示行前加入以下代碼:

struct in6_addr {

  u_char  s6_addr[16];

};

FILE_FLAG_FIRST_PIPE_INSTANCE未定義?替換為 0x00080000

JOBOBJECT_EXTENDED_LIMIT_INFORMATION未定義?在前面加入以下代碼:

typedef struct _JOBOBJECT_EXTENDED_LIMIT_INFORMATION {

  JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation;

  IO_COUNTERS                       IoInfo;

  SIZE_T                            ProcessMemoryLimit;

  SIZE_T                            JobMemoryLimit;

  SIZE_T                            PeakProcessMemoryUsed;

  SIZE_T                            PeakJobMemoryUsed;

} JOBOBJECT_EXTENDED_LIMIT_INFORMATION, *PJOBOBJECT_EXTENDED_LIMIT_INFORMATION;

IO_COUNTERS未定義?在前面加入以下代碼:

typedef struct _IO_COUNTERS {

  ULONGLONG ReadOperationCount;

  ULONGLONG WriteOperationCount;

  ULONGLONG OtherOperationCount;

  ULONGLONG ReadTransferCount;

  ULONGLONG WriteTransferCount;

  ULONGLONG OtherTransferCount;

} IO_COUNTERS, *PIO_COUNTERS;

JOB_OBJECT_LIMIT_BREAKAWAY_OK等未定義?在前面加入以下代碼:

#define JOB_OBJECT_LIMIT_SCHEDULING_CLASS       0x80

#define JOB_OBJECT_LIMIT_PROCESS_MEMORY         0x100

#define JOB_OBJECT_LIMIT_JOB_MEMORY             0x200

#define JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION 0x400

#define JOB_OBJECT_LIMIT_BREAKAWAY_OK           0x800

#define JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK    0x1000

#define JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE      0x2000

NOINLINE未被識别?替換為

((ULONG_PTR) req->event_handle | 1) 文法錯誤? 改為 ((DWORD) req->event_handle | 1)

IPPROTO_IPV6未定義?替換為 41

IPV6_MULTICAST_HOPS未定義?替換為 10

IPV6_MULTICAST_LOOP未定義?替換為 11

WSAID_CONNECTEX未定義?替換為 {0x25a207b9,0xddf3,0x4660,{0x8e,0xe9,0x76,0xe5,0x8c,0x74,0x06,0x3e}}

JobObjectExtendedLimitInformation未定義?替換為 9

UnregisterWait,UnregisterWaitEx,RegisterWaitForSingleObject等未定義?在winapi.h中加入以下代碼:

typedef BOOL (WINAPI* sUnregisterWait) (HANDLE WaitHandle);

typedef BOOL (WINAPI* sUnregisterWaitEx) (HANDLE WaitHandle, HANDLE CompletionEvent);

typedef VOID (CALLBACK* WAITORTIMERCALLBACK) (PVOID lpParameter, BOOLEAN TimerOrWaitFired);

typedef BOOL (WINAPI* sRegisterWaitForSingleObject)

             (PHANDLE phNewWaitObject,

              HANDLE hObject,

              WAITORTIMERCALLBACK Callback,

              PVOID Context,

              ULONG dwMilliseconds,

              ULONG dwFlags);

然後在winapi.h/.c中參考原有代碼補充必需的代碼(定義函數指針變量、對其指派等),然後把對UnregisterWait的調用改為pUnregisterWait(其他類推)。

對于其他在連結時找不到符号的Windows API函數,一并做如上處理。對于GetProcessMemoryInfo函數要做特殊處理,在兩個系統DLL去找其實作函數位址:

-----------------------------------------------------

  //http://msdn.microsoft.com/en-us/library/windows/desktop/ms683219(v=vs.85).aspx

  pGetProcessMemoryInfo = (sGetProcessMemoryInfo)

    GetProcAddress(kernel32_module, "GetProcessMemoryInfo");

  if(pGetProcessMemoryInfo == NULL) {

    pGetProcessMemoryInfo = (sGetProcessMemoryInfo)

      GetProcAddress(GetModuleHandleA("Psapi.dll"), "GetProcessMemoryInfo");

  }

-----------------------------------------------------

找不到IPHlpApi.h?在這裡下載下傳:

https://docs.google.com/file/d/0B-VpknkVIm1fdnNabE1ScmlLazQ/edit?usp=sharing

為防止“重複定義IN6_ADDR”的編譯錯誤,需要在 #include "Iphlpapi/iphlpapi.h" 之前先 #define s6_addr 。

找不到psapi.h?在這裡下載下傳:

https://docs.google.com/file/d/0B-VpknkVIm1fU3pDRTBDM3JXOWM/edit?usp=sharing

GlobalMemoryStatusEx未定義?在winapi.h加入以下代碼:

typedef struct _MEMORYSTATUSEX {

  DWORD     dwLength;

  DWORD     dwMemoryLoad;

  DWORDLONG ullTotalPhys;

  DWORDLONG ullAvailPhys;

  DWORDLONG ullTotalPageFile;

  DWORDLONG ullAvailPageFile;

  DWORDLONG ullTotalVirtual;

  DWORDLONG ullAvailVirtual;

  DWORDLONG ullAvailExtendedVirtual;

} MEMORYSTATUSEX, *LPMEMORYSTATUSEX;

typedef BOOL (WINAPI* sGlobalMemoryStatusEx) (LPMEMORYSTATUSEX lpBuffer);

然後在winapi.h/.c中參考原有代碼補充必需的代碼(定義函數指針變量、對其指派等),然後把對GlobalMemoryStatusEx的調用改為pGlobalMemoryStatusEx。

IP_ADAPTER_UNICAST_ADDRESS_XP未定義?替換為IP_ADAPTER_UNICAST_ADDRESS,然後

#include "Iphlpapi/iptypes.h" (來源見上文)

因為該頭檔案中定義的IP_ADAPTER_UNICAST_ADDRESS實質上就是IP_ADAPTER_UNICAST_ADDRESS_XP。

wmemcmp未定義?把原代碼 wmemcmp(data_block->Signature, L"PERF", 4) 修改為:

memcmp(data_block->Signature, L"PERF", 4 * sizeof(TCHAR))

IF_TYPE_SOFTWARE_LOOPBACK未定義?替換為 24

'sin6_scope_id' : is not a member of 'sockaddr_in6'? 注釋掉 uv-win.h 内的宏定義 UV_PLATFORM_HAS_IP6_LINK_LOCAL_ADDRESS。

或者加入下面的定義:

struct liigo_sockaddr_in6 {

  short sin6_family;

  u_short sin6_port;

  u_long sin6_flowinfo;

  struct in6_addr sin6_addr;

  u_long sin6_scope_id;

};

#define sockaddr_in6 liigo_sockaddr_in6

調用swprintf參數過多?swprintf(path2, len + 3, fmt, pathw)改為swprintf(path2, fmt, pathw)

_set_invalid_parameter_handler是VC2005才加入到CRT函數,VC6裡面沒有。在libuv源代碼裡面直接注釋掉對該函數的調用即可。

_BitScanReverse未定義?在前面加入以下代碼,然後删除VC下對_BitScanReverse的調用,統一用GCC版代碼即可:

//http://stackoverflow.com/questions/355967/how-to-use-msvc-intrinsics-to-get-the-equivalent-of-this-gcc-code

#ifndef __GNUC__

static unsigned int __declspec(inline) popcnt( unsigned int x )

{

    x -= ((x >> 1) & 0x55555555);

    x = (((x >> 2) & 0x33333333) + (x & 0x33333333));

    x = (((x >> 4) + x) & 0x0f0f0f0f);

    x += (x >> 8);

    x += (x >> 16);

    return x & 0x0000003f;

}

static unsigned int __declspec(inline) __builtin_clz( unsigned int x )

{

    x |= (x >> 1);

    x |= (x >> 2);

    x |= (x >> 4);

    x |= (x >> 8);

    x |= (x >> 16);

    return 32 - popcnt(x);

}

static unsigned int __declspec(inline) __builtin_ctz( unsigned int x )

{

    return popcnt((x & -x) - 1);

}

#endif

另外一個模拟實作_BitScanReverse的思路也是可行的:http://bbs.csdn.net/topics/350202744

_InterlockedOr8未定義?重新實作uv__atomic_exchange_set如下:

static char __declspec(inline) uv__atomic_exchange_set(char volatile* target) {

  //return _InterlockedOr8(target, 1);

  __asm mov ecx, target

  __asm mov al, byte ptr [ecx]

  __asm lock or byte ptr [ecx], 1

}

_aligned_malloc, _aligned_free, 等函數未定義?自己實作這兩個函數,代碼如下:

static void* _aligned_malloc(unsigned int size, unsigned int align) {

  unsigned int rem;

  unsigned char *op, *np;

  op = (unsigned char*) malloc(size + align);

  if(op == NULL) return NULL;

  rem = (unsigned int)op % align;

  np = op + align - rem;

  *(np-1) = (unsigned char)align - rem;

  return np;

}

static void _aligned_free(void* p) {

  if(p) {

    unsigned char n = *((unsigned char*)p - 1);

    free((unsigned char*)p - n);

  }

}

InterlockedCompareExchangePointer在新版VC中是編譯器生成的函數,并不存在于kernel.dll裡面,而VC6顯然不可能生成該函數。解決辦法:

pInterlockedCompareExchangePointer = (sInterlockedCompareExchangePointer) GetProcAddress(kernel32_module, "InterlockedCompareExchange");

//VC6 always create x86 programs. 因為VC6總是生成32位程式,不可能調用64位版的函數。

缺少常量IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, IPV6_DROP_MEMBERSHIP?在前面加入如下代碼:

#ifndef IPPROTO_IPV6

#define IPPROTO_IPV6 41

#endif

#ifndef IPV6_MULTICAST_IF

#define IPV6_MULTICAST_IF 9

#endif

#ifndef IPV6_ADD_MEMBERSHIP

#define IPV6_ADD_MEMBERSHIP 12

#endif

#ifndef IPV6_DROP_MEMBERSHIP

#define IPV6_DROP_MEMBERSHIP 13

#endif

常量ERROR_INVALID_REPARSE_DATA沒定義?改成下面這樣:

4392