移植版的 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'重定義,解決辦法:刪除定義,或註釋掉/*defined(_MSC_VER) ||*/
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/*ERROR_INVALID_REPARSE_DATA*/
INVALID_FILE_ATTRIBUTES未定義?修改爲 (DWORD)-1/*INVALID_FILE_ATTRIBUTES*/
'bad suffix on number'?把數值後面的ULL刪除即可(10000000ULL -> 10000000)
EAI_BADFLAGS等EAI_*未定義?加入以下代碼:
/* Error codes from getaddrinfo() */
#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_EXECUTELONGFUNCTION*/
WT_EXECUTEONLYONCE未定義?替換爲 0x00000008/*WT_EXECUTEONLYONCE*/
in6_addr未定義?在錯誤提示行前加入以下代碼:
struct in6_addr {
u_char s6_addr[16];
};
FILE_FLAG_FIRST_PIPE_INSTANCE未定義?替換爲 0x00080000/*FILE_FLAG_FIRST_PIPE_INSTANCE*/
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未被識別?替換爲 /*NOINLINE*/
((ULONG_PTR) req->event_handle | 1) 語法錯誤? 改爲 ((DWORD) req->event_handle | 1)
IPPROTO_IPV6未定義?替換爲 41/*IPPROTO_IPV6*/
IPV6_MULTICAST_HOPS未定義?替換爲 10/*IPV6_MULTICAST_HOPS*/
IPV6_MULTICAST_LOOP未定義?替換爲 11/*IPV6_MULTICAST_LOOP*/
WSAID_CONNECTEX未定義?替換爲 {0x25a207b9,0xddf3,0x4660,{0x8e,0xe9,0x76,0xe5,0x8c,0x74,0x06,0x3e}}/*WSAID_CONNECTEX*/
JobObjectExtendedLimitInformation未定義?替換爲 9/*JobObjectExtendedLimitInformation*/
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/*IF_TYPE_SOFTWARE_LOOPBACK*/
'sin6_scope_id' : is not a member of 'sockaddr_in6'? 註釋掉 uv-win.h 內的宏定義 UV_PLATFORM_HAS_IP6_LINK_LOCAL_ADDRESS。
或者加入下面的定義:
/* The ws2tcpip.h header included in VC6 doesn't define the
* sin6_scope_id member of sockaddr_in6. We define our own
* version and redefine sockaddr_in6 to point to this one.
*/
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/*ERROR_INVALID_REPARSE_DATA*/