隨筆 - 63  文章 - 0  trackbacks - 0
          <2009年5月>
          262728293012
          3456789
          10111213141516
          17181920212223
          24252627282930
          31123456

          常用鏈接

          留言簿(2)

          隨筆分類

          隨筆檔案

          搜索

          •  

          最新評論

          閱讀排行榜

          評論排行榜


                  如果在Java程序中你使用Java Native Interface(JNI) 來調用某個特定平臺下的本地庫文件,你就會發(fā)現(xiàn)這個過程很單調、乏味。Jeff Friesen一直在介紹一個知名度很低的Java開源項目:Java Native Access---它能夠避免因使用JNI導致的錯誤和乏味,同時它還能讓你通過編程的方式調用C語言庫。

                  在Java語言沒有提供必要的APIs的情況下,Java程序使用Java Native Interface (JNI)來調用特定平臺下的本地庫是必要的。例如:在Windows XP平臺中,我使用過JNI來調用通用串行總線和基于TWAIN的掃描儀器的庫;在更古老的Windows NT平臺中,調用過智能卡的庫。

                  我按照一個基本的、乏味的流程來解決這些問題:首先,我創(chuàng)建一個Java類用來載入JNI-friendly庫(這個庫能過訪問其他的庫)并且聲明這個類的本地方法。然后,在使用JDK中的javah工具為JNI-friendly庫中的函數(shù)---函數(shù)和這個類中的本地方法一一對應---創(chuàng)建一個代理。最后,我使用C語言寫了一個庫并用C編譯器編譯了這些代碼。

                  盡管完成這些流程并不是很困難,但是寫C代碼是一個很緩慢的過程---例如: C語言中的字符串處理是通過指針來實現(xiàn)的,這會很復雜的。而且,使用JNI很容易出現(xiàn)錯誤,導致內存泄漏、很難找到程序崩潰的原因。

                  在Java開源系列的第二篇文章中,我要介紹一個更簡單、更安全的解決方法:Todd Fast and Timothy Wall的Java Native Access (JNA) 項目。JNA能夠讓你在Java程序中調用本地方法時避免使用C和Java Native Interface。在這篇文章中,讓我以簡要的介紹        JNA和運行示例必需的軟件來開始下面的內容。然后,向你展示如何使用JNA將3個Windows本地庫中的有用代碼移植到Java程序中。

          Get started with JNA(JNA入門)

          Java Native Access 項目 在Java.net上,你可以到這個網(wǎng)站上現(xiàn)在這個項目的代碼和在線幫助文檔。雖然在下載有5個相關的jar文件,在本文中你僅僅需要下載其中的jna.jar和example.jar。

          Jna.jar提供基本的、運行這些示例文件必需的jna運行環(huán)境。這個jna.jar文件除了有Unix、Linux、Windows和Mac OS X平臺相關的JNT-friendly本地庫外,還包含其他幾個類包。每一個本地庫都是用來訪問相對應平臺下的本地方法的。

                  example.jar包含了不同的示例來表明JNA的用途。其中的一個例子是使用JNA來實現(xiàn)一個在不同平臺下的透明視窗技術的API。在文章最后的示例中將要展示如何使用這個API修復上個月的文章關于VerifyAge2應用中辨認透明效果的問題。

          獲取本地時間(Get local time)

          如果你在Java Native Access 首頁 看過“JNA如何入門”,你就會知道一個很簡單的關于調用Windows 平臺下的API函數(shù):GetSystemTime() 的JNA示例。這個不完整的例子只是展示了JNA的基本特點。(在例子的基礎上,我做了一個更完整的基于Windows的例子來介紹JNA)我在Windows平臺下完善了這個例子來介紹JNA。

          第一例子基于Windows GetLocalTime() API函數(shù)返回本地當前的時間和日期。和GetSystemTime()不同的是,返回的時間/日期是協(xié)調通用時間(UTC)格式的,GetLocalTime()返回的時間/日期信息的格式是根據(jù)當前時區(qū)來表示。

          在一個Java程序中使用JNA調用GetLocalTime,你需要知道這個函數(shù)所在的Windows平臺下的動態(tài)鏈接庫(DLL)的名稱(和可能所在的地理區(qū)域)。我們發(fā)現(xiàn)GetLocalTime()和GetSystemTime在同一個DLL文件中:kernel32.dll。你還需要知道GetLocalTime()在C語言環(huán)境中的申明。申明如下Listing 1:

          Listing 1. GetLocalTime在C語言中的申明

          typedef struct
          {
             WORD wYear;
             WORD wMonth;
             WORD wDayOfWeek;
             WORD wDay;
             WORD wHour;
             WORD wMinute;
             WORD wSecond;
             WORD wMilliseconds;
          }
          SYSTEMTIME, *LPSYSTEMTIME;

          VOID GetLocalTime(LPSYSTEMTIME lpst);


          這個基于C語言的申明表明傳到這個函數(shù)的參數(shù)數(shù)目和類型。在這個例子中,只有一個參數(shù)---一個指向Windows SYSTEMTIME結構體的指針。而且,每個結構體成員的類型是16bit長度的無符號整型。根據(jù)這些信息,你能夠創(chuàng)建一個完全描述GetLocalTime()函數(shù)的接口,如Listing 2中所示:

          Listing 2. Kernel32.java

          // Kernel32.java

          import com.sun.jna.*;
          import com.sun.jna.win32.*;

          public interface Kernel32 extends StdCallLibrary
          {
             public static class SYSTEMTIME extends Structure
             {
                public short wYear;
                public short wMonth;
                public short wDayOfWeek;
                public short wDay;
                public short wHour;
                public short wMinute;
                public short wSecond;
                public short wMilliseconds;
             }

             void GetLocalTime (SYSTEMTIME result);
          }


          Kernel32 接口(The Kernel32 interface)

          因為JNA使用通過一個接口來訪問某個庫中的函數(shù),Listing 2表示了一個描述GetLocalTime()的接口。根據(jù)約定,我把接口命名為Kernel32是因為GetLocalTime()在Windows的kernel32.dll庫。

          這個接口必須繼承com.sun..jna.Library接口。因為Windows API函數(shù)遵循stdcall調用協(xié)議(stdcall calling convention),為Windows API申明的接口也必須繼承com.sun.jna.win32. StdCallLibrary接口。因此這個接口共繼承了Library 和 com.sun.jna.win32.StdCall兩個接口。

          在前面,你已經(jīng)知道了GetLocalTime() 需要一個指向SYSTEMTIME結構體的指針作為它唯一的參數(shù)。因為Java不支持指針,JNA是通過申明一個com.sun.jna.Structure的子類來代替的。根據(jù)java文檔中抽象類的概念,在參數(shù)環(huán)境中,Structure相當于C語言的struct*。

          在SYSTEMTIME類中的字段和C結構體中的相對應的屬性字段的順序是一一對應的。保證字段順序的一致性是非常重要的。例如,我發(fā)現(xiàn)交換wYear和wMonth會導致wYear和wMonth值互換。

          每個字段在java中是short integer類型的。按照JNA首頁上 “默認類型映射”章節(jié)給出的提示,這個short integer分配類型是正確。然而,我們應該知道一個重要的區(qū)別:Windows平臺下的WORD類型等同于C語言環(huán)境中的16-bit的無符號的short integer,而java中short integer是16-bit有符號的short integer。

          一個類型映射的問題

          通過比較一個API 函數(shù)返回的整型值,你會發(fā)現(xiàn)Windows/C 語言的無符號整型和Java語言的有符號整型的JNA類型映射是有問題的。在比較的過程中,如果你不細心,那么錯誤的執(zhí)行過程可能導致決定性情況。導致這種后果是因為忘記任何數(shù)值的符號位的確定是根據(jù):在無符號整型的情況下會被解釋為正號,而在有符號整型的進制中被理解為負號的。

          通過Kernel32獲取本地時間(Access the local time with Kernel32)

          JNA首頁上的GetSystemTime()示例已經(jīng)表明必須使用預先申明的接口為本地庫分配一個實例對象。你可以通過com.sun.jna.Native類中靜態(tài)公用方法loadLibrary(String name, Class interfaceClass)來完成上述的目標。Listing 3 所示:

          Listing 3. LocalTime.java

          // LocalTime.java

          import com.sun.jna.*;

          public class LocalTime
          {
             public static void main (String [] args)
             {
                Kernel32 lib = (Kernel32) Native.loadLibrary ("kernel32",
                                                              Kernel32.class);
                Kernel32.SYSTEMTIME time = new Kernel32.SYSTEMTIME ();
                lib.GetLocalTime (time);
                System.out.println ("Year is "+time.wYear);
                System.out.println ("Month is "+time.wMonth);
                System.out.println ("Day of Week is "+time.wDayOfWeek);
                System.out.println ("Day is "+time.wDay);
                System.out.println ("Hour is "+time.wHour);
                System.out.println ("Minute is "+time.wMinute);
                System.out.println ("Second is "+time.wSecond);
                System.out.println ("Milliseconds are "+time.wMilliseconds);
             }
          }


          Listing 3 執(zhí)行Kernel32 lib = (Kernel32) Native.loadLibrary ("kernel32", Kernel32.class);來分配一個Kernel32實例對象并且裝載kernel32.dll。因為kernel32.dll是Windows平臺下標準的dll文件,所以不要指定訪問這個庫的路徑。然而,如果找不到這個dll文件,loadLibrary()會拋出一個UnsatisfiedLinkError異常。

          Kernel32.SYSTEMTIME time = new Kernel32.SYSTEMTIME ();創(chuàng)建了一個SYSTEMTIME結構體的示例。初始化后下面是lib.GetLocalTime (time);,這句話使用本地的時間/日期來給這個實例賦值。幾個System.out.println()語句是輸出這些值。

          編譯和運行這個應用(Compile and run the application)

          這部分很容易。假設jna.jar、Kernel32.java和LocalTime.java是放在當前文件夾中,調用java –cp jna.jar;. LocalTime.java來編譯這個應用的源代碼。如果在Windows平臺下,調用invoke java –cp jna.jar;. LocalTime 來運行這個應用。你可以得到類似與Listing 4的輸出結果:

          Listing 4. 從LocalTime.java生成的輸出

          Year is 2007
          Month is 12
          Day of Week is 3
          Day is 19
          Hour is 12
          Minute is 35
          Second is 13
          Milliseconds are 156


          獲取操縱桿信息(Accessing joystick device info)

          上面的例子已經(jīng)介紹了JNA,但是這個獲取本地時間和日期的例子并沒有很好的利用這個技術,甚至也沒有體現(xiàn)JNI的價值。Java語言中的System.currentTimeMillis()函數(shù)已經(jīng)以毫秒的格式返回了這些信息。因為Java語言沒有為游戲控制器提供API,所以獲取操縱桿的信息更適合JNA的使用。

          例如,你要構建一個平臺無關的Java庫,而且這些庫使用JNA調用Linux, Mac OS X, Windwos和Unix平臺中本地的操縱桿API。為了簡潔和方便起見,這個例子僅僅是調用Windows平臺下的操縱桿API。而且我將重點介紹這個API很小的一部分。

          類似GetLocalTime(),第一步是辨別出操作桿API的DLL,這個DLL是winmm.dll,和kernel32.dll在同一個文件夾中,它包含了操作桿的API和其他的多媒體APIs。還需知道要被使用的操作桿函數(shù)基于C語言的聲明。這些函數(shù)聲明已經(jīng)在Listing 5中列出來了。

          Listing 5. C-based declarations for some Joystick API functions

          #define MAXPNAMELEN 32

          typedef struct
          {
             WORD  wMid;                  // manufacturer identifier
             WORD  wPid;                  // product identifier
             TCHAR szPname  MAXPNAMELEN ; // product name
             UINT  wXmin;                 // minimum x position
             UINT  wXmax;                 // maximum x position
             UINT  wYmin;                 // minimum y position
             UINT  wYmax;                 // maximum y position
             UINT  wZmin;                 // minimum z position
             UINT  wZmax;                 // maximum z position
             UINT  wNumButtons;           // number of buttons
             UINT  wPeriodMin;            // smallest supported polling interval when captured
             UINT  wPeriodMax;            // largest supported polling interval when captured
          }
          JOYCAPS, *LPJOYCAPS;

          MMRESULT joyGetDevCaps(UINT IDDevice, LPJOYCAPS lpjc, UINT cbjc);

          UINT joyGetNumDevs(VOID);


          操作桿API的函數(shù)(Functions of the Joystick API)

          在Windows平臺下是通過以joy作為函數(shù)名開始的函數(shù)以及被各種函數(shù)調用的結構體來實現(xiàn)操作桿API的。例如,joyGetNumDevs()返回的是這個平臺下支持的操作桿設備最多的數(shù)目;joyGetDevCaps()返回的是每個連接上的操縱桿的質量。

          joyGetDevCaps()函數(shù)需要3個參數(shù):
          * 處在0到joyGetNumDevs()-1之間的操作桿ID
          * 保存返回的質量信息的JOYCAPS結構體的地址
          * JOYCAPS結構體的字節(jié)大小
          雖然它的結果不同,這個函數(shù)返回的是一個32位的無符號整型結果,而且0表示一個已經(jīng)連接的操縱桿。

          JOYCAPS結構體有3種類型。Windows平臺下的WORD(16位無符號短整型)類型對應的是Java語言中16位有符號短整型。除此之外,Windows下的UINT(32位無符號整型)類型是和Java語言中32位有符號整型相對應的。而Windows平臺上的text character就是TCHAR類型。

          微軟通過TCHAR類型使開發(fā)人員能夠從ASCII類型的函數(shù)參數(shù)平滑的轉移到Unicode字符類型的函數(shù)參數(shù)上。而且,擁有text類型參數(shù)的函數(shù)的實現(xiàn)是通過宏轉變?yōu)閷腁SCII或者wide-character的函數(shù)。例如,joyGetDevCaps()是一個對應joyGetDevCapsA() 和 joyGetDevCapsW()的宏。

          使用TCHAR(Working with TCHAR)

          使用TCHAR和將TCHAR轉變的宏會導致基于C語言的申明向基于JNA接口的轉換
          變得有點復雜—你在使用ASCII或者wide-character版本的操縱桿函數(shù)嗎?兩種版本都在如下的接口中展示了:

          Listing 6. WinMM.java

          // WinMM.java

          import com.sun.jna.*;
          import com.sun.jna.win32.*;

          public interface WinMM extends StdCallLibrary
          {
             final static int JOYCAPSA_SIZE = 72;

             public static class JOYCAPSA extends Structure
             {
                public short wMid;
                public short wPid;
                public byte szPname [] = new byte [32];
                public int wXmin;
                public int wXmax;
                public int wYmin;
                public int wYmax;
                public int wZmin;
                public int wZmax;
                public int wNumButtons;
                public int wPeriodMin;
                public int wPeriodMax;
             }

             int joyGetDevCapsA (int id, JOYCAPSA caps, int size);

             final static int JOYCAPSW_SIZE = 104;

             public static class JOYCAPSW extends Structure
             {
                public short wMid;
                public short wPid;
                public char szPname [] = new char [32];
                public int wXmin;
                public int wXmax;
                public int wYmin;
                public int wYmax;
                public int wZmin;
                public int wZmax;
                public int wNumButtons;
                public int wPeriodMin;
                public int wPeriodMax;
             }

             int joyGetDevCapsW (int id, JOYCAPSW caps, int size);

             int joyGetNumDevs ();
          }


          Listing 6沒有介紹JNA的新特性。實際上,JNA強調了對本地庫的接口命名規(guī)則。同時,還展示了如何將TCHAR映射到Java語言中的byte和char數(shù)組。最后,它揭示了以常量方式聲明的結構體的大小。Listing 7展示了當調用joyGetDevCapsA() 和 joyGetDevCapsW()時如何使用這些常量。

          Listing 7. JoystickInfo.java

          // JoystickInfo.java

          import com.sun.jna.*;

          public class JoystickInfo
          {
             public static void main (String [] args)
             {
                WinMM lib = (WinMM) Native.loadLibrary ("winmm", WinMM.class);
                int numDev = lib.joyGetNumDevs ();

                System.out.println ("joyGetDevCapsA() Demo");
                System.out.println ("---------------------\n");

                WinMM.JOYCAPSA caps1 = new WinMM.JOYCAPSA ();
                for (int i = 0; i < numDev; i++)
                     if (lib.joyGetDevCapsA (i, caps1, WinMM.JOYCAPSA_SIZE) == 0)
                     {
                         String pname = new String (caps1.szPname);
                         pname = pname.substring (0, pname.indexOf ('\0'));
                         System.out.println ("Device #"+i);
                         System.out.println ("  wMid = "+caps1.wMid);
                         System.out.println ("  wPid = "+caps1.wPid);
                         System.out.println ("  szPname = "+pname);
                         System.out.println ("  wXmin = "+caps1.wXmin);
                         System.out.println ("  wXmax = "+caps1.wXmax);
                         System.out.println ("  wYmin = "+caps1.wYmin);
                         System.out.println ("  wYmax = "+caps1.wYmax);
                         System.out.println ("  wZmin = "+caps1.wZmin);
                         System.out.println ("  wZmax = "+caps1.wZmax);
                         System.out.println ("  wNumButtons = "+caps1.wNumButtons);
                         System.out.println ("  wPeriodMin = "+caps1.wPeriodMin);
                         System.out.println ("  wPeriodMax = "+caps1.wPeriodMax);
                         System.out.println ();
                     }

                System.out.println ("joyGetDevCapsW() Demo");
                System.out.println ("---------------------\n");

                WinMM.JOYCAPSW caps2 = new WinMM.JOYCAPSW ();
                for (int i = 0; i < numDev; i++)
                     if (lib.joyGetDevCapsW (i, caps2, WinMM.JOYCAPSW_SIZE) == 0)
                     {
                         String pname = new String (caps2.szPname);
                         pname = pname.substring (0, pname.indexOf ('\0'));
                         System.out.println ("Device #"+i);
                         System.out.println ("  wMid = "+caps2.wMid);
                         System.out.println ("  wPid = "+caps2.wPid);
                         System.out.println ("  szPname = "+pname);
                         System.out.println ("  wXmin = "+caps2.wXmin);
                         System.out.println ("  wXmax = "+caps2.wXmax);
                         System.out.println ("  wYmin = "+caps2.wYmin);
                         System.out.println ("  wYmax = "+caps2.wYmax);
                         System.out.println ("  wZmin = "+caps2.wZmin);
                         System.out.println ("  wZmax = "+caps2.wZmax);
                         System.out.println ("  wNumButtons = "+caps2.wNumButtons);
                         System.out.println ("  wPeriodMin = "+caps2.wPeriodMin);
                         System.out.println ("  wPeriodMax = "+caps2.wPeriodMax);
                         System.out.println ();
                     }
             }
          }

          盡管和LocalTime這個示例類似,JoystickInfo執(zhí)行WinMM lib = (WinMM) Native.loadLibrary ("winmm", WinMM.class);這句話來獲取一個WinMM的實例,并且載入winmm.dll。它還執(zhí)行WinMM.JOYCAPSA caps1 = new WinMM.JOYCAPSA (); 和 WinMM.JOYCAPSW caps2 = new WinMM.JOYCAPSW ();初始化必需的結構體實例。

          編譯和運行這個程序(Compile and run the application)

          假如jna.jar,WinMM.java和JoystickInfo.java在同一個文件夾中,調用 javac -cp jna.jar;. JoystickInfo.java 來編譯這個應用的源代碼。
          在windows平臺下,調用java -cp jna.jar;. JoystickInfo就可以運行這個應用程序了。如果沒有操縱桿設備,你應該得到Listing 8中的輸出。

          將C語言中的string類型轉換為Java語言的String類型

          pname = pname.substring (0, pname.indexOf ('\0')); 這段代碼將一個C string 轉換成了Java string. 如果不使用這個轉換,C語言的string結束符’\0’和string后面的無用字符都會成為Java語言中String實例對象的內容。

          Listing 8. 輸出操縱桿信息(Output of JoystickInfo)

          joyGetDevCapsA() Demo
          ---------------------

          joyGetDevCapsW() Demo
          ---------------------


          上面的輸出是因為每次調用joyGetDevCap()返回的是一個非空值,這表示沒有操縱桿/游戲控制器設備或者是出現(xiàn)錯誤。為了獲取更多有意思的輸出,將一個設備連接到你的平臺上并且再次運行JoystickInfo。如下,將一個微軟SideWinder即插即用游戲觸摸板設備聯(lián)上之后我獲取了如下的輸出:

          Listing 9. 操縱桿連接上之后的運行結果(Output after running JoystickInfo with a joystick attached)

          joyGetDevCapsA() Demo
          ---------------------

          Device #0
            wMid = 1118
            wPid = 39
            szPname = Microsoft PC-joystick driver
            wXmin = 0
            wXmax = 65535
            wYmin = 0
            wYmax = 65535
            wZmin = 0
            wZmax = 65535
            wNumButtons = 6
            wPeriodMin = 10
            wPeriodMax = 1000

          joyGetDevCapsW() Demo
          ---------------------

          Device #0
            wMid = 1118
            wPid = 39
            szPname = Microsoft PC-joystick driver
            wXmin = 0
            wXmax = 65535
            wYmin = 0
            wYmax = 65535
            wZmin = 0
            wZmax = 65535
            wNumButtons = 6
            wPeriodMin = 10
            wPeriodMax = 1000


          窗口透明度(Transparent windows)

          在這系列文章中上篇文章是關于Bernhard Pauler's 氣泡提示(balloontip)工程的。我構建了一個叫做VerifyAge的、包含有一個氣泡提示的GUI應用。Figure 1中顯示了這個GUI應用的一個小問題:這個氣泡提示的沒有經(jīng)過修飾的對話框部分遮住了應用窗口的一部分邊框,導致了無法點擊這個邊框的最小化和最大化按鈕,并且使整個GUI很難看.
          image
          盡管未修飾部分的對話框不能顯示氣泡提示的透明度,java語言不支持窗口透明度。幸運的是,我們可以通過使用com.sun.jna.examples.WindowUtils類調用JNA的examples.jar文件來解決這個問題。
          WindowUtils提供在Unix,Linux,Mac OS X和Windows平臺上使用JNA’s來實現(xiàn)窗口透明的工具方法。例如, public static void setWindowMask(final Window w, Icon mask) 讓你根據(jù)像素而不是通過預定的掩罩(mask)參數(shù)來選取某部分的窗口。這個功能將在Listing 10中展示:

          Listing 10. Using JNA to render a window transparent


          // Create a mask for this dialog. This mask has the same shape as the
          // dialog's rounded balloon tip and ensures that only the balloon tip
          // part of the dialog will be visible. All other dialog pixels will
          // disappear because they correspond to transparent mask pixels.

          // Note: The drawing code is based on the drawing code in
          // RoundedBalloonBorder.

          Rectangle bounds = getBounds ();
          BufferedImage bi = new BufferedImage (bounds.width, bounds.height,
                                                BufferedImage.TYPE_INT_ARGB);
          Graphics g = bi.createGraphics ();
          g.fillRoundRect (0, 0, bounds.width, bounds.height-VERT_OFFSET,
                           ARC_WIDTH*2, ARC_HEIGHT*2);
          g.drawRoundRect (0, 0, bounds.width-1, bounds.height-VERT_OFFSET-1,
                           ARC_WIDTH*2, ARC_HEIGHT*2);
          int [] xPoints = { HORZ_OFFSET, HORZ_OFFSET+VERT_OFFSET, HORZ_OFFSET };
          int [] yPoints = { bounds.height-VERT_OFFSET-1, bounds.height-VERT_OFFSET
                             -1, bounds.height-1 };
          g.fillPolygon (xPoints, yPoints, 3);
          g.drawLine (xPoints [0], yPoints [0], xPoints [2], yPoints [2]);
          g.drawLine (xPoints [1], yPoints [1], xPoints [2], yPoints [2]);
          g.dispose ();
          WindowUtils.setWindowMask (this, new ImageIcon (bi));


          在Listing 10中的代碼段是從本文代碼文檔(code archive)里的加強版的VerifyAge2 應用中的TipFrame的構造函數(shù)結尾部分摘錄的。這個構造函數(shù)定義了圍繞提示氣泡的掩罩(mask)的形狀,在這個形狀范圍里描繪不透明的像素。
          假如你當前文件夾中有examples.jar, jna.jar, 和 VerifyAge2.java,調用 javac -cp examples.jar;balloontip.jar VerifyAge2.java 來編譯源文件.然后調用java -Dsun.java2d.noddraw=true -cp examples.jar;balloontip.jar;. VerifyAge2運行這個應用. Figure 2 展示了透明示例.
          image

          總結(In conclusion)

          JNA項目有很長的歷史了(追溯到1999年),但是它第一次發(fā)布是在2006年11月。從此以后它慢慢的被需要將本地C代碼整合到Java工程中的開發(fā)者注意到了。因為JNA能夠用來解決JuRuby中常見一個問題:缺乏對POSIX調用的支持(lack of support for POSIX calls),它也在JRuby程序員中掀起些波浪。JNA也同樣被作為實現(xiàn)用低級C代碼繼承Ruby的一種解決方案(extending Ruby with low-level C code)。
          我喜歡使用JNA來工作,相信你也會發(fā)現(xiàn)它比使用JNI來訪問本地代碼更簡單、更安全。無需多言,JNA還有更多的特性在本文中沒有體現(xiàn)出來。查閱它的資源部分:獲取這個開源java項目更多的信息(learn more about this open source Java project)。用它做demo,而且在論壇(discussion forum )上共享你的經(jīng)驗。 下一個月我會帶著另一個開源項目回來的,這個開源項目會給你每天的java開發(fā)帶來益處。

          附錄:WindowUtils.setWindowMask()的替代品

          在剛剛寫完這篇文章后,我發(fā)現(xiàn)java語言支持在6u10版本中支持窗口的透明和形狀定制。讀完Kirill Grouchnikov的博客后,我用WindowUtils.setWindowMask()的替代品修改了VerifyAge2,如下:
          // Create and install a balloon tip shape to ensure that only this part
          // of the dialog will be visible.

          Rectangle bounds = getBounds ();
          GeneralPath gp;
          gp = new GeneralPath (new RoundRectangle2D.Double (bounds.x, bounds.y,
                                                             bounds.width,
                                                             bounds.height-
                                                             VERT_OFFSET,
                                                             ARC_WIDTH*2-1,
                                                             ARC_HEIGHT*2-1));
          gp.moveTo (HORZ_OFFSET, bounds.height-VERT_OFFSET);
          gp.lineTo (HORZ_OFFSET, bounds.height);
          gp.lineTo (HORZ_OFFSET+VERT_OFFSET+1, bounds.height-VERT_OFFSET);
          AWTUtilities.setWindowShape (this, gp);


          這段代碼使用新類AWTUtilities(在com.sun.awt包中),而且public void setWindowShape(Window w, Shape s)函數(shù)將TipFrame和JDialog窗口設置氣泡形狀。
          posted on 2009-05-07 11:08 lanxin1020 閱讀(817) 評論(0)  編輯  收藏 所屬分類: j2se
          主站蜘蛛池模板: 鄂托克前旗| 措美县| 芜湖县| 甘泉县| 高雄市| 张家港市| 那坡县| 崇阳县| 华池县| 大方县| 晴隆县| 资兴市| 凤城市| 嘉峪关市| 桑植县| 台安县| 万州区| 安达市| 蒙自县| 陈巴尔虎旗| 黄冈市| 聂荣县| 西城区| 平原县| 珠海市| 嘉峪关市| 于都县| 潢川县| 蓝田县| 平利县| 河东区| 嘉义市| 比如县| 绵竹市| 江西省| 平潭县| 景东| 洞口县| 金湖县| 台中市| 谷城县|