Feeling

    三人行,必有我师焉

   ::  :: 新随笔 :: 联系 ::  :: 管理 ::
  185 随笔 :: 0 文章 :: 392 评论 :: 0 Trackbacks

#

  最近很多人问我SWT Extension 项目上的那个不需要的JREExample是如何做出来的。我以前也会执着于这个问题,毕竟如果不依赖于JRE的话,就不需要为用户准备一个容量极大的安装包,但是这种做法看似有利,实则有利有弊。

就我所知,目前把Java程序编译成本机可执行程序的方法有两种,一种是GCJ,免费的,一种是Excelsior JET,商业的。我已经很久没有碰过GCJ了,因为当初用起来实在是非常麻烦,现在的版本如何,我不太清楚。我自己使用的是Excelsior JET,版本为3.7。有一点要注意的的是,Excelsior JET的后续版本好像已经不支持这个功能了,3.7是我所知的最后一个版本,能支持当前所有的Win32平台和早期的Linux(当前比较流行的Ubuntu不支持,因为内核版本过高,不过企业版Redhat没有问题)。不过我是很久以前从0day当下来的,由于0day仓储只保留一年,故现在已经找不到了,我自己的机器上也没有安装包了(有一次大意之下,把整个Download目录全给删掉了,事后悔之晚矣)。

Excelsior JET无非就是用自己的Runtime来代替JRE,只是比JRE更加灵活,根据Java程序具体的依赖来生成对应的Runtime。其实这个Runtime也挺大的,通常10M左右,不过比起JRE,那要小很多了。SWT Extension上的那个例子只有6M,是因为我用ASPack把所有的DLL文件全部压缩过了,体积小了一半。

就我的感觉,Excelsior JETGCJ更加灵活,也更好用,毕竟是商业版的东西,它的网站上曾经有例子将Eclipse 3.0编译成本机程序,不过我当初照着例子试了一遍,没有成功。Excelsior JET的编译过程极为耗时,我上大学的时候,当时机器只有128M内存,编译了一天JRE也没有完成,后来找同学借了根256的内存,这才得以完成。

JAVA代码编译成本机程序的弊端也是有的,那意味着你将无法在线升级,GCJ也许可以,但是Excelsior JET是绝对不行的,这是因为它最后一步要对所有DLL进行链接,如果更换了DLL文件,它会检测出来并报错。

各位看官如果哪位有兴趣,可以自行在网上查找Excelsior JET3.7或其他版本。由于安装包我自己也没有,故无法提供下载,见谅。

posted @ 2008-05-18 15:38 三人行,必有我师焉 阅读(6785) | 评论 (7)编辑 收藏

在SWT Extension中,引入了Function这个类。基本上所有的Win32 JNI库都有这个类,用来直接操纵Win32 的部分API。有了这个Class,我们不用编写JNI,就可以实现某些简单的,甚至是较复杂的Win32 API。这里我们就以EnumWindows这个API举例,看看怎么Java来执行这个Win32 API。
    private static final String FUNTION_ENUMWINDOWS = "EnumWindows";
    
private static final String USER32_LIB = "user32";
    
private static List windowsList = new ArrayList();
    
    
public static int[] enumWindows()
    {
        windowsList.clear();
        Callback callback 
= new Callback(Windows.class"enumWindowsProc"2);
        
int address = callback.getAddress();
        
if (address != 0)
        {
            
try
            {
                Function function 
= new Function(USER32_LIB, FUNTION_ENUMWINDOWS);
                function.invoke_I(address, 
0);
                function.close();
            } 
catch (Exception e)
            {
                SWT.error(SWT.ERROR_INVALID_ARGUMENT);
            }
            callback.dispose();
        }
        
int[] handles = new int[windowsList.size()];
        
for (int i = 0; i < windowsList.size(); i++)
            handles[i] 
= ((LONG) windowsList.get(i)).value;
        
return handles;
    }

    
private static int enumWindowsProc(int hwnd, int lParam)
    {
        windowsList.add(
new LONG(hwnd));
        
return 1;
    }
EnumWindows是用来遍历Windows窗口的API,它需要传入一个返回boolean值的callback的地址作为参数。实际上在C里面,一个boolean值无非就是是否非0,如果为0,则为false,不为0,则为true。我们只需要new 一个function实例,传入这个API所在的Lib和API名字,然后执行invoke方法就OK了,在Function里面,可以最多执行含有4个简单类型参数的API。

让我们再来看看FindWindowEx这个API,它需要传入2个int变量和2个字符串指针,根据SWT的设计,我们是可以将Java的字符串转换为指针的,因此通过Function我们也可以实现这个API:
    private static final String FUNTION_FINDWINDOWEX = Extension.IsUnicode ? "FindWindowExW"
            : 
"FindWindowExA";
    
private static final String USER32_LIB = "user32";
    
    
public static int findWindowEx(int parent, int hwndChildAfter, String className,
            String windowName)
    {
        
int result = 0;
        
int lpClassName = 0;
        
int lpWindowName = 0;
        
int hHeap = Extension.GetProcessHeap();

        
if (className != null)
        {
            TCHAR buffer 
= new TCHAR(0, className, true);
            
int byteCount = buffer.length() * TCHAR.sizeof;
            lpClassName 
= Extension.HeapAlloc(hHeap, Extension.HEAP_ZERO_MEMORY, byteCount);
            Extension.MoveMemory(lpClassName, buffer, byteCount);
        }
        
if (windowName != null)
        {
            TCHAR buffer 
= new TCHAR(0, windowName, true);
            
int byteCount = buffer.length() * TCHAR.sizeof;
            lpWindowName 
= Extension.HeapAlloc(hHeap, Extension.HEAP_ZERO_MEMORY, byteCount);
            Extension.MoveMemory(lpWindowName, buffer, byteCount);
        }

        
try
        {
            Function function 
= new Function(USER32_LIB, FUNTION_FINDWINDOWEX);
            result 
= function.invoke_I(parent, hwndChildAfter, lpClassName, lpWindowName);
            function.close();
        } 
catch (Exception e)
        {
            SWT.error(SWT.ERROR_INVALID_ARGUMENT);
        }
        
if (lpClassName != 0) Extension.HeapFree(hHeap, 0, lpClassName);
        
if (lpWindowName != 0) Extension.HeapFree(hHeap, 0, lpWindowName);
        
return result;
    }
其实像这种简单参数类型的API,Win32 里还有很多,我们完全不必为其专门编写JNI,只需使用熟悉的Java即可。虽然不是调用全部的API,但大部分常用的API都是没有问题的,关键是如何灵活运用。现在的大型商业RCP应用中,其实多多少少都参和了JNI,用于提升对用户的友好性和软件的执行性能,毕竟Java天生就是客户端开发的矮子。对于JNI,我们既不能一味排斥,也不能滥用,要把握一个平衡点,使之成为Java客户端开发的利器。
posted @ 2008-05-11 19:31 三人行,必有我师焉 阅读(5823) | 评论 (7)编辑 收藏

网上有一篇关于JNI中文问题的文章,写得很详细,http://www.vckbase.com/document/viewdoc/?id=1611

我在这里主要是想说说我碰到的一些问题,并且希望能从各位老大身上获得答案。

因为一直从事Java编程,基本上没有涉及过C++的开发,最近因为开源项目SWT Extension,不得已需要用JNI来实现一些系统Native功能。但是总是需要一些Java字符串对应C++的字符串的问题。一边情况下我都是使用SWT的TCHAR来解决问题,少部分情况需要传递Java String到JNI。然而少部分的这些Case总是在某些问题下出现乱码或者异常。我一直使用的是网上比较流行的中文编码解决方案:
char* jstringToNative( JNIEnv  *env, jstring jstr )
{
  
int length = env->GetStringLength(jstr );
  
const jchar* jcstr = env->GetStringChars(jstr, 0 );
  
char* rtn = (char*)malloc( length*2+1 );
  
int size = 0;
  size 
= WideCharToMultiByte( CP_ACP, 0, (LPCWSTR)jcstr, length, rtn,(length*2+1), NULL, NULL );
  
if( size <= 0 )return NULL;
  env
->ReleaseStringChars(jstr, jcstr );
  rtn[size] 
= 0;
  
return rtn;
}

jstring nativeTojstring( JNIEnv
* env, char* str )
{
  jstring rtn 
= 0;
  
int slen = strlen(str);
  unsigned 
short * buffer = 0;
  
if( slen == 0 )
    rtn 
= env->NewStringUTF( str ); 
  
else
  {
    
int length = MultiByteToWideChar( CP_ACP, 0, (LPCSTR)str, slen, NULL, 0 );
    buffer 
= (unsigned short *)malloc( length*2 + 1 );
    
if( MultiByteToWideChar( CP_ACP, 0, (LPCSTR)str, slen, (LPWSTR)buffer, length ) >0 )
      rtn 
= env->NewString(  (jchar*)buffer, length );
  }
  
if( buffer )
  free( buffer );
  
return rtn;
}

一般情况下,这两个函数能够很好的工作。但是在读写注册表时,如果一个key的名字或者value的名字中包含了中文,jstringToNative的解决方案是不正确的,我在网上查了一下其它的关于Java访问注册表的开源项目,发现虽然它们都对字符串进行了处理,但依然存在着中文问题。我进行了数次尝试,但都没有成功。最后到了已经绝望的时候,用开头我提到的那篇文章中里说的最不可能用到的方法将问题成功地解决了:
char* jstringToNative( JNIEnv  *env, jstring jstr )
{
  
const char* pstr = env->GetStringUTFChars(jstr, false);
  
int nLen = MultiByteToWideChar( CP_UTF8, 0, pstr, -1, NULL, NULL );
  LPWSTR lpwsz 
= new WCHAR[nLen];    
  MultiByteToWideChar( CP_UTF8, 
0, pstr, -1, lpwsz, nLen );
  
int nLen1 = WideCharToMultiByte( CP_ACP, 0, lpwsz, nLen, NULL, NULL, NULL, NULL );    
  LPSTR lpsz 
= new CHAR[nLen1];
  
int size = 0;
  size 
= WideCharToMultiByte( CP_ACP, 0, lpwsz, nLen, lpsz, nLen1, NULL, NULL );
  
if( size <= 0 ){
      delete [] lpwsz;
      
return NULL;
  }
  env
->ReleaseStringUTFChars(jstr, pstr );
  delete [] lpwsz;
  
return lpsz;
}
问题虽然解决了,但是我却不求甚解,为什么直接通过env拿到unicode字串,然后转成多字节串不行,但是通过env拿到utf-8字串,然后转成unicode字串,再将这个unicode字串转成多字节串就能工作?

如果大家有兴趣的话,不妨试试,用JNI调用RegOpenKeyEx这个API,就能验证我说的这个Case。哪位老大对JNI比较在行的话,可以在评论中告诉我,不甚感激。
posted @ 2008-05-04 13:17 三人行,必有我师焉 阅读(4022) | 评论 (2)编辑 收藏

Glossy 效果,顾名思义,就是玻璃质的光泽透明的效果,在Windows平台下的应用越来越广泛,从Media Player10开始,它的button已经运用上了此效果。现在但凡是微软新发布的软件,十有八九都带有glossy特效。Glossy 效果使用了Gdi Plus的API,因此在Win98和Win2000下,必须安装gdiplus.dll才能使用,此动态链接库不大,只有700多K,但是想要在自己的应用程序中画出绚丽多彩的效果,那是少不了这个小东西的。关于Glossy效果的描述,可以参见CodeProject上的一片文章WindowsVistaRenderer,http://www.codeproject.com/KB/vista/WindowsVistaRenderer.aspx

Glossy特效有一个重要的组成部分,就是椭圆的光晕,此效果在Linux下可能并没有实现(并未下定论,我还没有深入研究过Linux下的图形库), 所以SWT的Gdip并没有将其公开出来,而是放入custom的API里,也就是说,你可以自行调用此效果的API,但是SWT并不负责为你提供封装, 因此你并不能使用GC来实现该特效,这对我们的界面开发极为不利,自己调用Gdip的API,繁琐不说,还很容易导致JVM退出。

为了能够方便的使用GC来画出此特效,我们不得不采用一些非常规的手段:反射。利用反射我们可以拿到GC的一些内部数据,在这个地方,我们只需要拿到GCData就可以,它包含了画图所需要具备的元素。Glossy效果需要使用PathGradientBrush,我们把这个刷子赋给GCData,就可以使用GC来画出glossy特效了。
 1 public class GCExtension {
 2 
 3     private GC gc;
 4 
 5     private GCData data;
 6 
 7     public GCExtension(GC gc) {
 8         this.gc = gc;
 9         data = getGCData(gc);
10     }
11 
12     public void fillGradientPath(Path path, float[] centerPoint,
13             Color centerColor, int centerColorAlpha, Color[] surroundColors,
14             int[] surroundColorAlphas) {
15         if (gc == null || gc.handle == 0)
16             SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
17         if (path == null || centerPoint == null || centerColor == null
18                 || surroundColorAlphas == null)
19             SWT.error(SWT.ERROR_NULL_ARGUMENT);
20         if (path.handle == 0 || centerPoint.length < 2
21                 || centerColor.handle == 0
22                 || surroundColors.length != surroundColorAlphas.length)
23             SWT.error(SWT.ERROR_INVALID_ARGUMENT);
24         for (int i = 0; i < surroundColors.length; i++) {
25             if (surroundColors[i] == null || surroundColors[i].handle == 0)
26                 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
27         }
28 
29         int brush = Gdip.PathGradientBrush_new(path.handle);
30         if (brush == 0)
31             SWT.error(SWT.ERROR_NO_HANDLES);
32         PointF point = new PointF();
33         point.X = centerPoint[0];
34         point.Y = centerPoint[1];
35         Gdip.PathGradientBrush_SetCenterPoint(brush, point);
36 
37         int colorRef = centerColor.handle;
38         int rgb = ((colorRef >> 16& 0xFF| (colorRef & 0xFF00)
39                 | ((colorRef & 0xFF<< 16);
40         int color = Gdip.Color_new(centerColorAlpha << 24 | rgb);
41         if (color == 0)
42             SWT.error(SWT.ERROR_NO_HANDLES);
43         Gdip.PathGradientBrush_SetCenterColor(brush, color);
44         Gdip.Color_delete(color);
45 
46         int[] colors = new int[surroundColors.length];
47         for (int i = 0; i < surroundColors.length; i++) {
48             colorRef = surroundColors[i].handle;
49             rgb = ((colorRef >> 16& 0xFF| (colorRef & 0xFF00)
50                     | ((colorRef & 0xFF<< 16);
51             colors[i] = Gdip.Color_new(surroundColorAlphas[i] << 24 | rgb);
52             if (colors[i] == 0)
53                 SWT.error(SWT.ERROR_NO_HANDLES);
54         }
55         Gdip.PathGradientBrush_SetSurroundColors(brush, colors,
56                 new int[] { colors.length });
57         for (int i = 0; i < surroundColors.length; i++) {
58             Gdip.Color_delete(colors[i]);
59         }
60         data.gdipBrush = brush;
61         boolean advanced = gc.getAdvanced();
62         if (!advanced)
63             gc.setAdvanced(true);
64         int mode = Extension.GetPolyFillMode(gc.handle) == Extension.WINDING ? Gdip.FillModeWinding
65                 : Gdip.FillModeAlternate;
66         Gdip.GraphicsPath_SetFillMode(path.handle, mode);
67         Gdip.Graphics_FillPath(data.gdipGraphics, data.gdipBrush, path.handle);
68         if (!advanced)
69             gc.setAdvanced(false);
70         if (data.gdipBrush != 0) {
71             Gdip.PathGradientBrush_delete(data.gdipBrush);
72             data.gdipBrush = 0;
73         }
74     }
75 
76     private GCData getGCData(GC gc) {
77         GCData data = null;
78         try {
79             Object obj = null;
80             Field field = gc.getClass().getDeclaredField("data");
81             if (field != null) {
82                 field.setAccessible(true);
83                 obj = field.get(gc);
84             }
85             if (obj != null && obj instanceof GCData)
86                 data = (GCData) obj;
87         } catch (Exception e) {
88 
89         }
90         return data;
91     }
92 }

特效截图:

posted @ 2008-04-30 15:54 三人行,必有我师焉 阅读(3540) | 评论 (3)编辑 收藏

SWT Win32 Extension写到现在的状况,在win32 natvie上面已经没有太多花样了,常用的一些功能我都已经做得差不多了,现在主要是做一些自定义的控件,就目前的进度,还只是完成了Shell,Menu,ToolBar 3个部分,还有很多内容可以慢慢完善。不过自从发布了自定义的菜单以后,SWT Win32 Extension的用户群大增,也对我提出了更高的要求。不过现在的主要任务是实现功能,因此代码的质量上肯定是差了点。现有的接口都是我自己通过Example的需求来加的,以后等功能做的完善上,再将现有的架构进行较大的重构,我想应该是一个不错的步骤。毕竟就我一个人做这个东西,还要兼职写Example,Document,测试,网站维护,虽然每天都在加班加点,但还是感觉时间不够用。每天都有用户发邮件来催进度,所以维护这个项目现在真的是让我废寝忘食了,但总体来说还是物有所值,毕竟辛辛苦苦的努力,还是有所回报的。开源嘛,本来就是一种奉献精神,回馈社会,让所有人都来分享自己的成果。

这些天一直忙着写新的Feature,今天写的差不多了,于是回过头来整理Example。本来上个版本我就想接管Eclipse Native的菜单,不过没能如愿,因为Eclipse的菜单都是LazyLoad的。今天又尝试了一下,终于成功的实现了这个功能。做完了才知道其实很简单,所遇到的重重障碍只不过是因为自己的框架老是蹦出新的bug。唉,自己测试自己开发的东西总是有盲点存在,实在是无能为力呀。

从本质上来说,我自定义的菜单和标准菜单控件的代码及事件上的实现基本一致,所以接管Eclipse原生的菜单并不是一件很难的事情,当自定义的菜单接收到一个事件的时候,只需将这个事件转发给Eclipse的原生菜单就好了,一切就是这么简单。重点就是Notify SWT.Selected 和 SWT.Show 事件,前者用来触发Action的行为,后者用来触发Eclipse原生菜单的LazyLoad。

截图如下:
posted @ 2008-04-20 20:37 三人行,必有我师焉 阅读(3371) | 评论 (4)编辑 收藏


The idea is from DotnetMagic, I copied its style, but different implement methods. DotnetMagic uses shell self message circulation, but I use swt event listener. I want to implement the custom style menu for all platforms initially, but it's impossible. It has to use a lot of advanced OS funcitions. If I use pure swt public functions, I can't implement  some features. For example, if I click the menu, the window shell will be deactivated, I click window shell's titlebar, swt event manager doesn't send messages to me. I must use WND Message hook to deal these messages. So I have to add the feature into SWT Win32 Extension, but not a independent project.

Some beautiful snapshots:


Office 2003 Style Menu


VS 2005 Style Menu


Project Download: http://www.swtui.cn/downloads/org.eclipse.swt.win32.extension.zip
JNLP Online Demo: http://www.swtui.cn/jws/example.jnlp

If you don't have installed Java Runtime, you can access it via http://www.swtui.cn/downloads/org.eclipse.swt.win32.extension.example.independence_native.zip

Eclipse Plugin Update Site URL: http://www.swtui.cn/update

 

posted @ 2008-04-02 11:25 三人行,必有我师焉 阅读(3129) | 评论 (5)编辑 收藏

Add Flash Control to SWT Win32 Extension, now you can check out the latest code from CVS and run it.

Project Main Page: http://www.swtui.cn

cvs -d:pserver:anonymous@feeling.cvs.sourceforge.net:/cvsroot/feeling login



Flash Control Listener   


Flash Control Hook Interceptor

 

posted @ 2008-03-14 22:00 三人行,必有我师焉 阅读(2650) | 评论 (0)编辑 收藏

在SWT Win32 Extension 中新添加了窗口系统菜单管理功能,现在可以自定义窗口系统菜单了。
posted @ 2008-03-10 20:38 三人行,必有我师焉 阅读(2262) | 评论 (0)编辑 收藏

窗口是一个应用程序的门面,有一个好看的窗口,用户的评价自然也会大大提高,经过几天的努力,终于在SWT Win32 Extension中加入了自定义的Window Theme,当然目前只完成了一些基本的实现,高级实现依然有待于研究。


Windows XP Theme


Custom Theme

  目前已完成了任意SWT Shell wrapper,系统菜单管理,不规则边框的切边等功能,尚未完成的功能还有很多,比如窗口菜单。未来准备加入用户自定义Theme的接口,只要符合规范都可以进行自动切换。由于功能仍然还有很多地方尚未完成,暂时还没有提供打包下载,有兴趣的话,可以从项目CVS上Checkout出来看看效果。

cvs -d:pserver:anonymous@feeling.cvs.sourceforge.net:/cvsroot/feeling login
posted @ 2008-01-15 14:25 三人行,必有我师焉 阅读(2805) | 评论 (0)编辑 收藏

做Java UI的人应该都很熟悉Look and Feel,Swing拥有的这个功能的确是相对于SWT的一个巨大优势。不过SWT的GC也可以画出自己的UI,前提是这个控件是Custom的,而不是系统级的。Eclipse Presentation 就是对eclipse本身UI提供的一个扩展。本来我还对这个扩展很友好的,不过现在觉得真是一锅粥里掉了个老鼠屎,完全变了味道。

由于闲来无视,把一个1年半前一个德国人写的Eclipse VS L&F Plugin down了下来,无奈bug太多,用户体验也和我个人感觉不一致,索性就把代码check out 出来,作了一些本地修改。不过让我吐血的地方就是,如果我从eclipse default L&F 切换到这个L&F,就问题一堆,从其它的L&F切换就没有问题。Debug了半天,毫无进展,只是发现很多地方都是Null Exception。于是把Eclipse 2.2 L&F的代码翻了出来,经过仔细对照,还是没有找到解决的方案,我就感觉怪怪的,也不知道哪儿出了问题,觉得大概是自己没有把Presentation的代码吃透的原因。于是又回头开始一行行的从有问题的代码处开始Debug,看看我的代码和2.2L&F的代码在运行时到底有什么区别,终于功夫不负有心人,我发现这个代码片断:
    /**
     * Sets the minimized state for this stack. The part may call this method to
     * minimize or restore itself. The minimized state only affects the view
     * when unzoomed.
     *
     * This implementation is specific to the 3.3 presentation's
     * min/max story; otherwise it just forwards the call.
     
*/

    
public void setMinimized(boolean minimized) {
        
// 'Smart' minimize; move the stack to the trim

        Perspective persp = getPage().getActivePerspective();
        
if (Perspective.useNewMinMax(persp)) 
{
原来3.3的L&F做了专门处理,而我的代码和3.3是一致的,和 2.2L&F是不一致的,不过我找了半天,也没有在2.2L&F的代码里找出异样之处,晕的不行。唯一的线索就是
boolean useNewMinMax = preferenceStore.getBoolean(IWorkbenchPreferenceConstants.ENABLE_NEW_MIN_MAX);

可恶的是,IWorkbenchPreferenceConstants.ENABLE_NEW_MIN_MAX 这个静态常量是无法在项目里找到eclipse自身的引用。2.2L&F plugin里也没有,该死的eclipse肯定是写死在代码里了。于是翻出editplus,对eclipse ui workbench的代码多文件搜索了一把,然后开始吐血,它居然把所有的L&F的配置都写在UI Workbench的Preference Page里,这还算什么插件,根本就没有把扩展的接口给出来,还是很重要的一个配置。其实2.2L&F以前只是它的一个内部package,后来重构成一个plugin,3.0L&F到现在依然还是一个package。不过我想既然2.2L&F做成插件了,怎么也改和系统独立开来呀,现在这样就成了一个半吊子的plugin.

Eclipse终于被我bs了一把^_^,林子大了,什么鸟都有。


改过之后L&F自己觉得好用了不少

 

posted @ 2007-12-25 19:19 三人行,必有我师焉 阅读(3549) | 评论 (5)编辑 收藏

仅列出标题
共9页: 上一页 1 2 3 4 5 6 7 8 9 下一页 
GitHub |  开源中国社区 |  maven仓库 |  文件格式转换