平台移植 - 4 (Cygwin与Win32程序的交互)

来源:互联网 发布:vb.net打开指定文件夹 编辑:程序博客网 时间:2024/05/11 21:20

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 <windows.h>
#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 <windows.h>
#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编写)

Imports System.Runtime.InteropServices

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 FunctionInCygwin(char *str)
{
    
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