星期三, 二月 11, 2004

字节编码和字符串关系(Java)

问题:
如果一个字节数组编码包含了非uncode文本,可以把其按照其原始字符集重构为Unicode.
细节:

Byte Encodings and Strings
If a byte array contains non-Unicode text, you can convert the text to Unicode with one of the String constructor methods. Conversely, you can convert a String object into a byte array of non-Unicode characters with the String.getBytes method. When invoking either of these methods, you specify the encoding identifier as one of the parameters.
The example that follows converts characters between UTF-8 and Unicode. UTF-8 is a transmission format for Unicode that is safe for UNIX file systems. The full source code for the example is in the file StringConverter.java.

The StringConverter program starts by creating a String containing Unicode characters:

String original = new String("A" + "\u00ea" + "\u00f1" +
"\u00fc" + "C");

When printed, the String named original appears as:

AêñüC

To convert the String object to UTF-8, invoke the getBytes method and specify the appropriate encoding identifier as a parameter. The getBytes method returns an array of bytes in UTF-8 format. To create a String object from an array of non-Unicode bytes, invoke the String constructor with the encoding parameter. The code that makes these calls is enclosed in a try block, in case the specified encoding is unsupported:

try {
byte[] utf8Bytes = original.getBytes("UTF8");
byte[] defaultBytes = original.getBytes();

String roundTrip = new String(utf8Bytes, "UTF8");
System.out.println("roundTrip = " + roundTrip);
System.out.println();
printBytes(utf8Bytes, "utf8Bytes");
System.out.println();
printBytes(defaultBytes, "defaultBytes");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}

The StringConverter program prints out the values in the utf8Bytes and defaultBytes arrays to demonstrate an important point: The length of the converted text might not be the same as the length of the source text. Some Unicode characters translate into single bytes, others into pairs or triplets of bytes.

The printBytes method displays the byte arrays by invoking the byteToHex method, which is defined in the source file, UnicodeFormatter.java. Here is the printBytes method:

public static void printBytes(byte[] array, String name) {
for (int k = 0; k < array.length; k++) {
System.out.println(name + "[" + k + "] = " + "0x" +
UnicodeFormatter.byteToHex(array[k]));
}
}

The output of the printBytes method follows. Note that only the first and last bytes, the A and C characters, are the same in both arrays:

utf8Bytes[0] = 0x41
utf8Bytes[1] = 0xc3
utf8Bytes[2] = 0xaa
utf8Bytes[3] = 0xc3
utf8Bytes[4] = 0xb1
utf8Bytes[5] = 0xc3
utf8Bytes[6] = 0xbc
utf8Bytes[7] = 0x43
defaultBytes[0] = 0x41
defaultBytes[1] = 0xea
defaultBytes[2] = 0xf1
defaultBytes[3] = 0xfc
defaultBytes[4] = 0x43

字符和字节流(Character and byte Steam)的关系/Java.

From:http://java.sun.com/docs/books/tutorial/i18n/text/stream.html

Character and Byte Streams
The java.io package provides classes that allow you to convert between Unicode character streams and byte streams of non-Unicode text. With the InputStreamReader class, you can convert byte streams to character streams. You use the OutputStreamWriterclass to translate character streams into byte streams. The following figure illustrates the conversion process:


This figure has been reduced to fit on the page.
Click the image to view it at its natural size.

When you create InputStreamReader and OutputStreamWriter objects, you specify the byte encoding that you want to convert. For example, to translate a text file in the UTF-8 encoding into Unicode, you create an InputStreamReader as follows:

FileInputStream fis = new FileInputStream("test.txt");
InputStreamReader isr = new InputStreamReader(fis, "UTF8");

If you omit the encoding identifier, InputStreamReader and OutputStreamWriter rely on the default encoding. You can determine which encoding an InputStreamReader or OutputStreamWriter uses by invoking the getEncoding method, as follows:

InputStreamReader defaultReader = new InputStreamReader(fis);
String defaultEncoding = defaultReader.getEncoding();

The example that follows shows you how to perform character-set conversions with the InputStreamReader and OutputStreamWriter classes. The full source code for this example is in StreamConverter.java. This program displays Japanese characters. Before trying it out, verify that the appropriate fonts have been installed on your system. If you are using the JDK software that is compatible with version 1.1, make a copy of the font.properties file and then replace it with the font.properties.ja file.

The StreamConverter program converts a sequence of Unicode characters from a String object into a FileOutputStream of bytes encoded in UTF-8. The method that performs the conversion is called writeOutput:

static void writeOutput(String str) {

try {
FileOutputStream fos = new FileOutputStream("test.txt");
Writer out = new OutputStreamWriter(fos, "UTF8");
out.write(str);
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}

The readInput method reads the bytes encoded in UTF-8 from the file created by the writeOutput method. An InputStreamReader object converts the bytes from UTF-8 into Unicode and returns the result in a String. The readInput method is as follows:

static String readInput() {

StringBuffer buffer = new StringBuffer();
try {
FileInputStream fis = new FileInputStream("test.txt");
InputStreamReader isr = new InputStreamReader(fis,
"UTF8");
Reader in = new BufferedReader(isr);
int ch;
while ((ch = in.read()) > -1) {
buffer.append((char)ch);
}
in.close();
return buffer.toString();
} catch (IOException e) {
e.printStackTrace();
return null;
}
}

The main method of the StreamConverter program invokes the writeOutput method to create a file of bytes encoded in UTF-8. The readInput method reads the same file, converting the bytes back into Unicode. Here is the source code for the main method:

public static void main(String[] args) {

String jaString =
new String("\u65e5\u672c\u8a9e\u6587\u5b57\u5217");

writeOutput(jaString);
String inputString = readInput();
String displayString = jaString + " " + inputString;
new ShowString(displayString, "Conversion Demo");
}

The original string (jaString) should be identical to the newly created string (inputString). To show that the two strings are the same, the program concatenates them and displays them with a ShowString object. The ShowString class displays a string with the Graphics.drawString method. The source code for this class is in ShowString.java. When the StreamConverter program instantiates ShowString, the following window appears. The repetition of the characters displayed verifies that the two strings are identical:

字符转换的说明及定义

Transfer Encoding Syntax (TES)
A transfer encoding syntax is a reversible transform of encoded data which may (or may not) include textual data represented in one or more character encoding schemes.

Typically TES’s are engineered either to:

Avoid particular byte values that would confuse one or more Internet or other transmission/storage protocols: base64, uuencode, BinHex, quoted-printable, etc., or to:
Formally apply various data compressions to minimize the number of bits to be passed down a communication channel: pkzip, gzip, winzip, etc.
SCSU (and RCSU: see UTR #6: A Standard Compression Scheme for Unicode) should also be conceived of as transfer encoding syntaxes. They should not be considered CES's, in part because the compressed forms are not unique, but depend on the sophistication of the compression algorithm.
The Internet Content-Transfer-Encoding tags "7bit" and "8bit" are special cases. These are data width specifications relevant basically to mail protocols and which appear to predate true TES’s like quoted-printable. Encountering a "7bit" tag doesn’t imply any actual transform of data; it merely is an indication that the charset of the data can be represented in 7 bits, and will pass 7-bit channels ?C it is really an indication of the encoding form. In contrast, quoted-printable actually does a conversion of various characters (including some ASCII) to forms like "=2D", "=20", etc., and should be reversed on receipt to regenerate legible text in the designated character encoding scheme.

8 API Binding
Most API’s are specified in terms of either code units or serialized bytes. An example of the first are Java String and char APIs, which use UTF-16 code units. Another example is C and C++ wchar_t interfaces used for DBCS processing codes. For code units, the byte order of the platform is generally not relevant in the API; the same API can be compiled on platforms with any byte polarity, and will simply expect character data (as for any integral-based data) to be passed to the API in the byte polarity for that platform.

C and C++ char* APIs use serialized bytes, which could represent a variety of different character maps, including ISO Latin 1, UTF-8, Windows 1252, as well as compound character maps such as Shift-JIS or 2022-JP. A byte API could also handle UTF-16BE or UTF-16LE, which are serialized forms of Unicode. However, these APIs must be allow for the existence of any byte value, and typically use memcpy plus length instead of strcpy for manipulating strings.

如何使用LATIN 1模拟Unicode数据存储?

前言:
Latin 1(iso-8859-1)属于SBCS(Singe-byte character set).
使用0-127表示ASCII,,不过是8位全用的。
汉字输入DBCS(DOUBLE-Bytes Character set)
UCS-2:DBCS
Unicode( Universal character encoding )???璺?锛?
1.jsp加入<@ page Encoding="text/html;charset=utf-8">
强制Browser把所有提交的form数据使用UTF8.
2.Servlet必须设置request的CharacterEncoding为utf-8,可以使用支持Servlet 2.3规范的Web Container来做的.
3.在插入数据库Unicode的时候,把UTF8数据库分解为2个8位表示的iso-8859-1字符串.refactor
new String(data.getBytes(),"iso-8859-1")

显示的时候把读出来的ISO-8859-1数据重新还原为UTF-8new String(data.getBytes("iso-8859-1","UTF-8"))
这样在jsp中显示出来的就是汉字了.
<%=new String(data.getBytes("iso-8859-1","UTF-8")%>注意:data是刚从数据中读出的数据

About character encodings

About character encodings: About character encodings
A character encoding maps each character in a character set to a numeric value that can be represented by a computer. These numbers can be represented by a single byte or multiple bytes. For example, the ASCII encoding uses seven bits to represent the Latin alphabet, punctuation, and control characters.

You use Japanese encodings, such as Shift-JIS, EUC-JP, and ISO-2022-JP, to represent Japanese text. These encodings can vary slightly, but they include a common set of approximately 10,000 characters used in Japanese.

The following terms apply to character encodings:

SBCS Single-byte character set; a character set encoded in one byte per character, such as ASCII or ISO 8859-1.
DBCS Double-byte character set; a method of encoding a character set in no more than two bytes, such as Shift-JIS. Many character encoding schemes that are referred to as double-byte, including Shift-JIS, allow mixing of single-byte and double-byte encoded characters. Others, such as UCS-2, use two bytes for all characters.
MBCS Multiple-byte character set; a character set encoded with a variable number of bytes per character, such as UTF-8.
The following table lists some common character encodings; however, there are many additional character encodings that browsers and web servers support:

Encoding
Type
Description

ASCII
SBCS
7-bit encoding used by English and Indonesian Bahasa languages

Latin-1
(ISO 8859-1)
SBCS
8-bit encoding used for many Western European languages

Shift_JIS
DBCS
16-bit Japanese encoding (Note that you must use an underscore character (_), not a hyphen (-) in the name in CFML attributes.)

EUC-KR
DBCS
16-bit Korean encoding

UCS-2
DBCS
Two-byte Unicode encoding

UTF-8
MBCS
Multibyte Unicode encoding. ASCII is 7-bit; non-ASCII characters used in European and many Middle Eastern languages are two-byte; and most Asian characters are three-byte


The World Wide Web Consortium maintains a list of all character encodings supported by the Internet. You can find this information at www.w3.org/International/O-charset.html.

Computers often must convert between character encodings. In particular, the character encodings most commonly used on the Internet are not used by Java or Windows. Character sets used on the Internet are typically single-byte or multiple-byte (including DBCS character sets that allow single-byte characters). These character sets are most efficient for transmitting data, because each character takes up the minimum necessary number of bytes. Currently, Latin characters are most frequently used on the web, and most character encodings used on the web represent those characters in a single byte.

Computers, however, process data most efficiently if each character occupies the same number of bytes. Therefore, Windows and Java both use double-byte encoding for internal processing.

GH Character Sets

GH Character Sets
Character sets are standards that map specific real world characters (code elements) to specific numbers (code points) and then encode those code points as bytes (character encoding or encoding schemes). EG:

The Yen cold element is ?.
The Yen's code point in different code point systems:
ASCII: out of range.
Window ANSI 1252: 165 = 0xA5.
ISO Latin-1 (iso-8859-1): 165 = 0xA5.
Unicode/UCS-2: 165 = 0xA5 = U+00A5.
Unicode/UCS-4: 165 = 0xA5 = U+000000A5
The Yen is character encoded this way in different encoding schemes:
ASCII: out of range.
Windows ANSI 1252: 0xA5 = 10100101.
ISO Latin-1 (iso-8859-1): 0xA5 = 10100101.
UTF-8: 11000010 10100101.
UTF-16: 00000000 10100101.
UTF-32: 00000000 00000000 00000000 10100101
You may also want to see my articles on Typography.

Once a set of bits have been mapped to a code point, then that code point can be mapped to its code element character, and then software can present that character and apply different fonts, styles, and sizes. EG:

In the ASCII character map the code element J has a code point of 74 (x4A). This code point is encoded as the binary number 1001010 (which is the x4A). The binary code can then be interpreted by programs such as browsers and presented to users:

Formatting Applied Example
Font J
Comic
J
Winding
(normally a smiley face)

Style J
underlined
J
italic

Size J
HTML Size 6
J
HTML Size 1


Choosing a character set is important especially if you have to deal with international code, multiple platforms, or databases. EG:

When SQL Server is installed, a Sort Order ID must be set that is based on a character code. If you try to restore a database to that installation, then it must have the same Sort Order ID. Otherwise you may have to rebuild the database, using something like rebuildm.exe.

Here are some of the major character sets:
ASCII (American Standard Code for Information Interchange, aka Standard ASCII; plain text, ISO-646). A SBCS (Single Byte Character Set) that uses 7 bits of a byte (0-127; x0-x7F) to make 96 basic English characters as well as 32 control characters. The 8th bit is used for parity checking. Most of the prevalent character sets are based on ASCII. Used by Linux/Unix.
SBCSs that use the 8th bit are called "High ASCII" since they encode additional characters by utilizing the bit above the ASCII range. Here are SBCSs that use all 8 bits of a byte (0-255; x0-FF) to encode up to 256 characters. There are different character sets (aka code pages) for different uses, languages or language groups. Many of the character sets are super sets of ASCII.
OEM character sets (Original Equipment Manufacturer). The 8th bit often contained characters for line drawings as a carry over from pre-GUI (Graphics User Interface) days. Used by DOS, OS/2, floppy disks, and the FAT system (File Allocation System).
ISO character sets (International Organization for Standardization). ISO Latin 1 (iso-8859-1) is good for most Western languages but I discuss it on my ANSI page because of it is closely tied to Windows ANSI 1252. Used by the Mac OS.
ANSI character sets (American National Standards Institute). Used by the Windows 3.x/9x OS.
Unicode, aka UCS (Universal Character Set). A MBCS (Multi-Byte Character Set) that uses 2-4 bytes worth of possible code points. The code points may be encoded using a 1-6 bytes per character. Unicode is the sensible international and cross-platform character set. Used by Windows NT/2000 and Linux/Unix.
Here is a summary table of the major character sets:

Character Set Bits Decimal Hexadecimal
ASCII 7/8 127 7F
High ASCII 8 255 FF
Unicode, UCS-2 16 65,536 FFFF
UCS-4 31 2,147,483,648 7FFF FFFF

Here are some of the available code page identifiers used by Windows..

Identifier Meaning OEM/ANSI Comment
037 EBCDIC Used in mainframes, esp. IBM.
437 MS-DOS United States OEM IBM DOS and OS/2.
Aka: IBM PC Extended Character Set; Extended ASCII; High ASCII; 437 U.S. English.
500 EBCDIC "500V1"
708 Arabic (ASMO 708) OEM
709 Arabic (ASMO 449+, BCON V4) OEM
710 Arabic (Transparent Arabic) OEM
720 Arabic (Transparent ASMO) OEM
737 Greek (formerly 437G) OEM
775 Baltic OEM
850 MS-DOS Multilingual (Latin I) OEM Standard MS DOS.
Aka: 850 Multilingual.
852 MS-DOS Slavic (Latin II) OEM
855 IBM Cyrillic (primarily Russian) OEM
857 IBM Turkish OEM
860 MS-DOS Portuguese OEM
861 MS-DOS Icelandic OEM
862 Hebrew OEM
863 MS-DOS Canadian-French OEM
864 Arabic OEM
865 MS-DOS Nordic OEM
866 MS-DOS Russian OEM
869 IBM Modern Greek OEM
874 Thai OEM/ANSI
875 EBCDIC
932 Japanese OEM/ANSI
936 Chinese (PRC, Singapore; Simplified) OEM/ANSI
949 Korean OEM/ANSI
950 Chinese (Taiwan; Hong Kong SAR, PRC; Traditional) OEM/ANSI
1026 EBCDIC
1200 Unicode (BMP of ISO 10646) ANSI Window NT/2000 and HTML.
Aka: ISO-1604-6; UCS;
Unicode (UTF-7): utf-7; csUnicode11UTF7, unicode-1-1-utf-7, x-unicode-2-0-utf-7; 65000.
Unicode (UTF-8): utf-8; unicode-1-1-utf-8, unicode-2-0-utf-8, x-unicode-2-0-utf-8; 65001.

1250 Windows 3.1 Eastern European ANSI
1251 Windows 3.1 Cyrillic ANSI
1252 Windows 3.1 US (ANSI) ANSI Windows 3.x/9x, Macs, and HTML.
ANSI comes in two versions (the difference is found at decimal 128-159 (hexadecimal 80-9F)):
Windows ANSI. Aka: Western European (Windows); windows-1252; US/Western European; Western.
ISO Latin 1 ANSI. Aka: Western European (ISO); iso-8859-1; ANSI_X3.4-1968; ANSI_X3.4-1986; ascii; cp367; cp819; csASCII; IBM367; ibm819; iso-ir-100; iso-ir-6; ISO646-US; iso8859-1; ISO_646.irv:1991; iso_8859-1; iso_8859-1:1987; latin1; us; us-ascii; x-ansi; iso-latin-1.

1253 Windows 3.1 Greek ANSI
1254 Windows 3.1 Turkish ANSI
1255 Hebrew ANSI
1256 Arabic ANSI
1257 Baltic ANSI
1258 Vietnamese
1361 Korean (Johab) OEM
10000 Macintosh Roman
10001 Macintosh Japanese
10006 Macintosh Greek I
10007 Macintosh Cyrillic
10029 Macintosh Latin 2
10079 Macintosh Icelandic
10081 Macintosh Turkish

Note that aliases in bold is the preferred charset ID for the HTML tag:



Character sets are often encoded in plain text documents such as HTML and XML using either NCR (Numeric Character References) or CER (Character Entity Reference). CERs use symbolic names so that authors need not remember code points. EG: For the Yen character (?), the NCR is either ¥ or ¥, while the CER is ¥.


How to Exploit Multiple Monitor Support in Memphis and Windows NT 5.0

How to Exploit Multiple Monitor Support in Memphis and Windows NT 5.0: "How to Exploit Multiple Monitor Support in Memphis and Windows NT 5.0"
How to Exploit Multiple Monitor Support in Memphis and Windows NT 5.0


David Campbell

This article assumes you're familiar with Win32


Code for this article: MultiMonitors.exe (11KB)
David Campbell is a support engineer in Microsoft Developer Support who supports user interface issues for Windows, specializing in shell extensions.



Have you noticed how precious screen real estate has become? The monitor never seems big enough and the resolution never seems high enough. I want to see Outlook?, Microsoft? Internet Explorer, Microsoft Developer Studio, and the app that I'm debugging all at the same time?I just don't have enough room!
If you're too low on the company totem pole to requisition a 35-inch video monitor, there is an alternative: multiple monitors. Memphis (the codename for the next version in the Windows? 95 family) and Windows NT? 5.0 both contain a set of features that will allow you to use multiple display devices at the same time; that is, multiple video cards and monitors on the same machine, all part of the same virtual desktop, all with seamless support built right into the operating system.
Previous versions of Windows 95 and Windows NT had no built-in support for multiple monitors. Only a few custom solutions existed, many of which imposed serious restrictions on a system such as the hardware required or supported, the shape of the desktop, the color depth and resolution, and, most significantly, compatibility with existing applications.
In this article I'll review the theory and operation of multiple monitor support, look at the new APIs and some of the programming issues you should consider, and go over how to actually install multiple monitors, including how the user interface changes in response to multiple monitors. My information is based on beta versions of the new operating systems. As with all articles that discuss beta software, keep in mind that the information here is preliminary, and could change suddenly and drastically. Don't ship a product based on this information until you have built and tested it on release versions of the software.

Theory and Operation of Multiple Monitors

Although your needs obviously will dictate how you set up your system, I'll discuss three options here for using multiple monitors.
Large desktops The Windows desktop can now cover more than one monitor with no restrictions on size, position, resolution, or refresh rates (see Figure 1). The system can be configured to the size and relative position of each monitor. Applications can be moved seamlessly from one monitor to another, or be displayed simultaneously on more than one monitor.




Screen duplication/remote display Alternatively, you can use secondary monitors to display the same data as the primary monitor (see Figure 2). This would be useful for training or for presentations to a group.Screen duplication could also be used to control remote applications such as in a support situation or for telecommuting.
Multiple independent displays A monitor does not need to be part of the Windows desktop for applications to have access to it. Applications can make use of an additional display even if it isn't part of the desktop. For example, if you have a large, high-resolution display for a CAD application, your application can use that monitor for output through Windows APIs, without requiring it to be part of the virtual desktop. That means you don't have to worry about accidentally dragging windows onto that screen. It's like having a display monitor you can draw on via GDI, but it isn't part of the Windows desktop so you don't have a taskbar or any other shell goodies to worry about.

Virtual Desktop

On single-monitor systems, the actual desktop is the same size and shape as the only monitor on the system. On a multimonitor system, each monitor is actually a view onto the underlying virtual desktop. The area that each monitor presents can be adjusted in the control panel. The primary monitor will always have compatible coordinates corresponding to 0,0 for the upper-left corner and the x and y resolution for the lower-right corner (see Figure 3). The actual coordinates that the secondary monitors view will depend on the layout of the monitors, which is also decided in the control panel and is usually modeled on the actual physical layout of the monitors on the user's desk.


Figure 3: Virtual Desktop


You can use the control panel to change the resolution of any of the monitors, but you can only change the coordinates of the secondary monitors. The primary monitor's top-left coordinates must remain 0,0 for compatibility. In addition, all the monitors must touch each other on the virtual desktop. This restriction allows the system to maintain the illusion of a single, large desktop that you can maneuver on freely, seamlessly crossing from one monitor to another. At no point do you lose track of the mouse while travelling between monitors.
Since the desktop area that each monitor actually views must be adjacent to another monitor, the virtual desktop can be calculated as the bounding rectangle of all of the rectangular areas that can be seen on all of the existing monitors. Given that the coordinate system must be continuous, the coordinates for the secondary monitor simply continue from the primary. For example, if a secondary monitor is adjacent to the right edge of the primary monitor, its coordinates will start at the primary monitor's x resolution + 1 and continue to primary x resolution + secondary x resolution. If the primary and secondary monitors each have a resolution of 1024 X 768, then a secondary monitor attached to the right of the primary monitor will have coordinates from 1024,0 to 2047,767.
Also, some of the virtual desktop area may actually be offscreen in the sense that there is no monitor that views that area. This may occur if the monitors are not completely lined up or if there are monitors with different resolutions. For example, say I have a 1024X768 primary monitor and an 800 X 600 secondary monitor. The primary monitor has coordinates 0,0 to 1023,767, and the secondary monitor, which is attached to the left of my primary, has coordinates ?800,168 to -1,767. This results in an area with coordinates from ?800,0 to -1,167 that is not displayed on any monitor. For the most part, you don't have to worry about this area since Windows will not let the user move the mouse there, but keep in mind that the area is included in the calculation of the virtual desktop. Therefore, for my system the virtual desktop has coordinates from ?800,0 to 1023,767.

What's New

OK, you've installed multiple monitors and now you want to take it further. Maybe you want to develop a custom app that is multimonitor-aware, or maybe you want to make use of a custom display device. Maybe you just want to make sure your existing application isn't doing anything that looks odd on a multimonitor system.
Several new APIs have been added for determining which monitor something is displayed on and for getting the settings for each monitor that a window may be visible on. Figure 4 is a summary of some of the key APIs.
You can now develop an application that is multimonitor-aware yet still runs on existing Windows 95 and Windows NT 4.0 machines. There is a new include file (see Figure 5) that uses GetProcAddress to link these APIs to the corresponding operating system APIs, if they exist. If not, the include file provides default implementations so the same EXE will run on Windows 95, Windows NT 4.0, Memphis, and Windows NT 5.0. On a Windows 95 or Windows NT 4.0 machine, your code will get stubbed to the versions of the APIs in the header file (which return correct values for those systems to your code). However, on an operating system that is multimonitor-aware, the code will pass through to the actual system APIs.
Now let's take a detailed look at the APIs for multimonitor support. Each physical display device is represented to the application by a monitor handle called an HMONITOR. A physical device has the same HMONITOR value throughout its lifetime, even across changes to display settings, as long as it remains a part of the desktop. A valid HMONITOR is guaranteed to be non-NULL. When a WM_DISPLAYCHANGE message is broadcast, any HMONITOR may have its settings changed in some way, or it may be removed from the desktop and become invalid.
The MonitorFromPoint API returns the monitor that contains pt.

HMONITOR MonitorFromPoint(POINT pt, DWORD dwFlags);


If no monitor contains pt, the return value depends upon the dwFlags field, which can be MONITOR_DEFAULTTONULL to return NULL, MONITOR_DEFAULTTOPRIMARY to return the HMONITOR of the primary monitor, or MONITOR_DEFAULTTONEAREST to return the HMONITOR nearest to the point pt.
The MonitorFromRect API returns the monitor that intersects lprc.

HMONITOR WINUSERAPI MonitorFromRect(LPCRECT lprc,
DWORD dwFlags);


If no monitor intersects lprc, the return value depends upon the dwFlags field. The flags from MonitorFromPoint are used. If the rect intersects more than one monitor, this returns the monitor containing most of the rectangle.
The MonitorFromWindow API returns the monitor that a window belongs to.

HMONITOR WINUSERAPI MonitorFromWindow(HWND hwnd,
DWORD dwFlags);


If a window doesn't belong to a monitor, the return value depends upon the dwFlags field. The flags from MonitorFromPoint are used. If the window intersects more than one monitor, this returns the monitor containing the majority of the window.
The well-known SystemParametersInfo API now includes changes to the uiAction values SPI_GETWORKAREA and SPI_SETWORKAREA. SPI_GETWORKAREA retrieves the size of the working area, which is the portion of the screen not obscured by the taskbar. The pvParam parameter points to the RECT structure that receives the coordinates of the working area. Likewise, SPI_SETWORKAREA sets the size of the work area. The pvParam parameter points to the RECT structure that contains the coordinates of the work area. SPI_SETWORKAREA has been modified to change the work area of the monitor that pvParam belongs to. If pvParam is NULL, the work area of the primary monitor is modified. SPI_GETWORKAREA always returns the work area of the primary monitor. If an app needs the work area of a monitor other than the primary one, it should call GetMonitorInfo (which I'll describe later).
The GetSystemMetrics API has had changes and clarifications made to some of its nIndex values. If you use SM_CXSCREEN or SM_CYSCREEN, you still get the pixel width and height of the screen, but this is only for the primary screen.


Figure 6: New GetSystemMetrics Values


The same goes for GetDeviceCaps(hdcPrimaryMonitor, HORZRES/VERTRES). If you use SM_XVIRTUALSCREEN, SM_YVIRTUALSCREEN, SM_CXVIRTUALSCREEN, or SM_CYVIRTUALSCREEN, you get the left, top, width, and height of the virtual screen in pixels, respectively (see Figure 6). The SM_SAMEDISPLAYFORMAT returns true if all monitors have the same color format. Note that two displays can have the same bit depth but different color formats if the red, green, and blue pixels have different sizes or are located in different places in a pixel. SM_CMONITORS tells you how many monitors are on the desktop.
This piece of sample code (just pretend there really is a Print function like this)


Print("SM_CMONITORS is %d", GetSystemMetrics(SM_CMONITORS));
Print("SM_SAMEDISPLAYFORMAT is %d", GetSystemMetrics(SM_SAMEDISPLAYFORMAT));
Print("SM_XVIRTUALSCREEN is %d", GetSystemMetrics(SM_XVIRTUALSCREEN));
Print("SM_YVIRTUALSCREEN is %d", GetSystemMetrics(SM_YVIRTUALSCREEN));
Print("SM_CXVIRTUALSCREEN is %d", GetSystemMetrics(SM_CXVIRTUALSCREEN));
Print("SM_CYVIRTUALSCREEN is %d", GetSystemMetrics(SM_CYVIRTUALSCREEN));


would produce the following output on my system:

SM_CMONITORS is 2
SM_SAMEDISPLAYFORMAT is 1
SM_XVIRTUALSCREEN is -800
SM_YVIRTUALSCREEN is 0
SM_CXVIRTUALSCREEN is 1824
SM_CYVIRTUALSCREEN is 768


As you can see, my sample system has two monitors, both using the same pixel color depth. One of my monitors is 1024X768 pixels and the other is 800 X 600 pixels. The 800 X 600 display is on the left, the 1024 X 768 display is on the right, and their bottom pixels are aligned.
The GetMonitorInfo API returns metric information relevant to a particular monitor (see Figure 7). cbSize is the size of the MONITORINFO struct. A valid instance of a MONITORINFO struct must have this field set equal to sizeof(MONITORINFO) or sizeof(MONITORINFOEX) before a call to GetMonitorInfo is made. rcMonitor is the rectangle of the monitor in the virtual screen. rcWork is the rectangle of the work area of the monitor in the virtual screen. dwFlags provide some additional information about the monitor. The only flag currently defined is MONITORF_ PRIMARY. szDevice is the name of the device, and it is only present in the MONITORINFOEX struct. Most apps will never use this field, and can pass in a MONITORINFO struct instead of a MONITORINFOEX. For example, this piece of code


MONITORINFOEX mi;
mi.cbSize = sizeof(mi);
GetMonitorInfo(hMonitor, (MONITORINFO*)&mi);

Print("Monitor %08X", hMonitor);
Print(" szDevice = '%s'", (LPSTR)mix.szDevice);
Print(" rcMonitor = [%d,%d,%d,%d]", mi.rcMonitor);
Print(" rcWork = [%d,%d,%d,%d]", mi.rcWork);
Print(" dwFlags = %08X", mi.dwFlags);



produces the output:


Monitor 00000FAE
szDevice = '\\.\Display1'
rcMonitor = [0,0,1024,768]
rcWork = [0,0,1024,740]
dwFlags = 00000001



The EnumDisplayMonitors API lets you paint into a DC that spans more than one display. It calls you back for each monitor that intersects your window and gives you an HDC that is appropriate to that monitor. The capabilities and color depth information from that HDC will match those of the monitor. The app can then paint the piece of its window on that monitor into that DC.
To illustrate, here's how an app like PowerPoint? could use this API. Assume half of a slide show window is on a 256-color monitor and the other half is on a 24-bit true color monitor. The operating system would call the app once for the 256-color monitor piece, and the app would dither the wash for the background. Then the operating system would call the app a second time for the piece on the 24-bit display. The presentation app would take advantage of all of the colors to draw a higher resolution screen.
Keep in mind that applications are not forced to do this; they can just continue painting, assuming the whole screen is the color depth of the primary monitor which will look as good as GDI can do by itself. But if an app wants to, it can paint optimally for the particular display using cus- tom algorithms smarter than GDI's defaults. In the API declaration


BOOL WINAPI EnumDisplayMonitors(
HDC hdc,
LPCRECT lprcClip,
MONITORENUMPROC lpfnEnum,
LPARAM dwData)



hdc is an HDC with a particular visible region. The hdcMonitor passed to MonitorEnumProc will have the capabilities of that monitor, with its visible region clipped to the monitor and hdc. If hdc is NULL, the hdcMonitor passed to MonitorEnumProc will be NULL. lprcClip is a rectangle for clipping the area. If hdc is non-NULL, the coordinates have the origin of hdc. If hdc is NULL, the coordinates are virtual screen coordinates. If lprcClip is NULL, no clipping is performed.
lpfnEnum is a pointer to the enumeration function. dwData is application-defined data that is passed through to the enumeration function


BOOL CALLBACK MonitorEnumProc(
HMONITOR hmonitor,
HDC hdcMonitor,
LPRC lprcMonitor,
DWORD dwData)



where hmonitor is the monitor. The callback is called only if it intersects the visible region of hdc and is non-NULL and lprcClip is non-NULL. hdcMonitor is an HDC with capabilities specific to the monitor and clipping set to the intersection of hdc, lprcClip, and the dimensions of the monitor. If hdc is NULL, hdcMonitor will be NULL. lprcMonitor is the clipping area that intersects that monitor. If hdcMonitor is non-NULL, the coordinates have the origin of hdcMonitor. If hdcMonitor is NULL, the coordinates are virtual screen coordinates. dwData is application-defined data that is passed in EnumDisplayMonitors.
Here are some examples of how to use EnumDisplayMonitors. To paint in response to a WM_PAINT message using the capabilities of each monitor, an app would write the following in its window procedure:


case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
EnumDisplayMonitors(hdc, NULL,
MyPaintEnumProc, 0);
EndPaint(hwnd, &ps);



To paint the top half of a window using the capabilities of each monitor, an app would write the following:


GetClientRect(hwnd, &rc);
rc.bottom = (rc.bottom - rc.top) / 2;
hdc = GetDC(hwnd);
EnumDisplayMonitors(hdc, &rc, MyPaintEnumProc, 0);
ReleaseDC(hwnd, hdc);



To paint the entire screen using the capabilities of each monitor, the app would call:


hdc = GetDC(NULL);
EnumDisplayMonitors(hdc, NULL, MyPaintScreenEnumProc, 0);
ReleaseDC(NULL, hdc);



To get information about all the displays on the desktop, the app would call:


EnumDisplayMonitors(NULL, NULL, MyInfoEnumProc, 0);



The EnumDisplayDevices API allows you to determine the actual list of devices available on a given machine:


BOOL WINAPI *EnumDisplayDevices(LPVOID lpReserved,
int iDeviceNum,
DISPLAY_DEVICE * pDisplayDevice,
DWORD dwFlags);



lpReserved is reserved for future use and must be zero. iDeviceNum is a zero-based index on the device from which you want to retrieve information. pDisplayDevice is a pointer to a DISPLAY_DEVICE structure for the return information. dwFlags must currently be zero. The DISPLAY_DEVICE structure looks like


typedef struct {
DWORD cb;
CHAR DeviceName[32];
CHAR DeviceString[128];
DWORD StateFlags;
} DISPLAY_DEVICE;



where the state flags are defined as


#define DISPLAY_DEVICE_ATTACHED_TO_DESKTOP 0x00000001
#define DISPLAY_DEVICE_MULTI_DRIVER 0x00000002
#define DISPLAY_DEVICE_PRIMARY_DEVICE 0x00000004
#define DISPLAY_DEVICE_MIRRORING_DRIVER 0x00000008
#define DISPLAY_DEVICE_VGA 0x00000010



Here's more sample code


DISPLAY_DEVICE dd;
ZeroMemory(&dd, sizeof(dd));
dd.cb = sizeof(dd);
for(i=0; EnumDisplayDevices(NULL, i, &dd, 0); i++)
{
Print("Device %d:", i);
Print(" DeviceName: '%s'", dd.DeviceName);
Print(" DeviceString: '%s'", dd.DeviceString);
Print(" StateFlags: %s%s%s%s",
((dd.StateFlags &
DISPLAY_DEVICE_ATTACHED_TO_DESKTOP) ?
"desktop " : ""),
((dd.StateFlags &
DISPLAY_DEVICE_PRIMARY_DEVICE ) ?
"primary " : ""),
((dd.StateFlags & DISPLAY_DEVICE_VGA) ?
"vga " : ""),
((dd.StateFlags &
DISPLAY_DEVICE_MULTI_DRIVER ) ?
"multi " : ""),
((dd.StateFlags &
DISPLAY_DEVICE_MIRRORING_DRIVER ) ?
"mirror " : ""));
}



and the output it produces:


Device 0:
DeviceName: '\\.\Display1'
DeviceString: 'ATI Graphics Pro Turbo PCI (mach64 GX)'
StateFlags: desktop primary vga
Device 1:
DeviceName: '\\.\Display2'
DeviceString: 'ATI Graphics Pro Turbo PCI (mach64 VT)'
StateFlags: desktop




Programming Considerations
Okay, maybe you don't need multimonitor support in your application, but what can you do to avoid being "multimonitor challenged"? There are some common Windows development practices that can make your application misbehave on a multimonitor system.
The most common problem to expect with multiple monitors is centering windows and dialogs on a monitor. In my opinion, this practice has questionable benefits to begin with. However, if you are going to do this you should center your window to the main application, not the monitor. If you must center on a monitor make sure you're using the correct one. Nothing looks cheesier than a window centered on the wrong monitor! If you are trying to center a dialog, use the DS_CENTER dialog style. This lets the operating system do the work and it will place the centered dialog on the correct monitor. Also, keep in mind that SM_CXSCREEN and SM_CYSCREEN always refer to the primary monitor, not necessarily the monitor that displays your application.
Using SM_xxVIRTUALSCREEN as a replacement or quick fix isn't a good idea either, since this results in your window being centered on the virtual desktop. There may be some cases where you can replace SM_xxSCREEN with SM_xxVIRTUALSCREEN, but if you do this, take into account what the code is doing rather than just using a global search and replace. (In particular, if you're calculating the center of a monitor for your splash screen, SM_xxVIRTUALSCREEN is not the right way to do it.)
Even without multiple monitors, centering windows and dialogs can be problematic. Let's say a user has a high-resolution monitor and the application is using only a small percentage of the screen area. If I center new windows to the monitor, they may come up in a location completely unrelated to where the application is.
Currently, most developers use code similar to the following to center a window or dialog:



void CenterWindowOld(HWND hWnd)
{
RECT rcWnd;

GetWindowRect(hWnd, &rcWnd);

// because assumptions were made about the
// origin and the screen, the equation
// to center was simplified in many cases to:
rcWnd.left = ((GetSystemMetrics(SM_CXSCREEN) -
(rcWnd.right - rcWnd.left)) / 2);
rcWnd.top = ((GetSystemMetrics(SM_CYSCREEN) -
(rcWnd.bottom - rcWnd.top )) / 2);

SetWindowPos(hWnd, NULL, rcWnd.left, rcWnd.top,0,0,
SWP_NOSIZE | SWP_NOZORDER SWP_NOACTIVATE);
}



Assuming that you cannot use the DS_CENTER window style (which is really the best way to center a dialog), you could try something similar to the code in Figure 8.
As I mentioned earlier, SM_CXSCREEN and SM_ CYSCREEN are now going to return the x and y resolution of the primary monitor, and SM_XVIRTUALSCREEN, SM_YVIRTUALSCREEN, SM_CXVIRTUALSCREEN, and SM_CYVIRTUALSCREEN are provided to get the origin and extent of the virtual desktop. Keeping that in mind, you can see that the code in Figure 8 is functionally equivalent to the code above for systems with only one monitor.


Coordinate Issues
Another problem existing apps will face is that 0,0 is no longer guaranteed to be the most visible upper-left position. This means that negative coordinates can not only exist, but they may be visible. Applications that use negative coordinates to hide their app or assume that there are no negative coordinates may run into problems. This is also true for coordinates greater than SM_CXSCREEN and SM_CYSCREEN.
Similarly, you may have problems if you use 0,0 or SM_CXSCREEN or SM_CYSCREEN for maintaining visibility. Some apps assume that if their coordinates are negative or greater than SM_CXSCREEN or SM_ CYSCREEN that they've somehow wandered off the screen and will move their windows or dialogs back into what they believe to be the visible region. This results in the unusual situation of an application "snapping" its windows or dialogs onto the primary monitor. These are all reasons that the primary monitor will have 0,0 as the upper-left coordinate and SM_CXSCREEN, SM_CYSCREEN as the lower right for compatibility. This will ensure that almost all applications will function as expected if they are running on the primary monitor.
Another potential problem area with SM_CXSCREEN and SM_CYSCREEN lies with the application desktop toolbars (also called appbars). An appbar is a window that is similar to the Microsoft Windows taskbar. The appbar is anchored to an edge of the screen and contains buttons that give the user quick access to other applications and windows. The system prevents other applications from using the desktop area occupied by an appbar. Any number of appbars can exist on the desktop at any given time.
Currently, most appbars can only support a single monitor because they use SM_CXSCREEN and SM_CYSCREEN in their calculations to determine the rectangle they want to occupy. For multimonitor support, you want to allow an appbar to be placed on any edge of any monitor. This requires that you handle your coordinates carefully and that you use the appropriate system metrics to calculate the edges of monitors for use by your appbar. (For more information on appbars, see the Application Desktop Toolbars section of the Win32? SDK available on MSDN, as well as at http://www.microsoft.com/msdn.)
It is important to note that you must also carefully crack coordinates to get the proper signed values. This was less of a problem in the past because there were no negative coordinates delivered; if you mixed up signed and unsigned coordinates, it didn't matter. Now that negative coordinates are valid, you may end up with invalid results if you do not properly extract the coordinates. I've included a sample macro that you can use to properly extract coordinates and maintain the correct sign. For example, this



(int)LOWORD(lParam)



should be:


(int)(short)LOWORD(lParam)



Better still, use the new macros provided in the SDK in Windowsx.h:


GET_X_LPARAM(lParam)
GET_Y_LPARAM(lParam)



A screen saver will only display on the primary monitor unless you link with the new Scrnsave.lib. The current ScrnSave.lib gets the Window size like this:


X=0;
Y=0;
dX=GetSystemMetrics(SM_CXSCREEN);
dY=GetSystemMetrics(SM_CYSCREEN);



As you can see, this only blanks the primary monitor. It has been updated and now does this:


hdc = GetDC(NULL);
GetClipBox(hdc, &rc);
ReleaseDC(NULL, hdc);
X = rc.left;
Y = rc.top;
dX = rc.right - rc.left;
dY = rc.bottom - rc.top;



This gets the RECT of the virtual desktop. This method will work correctly on Windows 95, Memphis, Windows NT 3.1, and Windows NT 4.0, so applications linked with the new Scrnsave.lib will also work on those systems.
If you want your screen saver to cover all the monitors by applying it to the virtual desktop, relinking with the new lib is all that is required. However, if you want independent images on the monitors, you'll need to use the new APIs described above and handle each screen separately. Likewise, you can use the new APIs to optimize the display of your images on the various monitors via EnumDisplayMonitors.
Anyone developing with DirectX? is probably wondering about the impact of multiple monitors on DirectX. Any existing DirectX application should continue to work correctly. However, if your application runs in full-screen mode, it may run only on the primary monitor. Windows-based applications should work on any monitor or device that is supported by DirectX. In fact, no new APIs have been added to DirectDraw? as DirectDrawEnumerate is all that is required (see Figure 9).



Figure 9 Information displayed with DXView


Some minor changes to ShellExecute and ShellExecuteEx ensure that any spawned applications come up on the same monitor as the parent application. If you specify an hWnd when calling ShellExecute or ShellExecuteEx, the new application window will appear on the same monitor as the window referred to by the hWnd.
The sample program (see Figure 10) simply calls the new APIs to get information on the system configuration and dumps out that information. The code was built with the multimon.h header file so it works on single-monitor systems as well as on Windows 95 and Windows NT 4.0.
Let's walk through some of the sample code, starting with multimon.h and then Figure 5. I'm going to skip over most of the constants and structure definitions of multimon.h since they're pretty self-explanatory. I'll start with the section labeled (via comments) "Implement the API stubs." Notice that just before this comment is an #ifdef COMPILE_ MULTIMON_STUBS. This file actually contains code and, therefore, must be included in only one module or it will generate "multiply defined" errors at link time. You should define this constant in one source file before including this include file; you should not define it in any other source files that require you to include this file.
If COMPILE_MULTIMON_STUBS is defined, then the code that follows in the header will be included. This declares a number of global function pointers that will be used by the stub code to locate the corresponding APIs built into the operating system, if present.
Looking at the first function, InitMultiplMonitorStubs, will clarify things somewhat. It should be called once before any of the APIs defined in the header can proceed, although you don't have to worry about it since the included API stub code calls it as necessary. This function determines if the underlying operating system has built-in support for multiple monitors. If it does, then it gets the correct addresses for the APIs (in the system file USER32) that correspond to those in this header file and initializes the global function pointers appropriately. If the underlying operating system doesn't support multiple monitors, then these pointers are set to NULL. In either case, the function sets a static flag that indicates whether this API was correctly initialized, and returns TRUE on a system that has built-in multimonitor support or FALSE on a system that does not. (You can see this in the very first if construct: if this function was already called, the function quickly exits using one of the function pointers to determine if there is built-in support on the platform.)
At this point, the file moves into stub function implementations. Since many of the stubs are implemented in the same manner, I'll cover only a few in detail. The first real API stub is the GetSystemMetrics function. Notice that the name of the stub is actually xGetSystemMetrics. This allows you to enhance or replace the underlying operating system API without getting compile errors by later redefining GetSystemMetrics as xGetSystemMetrics (see the #defines at the end of the include file).
First, the xGetSystemMetrics implementation makes sure InitMultipleMonitorStubs code was called to initialize things and to determine if you're on a multimonitor-aware operating system. If you are, control is passed directly to the operating system's implementation of GetSystemMetrics. If your system isn't multimonitor-aware, then review the flags, handle those that would not have been recognized on a system without multiple monitors, and return appropriate values (knowing that on such a system there will be only one monitor and that it'll have the standard coordinates). Finally, if the flag is not one of the new ones, you pass control to the operating system for handling as usual. This is a general theme throughout the stubs.
TestMM.C is a basic Windows application with many familiar features, including WinMain with a message loop and some support routines to allow easy printing into the main client area. It also has some standard menu items for executing API calls that illustrate the effect of using those APIs on the application window.
The interesting portion of the application is actually the DoTestMM function, which is called whenever the application window is moved or the user presses F5 (the standard refresh key). It is called every time the application is moved so that it can reprint information specific to the monitor the application is actually displayed on. Notice that the DoTestMM function includes a check that quickly exits if it's on the same monitor as it was the last time it was called (since none of the information would have changed in that case). From that point on, the code just calls the various multimonitor APIs and prints the resulting information into an edit box created and sized to fill the client area of the application window. Although not a very exciting application, it does show how to call the various APIs, their relevant structures and flags, and how to use them, as well as what to expect in response to those APIs.
Finally, the listings include an MMHelp file that contains routines that handle common tasks on a multimonitor system. Although the routines are fairly simple, I'll quickly review some of them here.
GetMonitorRect is used to determine the screen or work area, depending on the flag passed in the third parameter, for a window. This basically passes in the area of the screen that is closest to the window handle. You use this to clip a window onto a visible portion of the screen. The ClipRectToMonitor function uses GetMonitorRect to determine the best monitor to clip a rectangle to, and then returns the updated rectangle that represents the best place to put it. This might be useful if you want to display a dialog and make sure that it's visible. You can pass this function the coordinates that you'd like to use and it will find the closest location where that entire dialog can be visible. In fact, the new function ClipWindowToMonitor does just that. Given a window handle, it gets the bounding rectangle of the window, uses ClipRectToMonitor to find an appropriate location, and then moves the window to that location.
Similarly, CenterRectToMonitor determines the correct monitor on which to center a rectangle and then updates the rectangle so that it is centered on that monitor. CenterWindowToMonitor uses CenterRectToMonitor to determine the appropriate center location and then moves the window to that location. IsWindowOnScreen determines if your window is actually visible anywhere on any screen, and MakeSureWindowIsVisible makes sure your window becomes visible on a screen.


Installing Multiple Monitors
Multiple monitor support is available only on the Memphis and Windows NT 5.0 operating systems that have updated video drivers (that is, updated to support multiple monitors). Any video card that is supported by Windows can be used as your primary display, but the extra cards must have updated drivers to support the new features. Currently, Microsoft has updated drivers for a number of video cards designed for use on a PCI bus that will be shipped with the operating system. (For more details on hardware, see the section on supported hardware in your documentation.) At the time I wrote this article, drivers for the following cards were available:


ATI Mach64, Rage I, II & III

S3 764(Trio), 764V+(765)

Cirrus 5436,7548,5446

Imagine 128 I and II

S3 M65

Matrox Millenium

Matrox Mystique

Cirrus Laguna

ET6000

Rendition

3DFx

STB

Setup is pretty much plug and play. First, you must get your system working with one monitor. Then, shut down the system and install another video card and monitor. When you restart your machine, Windows should automatically detect the new card (and possibly the monitor). Once the proper drivers get copied to your machine, you can go to the control panel to configure the physical mapping of your virtual desktop to your monitors, as well as the resolution, color depth, and refresh rates for each.
Note that the order of the cards on the bus may impact the configuration. In particular, the VGA monitor where startup MS-DOS text is initially displayed is chosen by the system before Windows even starts. As a result, you may need to rearrange the cards in order to get the configuration the way you want it.


User Interface Changes
One of the nice things that you will notice about adding multiple monitors is that not much has changed in order to provide this support. In fact, if you have only one monitor there will be no noticeable difference on your machine because most of the changes were actually made beneath the surface of the operating system. However, you will notice a few things if you install multiple video cards and monitors.




Figure 11 Monitors tab for multimonitors


There was a small change to the Display Properties control panel. The Settings tab was replaced with a Monitors tab, under which the settings for each monitor can be adjusted (see Figure 11). Any changes are then made on a per-monitor basis. If you only have one monitor, you'll still see the Settings tab as illustrated in Figure 12 since there is no need to choose the monitor you want to configure.



Figure 12 Settings tab for a single monitor


Some minor changes were made to make the shell multimonitor-aware. This included having the shell's desktop appear on all monitors and adding support for placing the taskbar on any edge of any monitor. (If you combine this with the auto-hide feature, you'll have a lot more places to lose it!) These changes allow you to drag items, such as shortcuts, from the desktop of one monitor to the desktop of another. The system will also try to start an application on the monitor that contained the shortcut. For example, if you want to start an app on a specific monitor, one way would be to place a shortcut on the part of the desktop that is on that monitor.


Differences Between Memphis and Windows NT 5.0
There are some minor differences in the implementation of multimonitor support between Memphis and Windows NT 5.0. However, it's not clear at this time which, if any, of these differences will remain in the final release. Therefore, rather than getting into details of current differences, I'll give you my standard "test on both" speech.
As with most Win32 applications, the user can generally use the application on either Windows 95 or Windows NT since both are Win32 platforms. As a developer, you must make sure your application works as expected on each targeted platform. Test with one monitor and with two, test at high resolutions and in VGA mode, test on fast machines and on slow ones?are you getting the picture? Often there is no difference, but in some cases there may be and it's a lot easier to address the differences before you release an application than it is afterward. All too often developers fail to notice serious flaws because the application works great on the super whiz-bang machine it was developed on, only to fail miserably on the target low-end machine.



The Cliff Notes
Writing multimonitor applications is easy, and making your current application multimonitor-aware is even easier. Let's just summarize what you need to know.


When centering windows and dialogs, make sure you center them to your application window. If you want to center on a monitor, make sure you use the correct system metrics. (Use the MMHelp routines to make it simpler.)

If you are developing screen savers, make sure you are using the latest version of Scrnsave.lib. It runs properly on existing systems as well as multimonitor machines. Make sure you are correctly extracting coordinates to ensure you get the correct sign. Negative coordinates are now valid.

If you are saving window positions to restore later, make sure you check that the positions are still valid before using them. Since the user can move the coordinates of secondary monitors on the fly in the control panel, or even remove a monitor altogether, the position you saved may no longer be valid. (You can use the routines in MMHelp to address this problem.)

Remember that negative coordinates or coordinates larger than SM_CXSCREEN, SM_CYSCREEN may be visible. If you are using off-screen coordinates as a method of hiding windows, use the appropriate system metrics or you may be in for a surprise.

Likewise, don't use 0,0 and SM_CXSCREEN, SM_CYSCREEN to clip windows, menus, and so on onto the screen. This results in inappropriate behavior when your application is not on the primary monitor. (I know of several Microsoft applications that must now be corrected, including Visual C++ 5.0.) Here again, the routines in MMHelp will come in handy.

If you use ShellExecute or ShellExecuteEx in your application, supply an hWnd so that the system can open any new windows on the same monitor as the calling application. This is what the user will expect.

Use multimonitor support to run your development and debugging environment on one monitor and your application on another. This is particularly true for full-screen DirectDraw applications.

For developers, the golden rule is Test! Test! Test! Make sure you test your application thoroughly on all target platforms, with and without multiple monitors. Make sure you test your app on both the primary and secondary monitors. If it's going to mess up, it'll probably be on a secondary monitor.

Finally, don't panic over existing applications. The primary display is always set up to be as compatible as possible with existing applications. If you have released applications that may be multimonitor challenged, they should still work on the primary monitor.


Conclusion
Having multiple monitors on the same machine and as part of the same desktop is very cool, but it also opens up a whole new set of possibilities for applications?from games that use multiple monitors to give you a more panoramic view to CAD and presentation applications that have built-in support for custom display devices. Since the support is built into the operating system, compatibility with existing applications is excellent and the impact on existing applications is minimal.
If you have a PCI machine and a couple of old monitors lying around, get a few more video cards and I guarantee you'll never go back to just one monitor!




Terminology
The following terminology for multimonitor support is currently used in the Windows documentation.
VGA monitor This is the main monitor that you see text on when the computer initially boots. It's also the monitor that DOS apps will run on when running exclusively (in DOS mode) or when they are running full-screen.
Primary monitor Another name might be the compatibility monitor since this is the monitor that is guaranteed to contain traditional screen coordinates. Almost all multimonitor-challenged apps will run correctly on this monitor. The primary monitor may or may not be the VGA monitor. The VGA monitor is determined by the system because of its location on the bus. On the other hand, Windows determines the primary monitor.
Secondary monitors These are all of the monitors that are not the primary monitor but are included in the Windows desktop area.
Independent displays These monitors are present on the system, but are not part of the Windows desktop. These monitors can still be used by applications, but they do not contain any part of the Windows desktop. The calculation of the virtual desktop does not include this area and, as a result, you cannot drag application windows to or from this monitor to other monitors on the system?even if an application is running that makes use of this monitor.
Mirrored monitors These are monitors that are present on the system that receive a duplicate of all activity on the primary monitor. This allows a user to present the same information on multiple displays. A user can configure devices to be mirrors using the control panel. (There aren't really any development aspects to this type of monitor, but it's worth knowing about.)



From the June 1997 issue of Microsoft Systems Journal. Get it at your local newsstand, or better yet, subscribe.


Windows 98/2000下多屏显示技术的实现

2002-11-28 刘 涛??yesky

前言

  一般情况下,计算机用户都是使用单一的显示屏来观察显示的信息,目前比较流行的是17英寸的显示器,可以满足一般用户的需求。但是在一些特殊的场合下,例如需要对现场进行大范围的监控、CAD绘图、或应用电子地图技术等的情况下,需要全景显示时,用户就不得不要求计算机有足够大的显示桌面,以便对场景的细节看的更清楚、操作起来更方便。针对上述情况,即使是二十九寸的显示器其有效的显示尺寸也是十分有限的,这时候解决桌面大小问题就不得不采用多屏显示技术,Windows操作系统支持这种技术,这里的多屏显示并不是指采用多屏分配器驱动多个显示器,从而使多个屏幕显示相同的画面,就如同VC界面编程中的动态拆分效果,而是指在一台计算机上安装多个显卡带多台显示器,实现成倍的扩展计算机桌面尺寸的目的。例如台湾Dream公司推出的ViewPacket软件,可以一边显示视频流,一边编辑Word文档,一边上网浏览网络上的信息,极大的方便了用户的使用。
  一、多屏显示的系统配置方法

  为了安装多显示器,首先检查机器硬件,当前Windows操作系统只支持PCI适配器来实现多显示器桌面,它要求显卡必须使用以下芯片组中的一种:ATI Mach64、S3 764和764+、Cirrushgic5436、5446和7458、Imagine 128 Series1和2、S3 Virge或ET6000、M9711等。一般情况下,一个显卡驱动一个显示器,一台机器最多可以驱动7台显示器,其中一台为主显示器,其余为副显示器。主显示器也是Windows启动画面出现的那个显示器。

  进入"控制面板"-"显示",双击"显示",弹出"显示属性"对话框,选中"设置",如果安装了多个监视器,这时候对话框的上半部分显示系统中目前显示器的数量。可右键单击一个监视器图标,再单击"标识"查看对应于单击其图标的监视器的显示编号。请将它们拖动到代表在监视器间移动项目方式的位置。例如,如果您正使用两个监视器,并希望通过左右拖动来将项目从一个监视器中移到另一个中,请将图标并排排列。要通过上下拖动来在监视器间移动项目,请将图标上下排列。图标的位置并不一定与监视器的物理位置相对应。您可以将图标上下放置,即使监视器是并排放置的。

  二、多屏显示的软件实现

  通过上面的设置,虽然使Windows支持多显示器扩展桌面的功能,但也仅仅是实现了设备的连接与管理,一般Windows应用程序的窗口不能自动运行到副显示器上,用户只能通过鼠标拖动来将程序的窗口扩展到副显示器上,如果需要将程序窗口自动扩展到不同的显示器显示,必须采用相应的多屏显示编程技术。在这种技术中,可以使用Windows的User32.DLL动态连接库提供的API函数SetWindowPos()(如果采用VC这个开发平台,CWnd类提供了一个同样的成员函数),它的功能是更改窗口的大小、位置及窗口的顺序,因此可以用来在多显示器桌面上实现应用程序窗体的定位显示。下面对这个函数进行详细的解释:


BOOL SetWindowPos(
HWND hWnd, // 窗口的句柄;
HWND hWndInsertAfter, // 窗口次序句柄,
int X, // 窗口左上角的水平位置;
int Y, // 窗口左上角的垂直位置;
int cx, // 窗口的宽度;
int cy, // 窗口的高度;
UINT uFlags file://标志位;
);

  这里需要解释的有hWndInsertAfter、uFlags两个参数

  hWndInsertAfter

  在窗口的"Z"序中排在需要的设置的窗口之前的窗口句柄;这个参数取以下值:


Value Meaning
HWND_BOTTOM 将要操作的窗口放置在所有窗口之下;
HWND_NOTOPMOST 将要操作的窗口放置在"non-topmost"窗口之下;
HWND_TOP 将操作的窗口放置在所有的窗口之上;
HWND_TOPMOST 将要操作的窗口放置在"non-topmost"窗口之上;

  uFlags

  规定窗口的尺寸和位置的标志,这个参数通常取以下值:


Value Meaning
SWP_DRAWFRAME 窗口重画时,绘制出窗口类中所描述的窗口的框架
SWP_HIDEWINDOW 隐藏窗口;
SWP_NOACTIVATE 窗口无任何动作;
SWP_NOMOVE
保持窗口的当前位置;
SWP_NOREDRAW 在新位置并不重画窗口;
SWP_NOSIZE 改变窗口的位置,并不改变窗口的大小,既忽略cx,cy两个参数的值;
SWP_NOZORDER 窗口重画时,保留当前的顺序,既忽略hWndInsertAfter参数的值
SWP_SHOWWINDOW Displays the window.

  另,该参数还可以取其它的值,具体含义读者可以参考MSDN。

  为了说明问题,下面的VB代码仅仅实现了在两个显示器分别显示不同的窗口,用户点击按钮后,主显示器显示窗口Form1,副显示器显示窗口Form2,感兴趣的读者可以稍加修改,就可以应用到实际项目开发中去。

  首先在VB全局模块中声明:


Public Declare Function SetWindowPos Lib "user32" Alias "SetWindowPos" (ByVal hwnd As Long, ByVal hWndInsertAfter As Long, ByVal x As Long, ByVal y As Long, ByVal cx As Long, ByVal cy As Long, ByVal wFlags As Long) As Long

  其次在需要定位的两个窗口模块中调用该函数。具体的VB代码是:


Private Sub Form1_Load( )
Const HWND_TOPMOST=-1
Const SWP_NOACTIVATE=&H10
SetWindowPos Me.hWnd, HWND_TOPMOST, 0, 0, ScreenWidht/2, ScreenHeight, SWP_NOACTIVATE '在主显示器的显示区域上显示窗口1;

End Sub
Private Sub Form2_Load( )
Const HWND_TOPMOST=-1
Const SWP_NOACTIVATE=&H10
SetWindowPos Me.hWnd, HWND_TOPMOST, ScreenWidht/2, 0, ScreenWidht/2, ScreenHeight, SWP_NOACTIVATE '在副显示器的显示区域上显示窗口2;
End Sub


  为了正确的显示,需要将窗体的ScaleMode属性设置为twips,这样才可以与Windows系统中ScreenWidth和ScreenHeight的计量单位twips保持一致。

星期二, 二月 10, 2004

java get system environment variable

/**
* 得到java VM预先定义的系统变量
* jdk verison 1.4.2
*/
public class listallproperties
{
Properties ttl= System.getProperties();
Enumeration te = ttl.propertyNames();
while(te.hasMoreElements())
{
String objectname=te.nextElement();
System.out.println(objectname +" = "+System.getProperty((objectname)));
}
}

/**BeanShell 版本
*修改System.out.println为print方法.
*/
import java.util.*;
Properties ttl= System.getProperties();
Enumeration te = ttl.propertyNames();
while(te.hasMoreElements())
{
String objectname=te.nextElement();
print(objectname +" = "+System.getProperty((objectname)));
}



星期六, 二月 07, 2004

CSDN_文档中心_选择JDO还是CMP?

CSDN_文档中心_选择JDO还是CMP?
选择JDO还是CMP? sun2bin(翻译)

关键字 JDO persistence ValueObject DAO O/R mapping

出处 http://www.onjava.com/pub/a/onjava/2003/05/21/jdo.html



选择JDO还是CMP?
由孙宾译自http://www.onjava.com/pub/a/onjava/2003/05/21/jdo.html

作者:David Jordan 和 Craig Russell
著有《Java Data Objects》一书
2003年5月21日

  作者注:JDO和CMP方式的EJB目前正在同时向前发展,但采取的是不同的路线。JDO的核心思想是在企业应用软件架构的不同层面中存储传统的Java对象(Plain Old Java Objects,下称POJOs),而CMP方案则基于容器环境,并针对特殊的需求。

  两者之间的异同在规范出台之初便成为众所争论的话题。你可以到JDOCentral.com上看到这类的争论,而在6月中旬即将在旧金山开幕的2003年JavaOne大会上,也会有一些演示和讲解来比较这两种不同的技术。

  在这次JavaOne大会上,3368号技术对话将讨论JDO与Struts(一个著名的Web应用架构设计的开源软件)集成的可行性和实践经验;3236号专题研究JDO与EJB容器的结合;1289号专题将对比使用JDO、JDBC和EJB时,设计模式在开发中的应用。

  在我们的《Java Data Objects》的第17章有一小段话描述使用JDO和CMP的平衡点。--Craig Russell



JDO还是CMP?
  在你对项目开发策略下决定之前,CMP实体Bean和JDO都是值得考虑的方式。

  JDO对粗粒度和细粒度的数据对象设计都很适合,具体在一个应用服务器环境中,一般用于SessionBean后面。CMP也是用于SessionBean之后,它的远程调用很少直接用到。

  JDO编写的类只须编译一次就可用于分布式架构中的任何一层,并且在集成到Web或应用服务器之前可以以单层或两层的方式先调试好。而CMP只能在发布到一个具体的应用服务器后才能调试。

  JDO没有直接定义远程行为,这一点与Servlet、JSP和EJB组件不同。所有的分布处理、事务和安全方面的策略都基于独立存储管理器,该管理器负责处理你的对象模型中所有的类实例。这一点也说明你可以在分布式环境中的任何层面上使用JDO,而其远程行为由容器来实现,而不是JDO厂商。

  CMP组件提供了较高的可移植性,Bean类及其描述符都是规范化的。多数的不兼容性只存在于规范所未能尽述的地方,包括如何将类映射到具体的数据库(不限于关系数据库)、类似只读类的可选功能、其它方面的厂商扩展。而JDO产品视具体厂商所支持的可选功能而有所不同。

  CMP中,对象之间的关系是受控的,即一端的改变会影响到另一端,并且对应用是可见的。而JDO不支持关系的管理,只是一些厂商以扩展的方式提供类似功能。

  继承是对真实对象建模时常用的概念,但CMP并不支持它。CMP在组件的定义和实现时并不一致,在具体实现一个EntityBean接口时,实现的类可以具有继承关系,但在定义这个EntityBean时却不行。类之间的关系也只是在接口之间,而不是在实现类之间,因此这些关系也不存在多态性。举例来说,一个名为MediaItem的CMP Bean类不能直接联系到名为MediaContent的类,因为MediaContent是抽象的,类并无具体实例。要建立这样的联系,你只能将其转换为两个关系:一个是MediaItem与Movie类,一个是ModiaItem与Game类,并且在每个相关方法中,你必须针对两个关系区别对待。

  在访问对象属性上,CMP和JDO也天差地别。CMP Bean中,所有的属性和对象关系都是作为抽象的get和set方法定义在描述符中,对实际属性的访问只能由具体的由相关工具生成的实现类去完成。而JDO中,可保存的属性和关系在描述符中声明,并且在代码中也可以直接访问这些属性,包括JDO产品生成的代码在内。JDO增强器会在增强时适当地改造这些代码。

  JDOQL和EJBQL都提供了类似的查询数据的方法。两者都可以在程序中查询并访问数据对象,都采取“读-改-写”的策略,都不是完整的数据操纵语言(比如没有数据更新语句),它们都只用于查找数据对象并在代码中访问。

  CMP要求所有的访问操作都在事务环境中,非事务方式的访问不受支持。而JDO允许你决定是否采用事务方式。对需要更新数据的地方,JDO要求采用事务,而只是读取的代码中,包括缓冲在内,JDO支持非事务方式的访问。

  表17-1是对CMP Bean和JDO可存储类的比较

表17-1 CMP beans与JDO的比较

特性 CMP beans JDO可存储类
环境方面
应用可移植性 基本可以,很少有负面影响 规范定义的原则下可移植
应用环境 应用服务器 单层、两层、Web服务器、应用服务器
可存储类相对于环境的独立性 低:类必须实现EJB接口,并在EJB容器中执行 高:类不需要实现任何特定接口,可在任何环境中执行
元数据(metadata)
标记可存储类 发布描述符标明所有的可存储类 元数据标明所有可存储类
标记可存储属性 发布描述符标记所有的可存储属性和关系 元数据中默认大部分可存储属性和关系(简洁)
建模
域类的建模对象 CMP bean (抽象的底层数据库) 可存储类
域类间的继承 不支持 完全支持
属性访问 抽象的get/set方法 任何方式的访问,包括直接访问和get/set方法
Collection, Set 支持 支持
List, Array, Map 不支持 可选功能
关系 体现为对CMP本地接口的引用 体现为对JDO可存储类或接口的引用
多态性 不支持 支持
编程
查询语言 类似SQL的EJBQL 基于Java布尔判断的JDOQL
远程方法调用 支持 不支持
生命周期管理对类方法的要求 setEntityContext
unsetEntityContext
ejbActivate
ejbPassivate
ejbLoad
ejbStore
ejbRemove
无参构造器(可以是private的)
可选的回调方法 ejbCreate
ejbPostCreate
ejbFind jdoPostLoad
jdoPreStore
jdoPreClear
jdoPreDelete
关系数据库映射 厂商相关 厂商相关
方法安全策略 支持 不支持
方法的事务策略 支持 不支持
非事务访问 非标准 支持
需要的类/接口 EJBLocalHome,本地接口(如果支持本地访问)

EJBHome,远程接口(如果支持远程访问)

抽象类必须实现EntityBean

标识类(如果不是原始类型)
可存储类

标识类(只用于自定义标识)

事务同步回调 不支持 支持

  David Jordan创立了Object Identity, Inc.,提供Java Data Objects(JDO)的咨询和培训服务。David也是《Java Data Objects》一书的作者之一,另一作者是Craig Russell。

  Craig Russell是Sun微系统公司的JDO规范领导者。

  O'Reilly及其协会最近(2003年4月)出版了《Java Data Objects》。

第一章 概述,可以免费下载
你还可以查看该书的目录、索引和完整说明
以上两部分的中文版本:CSDN版,或JavaResearch版 (由孙宾翻译)
更多信息或购买该书,请看这里

ONJava.com: JDO or CMP? [May. 21, 2003]

ONJava.com: JDO or CMP? [May. 21, 2003]
JDO or CMP?
by David Jordan and Craig Russell, authors of Java Data Objects
05/21/2003

Author's note: Java Data Objects (JDO) and Enterprise JavaBeans (EJB) Container Managed Persistence (CMP) were developed concurrently, but took different paths.


JDO's design center is making Plain Old Java Objects (POJOs) persistent in any tier of an enterprise architecture, while CMP's solution is container-bound with elaborate requirements for development and deployment.

The similarities and differences between these technologies has been the subject of debate since the specifications were published. For example, see the discussions at JDOCentral.com. There are presentations comparing these approaches coming up at the JavaOne 2003 conference in San Francisco, California, June 10-13.

Technical session 3368 examines the integration possiblities and best practices with JDO and Struts, an open source Web application framework for JSP/Servlet containers. BOF 3236 discusses integrations with JDO and EJB containers. BOF 1289 contrasts design patterns for application development with JDO, JDBC, and EJB.

Here's a brief excerpt from Chapter 17 of our book, Java Data Objects, which summarizes the key trade-offs between using JDO and CMP for your enterprise application. --Craig Russell

JDO or CMP?
Both CMP beans and JDO persistent classes have features that you should consider before committing your project to use either strategy.

JDO persistent classes are suitable for modeling both coarse-grained and fine-grained persistent instances and in an application server are typically used behind session beans. CMP beans are typically used behind session beans; their remote behavior is seldom exploited.

JDO persistent classes can be used without recompilation in any tier of a distributed architecture and can be debugged in a one- or two-tier environment prior to integration into a web or application server. CMP beans can be debugged only after deployment into the application server.

Unlike servlets, JSP pages, and EJB components, there is no built-in remote behavior with JDO classes. All of the distributed, transaction, and security policies are based on the single persistence manager that manages all of the persistent instances of your model. This means that JDO persistent classes can be used in any tier of a distributed application and remote behavior is implemented by the container, not the JDO implementation.

CMP beans give you a high degree of portability across application servers. The bean class and required deployment descriptor are standard. Most of the incompatibilities between implementations are found in unspecified areas of mapping beans to the underlying datastore, optional features such as read-only beans, and extensions in deployment and management of beans. JDO implementations vary with regard to the optional features that they support.

Related Reading


Java Data Objects
By David Jordan, Craig Russell

Table of Contents
Index
Sample Chapter

Read Online--Safari Search this book on Safari:

Only This Book All of Safari
Code Fragments only
With CMP, you identify every bean class, persistent field, and persistent relationship in the deployment descriptor. Using JDO, you identify every persistent class in the metadata, but you can usually take the default for the persistence of fields, including relationships.

With CMP, relationships are managed; this means that during the transaction a change to one side of the relationship immediately affects the other side, and the change is visible to the application. JDO does not support managed relationships, although some vendors offer them as optional features.

Inheritance is a common paradigm for modeling real-world data, but CMP beans do not support inheritance. CMP makes a distinction between the implementation class and the bean. The abstract bean-implementation classes and the local and remote interfaces can form inheritance relationships, but the CMP beans that model the application's persistent classes cannot. Relationships in CMP are between CMP beans, not implementation classes, and these relationships cannot be polymorphic. In our example, it would be impossible for a MediaItem CMP bean to have a relationship with a MediaContent CMP bean, because MediaContent has no instances. In order to implement this kind of model, you would need to change the MediaItem CMP bean to have two different relationships: one between MediaItem and Movie, and another between MediaItem and Game. You would need to treat the relationships separately in every aspect of the bean.

The programming model used to access fields is very different between CMP beans and JDO. With CMP beans, all persistent fields and relationships are defined by abstract get and set methods in the abstract bean class, plus a declaration in the deployment descriptor. Access to the field value is the responsibility of the concrete implementation class generated by the CMP code-generation tool. With JDO, persistent fields and relationships are declared or defaulted in the metadata, and access to the field values is provided by the code in the class for transient instances or by the JDO implementation for persistent instances. The JDO enhancer generates the appropriate field-access code during the enhancement process.

JDOQL and EJBQL provide similar access to data in the datastore. Both allow you to select persistent instances from the datastore to use in your programs. Both use the read-modify-write pattern for updating persistent data. Neither language is a complete data-manipulation language; both are used only to select instances for manipulation by the programming language.

CMP beans require active transactions for all business methods. Nontransactional access is not standard or portable. JDO allows you to choose whether transactions are required. JDO requires inserts, deletes, and updates to be performed within transactions, but read-only applications, including caching, can be implemented portably without transactions.

Table 17-1 is a summary comparing CMP beans with JDO persistent classes.

Table 17-1: Comparison of CMP beans and JDO

Characteristic
CMP beans
JDO persistent classes

Environmental

Portability of applications
Few portability unknowns
Documented portability rules

Operating environment
Application server
One-tier, two-tier, web server, application server

Independence of persistent classes from environment
Low: beans must implement EJB interfaces and execute in server container
High: persistent classes are usable with no special interface requirements and execute in many environments

Metadata

Mark persistent classes
Deployment descriptor identifies all persistent classes
Metadata identifies all persistent classes

Mark persistent fields
Deployment descriptor identifies all persistent fields and relationships
Metadata defaults persistent fields and relationships

Modeling

Domain-class modeling object
CMP bean (abstract schema)
Persistent class

Inheritance of domain-class modeling objects
Not supported
Fully supported

Field access
Abstract get/set methods
Any valid field access, including get/set methods

Collection, Set
Supported
Supported

List, Array, Map
Not supported
Optional features

Relationships
Expressed as references to CMP local interfaces
Expressed as references to JDO persistent classes or interfaces

Polymorphic references
Not supported
Supported

Programming

Query language
EJBQL modeled after SQL
JDOQL modeled after Java Boolean expressions

Remote method invocation
Supported
Not supported

Required lifecycle methods
setEntityContext, unsetEntityContext, ejbActivate, ejbPassivate, ejbLoad, ejbStore, ejbRemove
no-arg constructor (may be private)

Optional lifecycle callback methods
ejbCreate, ejbPostCreate, ejbFind
jdoPostLoad, jdoPreStore, jdoPreClear, jdoPreDelete

Mapping to relational datastores
Vendor-specific
Vendor-specific

Method security policy
Supported
Not supported

Method transaction policy
Supported
Not supported

Nontransactional access
Not standard
Supported

Required classes/interfaces
EJBLocalHome, local interface (if local interface supported);

EJBHome, remote interface (if remote interface supported);

Abstract beans must implement EJBEntityBean;

Identity class (if nonprimitive identity)
Persistent class;

objectid class (only for application identity)

Transaction synchronization callbacks
Not supported
Supported


David Jordan founded Object Identity, Inc. to provide Java Data Objects (JDO) consulting and training services. David is also a coauthor of O'Reilly's book on Java Data Objects, with Craig Russell.

Craig Russell is the specification lead for JDO at Sun Microsystems

星期五, 二月 06, 2004

关于有关J2EE平台预定定义环境参数的方法一览

1.EJB 得到在ejb-jar.xml中定义的变量
1.1定义
<env-entry>
<env-entry-name>Discount Level</env-entry-name>
<env-entry-type>java.lang.Double</env-entry-type>
<env-entry-value>1000.00</env-entry-value>
</env-entry>
1.2 在Sessinbean中 得到改变量代码
Context initial = new InitialContext();
Context environment = (Context)initial.lookup("java:comp/env");
Double discountLevel = (Double)environment.lookup("Discount Level");

2.Servlet 的到在web.xml中定义的变量
2.1 定义
<env-entry>
<env-entry-name>myossystem</env-entry-name>
<env-entry-value>涓????windowsnt</env-entry-value>
<env-entry-type>java.lang.String</env-entry-type>
</env-entry>

2.2 在Servlet 中得到
InitialContext context = new InitialContext();
context.lookup("java:comp/env/myossystem");

3.Servlet 得到在web.xml定义的同特定servlet class 关联的参数
3.1 定义
<filter>
<filter-name>action.JSPEncoding</filter-name>
<filter-class>action.JSPEncoding</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
3.2 在Servlet 得到
public void init(FilterConfig config) {

this.filterConfig = config;
this.targetEncoding = config.getInitParameter("encoding");
if (config != null) {
if (debug) {
log("JSPEncoding:Initializing filter");
}
}

Fix I18n Solution Bug

发表于:computerworld中的有关国际化的文章有算法错误.修正如下:

Servlet Filter 设置中文显示编码的修整错误代码:
原来为:
sresponse.addHeader("charset", targetEncoding);

修改为:
HttpServletResponse sresponse=(HttpServletResponse)response;
sresponse.setContentType("text/html;charset="+targetEncoding);

Tomact 4.1.27 Engine可以正常运行.高于的版本包括5.0不可以运行.有bug.

J2EE WebModule and Servlet/Filter Map-pattern relationship

BeanShell Test Servlet

<url-pattern>/laal</url-pattern>
在JSP中的action应用必须是在web Module context root 下应用.
比如:<url-pattern>/laal </url-pattern>而WebModule的名字为webapp

在jsp中必须写成:

星期三, 二月 04, 2004

使用 Beanshell实现公式管理

用BeanShell实现公式管理>
用BeanShell实现公式管理



内容:

前言
BeanShell简介
JDOM简介
公式管理系统的目标
公式管理系统的设计
何时应用BeanShell
更好的了解该系统
结束语
参考资料
关于作者


Java 专区中还有:

教学
工具与产品
代码与组件
所有文章
实用技巧




使用Java脚本构建强大、灵活的公式管理系统
杨铁军(yattie@163.com或helloyattie@hotmail.com)
Java技术爱好者,系统设计师
2003年7月

在很多中大型的应用中,如SCM(供应链管理)、CRM(客户关系管理)和ERP(企业资源计划)等,使用者往往要根据自身的需求,灵活的对某一些参数值进行变更,使得按照某固定公式计算的结果符合目前的情况。如不同时期商品价格的折扣率需要根据实际情况进行调整,或者职员的奖金百分比要根据公司的业绩而定。这就需要有一个强大的公式管理机制来对一些参数进行灵活调整。本文用BeanShell(一种Java 解释器)实现了一个这样的公式管理系统。从该系统的实现我们可以了解到BeanShell带给我们灵活的Java 脚本机制;并且,我们还可以在该系统的基础上,定制自己的公式管理系统。
前言

客户的需求是在不断变化的。虽然他们说现在他们公司的职员奖金应该就是按照那个公式计算,但是过了几个月他们会告诉你这个公式并不是很合理,还需要加一些参数。你可能会说,这个没问题,我们可以改程序。但是当这样的变更不是一次一个的发生,而是频繁的、大量的出现时,你也许就在想应该有一个公式管理系统来完成这些琐碎却很重要的变更了。是的,在很多系统中已经这样做了。这里将介绍一种简单的、易扩展的公式管理系统,它采用简单灵活的BeanShell脚本机制,并且结合JDOM技术来实现。在阅读本文以前,你需要对BeanShell和JDOM有所了解。

BeanShell简介

BeanShell是一种Java 解释器,它包含的脚本语言基本与Java 语言兼容,具有体积小、简单、符合Java 风格等特点。本文不是介绍BeanShell的语法和用法,而是基于BeanShell脚本实现一个公式管理器来说明BeanShell的强大脚本功能,从而简化Java程序员的编程工作,使他们更深入的了解什么时候使用BeanShell技术将使得构建的系统更灵活。你可以阅读参考资料了解更多BeanShell的知识。类似的Java脚本技术还有DynamicJ等,想要详细了解它们的更多信息,请查阅后面的参考资料。(请注意:这里的Java脚本不是Javascript。)

JDOM简介

JDOM使得用Java操作xml文件更轻松。这里使用xml文件格式对用户的自定义公式库进行存储,即简单又容易管理。利用JDOM技术,能够简单、快速的完成这个任务。如果你想详细了解有关JDOM的知识,请查阅文章后面的参考资料。

公式管理系统的目标

公式管理系统实现的主要目标是:用户可以根据自己的需要自定义公式,包括添加、修改和删除公式或者公式包含的参数;提供接口使得用户或其它系统能够利用公式库中的公式进行计算求值。从以上系统的主要功能,可以知道该系统主要包含两个用例:自定义公式和计算表达式。自定义公式是用户预先定义好某公式包含的参数(包括参数名、参数类型等),然后将这些参数用运算符按照一定的法则组合成所需要的公式。公式定义好后,将被保存到公式库中,供以后用户或其它系统计算时调用。这是管理者根据自身的需求,灵活更改公式或公式包含的参数的系统功能;计算表达式是用户给相应参数赋值,然后指定要遵照的公式进行计算求值。这是该系统提供给使用者的外部接口。一般的,使用者只需要提供要遵照的公式ID和相关的参数值,就可以调用该接口进行计算。该系统的用例图如下:



图1. 公式管理系统用例图

公式管理系统的设计

明确了系统的目标,我们第一步要做的就是找出系统的核心类。首先,我们需要一个公式管理器(FormulaParser),它负责将用户自定义的公式转换成系统格式并保存到公式库中,它提供外部接口,供外部需要根据公式计算时使用;我们还需要一个计算器(Calculator),它的工作就是进行计算。它包含了所有的运算类型,如加、减、乘或除,当然还可以包含大(小)于、等于等其它运算,可以根据需要进行扩展;最后,我们还需要一个公式类(Formula)。顾名思义,公式对象就代表一个公式。公式管理器就是一个管理者,它既要接受用户的自定义公式,又要提供接口给外部系统,调用相应的公式并用计算器进行计算。该系统的核心类如下图所示。



图2. 核心类图

很显然,这样的类设计有利于BeanShell的实施(笔者一直认为:设计人员中至少要包括一位语言专家,这样才能设计得更灵活、更简单,为编码阶段做铺垫)。那么,BeanShell应用于系统哪一部分将最为合适呢?

何时应用BeanShell

答案是要根据实际情况而定。你可能会说这是废话,但是这是事实的真相。一般的,当系统的某个部分变化很多,而且需要根据实际情况动态的调用一些方法和参数,那么你可以试着去实施BeanShell到你的系统中。如果你发现问题变得简单而且灵活性大大提高,那么BeanShell就值得你去试一试;相反的,如果你发现问题不仅没有简化,甚至变得更加复杂,那么你就要考虑一下你的方法是否合适,或者找一些BeanShell应用专家交流一下。注意,我们不是追求新技术,而是追求一种能够提高我们生产效率的新方法。

那么,在这个公式管理系统中我们如何应用BeanShell技术呢?从以上的分析我们知道,该系统中用户需要将自定义的公式保存起来,以供外部系统计算时调用。设想一下,当外部系统调用该接口时,它至少应该传入两个参数:一个是计算需要遵照的公式ID;另外一个是一个Hashtable对象,存放"参数名-参数值"列表。FormulaParser类接收到这些参数后,根据公式ID从公式库中装载指定的公式,然后将参数值赋值给公式包含的对应参数,最后根据定义好的公式表达式进行计算求值。在没有BeanShell以前,我们一般通过分析字符窜的方式完成自定义公式到系统格式公式的转换、参数赋值、表达式求值等,这样做虽然比较直接,但是很复杂,尤其当运算符、运算优先级很多的时候。笔者就曾经见过一个1000多行代码的公式解析器,而且功能很简单。原因就在于它的大部分代码是在做字符窜的解析。那么,你应该抓住了问题的所在。我们试试让BeanShell来完成这些解析工作。

首先你必须对BeanShell脚本的语法有所了解,并且应该熟悉一下它的文档中的几个例子。然后我们将重点放在BeanShell应用的地方――外部接口caculateByFormula(Formula formula, Hashtable parameters)。其中的两个参数,formula是计算要遵照的公式,parameters是"参数名-参数值"列表,它的返回值是计算的结果。假设公式库中我们已经定义好了两个公式,分别是商品折扣后价格公式和职员奖金计算公式。公式库xml文件如下所示:

清单1.


<formulas>
<formula id="1001" name="F_DISCOUNT">
<parameters>
<parameter type="double" name="price"/>
<parameter type="double" name="discount"/>
</parameters>
<script>result= Calculator.mutiply(price, discount)</script>
</formula>
<formula id="1002" name="F_BONUS">
<parameters>
<parameter type="double" name="sale"/>
<parameter type="double" name="score"/>
</parameters>
<script>result= Calculator.add(Calculator.mutiply(sale, 0.1),Calculator.mutiply(score, 10000))</script>
</formula>
</formulas>



公式库中每个公式有一个唯一的ID来标识,并且有一个易记忆的名字。每个公式包含一个parameters元素和一个script元素,它们分别对应公式包含的参数列表和公式计算脚本。公式计算脚本就是BeanShell脚本。这样做的好处是:无需对表达式字符窜的解析,用BeanShell解释器来完成对脚本的求值。下面的代码简洁的完成了对表达式的求值:

清单2.


public double caculateByFormula(Formula formula, Hashtable parameters) {
double result=0.0;
try
{
Interpreter i = new Interpreter();
// 实例化一个BeanShell解释器
i.eval("import parse.*;");
//引用公式管理系统
Vector para= formula.getParameters();
//获取公式中包含的参数列表
Iterator it= para.iterator();
//设置参数值
while (it.hasNext()){
String[] dec= (String[])it.next();
String declare= dec[1]+ " "+ dec[0];
i.eval(declare);
String value= ((Double)parameters.get(dec[0])).toString();
if (value != null){
String assign_value= dec[0]+ "="+ value;
i.eval(assign_value);
}else{
System.out.println("caculateByFormula():"+ dec[0]+ "参数名不符或改参数不存在"); System.exit(1);
}
}
//参数设置成功,根据公式计算脚本进行计算,仅用了一行代码就完成了求值过程,BeanShell值得你去了解 i.eval(formula.getScript());
Double rst= (Double)i.get("result");
result= rst.doubleValue();
}catch(Exception e){
System.out.println("caculateByFormula():"+ e.getMessage());
}
return result;
}



也许你曾经用解析字符窜的方法做过类似的编程工作,当你看到这里仅用一行代码就完成了对表达式的求值,是不是很惊讶?首先,实例化一个BeanShell解释器,然后将要使用的系统包import 进来,Interpreter.eval()方法就是对括号内的脚本求值或者说是解释执行脚本。然后,将formula的参数取出来,用BeanShell脚本方式声明这些参数,再将parameters中参数对应的值再次用BeanShell脚本的方式赋值给对应的变量。请注意,如果这时候传入的参数名不正确或者是参数名对应的参数类型不符,就会抛出系统异常。如果参数设置成功,则调用formula的公式计算脚本。仅用一行代码,BeanShell完成了大部分的工作:i.eval(formula.getScript())。最后,从解释器中取得计算结果,该结果存放在result变量中(可以查看清单1.中公式计算脚本)。现在是不是觉得BeanShell是一个好帮手?只要使用恰当,BeanShell将帮助你大大简化你的编程工作,这是一件非常快乐的事情。

另外,装载formula使用了JDOM技术,它从公式库中找到对应的公式,然后将该公式的参数列表以及计算脚本读出来组装成一个公式对象。见如下代码:

清单3.


public Formula loadFormula(String formulaID) {
Vector paras= new Vector();
try{
SAXBuilder builder= new SAXBuilder();
Document doc= builder.build(prefix+ "Formulas.xml");
//prefix是一个字符窜,用来指定公式库实际所在的位置
Element root= doc.getRootElement();
List formulas= root.getChildren("formula");
Iterator it= formulas.iterator();
Element formula= null;
while( it.hasNext()){
formula= (Element)it.next();
if(formula.getAttributeValue("id").equals(formulaID)){
break;
}
}
//获取参数列表
List parameters= formula.getChild("parameters").getChildren();
Iterator itp= parameters.iterator();
while(itp.hasNext()){
String[] s_para= new String[2];
Element e_para= (Element)itp.next();
s_para[0]= e_para.getAttributeValue("name");
s_para[1]= e_para.getAttributeValue("type");
paras.add(s_para);
}
Element script= formula.getChild("script");
String s_script= script.getTextTrim();
return new Formula(s_script, paras);
//将读出的信息组装成一个公式对象
}catch(Exception e){
System.out.println("loadFormula():"+ e.getMessage());
}
return null;
}




更好的了解该系统

上面介绍了系统应用BeanShell的部分,也就是系统的外部接口实现部分。你可能觉得有些迷惑,不是要自定义公式吗?怎么公式库早就有公式了呢?其实细心的读者早就发现,要自定义公式完成系统的另外一个重要功能并不是什么难事,你甚至可以想到直接编辑公式库来添加、修改、删除公式。当然你可以开发一个友好易用的自定义公式界面,你完全可以这样做。你需要完成的只是将用户输入的自定义公式信息(包括公式的参数及参数类型,运算表达式)转换成公式库中的参数列表和公式计算脚本。如果你要这样做,你不可避免的陷入到字符窜的解析当中去了。不过建议不要这样做,因为用户来写公式计算脚本将是可行的。再看看公式库的两个公式计算脚本,相信你会同意这一点。因为本文的讨论重点是BeanShell应用,所以这部分工作不做详细讨论,读者可以选择一个合适的方式来完成自定义公式的用户接口。下面是该系统的循序图和FormulaParser的状态图,能够帮助你更好的理解该系统。



图3. 调用公式计算外部接口



图4. FormulaParser状态图

最后,我们给出一个测试用例来说明如何使用该系统。公式库中有两个公式,一个用来计算商品折扣价格,一个用来计算职员奖金。我们将公式包含的参数值传给系统外部接口。见下面代码:

清单4.


FormulaParser fp= new FormulaParser();
Hashtable paras= new Hashtable();
paras.put("price", new Double(100.0));
//价格paras.put("discount", new Double(0.9));
//折扣率为0.9
System.out.println("计算结果:"+ fp.caculateByFormula(fp.loadFormula("1001"), paras));
//遵照公式1001计算,计算预期结果为90.0
FormulaParser fp1= new FormulaParser();
Hashtable paras1= new Hashtable();
paras1.put("sale", new Double(11000.0));
//销售额
paras1.put("score", new Double(0.8));
//表现得分
System.out.println("计算结果:"+ fp1.caculateByFormula(fp1.loadFormula("1002"), paras1));
// 遵照公式1002计算,计算预期结果为9100.0





程序输出为:


计算结果:90.0
计算结果:9100.0




与预期结果完全一致。开发环境为JDK1.3.1。

结束语

这就是BeanShell给我们带来的奇妙体验。并且,基于BeanShell的公式管理系统是一个很有用的工具。你可以试着将更多的运算法则加入到系统的计算器中,试着扩展该公式系统以集成到你目前的工作当中,你也可以提供一个非常友好的界面给用户,让他们轻松的定制自己的公式。一个增强功能的公式管理系统已经成功应用到笔者参与的一个电信系统中。当然,本文的方法可能不是最好的,欢迎你与我讨论。系统相关的源代码你可以在参考资料中找到。衷心希望“开源世界里充满了思想者。”

参考资料


你可以到BeanShell的发源地http://www.beanshell.org/了解更多关于BeanShell的知识
BeanShell的最新下载地址是http://www.beanshell.org/download.html
你还可以参考一篇developerWorks上介绍BeanShell的文章http://www-900.ibm.com/developerWorks/cn/java/l-beanshell/index.shtml
要想了解更多关于JDOM的知识,请访问http://www.jdom.org和参考developerWorks上的相关文章
系统相关的所有源代码点击这里下载

星期一, 二月 02, 2004

MD5 算法介绍

??????? | ???????? --- MD5???о?(1)
就是以MD5(或其它类似的算法)经加密后存储在文件系统中。当用户登录的时候,系统把用户输入的密码计算成MD5值,然后再去和保存在文件系统中的MD5值进行比较,进而确定输入的密码是否正确。通过这样的步骤,系统在并不知道用户密码的明码的情况下就可以确定用户登录系统的合法性。这不但可以避免用户的密码被具有系统管理员权限的用户知道,而且还在一定程度上增加了密码被破解的难度。

  正是因为这个原因,现在被黑客使用最多的一种破译密码的方法就是一种被称为"跑字典"的方法。有两种方法得到字典,一种是日常搜集的用做密码的字符串表,另一种是用排列组合方法生成的,先用MD5程序计算出这些字典项的MD5值,然后再用目标的MD5值在这个字典中检索。我们假设密码的最大长度为8位字节(8 Bytes),同时密码只能是字母和数字,共26+26+10=62个字符,排列组合出的字典的项数则是P(62,1)+P(62,2)….+P(62,8),那也已经是一个很天文的数字了,存储这个字典就需要TB级的磁盘阵列,而且这种方法还有一个前提,就是能获得目标账户的密码MD5值的情况下才可以。这种加密技术被广泛的应用于UNIX系统中,这也是为什么UNIX系统比一般操作系统更为坚固一个重要原因。

  算法描述

  对MD5算法简要的叙述可以为:MD5以512位分组来处理输入的信息,且每一分组又被划分为16个32位子分组,经过了一系列的处理后,算法的输出由四个32位分组组成,将这四个32位分组级联后将生成一个128位散列值。

  在MD5算法中,首先需要对信息进行填充,使其字节长度对512求余的结果等于448。因此,信息的字节长度(Bits Length)将被扩展至N*512+448,即N*64+56个字节(Bytes),N为一个正整数。填充的方法如下,在信息的后面填充一个1和无数个0,直到满足上面的条件时才停止用0对信息的填充。然后,在在这个结果后面附加一个以64位二进制表示的填充前信息长度。经过这两步的处理,现在的信息字节长度=N*512+448+64=(N+1)*512,即长度恰好是512的整数倍。这样做的原因是为满足后面处理中对信息长度的要求。

  MD5中有四个32位被称作链接变量(Chaining Variable)的整数参数,他们分别为:A=0x01234567,B=0x89abcdef,C=0xfedcba98,D=0x76543210。

  当设置好这四个链接变量后,就开始进入算法的四轮循环运算。循环的次数是信息中512位信息分组的数目。

  将上面四个链接变量复制到另外四个变量中:A到a,B到b,C到c,D到d。

  主循环有四轮(MD4只有三轮),每轮循环都很相似。第一轮进行16次操作。每次操作对a、b、c和d中的其中三个作一次非线性函数运算,然后将所得结果加上第四个变量,文本的一个子分组和一个常数。再将所得结果向右环移一个不定的数,并加上a、b、c或d中之一。最后用该结果取代a、b、c或d中之一。
以一下是每次操作中用到的四个非线性函数(每轮一个)。

   F(X,Y,Z) =(X&Y)|((~X)&Z)
   G(X,Y,Z) =(X&Z)|(Y&(~Z))
   H(X,Y,Z) =X^Y^Z
   I(X,Y,Z)=Y^(X|(~Z))
   (&是与,|是或,~是非,^是异或)

  这四个函数的说明:如果X、Y和Z的对应位是独立和均匀的,那么结果的每一位也应是独立和均匀的。
F是一个逐位运算的函数。即,如果X,那么Y,否则Z。函数H是逐位奇偶操作符。

  假设Mj表示消息的第j个子分组(从0到15),<<
   FF(a,b,c,d,Mj,s,ti)表示a=b+((a+(F(b,c,d)+Mj+ti)<<    GG(a,b,c,d,Mj,s,ti)表示a=b+((a+(G(b,c,d)+Mj+ti)<<    HH(a,b,c,d,Mj,s,ti)表示a=b+((a+(H(b,c,d)+Mj+ti)<<    II(a,b,c,d,Mj,s,ti)表示a=b+((a+(I(b,c,d)+Mj+ti)<<
  这四轮(64步)是:

  第一轮

   FF(a,b,c,d,M0,7,0xd76aa478)
   FF(d,a,b,c,M1,12,0xe8c7b756)
   FF(c,d,a,b,M2,17,0x242070db)