Feeling

    三人行,必有我师焉

   ::  :: 新随笔 :: 联系 ::  :: 管理 ::
  80 随笔 :: 0 文章 :: 359 评论 :: 0 Trackbacks

2008年1月15日 #

Java应用定制工厂(以下简称为JCB,Java Customization Builder)是一个针对Java轻量级桌面应用进行精简优化的小工具,使用它可以精简你的jar包,并自动生成一个精简的JRE,也可以使用它生成一个Exe启动引导程序,并且能够对你的Java应用自动做Pack200和Unpack200处理。使用本工具定制的Java桌面应用通常不会超过10M(包含JRE),SWT客户端程序相对于Swing客户端程序更小,一般不会超过5M。

JCB是一个Java应用,所以目标机器上必须安装1.5以上版本的JDK用以启动JCB,但是JCB可以用来精简1.4版的JRE,并且JRE1.4精简后的体积远小于1.5以上的版本。

1.新建JCB项目
精简JRE的步骤比较繁琐,有可能精简失败,为了不重复之前的步骤,JCB提供一个项目文件用来保存精简配置信息,扩展名为jcprj。这里我们创建一个项目,名为JCB


Wizard需要输入一个工程名和指定工程位置,至于下面的应用程序位置和定制JRE位置由JCB自动指定,这儿显示出来仅供参考。

此时最好Ctrl+S保存一下项目,否则退出后你之前的配置信息会全部丢失,因为你并没有制定一个可用的项目配置文件。

2. 配置JCB项目


首先指定项目需要的jar文件,然后依次选择项目的main class,启动路径默认为空,一般来说无需指定。然后设定应用程序参数和虚拟机参数。最后选定需要精简的JRE,JCB当前支持1.4-1.7版本的JRE,未来可能会支持更高版本的JRE。

右下角有2个单选按钮:全部重新运行和增量运行。全部重新运行就会放弃之前的运行结果,增量运行就是会保留以前的运行结果。

然后点击“以Verbose模式运行”按钮。Verbose模式运行Java程序,会显示JVM加载的全部类信息,JCB需要这些类信息进行JRE的精简,所以请尽可能的把应用所有的功能尽可能的跑一遍,跑的越全面,导致精简出错的可能性就越低。



Verbose运行结果,这个页面的显示信息仅供参考,无实际用处。

3. 分析项目的类依赖项


分析类依赖模式有2个选项:重新完全分析和增量分析。完全分析会花费较多的时间。当使用verbose模式增量运行后,可以使用增量模式分析类依赖项,这样可以节约大量的时间。类依赖分析会反编译所有运行的类,分析类引用关系,但是无法获取Class.forName这类动态类加载信息,所以需要Verbose模式运行的尽量全面,以避免这些动态加载的类的缺失。

为什么需要分析类依赖关系呢?因为不同的操作系统,不同的硬件配置,JRE可能会采取策略模式加载不同的类,或者一些异常,Verbose模式一般不会加载,这样换个硬件环境,仅仅使用Verbose模式的类可能会导致ClassNotFound这样的异常,导致Java程序崩溃。


4. 精简JRE


精简JRE有两种模式:使用Verbose运行结果和使用类依赖分析结果。前者只包含Verbose分析出来的类,精简出来的JRE包很小,但是基本不具备跨平台性。所以一般来说推荐选择后者。

如果你的程序包含Swing的客户端,并且比较复杂的话,最好选中包含Swing选项。因为Swing的设计完全是动态化的加载,全部使用Class.forName方式,类依赖分析对Swing是无效的。当然选中该选项后,JRE的体积会增加许多。比较好的解决方案,是使用SWT替代Swing进行开发,或者尽量把你的程序跑全面,包括各种异常界面都跑出来。

右下角有两个按钮,是用来自定义类和资源文件的,比如移除JAR包的MD5文件或者无用的文件。或者测试运行发现ClassNotFound异常,手动把缺少的类加进去,然后JCB会自动运行增量类依赖分析加载所有可能需要的类。

选择左上角的“精简Jar包”按钮,就可以对JRE进行精简了,精简完毕后可以点击“查看精简结果”按钮进行查看。

5.定制JRE


上图显示了JRE精简结果,JCB会自动分析所有的Class,生成精简版JRE,包括需要的JAR,DLL和资源文件。一般来说精简出来的JRE,普通功能都能正确完成,但是不排除有些功能不能正常使用,比如缺少某个资源文件或者DLL,需要手工添加。

为了保证精简的正确性,你需要进行运行测试,这一步是必须的,而且最好和Verbose运行模式一样,把所有的功能都跑一遍,确认精简无误。



如果测试运行有误的话,请根据运行错误报告进行分析,如果缺少类,请使用Verbose模式重新运行相应的功能,或者在步骤四手工添加需要的类,然后重新生成依赖的JRE。如果缺少相关的DLL或者资源文件,也请手工添加,并且取消步骤四的“清理工作区选项”,否则每次精简JRE都需要重新手工添加。

到此为止,精简JRE部分就算全部完成了,你最好使用Ctrl+S保存一下结果,以避免下次重做项目。

JCB项目下载地址:http://www.sourceforge.net/projects/jcb
posted @ 2013-03-03 17:25 三人行,必有我师焉 阅读(1690) | 评论 (9)编辑 收藏

1. 40亿个无符号整数,找出一个不在这40亿个整数中的数。可以换个方向思考, 99个小于100的数,找出一个不在这99个数中的小于100的数。
首先把这99个数分为10组,按高位为0-9分,然后计算每组的数量,数量最少的那个肯定就是缺失的那个,然后递归……找最少的那个,组合起来的数肯定是缺失的。答案是按位运算找,和这个类似。

2. 43亿个无符号整数,找出一个重复的整数。也就是101个小于100的数,找出重复的那个数来。
首先把这99个数分为10组,按高位为0-9分,然后计算每组的数量,数量最多的那组,肯定有重复的,一次类推找第二位……
posted @ 2012-11-24 22:21 三人行,必有我师焉 阅读(124) | 评论 (0)编辑 收藏

When a object creates a new object, please use the dependency.

When a object just uses a object, please use the association. 
posted @ 2012-11-19 13:16 三人行,必有我师焉 阅读(76) | 评论 (0)编辑 收藏

comparator 

Decorator Pattern and Adapter Pattern have the same alias name: wrapper. But they face different aspects. Decorator pattern changes the object function, but the adapter pattern changes the interface.

The typical decorator pattern is the java OutputStream, you can use the BufferedOutputStream to wrap it, then get the extra function.
The typical adapter pattern in the BIRT is the ElementAdapter, it can convert any object to an other object.

Decorator pattern must extend the class which you want to wrap, but the adapter class must implements the interface using by the client.


FlyWeight pattern extracts the same part of some different objects, and the part doesn't be changed when these objects changed. String class uses the FlyWeight pattern, jface 
ImageRegistry also uses it. 
FlyWeight can have a interface to get external data, and change the external data's status, but FlyWeight internal status shouldn't be changed.

The Collections.sort() method implementation contains template method design pattern and strategy design pattern, but it doesn't contain the visitor design pattern. The Collections.sort() method uses the merge sort algorithm, you can't change it, but you can change the comparator logic, it's one step of the sort algorithm. So it's a template method pattern, but not a classic implementation, it uses the callback method to implement the pattern, but not extending the parent template class. The comparator class use the strategy design pattern, it not a visitor pattern, visitor pattern have a accept method to operate the element to deal some logic. 



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

1 归并排序(MergeSort)

归并排序最差运行时间是O(nlogn),它是利用递归设计程序的典型例子。

归并排序的最基础的操作就是合并两个已经排好序的序列。

假设我们有一个没有排好序的序列,那么首先我们使用分割的办法将这个序列分割成一个一个已经排好序的子序列。然后再利用归并的方法将一个个的子序列合并成排序好的序列。分割和归并的过程可以看下面的图例。



从上图可以看出,我们首先把一个未排序的序列从中间分割成2部分,再把2部分分成4部分,依次分割下去,直到分割成一个一个的数据,再把这些数据两两归并到一起,使之有序,不停的归并,最后成为一个排好序的序列。

如何把两个已经排序好的子序列归并成一个排好序的序列呢?可以参看下面的方法。

假设我们有两个已经排序好的子序列。
序列A:1 23 34 65
序列B:2 13 14 87
那么可以按照下面的步骤将它们归并到一个序列中。

(1)首先设定一个新的数列C[8]。
(2)A[0]和B[0]比较,A[0] = 1,B[0] = 2,A[0] < B[0],那么C[0] = 1
(3)A[1]和B[0]比较,A[1] = 23,B[0] = 2,A[1] > B[0],那么C[1] = 2
(4)A[1]和B[1]比较,A[1] = 23,B[1] = 13,A[1] > B[1],那么C[2] = 13
(5)A[1]和B[2]比较,A[1] = 23,B[2] = 14,A[1] > B[2],那么C[3] = 14
(6)A[1]和B[3]比较,A[1] = 23,B[3] = 87,A[1] < B[3],那么C[4] = 23
(7)A[2]和B[3]比较,A[2] = 34,B[3] = 87,A[2] < B[3],那么C[5] = 34
(8)A[3]和B[3]比较,A[3] = 65,B[3] = 87,A[3] < B[3],那么C[6] = 65
(9)最后将B[3]复制到C中,那么C[7] = 87。归并完成。

如果我们清楚了上面的分割和归并过程,那么我们就可以用递归的方法得到归并算法的实现。

    public class MergeSorter
    {
        
private static int[] myArray;
        
private static int arraySize;

        
public static void Sort( int[] a )
        {
            myArray 
= a;
            arraySize 
= myArray.Length;
            MergeSort();
        }

        
/// <summary>
        
/// 利用归并的方法排序数组,首先将序列分割
        
/// 然后将数列归并,这个算法需要双倍的存储空间
        
/// 时间是O(nlgn)
        
/// </summary>
        private static void MergeSort()
        {
            
int[] temp = new int[arraySize];
            MSort( temp, 
0, arraySize - 1);
        }

        
private static void MSort(int[] temp, int left, int right)
        {
            
int mid;

            
if (right > left)
            {
                mid 
= (right + left) / 2;
                MSort( temp, left, mid); 
//分割左边的序列
                MSort(temp, mid+1, right);//分割右边的序列
                Merge(temp, left, mid+1, right);//归并序列
            }
        }

        
private static void Merge( int[] temp, int left, int mid, int right)
        {
            
int i, left_end, num_elements, tmp_pos;

            left_end 
= mid - 1;
            tmp_pos 
= left;
            num_elements 
= right - left + 1;

            
while ((left <= left_end) && (mid <= right)) 
            {
                
if (myArray[left] <= myArray[mid]) //将左端序列归并到temp数组中
                {
                    temp[tmp_pos] 
= myArray[left];
                    tmp_pos 
= tmp_pos + 1;
                    left 
= left +1;
                }
                
else//将右端序列归并到temp数组中
                {
                    temp[tmp_pos] 
= myArray[mid];
                    tmp_pos 
= tmp_pos + 1;
                    mid 
= mid + 1;
                }
            }

            
while (left <= left_end) //拷贝左边剩余的数据到temp数组中
            {
                temp[tmp_pos] 
= myArray[left];
                left 
= left + 1;
                tmp_pos 
= tmp_pos + 1;
            }
            
while (mid <= right) //拷贝右边剩余的数据到temp数组中
            {
                temp[tmp_pos] 
= myArray[mid];
                mid 
= mid + 1;
                tmp_pos 
= tmp_pos + 1;
            }

            
for (i=0; i < num_elements; i++//将所有元素拷贝到原始数组中
            {
                myArray[right] 
= temp[right];
                right 
= right - 1;
            }
        }
    }


归并排序算法是一种O(nlogn)的算法。它的最差,平均,最好时间都是O(nlogn)。但是它需要额外的存储空间,这在某些内存紧张的机器上会受到限制。

归并算法是又分割和归并两部分组成的。对于分割部分,如果我们使用二分查找的话,时间是O(logn),在最后归并的时候,时间是O(n),所以总的时间是O(nlogn)。

2 堆排序(HeapSort)

堆排序属于百万俱乐部的成员。它特别适合超大数据量(百万条记录以上)的排序。因为它并不使用递归(因为超大数据量的递归可能会导致堆栈溢出),而且它的时间也是O(nlogn)。还有它并不需要大量的额外存储空间。

堆排序的思路是:

(1)将原始未排序的数据建成一个堆。
(2)建成堆以后,最大值在堆顶,也就是第0个元素,这时候将第零个元素和最后一个元素交换。
(3)这时候将从0到倒数第二个元素的所有数据当成一个新的序列,建一个新的堆,再次交换第一个和最后一个元素,依次类推,就可以将所有元素排序完毕。

建立堆的过程如下面的图所示:


堆排序的具体算法如下:

public class HeapSorter 
    {
        
private static int[] myArray;
        
private static int arraySize;

        
public static void Sort( int[] a )
        {
            myArray 
= a;
            arraySize 
= myArray.Length;
            HeapSort();
        }

        
private static void HeapSort()
        {
            BuildHeap();            
//将原始序列建成一个堆

            
while ( arraySize > 1 )
            {
                arraySize
--;
                Exchange ( 
0, arraySize );//将最大值放在数组的最后
                DownHeap ( 0 );  //将序列从0到n-1看成一个新的序列,重新建立堆
            } 
        }

        
private static void BuildHeap()
        {
            
for (int v=arraySize/2-1; v>=0; v--)
                DownHeap ( v );
        }

        
//利用向下遍历子节点建立堆
        private static void DownHeap( int v )
        {
            
int w = 2 * v + 1;                     // 节点w是节点v的第一个子节点

            
while (w < arraySize)
            {
                
if ( w+1 < arraySize )        // 如果节点v下面有第二个字节点
                    if ( myArray[w+1> myArray[w] ) 
                        w
++;                        // 将子节点w设置成节点v下面值最大的子节点

                 
// 节点v已经大于子节点w,有了堆的性质,那么返回
                if ( myArray[v] >= myArray[w] ) 
                    
return;   
                
                Exchange( v, w );     
// 如果不是,就交换节点v和节点w的值
                v = w;        
                w 
= 2 * v + 1;            // 继续向下找子节点
            }
        }

        
//交换数据
        private static void Exchange( int i, int j )
        {
            
int t = myArray[i];
            myArray[i] 
= myArray[j];
            myArray[j] 
= t;
        }
    }    


 

堆排序主要用于超大规模的数据的排序。因为它不需要额外的存储空间,也不需要大量的递归。

3 几种O(nlogn)算法的初步比较

我们可以从下表看到几种O(nlogn)算法的效率的区别。所有的数据都使用.Net的Random类产生,每种算法运行100次,时间的单位为毫秒。


500随机整数5000随机整数20000随机整数
合并排序0.31251.56257.03125
 Shell排序0.31251.256.875
堆排序0.468752.18756.71875
快速排序0.156250.6252.8125

从上表可以明显地看出,快速排序是最快的算法。这也就给了我们一个结论,对于一般的应用来说,我们总是选择快速排序作为我们的排序算法,当数据量非常大(百万数量级)我们可以使用堆排序,如果内存空间非常紧张,我们可以使用Shell排序。但是这意味着我们不得不损失速度。 

/******************************************************************************************
 *【Author】:flyingbread
 *【Date】:2007年2月2日
 *【Notice】:
 *1、本文为原创技术文章,首发博客园个人站点(http://flyingbread.cnblogs.com/),转载和引用请注明作者及出处。
 *2、本文必须全文转载和引用,任何组织和个人未授权不能修改任何内容,并且未授权不可用于商业。
 *3、本声明为文章一部分,转载和引用必须包括在原文中。
 ******************************************************************************************/
posted @ 2012-11-10 23:18 三人行,必有我师焉 阅读(119) | 评论 (2)编辑 收藏

1 快速排序(QuickSort)

快速排序是一个就地排序,分而治之,大规模递归的算法。从本质上来说,它是归并排序的就地版本。快速排序可以由下面四步组成。

(1) 如果不多于1个数据,直接返回。
(2) 一般选择序列最左边的值作为支点数据。
(3) 将序列分成2部分,一部分都大于支点数据,另外一部分都小于支点数据。
(4) 对两边利用递归排序数列。

快速排序比大部分排序算法都要快。尽管我们可以在某些特殊的情况下写出比快速排序快的算法,但是就通常情况而言,没有比它更快的了。快速排序是递归的,对于内存非常有限的机器来说,它不是一个好的选择。 

2 归并排序(MergeSort)

归并排序先分解要排序的序列,从1分成2,2分成4,依次分解,当分解到只有1个一组的时候,就可以排序这些分组,然后依次合并回原来的序列中,这样就可以排序所有数据。合并排序比堆排序稍微快一点,但是需要比堆排序多一倍的内存空间,因为它需要一个额外的数组。

3 堆排序(HeapSort)

堆排序适合于数据量非常大的场合(百万数据)。

堆排序不需要大量的递归或者多维的暂存数组。这对于数据量非常巨大的序列是合适的。比如超过数百万条记录,因为快速排序,归并排序都使用递归来设计算法,在数据量非常大的时候,可能会发生堆栈溢出错误。

堆排序会将所有的数据建成一个堆,最大的数据在堆顶,然后将堆顶数据和序列的最后一个数据交换。接下来再次重建堆,交换数据,依次下去,就可以排序所有的数据。

4 Shell排序(ShellSort)

Shell排序通过将数据分成不同的组,先对每一组进行排序,然后再对所有的元素进行一次插入排序,以减少数据交换和移动的次数。平均效率是O(nlogn)。其中分组的合理性会对算法产生重要的影响。现在多用D.E.Knuth的分组方法。

Shell排序比冒泡排序快5倍,比插入排序大致快2倍。Shell排序比起QuickSort,MergeSort,HeapSort慢很多。但是它相对比较简单,它适合于数据量在5000以下并且速度并不是特别重要的场合。它对于数据量较小的数列重复排序是非常好的。

5 插入排序(InsertSort)

插入排序通过把序列中的值插入一个已经排序好的序列中,直到该序列的结束。插入排序是对冒泡排序的改进。它比冒泡排序快2倍。一般不用在数据大于1000的场合下使用插入排序,或者重复排序超过200数据项的序列。

6 冒泡排序(BubbleSort)

冒泡排序是最慢的排序算法。在实际运用中它是效率最低的算法。它通过一趟又一趟地比较数组中的每一个元素,使较大的数据下沉,较小的数据上升。它是O(n^2)的算法。

7 交换排序(ExchangeSort)和选择排序(SelectSort)

这两种排序方法都是交换方法的排序算法,效率都是 O(n2)。在实际应用中处于和冒泡排序基本相同的地位。它们只是排序算法发展的初级阶段,在实际中使用较少。

8 基数排序(RadixSort)

基数排序和通常的排序算法并不走同样的路线。它是一种比较新颖的算法,但是它只能用于整数的排序,如果我们要把同样的办法运用到浮点数上,我们必须了解浮点数的存储格式,并通过特殊的方式将浮点数映射到整数上,然后再映射回去,这是非常麻烦的事情,因此,它的使用同样也不多。而且,最重要的是,这样算法也需要较多的存储空间。

9 总结

下面是一个总的表格,大致总结了我们常见的所有的排序算法的特点。
排序法 平均时间最差情形稳定度额外空间备注
冒泡 O(n2)  O(n2) 稳定O(1)n小时较好
交换  O(n2)  O(n2)不稳定O(1)n小时较好
选择 O(n2) O(n2)不稳定O(1)n小时较好
插入 O(n2) O(n2)稳定O(1)大部分已排序时较好
基数O(logRB)O(logRB)稳定O(n)

B是真数(0-9),

R是基数(个十百)

ShellO(nlogn)O(ns) 1<2不稳定O(1)s是所选分组
快速O(nlogn)O(n2)不稳定O(nlogn)n大时较好
归并O(nlogn)O(nlogn)稳定O(1)n大时较好
O(nlogn)O(nlogn)不稳定O(1)n大时较好

posted @ 2012-11-10 22:30 三人行,必有我师焉 阅读(86) | 评论 (0)编辑 收藏

     摘要: Eclipse Class Decompiler,整合了目前最好的2个Java反编译工具Jad和JD-Core,并且和Eclipse Class Viewer无缝集成,能够很方便的使用本插件查看类库源码,以及采用本插件进行Debug调试。  阅读全文
posted @ 2012-10-30 13:48 三人行,必有我师焉 阅读(9019) | 评论 (33)编辑 收藏

The original author of the JadClipse project maintains it no more, and the latest build 3.3.0 doesn't support eclipse 4.x, so I download the source code and update it.

JadClipse for Eclipse 4.x also support Eclipse 3.x, and provides several new features:
 
1. Integrate jad.exe into the plugin, don't need to set jad path in the preference page again.
2. Add two options in the JadClipse main preference page:
    (1) Use Eclipse member sorter
    (2) Show decompiler report
3. Update the formatting preference default settings, the "Output fields before methods" setting's default value changes to true.

JadClipse for Eclipse 4.x Update Site: http://feeling.sourceforge.net/update

Offline Archive Update File Download: 
1. http://feeling.sourceforge.net/downloads/org.sf.feeling.decompiler_1.0.3.zip
2. http://www.blogjava.net/Files/cnfree/org.sf.feeling.decompiler_1.0.3.zip
posted @ 2012-10-15 17:45 三人行,必有我师焉 阅读(2542) | 评论 (10)编辑 收藏

http://www.open-open.com/lib/view/1326265166952

http://blog.chinaunix.net/uid-22342564-id-3183018.html
posted @ 2012-05-31 02:42 三人行,必有我师焉 阅读(269) | 评论 (0)编辑 收藏

com\maxmpz\audioplayer\widget\listwrappers\D800DC00 这个package 主要用于 PlayList 模型的操作。

com\maxmpz\audioplayer\widget\listwrappers\D800DC00\D803DC04.java

里面有个
    private static int D801DC01(Activity activity, int i)
    {
        TypedArray typedarray 
= activity.obtainStyledAttributes(null, com.maxmpz.audioplayer.j.true.p, 00);
        
int j = typedarray.getResourceId(i, 0);
        typedarray.recycle();
        
return j;
    }

Context.obtainStyledAttributes 实现控件属性与XML定义绑定的代码。 

TypedArray其实就是一个存放资源的Array,首先从上下文中获取到R.styleable。。。这个属性资源的资源数组。 attrs是构造函数传进来,应该就是对应attrs.xml文件。 a.getString(R.styleable。。。);这句代码就是获取attrs.xml中定义的属性,并将这个属性的值传给本控件的mValue.最后,返回一个绑定结束的信号给资源:a.recycle();绑定结束

相关学习文章:
http://blog.csdn.net/aomandeshangxiao/article/details/7449973

com.maxmpz.audioplayer.widget.listwrappers.0xE9 这个类,用于显示文件夹列表,右上方有2个自定义的RadioButton,用来设置是平铺模式显示还是层级显示。
定义了一个ID为:flat_hier_group 的RadioGroup,里面有个2个自定义的RadioButton。

<RadioGroup android:gravity="right" android:orientation="horizontal" android:id="@id/flat_hier_group" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_centerVertical="true"
  xmlns:android
="http://schemas.android.com/apk/res/android">
    
<RadioButton android:id="@id/flat_folders_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="0.0dip" android:button="@drawable/matte_flat_folders_selector" />
    
<RadioButton android:id="@id/hier_folders_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginRight="-8.0dip" android:button="@drawable/matte_hier_folders_selector" />
</RadioGroup>

matte_flat_folders_selector的XML定义为:
 

<selector
  
xmlns:android="http://schemas.android.com/apk/res/android">
    
<item android:state_checked="true" android:drawable="@drawable/matte_flat_folders_selected" />
    
<item android:state_checked="false" android:drawable="@drawable/matte_flat_folders" />
</selector>

自定义的RadioButton实际上就是张背景透明的图片罢了。


播放器列表的Layout布局文件为 list_with_big_header.xml。

里面有个android:ellipsize属性:
EidtText和textview中内容过长的话自动换行,使用android:ellipsize与android:singleine可以解决,使只有一行。EditText不支持marquee

关于android:ellipsize属性更详细的文章:http://www.cnblogs.com/chiao/archive/2011/08/20/2147250.html


里面还有个 android:textAppearance 属性,这里涉及到了Android的theme和style设置了,更详细的文章参见:http://jiayanjujyj.iteye.com/blog/1392541
posted @ 2012-05-23 11:59 三人行,必有我师焉 阅读(133) | 评论 (0)编辑 收藏

获取屏幕Display: Activity.getWindowManager().getDefaultDisplay();
获取扩展存储目录:Environment.getExternalStorageDirectory()
通过文件获取Uri:Uri.fromFile(File)
根据文件路径获取图片:BitmapFactory.decodeFile(imageFilePath, bmpFactoryOptions)
获取相机Intent:new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
获取相机拍照后的图片:
Bundle extras = intent.getExtras();
Bitmap bmp = (Bitmap) extras.get("data");
触摸事件:onTouchEvent(MotionEvent ev)
媒体播放器:android.media.MediaPlayer
媒体控制器:android.widget.MediaController(和播放器不在同一个package下)
SurfaceView是视图(View)的继承类,这个视图里内嵌了一个专门用于绘制的Surface,类似于Canvas,但感觉比Canvas更高级。
android.provider.MediaStore里包含了相关的Image,Video,Audio信息,可通过managedQuery方法来查询和遍历。
Android中的AdapterView使用Adapter来获取数据,和JFace中的ContentProvider对应。
根据字符串路径获取Uri:
Uri.parse((String)Path)
封装好的视频View:android.widget.VideoView
视频录制:android.media.MediaRecorder
相机高画质:CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH);


设置透明度(这是窗体本身的透明度,非背景)

WindowManager.LayoutParams lp=getWindow().getAttributes();
                lp.alpha=0.3f;
              getWindow().setAttributes(lp);
                
alpha在0.0f到1.0f之间。1.0完全不透明,0.0f完全透明


设置黑暗度

                WindowManager.LayoutParams lp=getWindow().getAttributes();
                lp.dimAmount=0.5f;
                getWindow().setAttributes(lp);
                getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);

dimAmount在0.0f和1.0f之间,0.0f完全不暗,1.0f全暗


设置背景模糊

getWindow().setFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND,     
           WindowManager.LayoutParams.FLAG_BLUR_BEHIND);



//调用浏览器 
Uri uri = Uri.parse(""); 
Intent it = new Intent(Intent.ACTION_VIEW,uri); 
startActivity(it); 

//显示某个坐标在地图上 
Uri uri = Uri.parse("geo:38.899533,-77.036476"); 
Intent it = new Intent(Intent.Action_VIEW,uri); 
startActivity(it); 

//显示路径 
Uri uri = Uri.parse("http://maps.google.com/maps?f=d&saddr=startLat%20startLng&daddr=endLat%20endLng&hl=en"); 
Intent it = new Intent(Intent.ACTION_VIEW,URI); 
startActivity(it); 

//拨打电话 
Uri uri = Uri.parse("tel:10086"); 
Intent it = new Intent(Intent.ACTION_DIAL, uri); 
startActivity(it); 

Uri uri = Uri.parse("tel.10086"); 
Intent it =new Intent(Intent.ACTION_CALL,uri); 
//需要添加 <uses-permission id="android.permission.CALL_PHONE" /> 这个权限到androidmanifest.xml 

//发送短信或彩信 
Intent it = new Intent(Intent.ACTION_VIEW); 
it.putExtra("sms_body", "The SMS text"); 
it.setType("vnd.android-dir/mms-sms"); 
startActivity(it); 

//发送短信 
Uri uri = Uri.parse("smsto:10086"); 
Intent it = new Intent(Intent.ACTION_SENDTO, uri); 
it.putExtra("sms_body", "cwj"); 
startActivity(it); 

//发送彩信 
Uri uri = Uri.parse("content://media/external/images/media/23"); 
Intent it = new Intent(Intent.ACTION_SEND); 
it.putExtra("sms_body", "some text"); 
it.putExtra(Intent.EXTRA_STREAM, uri); 
it.setType("image/png"); 
startActivity(it); 

//发送邮件 
Uri uri = Uri.parse("mailto:android123@163.com"); 
Intent it = new Intent(Intent.ACTION_SENDTO, uri); 
startActivity(it); 

Intent it = new Intent(Intent.ACTION_SEND); 
it.putExtra(Intent.EXTRA_EMAIL, android123@163.com); 
it.putExtra(Intent.EXTRA_TEXT, "The email body text"); 
it.setType("text/plain"); 
startActivity(Intent.createChooser(it, "Choose Email Client")); 

Intent it=new Intent(Intent.ACTION_SEND); 
String[] tos={"me@abc.com"}; 
String[] ccs={"you@abc.com"}; 
it.putExtra(Intent.EXTRA_EMAIL, tos); 
it.putExtra(Intent.EXTRA_CC, ccs); 
it.putExtra(Intent.EXTRA_TEXT, "The email body text"); 
it.putExtra(Intent.EXTRA_SUBJECT, "The email subject text"); 
it.setType("message/rfc822"); 
startActivity(Intent.createChooser(it, "Choose Email Client")); 

//播放媒体文件 
Intent it = new Intent(Intent.ACTION_VIEW); 
Uri uri = Uri.parse("file:///sdcard/cwj.mp3"); 
it.setDataAndType(uri, "audio/mp3"); 
startActivity(it); 

Uri uri = Uri.withAppendedPath(MediaStore.Audio.Media.INTERNAL_CONTENT_URI, "1"); 
Intent it = new Intent(Intent.ACTION_VIEW, uri); 
startActivity(it); 

//卸载APK 
Uri uri = Uri.fromParts("package", strPackageName, null); 
Intent it = new Intent(Intent.ACTION_DELETE, uri); 
startActivity(it); 

//卸载apk 2 
Uri uninstallUri = Uri.fromParts("package", "xxx", null); 
returnIt = new Intent(Intent.ACTION_DELETE, uninstallUri); 

//安装APK 
Uri installUri = Uri.fromParts("package", "xxx", null); 
returnIt = new Intent(Intent.ACTION_PACKAGE_ADDED, installUri); 

//播放音乐 
Uri playUri = Uri.parse("file:///sdcard/download/sth.mp3"); 
returnIt = new Intent(Intent.ACTION_VIEW, playUri); 

//发送附近 
Intent it = new Intent(Intent.ACTION_SEND); 
it.putExtra(Intent.EXTRA_SUBJECT, "The email subject text"); 
it.putExtra(Intent.EXTRA_STREAM, "file:///sdcard/cwj.mp3"); 
sendIntent.setType("audio/mp3"); 
startActivity(Intent.createChooser(it, "Choose Email Client")); 

//market上某个应用信,pkg_name就是应用的packageName 
Uri uri = Uri.parse("market://search?q=pname:pkg_name"); 
Intent it = new Intent(Intent.ACTION_VIEW, uri); 
startActivity(it); 

//market上某个应用信息,app_id可以通过www网站看下 
Uri uri = Uri.parse("market://details?id=app_id"); 
Intent it = new Intent(Intent.ACTION_VIEW, uri); 
startActivity(it); 

//调用搜索 
Intent intent = new Intent(); 
intent.setAction(Intent.ACTION_WEB_SEARCH); 
intent.putExtra(SearchManager.QUERY,"android123") 
startActivity(intent); 

//调用分享菜单 
Intent intent=new Intent(Intent.ACTION_SEND); 
intent.setType("text/plain"); //分享的数据类型 
intent.putExtra(Intent.EXTRA_SUBJECT, "subject"); //主题 
intent.putExtra(Intent.EXTRA_TEXT, "content"); //内容 
startActivity(Intent.createChooser(intent, "title")); //目标应用选择对话框的标题


获取Location:
Criteria criteria = new Criteria();
criteria.setAccuracy(Criteria.ACCURACY_COARSE);
criteria.setPowerRequirement(Criteria.POWER_LOW);
LocationManager locManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
if(locManager.getBestProvider(criteria, true) != null)
myLocation = locManager.getLastKnownLocation(locManager.getBestProvider(criteria, true));
else {
myLocation = new Location("gps");
myLocation.setLatitude(47.100301);
myLocation.setLongitude(-119.982465);
}
posted @ 2012-05-05 19:41 三人行,必有我师焉 阅读(229) | 评论 (0)编辑 收藏


3.1以下版本升级到最新版最好先还原到初始档,然后重新备份一下初始档。

如果不希望重新开局,以下提供正确的操作步骤:
1、进入备份与还原界面。
2、选择“备份当前游戏数据“进行备份。
3、选择“ 还原游戏至初始档状态“进行还原。
4、选择“设置当前游戏数据作为游戏初始档”进行备份。
5、在还原备份数据的列表中选择步骤2备份的数据存档进行还原

就可以正常进入游戏,并且更新了原始档。


3.9版更新内容:
1、修正了若干已知Bug。
2、象兵及战车数据使用兵种第二防御数据。
3、全面更新地图坐标算法,防止无法进入游戏或者武将位于山地无法移动。
4、增加了切换游戏势力功能,可以无需开档游玩任意势力。
5、增加了东州兵和骆驼兵模的防跳补丁。


3.8版更新内容:
1、增加了解锁朝廷、在野、乱军势力的功能。
2、增加了解除诸葛亮外出云游状态的脚本补丁,玩家可以使用任何势力收买诸葛亮,并且激活三顾茅庐技能。
3、增加了全势力官爵升级的脚本补丁。
4、增加了部分武将特殊技能全势力激活的脚本补丁。
5、增加了创建新兵种功能。


3.7版更新内容:
1、修正了一些细节上的bug。
2、增加了雾隐版吕玲绮卫队女兵模型的导入。
3、增加了武将模型作为骑兵模型的导入,并且可自由移除已导入的模型。
4、步兵的士兵模型可以选择将军模型,骑兵的士兵模型可以选择已导入的将军模型和雾影女兵模型。

3.6版更新内容:
1、修正了导入弩兵陷阵营战斗跳出的bug。
2、修正了收买武将修改器发生异常的bug。
3、修正了更换异族武将势力游戏跳出的bug。
4、修改了一些2种势力都有的武将相关的bug。
5、新建武将后清空武将编辑页面状态。
6、编辑武将能力页面新增了编辑武将卫队经验选项。
7、基本修改页面增加了所有兵种生产回合、费用下调的修改。
8、基本修改页面增加了所有城市建筑生产回合、费用下调的修改。

3.5版更新内容:
1、修正了兵种修改页面中兵种名称显示错乱的bug,现在很容易分清雇佣兵和城市招募了。
2、增加了乱军刷新概率的修改。
3、增加导入弩骑高顺卫队和弩兵陷阵营的功能。

3.4版更新内容:
1、修正了编辑大众卫队武将名字时会改变大众卫队名称的bug。
2、增加了编辑势力地图颜色的功能。
3、增加了降低敌方所有武将忠诚度的功能。
4、解决游戏中部分武将心灰意冷状态下无法被收买的bug。

3.3版更新内容:
1、修正了一些bug,比如编辑武将坐标会发生变化的bug。
2、创建新武将页面的爵位增加了自定义功能。
3、难度修改页面中,增加了禁用电脑驿站加成,禁止电脑生产刺客,以及能够编辑电脑太守加成几率的新功能。

3.2版更新内容:
1、增加了1.9a中特有的马匹,1.9a特有的马匹一般冲量都比较大,老版马匹冲量较小。
2、修正了武将地图坐标自动计算错误的bug。我把RGB颜色值写错了,另外遗漏了森林颜色。
3、修正了编辑新武将坐标和兵模无效生效的bug。
4、增加了孙刘曹中特殊武将不能被交换到其他势力的注释。

3.1版更新内容:
1、修正了未对掌旗官旗帜,城市旗帜,援军,盟军旗帜做修改的bug。
2、修正了编辑武将人物列传未繁体化,换行的bug。
3、增加了修改势力后代武将姓氏的功能。

3.0.1版更新内容:
1、更新了压缩武将图片的算法,让武将图片显示的更加清晰。
2、修正了解压缩更新包,仅支持GBK编码的bug。

3.0版更新内容:
1、新增编辑势力功能。
2、修正编辑武将坐标可能导致游戏异常的bug。
3、修正禁止电脑驿站暴兵的bug(此处bug是由于1.9和1.7的驿站代码不一样导致的)。
4、增加了添加势力兵种对兵营的要求,提高游戏的可玩性。
5、增加了对城墙生命值的修改。
6、增加了武将身份设置功能,方便玩家自定义君主或者继承人。
7、增加了修改器的自动更新功能,3.0及其后续版本若发现修改器有新版本,将会自动更新到最新版本。

注意事项:
因为3.0版改动较大,若使用编辑势力功能,请首先还原游戏数据到初始状态,重新做一次原始备份档,否则编辑势力后,无法还原到游戏原始状态。


说明:
1、修改器安装文件夹路径最好不要包含中文或者空格(有个别用户因为中文和空格路径问题无法启动修改器,调试器会提示:
Error occurred during initialization of VM
java.lang.UnsatisfiedLinkError: no zip in java.library.path


2、正常情况下,请使用“三国全面战争1.9a修改器”运行修改器。

3、非正常情况下,比如修改器无法启动成功,或者一直处于系统初始化状态,或者弹出错误窗口(不包括修改失败),请使用“三国全面战争1.9a修改器(调试器)”运行修改器。调试器启动时会显示一个黑色的dos窗口,如运行时发生错误,错误信息会打印到黑色dos窗口,请截图或者复制错误信息,发送到:http://www.1mod.org/thread-37918-1-1.html,我看见后会及时修正错误。

4、游戏原始备份档下载地址:http://feeling.sourceforge.net/patch/1.9a/default.zip
如果你需要恢复原始数据,而又未作原始档备份,可以从此地址下载。
恢复原始数据的2种方法:
1、解压缩后,复制data目录下的数据文件到游戏中,替换游戏数据文件。
2、复制压缩文件到游戏目录下的/patch/bak目录下,替换掉你自己的原始备份档,然后通过修改器恢复原始备份档。

5、关于杀毒软件误报问题:
(1)如果是国产杀毒软件报病毒,请你不要抱怨修改器,去抱怨那些垃圾杀毒软件吧,技术不过关,老是误报。
(2)如果是国外知名杀毒软件报病毒,比如卡巴斯基,麦咖啡,微软MSE等等,你可以截图发给我。
(3)推荐使用微软MSE,口碑不错,完全免费。


6、修改需理智,请谨慎修改,方能提高游戏的可玩性。










下载地址:http://feeling.sourceforge.net/patch/1.9a/patch_1.9a_3.9.zip

修改器简易教程:http://tieba.baidu.com/p/1418490788


小技巧:
1.如果希望新建武将成为势力君主,可以在创建后和武将势力君主交换。
2.能够更换所属势力的武将不能有后代,也不能是势力君主或者继承人,如果希望更换这些武将的势力,可以先和势力内的没有后代的垃圾武将交换一下,让他们成为自由身,就可以更换武将所属势力了。
posted @ 2012-01-25 22:57 三人行,必有我师焉 阅读(62537) | 评论 (76)编辑 收藏

关于PaletteData的生成:
case Gdip.PixelFormat16bppARGB1555:                                        
case Gdip.PixelFormat16bppRGB555: 
    paletteData = new PaletteData(0x7C00, 0x3E0, 0x1F); 
break;
case Gdip.PixelFormat16bppRGB565: 
    paletteData = new PaletteData(0xF800, 0x7E0, 0x1F); 
break;
case Gdip.PixelFormat24bppRGB: 
    paletteData = new PaletteData(0xFF, 0xFF00, 0xFF0000); 
break;
case Gdip.PixelFormat32bppRGB:
case Gdip.PixelFormat32bppARGB: 
    paletteData = new PaletteData(0xFF00, 0xFF0000, 0xFF000000); 
break;

32位ImageData中的data是以RGBA的顺序存储的。data[0]:red,data[1]:green,data[2]:blue,data[3]:alpha

从byte[]中读取RGB pixel:
public static int getPixelFromRGBA( int depth, byte[] data )
{
        switch ( depth )
        {
            case 32 :
                return ( ( data[0] & 0xFF ) << 24 )
                        + ( ( data[1] & 0xFF ) << 16 )
                        + ( ( data[2] & 0xFF ) << 8 )
                        + ( data[3] & 0xFF );
            case 24 :
                return ( ( data[0] & 0xFF ) << 16 )
                        + ( ( data[1] & 0xFF ) << 8 )
                        + ( data[2] & 0xFF );
            case 16 :
                return ( ( data[1] & 0xFF ) << 8 ) + ( data[0] & 0xFF );
            case 8 :
                return data[0] & 0xFF;
        }
        SWT.error( SWT.ERROR_UNSUPPORTED_DEPTH );
        return 0;
}

从pixel中取出RGB值:
RGB rgb = imagedata.palette.getRGB( pixel );

生成一个空的32位图片:
ImageData dest = new ImageData( width,
                height,
                32,
                new PaletteData( 0xFF00, 0xFF0000, 0xFF000000 ) );

24位透明图片转成32位透明图片:
    public static ImageData convertToRGBA( ImageData src )
    {
        ImageData dest = new ImageData( src.width,
                src.height,
                32,
                new PaletteData( 0xFF00, 0xFF0000, 0xFF000000 ) );

        for ( int x = 0; x < src.width; x++ )
        {
            for ( int y = 0; y < src.height; y++ )
            {
                int pixel = src.getPixel( x, y );
                RGB rgb = src.palette.getRGB( pixel );

                byte[] rgba = new byte[4];

                rgba[0] = (byte) rgb.red;
                rgba[1] = (byte) rgb.green;
                rgba[2] = (byte) rgb.blue;

                if ( pixel == src.transparentPixel )
                {
                    rgba[3] = (byte) ( 0 );
                }
                else
                {
                    rgba[3] = (byte) ( 255 );
                }
                dest.setPixel( x, y, getPixelFromRGBA( 32, rgba ) );
            }
        }
        return dest;
    }

posted @ 2012-01-15 13:49 三人行,必有我师焉 阅读(1513) | 评论 (1)编辑 收藏

SourceForge最近实在太慢了,忍无可忍,每天浪费我大量时间……终于让我不得不投向GitHub的怀抱……
posted @ 2012-01-14 16:33 三人行,必有我师焉 阅读(1710) | 评论 (0)编辑 收藏

JDK1.6的File.createTempFile方法有bug,在我的机器上第一次调用该方法需要耗时5秒时间,换了好几个1.6的版本均有该问题。JDK1.4,1.5则无此问题。

不一定所有的机器都有此问题,不过这儿肯定是有问题的,起码在我的机器上有问题。
posted @ 2012-01-13 15:51 三人行,必有我师焉 阅读(1241) | 评论 (2)编辑 收藏

Java应用定制工厂(以下简称为JCB,Java Customization Builder)是一个针对Java轻量级桌面应用进行精简优化的小工具,使用它可以精简你的jar包,并自动生成一个精简的JRE,也可以使用它生成一个Exe启动引导程序,并且能够对你的Java应用自动做Pack200和Unpack200处理。使用本工具定制的Java桌面应用通常不会超过5M(包含JRE),SWT客户端程序相对于Swing客户端程序更小,一般不会超过3M。

JCB1.0.3主要功能是可以支持
添加Exe的版本信息,并且能够更细节的定义Exe的启动行为,比如是否显示一个splash窗口,是否优先使用精简版的jre,以及增加了对unpack操作的优化,让应用程序第一次运行启动的更快。同时也增加了对系统权限的支持,不再要求需要管理员权限才能运行软件,Guest用户一样的使用。

下一个版本考虑增加一个Au3的编辑器,具有语法高亮和智能辅助功能,可以让高级用户定制自己的Exe行为。

软件主页:http://jcb.sourceforge.net
JCB1.0.3下载地址:http://sourceforge.net/projects/jcb/files/JCB_1.0.3.zip/download


posted @ 2011-12-26 22:13 三人行,必有我师焉 阅读(1181) | 评论 (3)编辑 收藏

在一个线程中启动了一个线程钩子,然后死活拿不到主线程中的消息,全局钩子就没问题。折腾了一下午才发现这个线程钩子是在子线程中启动的,GetCurrentThreadId 方法拿到的不是主线程的threadId,自然也就获取不到主线程的系统消息。Google了好多文章,都没有提到这一点,真TMD郁闷,完全浪费我时间,还以为是我的类库写错了,闹了半天是调用的参数传的不对。
posted @ 2011-12-24 18:58 三人行,必有我师焉 阅读(1182) | 评论 (1)编辑 收藏

同样的API, IShellFolder遍历控制面板里面节点的children,所有的.net代码都能获取,所有的非.net代码都无法获取???

本来还以为是代码的问题,debug一天死活没找到问题,最后在codeproject上找了十几个例子,发现都一样的结果,why? 

难道仅仅对.net的才开放遍历的权限?
posted @ 2011-12-14 12:35 三人行,必有我师焉 阅读(1508) | 评论 (1)编辑 收藏

1、安装VS2008
2、安装 Microsoft Windows SDK for Windows 7 and .NET Framework 3.5 SP1
3、修改SDK的SetEnv.cmd的第146行 
Set "VCTools=%VCRoot%\VC" 为 Set "VCTools=%VCRoot%\VC\Bin"
4、修改build.bat的环境配置
call "C:\Program Files\Microsoft SDKs\Windows\v6.1\Bin\SetEnv" /xp /x86 /Release (不是%PROGRAMFILES%目录,编译字幕为绿色,PROGRAMFILES%目录编译出来的dll有400多k。)
call "%PROGRAMFILES%\Microsoft Visual Studio 9.0\VC\vcvarsall.bat"
posted @ 2011-12-13 13:16 三人行,必有我师焉 阅读(92) | 评论 (0)编辑 收藏

     摘要: Java应用定制工厂(以下简称为JCB,Java Customization Builder)是一个针对Java轻量级桌面应用进行精简优化的小工具,使用它可以精简你的jar包,并自动生成一个精简的JRE,也可以使用它生成一个Exe启动引导程序,并且能够对你的Java应用自动做Pack200和Unpack200处理。使用本工具定制的Java桌面应用通常不会超过10M(包含JRE),SWT客户端程序相对于Swing客户端程序更小,一般不会超过5M。  阅读全文
posted @ 2011-12-12 16:27 三人行,必有我师焉 阅读(2439) | 评论 (10)编辑 收藏

GreenJVMMake 是一个精简JRE的小工具,详细信息请参见项目:

http://code.google.com/p/greenvm/

javaonepackage 是一个集成了GreenJVMMake的IDE,可以方便的生成自定义的JRE,详细信息请参见项目:

http://code.google.com/p/javaonepackage/

比较遗憾的是javaonepackage的作者遗失了代码,导致项目无法进一步的更新,影响了使用。

用以上工具生成的精简JRE通常不超过2M,可是用exe4j将Java应用转换成exe的时候,却会抛出Couldn't load main class,原因是因为exe4j使用自己的jar来加载用户的Jar包,这其中用到了一些反射机制,精简JRE里面当然是不含有多余的class文件的,所以加载必然失败。我们需要做的就是把这些相关的class添加到精简JRE中,这些class包含在 java\lang, java\util, sun\reflect 这三个包中。将这些class添加到精简jre中,大小也仅仅增加数百K而已,在我们可以的接受范围之内。

PS:由于GreenJVMMake的机制是检查运行时加载的class文件,所以它很可能遗失Jar运行所需要的exception class,这会导致运行时JVM的崩溃,用户需手动加载所有可能会产生的exception class,包含runtime的和非runtime的。
posted @ 2010-09-02 10:53 三人行,必有我师焉 阅读(2020) | 评论 (1)编辑 收藏

现在网络上越来越流行.net和java写的客户端的小应用程序,而且后缀是exe。本文讨论的是如何从exe4j封装的exe文件中将自己想要的jar抽取出来。

exe4j一直是一种比较通用的java exe封装工具,但是其并没有将jar转换为本地文件,而是将jar文件通过特殊处理后,封装成的一个exe文件。因此只要我们了解了exe4j的原理,就可以将jar文件从exe文件中提取出来,并通过反编译工具来查看程序代码。

1. 分析Exe4J,得知其在添加文件到.exe时,使用0x88将文件内容Xor,所以第一步,我们需要将原始的数据提取出来:
import java.io.*;
import java.util.*;

public class gen {
    
public static void main(String args[]) throws IOException {
        FileInputStream fin 
= new FileInputStream(args[0]); // 可以将整个exe文件解码
        FileOutputStream fout = new FileOutputStream(args[1]);
        BufferedInputStream bin 
= new BufferedInputStream(fin);
        BufferedOutputStream bout 
= new BufferedOutputStream(fout);
        
int in = 0;
        
do {
            in 
= bin.read();
            
if (in == -1)
                
break;
            in 
^= 0x88;
            bout.write(in);
        } 
while (true);
        bin.close();
        fin.close();
        bout.close();
        fout.close();
    }
}

2.分析提取出来的数据文件,使用WinHex查看其16进制代码。由于Jar文件的开头总是PK开头,并且总包含有manifest.mf文件,并且结尾总是有3个00,同时结尾段有整个Jar包文件的索引,我们可以根据这一特性来分析我们需要的片段。

1、搜索Jar的manifest,然后往前找,找到的第一个PK段,即为一个Jar的开头。
2、查看片段里Jar里的每个class信息,直到最后的文件索引片段。
3、一个Jar的结束片段位于索引片段之后,仍然包含着PK段,并且最后包含着3个00,且这3个00距离PK大概20个字节左右

根据以上3条准则,足以提取整个Jar数据段,然后导入新文件中,并且以zip字段命名,尝试用ZIP解压缩软件打开,看看是否抽取正确。

需要注意的是WinHex非注册版,只能保存280K大小的文件,更大的Jar文件,需要注册版的WinHex才行。
posted @ 2010-08-22 01:03 三人行,必有我师焉 阅读(3042) | 评论 (1)编辑 收藏

1、调用截图工具:

http://iecapt.sourceforge.net/

2、使用Java类库,通过Render生成Java2D图像:

http://www.ldotc.com/Web+Page+Thumbnails+in+Java.html

方法1的优点在于,图像不会失真,速度快,但是缺点是依赖于服务器端的UI,无UI的话,则无法使用。
方法2的优点是不依赖于服务器端的UI,但是图像容易失真,且速度相对较慢……
posted @ 2010-08-17 12:50 三人行,必有我师焉 阅读(1237) | 评论 (0)编辑 收藏

     摘要: TGA or TARGA format is a format for describing bitmap images, it is capable of representing bitmaps ranging from black and white, indexed colour, and RGB colour, the format also supports various compr...  阅读全文
posted @ 2010-03-29 13:02 三人行,必有我师焉 阅读(1581) | 评论 (0)编辑 收藏

Eclipse 是一个集成开发环境,同时又是一个产品平台。这样有时候我们就会碰到一个问题,在开发Debug项目的时候没有问题,打包发布的时候就有问题了,这可如何是好?由于Debug时候和发布后plugin的ClassLoader机制不尽相同,出现这个问题的几率还有会有的。

Java 在这一点上为我们提供了方法,那就是远程Debug。远程Debug一般用于Web开发,或者客户端无法负载大规模的应用时才会运用到,所以Desktop developer 很少会涉及到这个概念。不过Eclipse 的产品平台却让我再次体会到了Java的强大。

关于Eclipse Remote Debug的文章,大家可以从此处学习:

http://www.ibm.com/developerworks/cn/opensource/os-eclipse-javadebug/index.html

Remote Debug 需要2方面的设置,一个是产品平台JVM启动参数的设置,需要让平台以远程Debug的模式启动。然后是在开发端监听产品平台的运行状态。当产品平台运行到断点代码时,Eclipse开发端就会进入Debug界面,像普通debug一样正常debug了。

posted @ 2009-09-10 16:59 三人行,必有我师焉 阅读(2187) | 评论 (1)编辑 收藏

网上关于Eclipse Fragment的资料比较少,引用Eclipse Wiki的一段话:

An Eclipse Fragment is a way of putting your own classes into the "class loader" of another package (basically, it's as though your class was actually in the other package). If you combine this ability of a Fragment with the notion of a plugin's classpath ordering, then you can force your class to load before a like-named class in the original package.

简而言之,Fragment可以利用Eclipse平台的ClassLoader机制替换原有Plugin的某些文件,以便实现自己的功能。如果仅仅是因为Plugin扩展,而需要替换自己项目的某些Plugin,可以参考文章:

http://wiki.eclipse.org/Steps_to_use_Fragments_to_patch_a_plug-in  (来之不易,感兴趣的可以收藏下)

读完这篇文章,你大概就应该能够了解到Fragment的实现分为两部分:Host 和 Patch。这两部分对应的plugin的manifest.mf文件都需要做特殊处理,对于Patch的build方式也要特殊处理,那就是build出来的plugin jar里放置的不是松散的class文件,而是一个特殊的jar文件,这个 jar 文件定义在Host plugin的manifest.mf 的classpath里面。

通常情况下,按照这篇文章的做法是没有问题的。但是在开发阶段,有一种情况可能无法实现class的替换。

我公司的项目是使用perforce进行项目版本控制的,但是perforce比较傻,有些重要的功能没有实现,而又很关键,因此我想自己针对perforce的eclipse plugin做一个fragment,添加自己想要的功能。但是这个plugin没有source code,而我自己的hack也是反编译class文件进行的。所以在我的work space里,并没有Host plugin的 project。结果按照这篇文章的做法,始终不能在workbench debug的状态下,正确load我hack过的class。因为在代码模式下面是可以正确load的,而现在没有代码,在数次检验无果之后,只能针对ClassLoader去思考了。Host文件里要求一个jar文件,我就用PDE 将 fragment export出来,把Fragment plugin jar 包里包含的那个 jar 文件解压出来,放到 patch project 里,然后重新Debug, OK,这次果然没问题了。一个小小的Class Loader问题,真的能够要人命呀……这应当算是Eclipse的一个bug吧,除非是对一个Plugin进行hack,否则一般也不会碰到这种情况。普通的项目开发,肯定会包含Host Plugin的Project。

我的经历权且当做饭后谈资,不足为虑。我这儿想说的是Fragment机制非常好用,也易于Plugin扩展。比如你的项目分为Open Source 和 Commercial 2种的话, Commercial 部分也可以通过Fragment来实现,不一定需要走Extension Point路线。由于网上相关文章不多,还需要自己多多研究,了解其机制。

posted @ 2009-09-10 16:42 三人行,必有我师焉 阅读(1420) | 评论 (0)编辑 收藏

自定义控件通常是从一个Composite或者Canvas继承而来,但是缺省状态下,这两个控件都无法通过键盘的Tab键得到焦点。通过对SWT的debug,我发现要实现该事件,必须满足一个必要条件:为自定义控件安装一个KeyEvent的监听器

实际上我个人认为这出自于SWT的实现者自己的考虑,可能认为如果没有KeyListener,也就意味着没有Key的操作,那么也无需通知Travserse事件,Travserse事件的前提就是Key操作。但是在某些特定情况下我们不需要KeyListener,也可以模拟出KeyEvent的效果,比如通过TraverseListener的keyTraversed方法。总而言之,想通过键盘为某一个自定义控件获取焦点,就老老实实的给控件加一个KeyListener吧。

此处附加一些和Traverse相关的小知识:

1、如何通过键盘的Tab键跳出 Multiple Style 的Text?
        请使用 Ctrl+Tab 组合键。

2、如何通过键盘操作让上一个控件获取焦点?
        请使用 Shift+Tab 或者 Ctrl+Shift+Tab 组合键。

3、Button 可以通过回车键和空格键激活。所以如果有些地方无法使用回车键(比如按钮在Dialog中),可以考虑使用空格键。

4、一些特殊的KeyCode:
        回车键:SWT.KEYPAD_CR
        小键盘的回车键:SWT.TRAVERSE_RETURN
        方向键:SWT.ARROW_UP,SWT.ARROW_DOWN,SWT.ARROW_LEFT,SWT.ARROW_RIGHT
posted @ 2009-04-07 11:22 三人行,必有我师焉 阅读(1642) | 评论 (2)编辑 收藏

项目地址:http://findbugs.sourceforge.net/

用于分析项目代码,自动发现项目潜在Bug,万中无一的好东西,绝对值得下载一试,有兴趣的自己研究。
TeamLeader和PM 强烈推荐,用于Code Review。
posted @ 2009-02-16 00:02 三人行,必有我师焉 阅读(2624) | 评论 (5)编辑 收藏

做不下去了,Vista快把我逼疯了,啥都不支持,随意取消n多API,开发人员能做的事情太有限了,Vista你到底想干什么?

看一看MSDN,全是抱怨Vista的。管理员给出的答案就是按照兼容XP的方式运行,这算哪门子解决方案呀。

Have you tried running your application elevated? (Right Click->Run As Administrator) If this doesn't work try applying an XP SP2 Compatibility shim from the Compatibility Tab in the properties dialog. (Right Click->Properties | Compatibility).

Let me know if this works.

Thanks!


怒呀!!!!!!!

从Win3.1 到 Vista,没有哪个版本比Vista更失败的,无论是从用户角度,还是从开发角度。微软是不是想倒闭不做了?
posted @ 2008-12-05 18:16 三人行,必有我师焉 阅读(2110) | 评论 (5)编辑 收藏

沉寂了一段时间,现在继续SWT Win32 Extension的开发,说实话,最近的进展挺失败的。Black Glossy效果我发现在某些机器上的显示效果非常的卡,我自己的机器却没有任何问题。另外就是我最近在XP下实现了Window Mixer API,用来管理系统声音。Win98,2000,XP都没有问题,结果Vista让我郁闷了,Vista居然取消了Mixer,我哭呀。以下是微软员工给出的答案:

That's because the mixer APIs are virtualized on Windows Vista - you don't get to see the real audio hardware by default, only a virtualized version.  We did this because the vast majority of applications that used the mixer APIs were using them to control their own volume, which is quite rude (it says "I own the box, no other sounds on the system matter").

 You have two choices.  The first is to run your application in XP compatibility mode, in which case you'll be able to access the real audio hardware (please note: you'll see exactly what the hardware provides, which may lead to surprising results).

 The other choice is to use the new Vista audio engine APIs.  either the IAudioEndpointVolume API which allows you access to the master volume for each of the audio endpoints on the machine.  If you really need to access the actual audio controls the IDeviceTopology interface will allow you direct access to the various controls on the audio hardware.

没脾气了,只能专门为Vista实现一套简单的API了。
posted @ 2008-10-23 18:25 三人行,必有我师焉 阅读(2146) | 评论 (5)编辑 收藏

最近做了一个可视化编辑器相关的项目,采用了GMF。现在项目即将进入尾声,以后可能不再接触这个东西,so在还没有忘掉之前,将经验记录下来以供大家参考。 当然做这个项目之前,我对GMF,EMF一无所知,只是对GEF有所了解,所以可能会有些囫囵吞枣的感觉,但是相信我的理解还是会对各位有所帮助。

GMF其实是一个整合了GEF,EMF的自动化生成代码的项目。使用GMF,可以快速的生成一个包含可视化编辑器的项目,这一点网上有文章介绍:15分钟学会GMF。15分钟是夸张了一点,不过15天完成一个可视化项目,对一个熟练的GMF程序员来说,却绝对不是什么难事。但是既然使用了GMF框架,你就不得不面对以下几个问题:

一、既然是框架,自然要遵守框架的规则,GMF是模式驱动设计的,也就是说必须建好模型,才能进行下一步的开发工作。但是国内很多项目,需求总是不断更新,这种情况下,不要轻易使用GMF。
二、使用GMF框架,自然不具备GEF的灵活性,很多地方都被限制住了,不适合做灵活性非常大的图形设计。
三、不得不忍受GMF里大量的bug。

我比较过GMF1.0,2.0,2.1三个版本,其中1.0完全不能容忍,2.0比较傻,2.1还过得去,所以GMF还是值得大家期待的,毕竟是越做越好。


一个比较简单的GMF流程编辑器

个人感觉GMF非常适合做流程编辑器,主要是图形要求简单,并且适合GMF自动布局,兼之对模型要求不高。

在学习GMF之前,有必要研究一下GEF和EMF,其中GEF是必须要有所了解的,而对EMF要求不算太高,能建一个ecore模型,了解emf的commandstack就够了(其实我本人不太喜欢EMF,我更喜欢用自己的模型框架)。

GMF的学习周期大概2周左右,上手到熟练大概需要1个月的时间(我自己的学习周期),当然这期间会碰到各种各样的技术问题,针对不同的case,碰到的问题也会不一样,而我这个系列的文章,主要就是把我所遇到的问题陈列出来,并提供一个解决之道。

附GMF相关资料:
八进制:GMF常见问题
GMF Newsgroup Q and A
posted @ 2008-09-04 14:52 三人行,必有我师焉 阅读(2264) | 评论 (4)编辑 收藏

在Birt Designer中,Binding 是无处不在的,如何正确的是用Birt的Binding呢?首先我们需要知道Birt的Binding Type。

Birt的Binding type随着Birt的版本的升级而越来越丰富,早期的Birt版本只有2种类型:为自己创建一个Data Column Binding,和使用Container的Data Binding,随着CrossTab的出现,进而出现了Cube,ReportItem Reference Binding的概念。

普通的Binding,一般是通过属性编辑器的BindingPage来创建,可以set一个Data set,然后自动创建一个Binding列表。Crosstab和Chart两种类型的Report Item 可以不依赖于Data set,而采用Cube来作为Bingding源。而ReportItem Reference 的概念更是简便了Binding的生成,我们可以让一个ReportItem 直接引用另外一个ReportItem的Binding,而非仅仅是Container的Binding,当然既然是引用,那么你是无法编辑这些Binding的,而且被引用的ReportItem必须包含一个名字,有些ReportItem比如Table本身是可以不设名字的,但在这个地方你就要加上了。

再来说说Binding的设置,我们可以通过Binding Dialog和Binding Page来设置,这两种设置是不同的,如果在Binding Dialog上添加一个Binding,那么这个Binding是添加到这个Report Item的BindingHolder身上,如果在 Binding Page上设置,则Bindnig会添加到自身,让自己成为BindingHolder。
posted @ 2008-07-04 11:47 三人行,必有我师焉 阅读(2284) | 评论 (1)编辑 收藏

通过打开Birt透视图,然后Reset至缺省的Layout,我们能够看到基本的一些View和一个主要的报表可视化编辑器。

先来说一下View,Birt主要的View包含7块:

左上角包含3个视图,Palette,Data Explorer和Resource Explorer,Palette里放置了报表常用的可视化组件,直接将这些组件拖到报表设计器的时候,这些组件并没有作特殊的初始化处理,而从Data Explorer里向设计器拖入一个Dataset会自动生成一个Table,拖入一个Cube会生成一个Crosstab,拖入一个Dataset Column会生成一个DataItem。Data Explorer主要是用来管理和显示数据源。Resource Explorer 老版本里是Library Explorer,新版本则变更为了Resource Explorer,用来特别显示Library,CSS文件,其他文件则不进行特殊处理。

左下角有2个视图,包含Navigator视图和Online视图,Navigator视图是用来建立Birt项目用的,如果是Birt Rcp版本,我们则看不到这个视图,这是IDE版本专有的一个视图,在Rcp版本里没有Project这个概念,直接以文件的形式进行管理。Outline是Birt里较为重要的视图,所有的报表部件都会在这个View里显示并会随着报表的变化实时刷新。

右下角包含了Property Editor 和 Problems两个视图, Property Editor 用来编辑每个可视化报表元素的属性,一般比较常用的属性都会在前几个Tab页里,但是有些属性前几个Tab页里都没有,这时候需要选择Advanced这个Tab页,它里面包含了这个元素所有可用的属性,如果连这儿也没有,那么说明该元素不存在你想要的属性。Problems视图则用来显示报表收集到的一些问题,如果报表校验的时候发生错误,会在此处显示出来。

以上的那些视图属于缺省视图,但还有几个视图也比较有用,一个是Error log视图,一个是Example视图。Error log视图主要是开发用的,当你使用Birt进行二次开发的时候,难免会碰到一些bug,当你感觉有问题的时候,不妨打开error log视图,只要Birt捕捉到了异常,一般都会显示在这个视图里。而Example视图里提供了各种各样的视图,可以Open 和 Save, Open的时候会自动帮你在workspace里建立一个项目,以便你浏览该项目文件。Save则是把这个Example保存到本地某个目录。

说完视图,再来看看Birt可视化的报表设计器,这个设计器包含了五个部分:Layout,Master Page,Script,XML Source,Preview。

Layout为设计器的主要部分,只要通过可视化的拖拽,一个报表就会被自动生成出来,当然要想灵活运用报表设计器,就必须对各个组件的属性了如指掌,Birt提供了丰富灵活的属性供用户选择。MasterPage主要用来设置页眉页脚,以及打印显示之类的功能。Script页面,当你在Layout页面里选中一个元素之后,切换到Script页面,就可以对这个元素进行脚本编码,主要用来监听各种事件,使用Javascript,在采用Web显示里,这些脚本会生效。XML Source则是将这张报表背后的XML source显示出来,用户如果觉得自己对Birt很熟,可以直接在这儿手工修改代码。Preview则是预览Birt报表,Birt会启动Tomcat显示Web运行效果。

基本上Birt还是比较强大的,基本的功能一应俱全。可能你会发现缺少一些更高级花哨的功能,比如flash之类的,其实怎么说呢,不是Birt没有,而是开源版的没有,这个功能在Birt商业版里,可以在 http://www.actuatechina.com/download.php 下载专业版试用。如同IBM,这是这类公司特有的策略,免费上面做收费。喜欢的话,可以试一下专业版,呵呵,可惜网上找不到破解版。

posted @ 2008-06-20 11:48 三人行,必有我师焉 阅读(3618) | 评论 (1)编辑 收藏

Eclipse3.4马上就要Release了,相信Eclipse的fans都已经开始翘首以待,望穿秋水了。不过现在的RC版本用得很不爽呀,性能非常差,Eclipse的惯例就是最后一个月的工作基本上就是对性能做优化,以达到最佳使用效果。

随着Plugin的增多,Eclipse采用了特殊的策略,增加了一个子目录dropins,用来放用户新增加的plugin,而原有的plugins目录,则基本用于系统基本功能,2者的区别就是,前者可以任意添加删除,后者则基本上是一个ReadOnly的状态,添加了就不能再作修改了,Eclipse会将每一个添加的plugin记录下来,以后启动就不再检查这些plugin了。

不过对于我来说,我一直都习惯于使用plugins目录,下了一个插件直接解压,就直接覆盖安装到plugins目录了,而且有一些plugin不支持dropins目录,必须在plugins目录下才能正常工作。不过一旦插件安装失败,想再reset就比较麻烦了。Eclipse不会自动恢复到初始安装状态,经过测试,找到了一个解决方案,用原始的eclipse的文件替代2个目录:configuration和p2目录。plugins文件位置记录在configuration\org.eclipse.equinox.simpleconfigurator\bundles.info里,p2目录里则记录了更多的初始化信息。要想Reset Eclipse3.4,这两个目录必须被恢复到初始化状态,然后就可以正常使用了。

posted @ 2008-06-17 22:30 三人行,必有我师焉 阅读(7374) | 评论 (5)编辑 收藏

最近的项目需要使用报表,因为是RCP应用,所以选择了Birt,用了一下,感觉还可以,就是网上资料少了点,不过以前也研究过一些Eclipse相关技术,这些都不重要了,找了SDK版本Debug,啥研究不出来?

BIRT是一个Eclipse-based开放源代码报表系统。它主要是用在基于Java与J2EE的Web应用程序上。BIRT主要由两部分组成:一个是基于Eclipse的报表设计和一个可以加到你应用服务的运行期组件。BIRT同时也提供一个图形报表制作引擎。

官方主页:http://www.eclipse.org/birt
官方BBS支持:http://www.actuatechina.com/forum2.html

基本上来说Birt功能还是很强大的,支持时下比较流行的WebService,Ajax技术,既可用于Web,也可以用于桌面,更新也算稳定,基本上遵循Eclipse的开发步骤,一个一个大版本,同时支持脚本调用,debug开发等等。唯一不足的就是中国的国情支持得还不够完善,毕竟中国比较特殊,我以前给公司做党务报表,要按照纸质报表画,一分一毫都不能变差,那个变态呀,在电脑上画报表还是拿尺子量。

刚刚开始用,慢慢研究,看了下Birt自带的Example,的确是很强大,做得也很漂亮,自己试着创建一个报表也很简单,希望能够比较快的上手吧。

在网上找了一些资源:
http://blogger.org.cn/blog/more.asp?name=sixsun&id=13933 BIRT 中文指南
http://www.springside.org.cn/docs/reference/Birt.htm BIRT报表
http://www-128.ibm.com/developerworks/cn/opensource/os-ecl-birt/ Birt的IBM DW的中文教程
http://download.eclipse.org/birt/downloads/demos/FirstReport/MyFirstReport.html Birt Flash Demo.
posted @ 2008-06-12 12:02 三人行,必有我师焉 阅读(5827) | 评论 (8)编辑 收藏

  最近很多人问我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 三人行,必有我师焉 阅读(4759) | 评论 (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 三人行,必有我师焉 阅读(4941) | 评论 (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 三人行,必有我师焉 阅读(3578) | 评论 (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 三人行,必有我师焉 阅读(3294) | 评论 (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 三人行,必有我师焉 阅读(3025) | 评论 (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 三人行,必有我师焉 阅读(2922) | 评论 (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 三人行,必有我师焉 阅读(2474) | 评论 (0)编辑 收藏

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