http://blog.csdn.net/null_shadow/article/details/1807797
前面几章谈到了Cygwin的安装, 培植, 编译, 运行 等基本步骤, 本章将要讨论整个平台代码移植中的重点 : 程序的交互. (Cygwin <-> Win32)
根据调用的方式, 我把交互简单分为下面2类:
1. Cygwin程序调用Win32-Function :
据前所述, Cygwin平台下是架构在Win32平台上的一个子平台, 所以其本身就有调用Win32-API的能力.(可以用VC6提供的Dependency-Walker打开/bin/cygwin1.dll查看,会发现其主要依赖于KERNEL32.dll), 所以在Cygwin里面, 如果想调用Win32-API如 : MessageBoxA(...), 只需要在Source里简单的include <windows.h> 再调用就可以了, 用Cygwin-GCC编译测试, 可以看到程序正常运行.
所以, Cygwin调用Win32程序没有太大的困难, 至少知道是有途径可以调用成功的.
2. Win32程序调用Cygwin-Function :
Cygwin官方并不提供这种直接的Win32调用Cygwin-DLL里面Function的能力. 原因大致是: 依赖于Cygwin平台的程序在入口的时候(特别的, 是在main-function执行之前),需要由Cygwin进行必要的Init. 如果是以Win32程序作为入口点, 则没办法由Cygwin平台直接提供这样的支持.(因为Cygwin只提供基于Cygwin平台的编译器)
不过,非官方的提供了这样的途径用于Win32-EXE调用Cygwin-DLL. 方法有静态调用和动态调用两种之分.
下面主要介绍的是动态调用 (很遗憾, 静态调用没有测试成功, 有调用成功的朋友敬请提出解决方案:)~)
动态调用的过程大致如下:
1.Make sure you have 4K of scratch space at the bottom of your stack.
-- Do this before or just at your program starting.
2.Dynamic - loading Cygwin1.dll, and call the method : "cygwin_dll_init" to do the Init. work.
3.Dynamic - loading the function u wanna in Cygwin-DLL.
即确保你的堆栈底部有大约4K的空间 (其实是Cywin里面有个叫cygtls的结构体决定的, 一般不会超过4KB). 这4KB空间即是Cygwin平台运行时,其做一些操作所需要的一个空间, 所以必须确保这块空间留给Cywin, 而不是应用程序本身. 然后, 动态调用Cygwin1.dll里面的cygwin_dll_init方法做初始化动作. 到此, 初始化的动作就全部完成了. 接下来, 就可以动态调用任意你想要的在Cygwin-DLL里面的Function了.
在实现方法上, 由Max Kaehn提供了一种简单的方法实现. (见下面的参考链接-2)
主要的代码贴在这里: (由Max Kaehn提供, 再由我的一位同事XiaoHu精简改写 ^_^ ~~)
padding.cpp :
#include < stdio.h >
#include < iostream >
#include < vector >
#include " padding.h "
using std::cout;
using std::cerr;
using std::endl;
padding * padding::_main = NULL;
DWORD padding::_mainTID = 0 ;
padding::padding ()
{
_main = this;
_mainTID = GetCurrentThreadId ();
_end = _padding + sizeof (_padding);
char *stackbase;
#ifdef __GNUC__
__asm__ (
"movl %%fs:4, %0"
:"=r"(stackbase)
);
#else
__asm
{
mov eax, fs:[4];
mov stackbase, eax;
}
#endif
_stackbase = stackbase;
// We've gotten as close as we can to the top of the stack. Even
// subverting the entry point, though, still doesn't get us there-- I'm
// getting 64 bytes in use before the entry point. So we back up the data
// there and restore it when the destructor is called:
if ((_stackbase - _end) != 0)
{
size_t delta = (_stackbase - _end);
_backup.resize (delta);
memcpy (&(_backup[0]), _end, delta);
}
}
padding:: ~ padding ()
{
_main = NULL;
if (_backup.size ())
{
memcpy (_end, &(_backup[0]), _backup.size ());
}
}
void padding::check ()
{
if (_main == NULL)
throw std::runtime_error ("No padding declared!");
if (_mainTID != GetCurrentThreadId ())
throw std::runtime_error ("You need to initialize cygwin::connector "
"in the same thread in which you declared the "
"padding.");
if (_main->_backup.size ())
cout << "Warning! Stack base is "
<< static_cast<void *>(_main->_stackbase)
<< ". padding ends at " << static_cast<void *>(_main->_end)
<< ". Delta is " << (_main->_stackbase - _main->_end)
<< ". Stack variables could be overwritten!" << endl;
}
cygload.cpp :
#include < stdio.h >
#include < vector >
#include " padding.h "
HMODULE hModule = NULL;
void InitCygwin()
{
if(hModule == NULL)
{
hModule = LoadLibrary(TEXT("cygwin1.dll"));
void (*cygwin_dll_init)() =(void (*)())GetProcAddress(hModule, "cygwin_dll_init");
cygwin_dll_init();
}
}
void FreeCygwin()
{
if(hModule != NULL)
{
FreeLibrary(hModule);
hModule = NULL;
}
}
extern " C " void CygwinStartUp( void ( * main)())
{
padding padding;
InitCygwin();
main();
FreeCygwin();
}
将上面的代码用VS.NET编译成CygLoad.dll.
下面是Main-Function开始时调用的代码(用VB.NET编写)
Friend Class Program
' Nested Types
Public Delegate Sub MainEntryMethod()
' Methods
<DllImport("CygLoad.dll")> _
Public Shared Sub CygwinStartUp(ByVal mainEntry As MainEntryMethod)
End Sub
<STAThread()> _
Friend Shared Sub Main()
Program.CygwinStartUp(New Program.MainEntryMethod(AddressOf Program.MainEntry))
End Sub
Friend Shared Sub MainEntry()
Application.EnableVisualStyles()
Application.SetCompatibleTextRenderingDefault(False)
Application.Run(New MainForm())
End Sub
End Class
上面的代码即是做初始化工作, 完成后, 就可以在你的代码的任意地方动态调用function了, 如下:
{
void (*lpFunctionInCygwin)(char*);
HMODULE hExport = LoadLibrary(TEXT("FunctionInCygwin.dll")); //Cygwin编译的实际的DLL
lpFunctionInCygwin= (void(*)(char*))GetProcAddress(hExport,"FunctionInCygwin");
lpFunctionInCygwin(str); //动态调用该DLL里面的FunctionInCygwin方法.
FreeLibrary(hExport);
}
到此调用全部Over. 在你的代码里面直接调用FunctioninCygwin即可(实际上是动态调用Cygwin编译的FunctionInCygwin.dll 里面的 FunctionInCygwin(char *) 方法.
参考:
http://cygwin.com/faq/faq.programming.html#faq.programming.msvs-mingw
http://cygwin.com/ml/cygwin-patches/2005-q2/msg00102.html