text:
COM Interface Hooking and Its Application, Chapter I
Desktop-as-a-Service Designed for Any Cloud ? Nutanix Frame
Environment: VC6+SP5 or VC7 (including SPY++, Depends, DataObject Viewer and OLE Viewer), MS Platform Core SDK, MSN Messenger 6.0, Resource Hacker, Process Explorer, and Process Spy. Win 2K/XP/2003 ONLY (local administrative member identity needed to experiment with the code sample)
Prerequisite Knowledge: COM, DLL, Windows Hook, Process/Thread/Module, PE format, Multithread, Memory Management, SEH
Special Notes:
- You need two active MSN logins to simulate chatting or experiment together with friends. FYI: You can use WinXP/2003 Fast User Switch (FUS) to simulate multiple MSN user login on one physical machine.
- All my code has nothing to do with "Messenger API." You can download it from MSDN Online. This set of API hass not been updated since MSN Messenger 4.7 was released, and it may be obsolete, though you can use it with MSN Messenger 6.0. (at least I have tried with automatic logon, but do not ask me about it, I only used it for hours).
Keywords for Search Engine: COM Interface Hook, C++ Virtual Table Hook, C++ V-table Hook, C++ Vtbl Hook, MSN Messenger 6 Hook, Modify C++ class Virtual Table, Modify C++ class V-table, Modify C++ class Vtbl, Early Binding
It has been nearly one year since I published "MessengerSpy++ for MSN Messenger/Window Messenger" (to interact with MSN Messenger 4.6, 4.7, and 5.0) in www.CodeGuru.com in the Autumn of the year 2002. During this time this IM keeps its booming pace and now owns about six million users around the world.
With MS releasing MSN Messenger 6.0 in July 2003, which adopted different architecture from its predecessor and made my MessengerSpy++ not work with this 6.0 version, I decided to write this article to demonstrate how to make a program interacting with MSN Messenger 6.0. This time, just as the title shows, I make it as a tutorial, in which I would like to introduce you to two new concepts—COM Interface Hooking and COM Interface Method Hooking.
Yes, it is COM Interface Hooking and Method Hooking, which means your interface method takes over the function call before routing to the hooked interface method, just like API hooking and Windows Message hooking, as you may have known.
As a tutorial, this time I will try to not only provide you with a bulk of serious code but also present you the working pattern of developing a program interacting with something running out of your control. Before you continue, I highly recommend you to read my previous articles "MessengerSpy++ for MSN Messenger/Window Messenger" and "Keystroke Logger and More" Series, Article No. 2 to get acquainted with MSN Messenger topics that will help you understand the following discussion greatly for I will intentionally omit what is available in these two articles to save space here.
The last note I want to make here is: This is the first article of the "Interacting with MSN Messenger 6.0 with COM Hook" series I put on my writing schedule list. The following articles, maybe, will demonstrate how to make MS Office 2K/XP and MSN Messenger 6.0 interact with each other, how to take advantage of MSN Network to construct a P2P network (without your server!) to interchange data or make remote inter-program communication, and upgrade my MessengerSpy++ to version 6.0.
What's New in MSN Messenger from the Developer's Side
One dozy afternoon in this July, I downloaded a beta MSN Messenger 6.0 from MS, and found its chat window's GUI had been redesigned completely differently from MSN Messenger 5.0. As you know, MSN Messenger before 5.0 (inclusive) uses a "RichEdit" common control as the chat input area and chat contents area, the "Send" button is a genuine "BUTTON" windows control. To interact with it, your program use a hook or whatever remote injection ways to penetrate into MSN Messenger process space, and conduct button-pushing and text-reading just the same as doing this in a dialog-based GUI program we all have been writing.
But, in MSN Messenger 6.0, when you use SPY++ to check its windows layout, there is only a "DirectUIHWND" window. "DirectUIHWND" is a widely used wrapper windows class since the emergence of Windows XP, according to my observation. If you are using WinXP/2003, you can modify Mr. Keith Brown's tool CmdRunAs in Feb 2000, MSJ or Mr. Martyn 'Ginner' Brown's tool Start a Command As Any User in www.codeguru.com 2001, or if you are a lazy typist, use my GUI-based "RunAs" directly to launch SPY++ under "LocalSystem" account to your logon desktop(WinSta0\Winlogon).
- You must be a Administrator Group Member to do so.
- No need to try WinXP/2003's "Runas..."; shell command because it always launches in the current desktop (WinSta0\Default)
- You may need to "Alt+Esc" to make SPY++ visible when turn to the logon desktop.
Now you will find the "DirectUIHWND" window. For the sake of these non-WinXP users, here is the screen shot of this scenario.
Figure 1. Using GUI RunAs, You Only Need Your Selection Of Target Desktop
Click here for a larger image.
Figure 2. With Spy++ running on a WinXP Logon Desktop, you will see the Whole Blue Window is "DirectUIHWND"
New things do not stop here. If you open "msnmsgr.exe" (the main exe of MSN Messenger 6.0, should be located in "%Program Files%MSN Messenger") with the Depends utility from Visual Studio, and check which API it uses, you will find it needs "UnhookWindowsHookEx" exported from "USER32.DLL" but not "SetWindowsHookEx"!? A possible explanation to this may be that the MS developer used "LoadLibrary(Ex)" and "GetProcAddress" to explicitly call "SetWindowsHookEx". BTW, in MSN Messenger 5.0, its import table includes both functions.
Anyway, one thing is certain: MSN Messenger 6.0, to save system resources, actively loads and unloads some DLL dynamically. You can launch the "Process Explorer"/"Process Spy" (the latter is lighter with less functionality, but can show you the DLL loading status; read "Escape from DLL Hell with Custome Debugging and Instrumentation Tools and Utilities" by Christophe Nasarre, MSDN Magazine June 2003 for details), then start and close a chat. You will find the DLL list changed a lot. An example is its unloading RICHEDIT20.DLL, when the user closed the last chat window.
Click here for a larger image.
Figure 3: Depends revealed an asymmetrical import function table in MSN Messenger 6.0
In the end, now, MSN Messenger 6.0 permits users to save their chat contents using an RTF format instead of plain text. Still, it cannot support the saving of the embedded Emotional icons, which is a pity. What's more, it still permits the user to close the chat directly without reminding "You are going to close an active chat, and the contents will be lost, proceed? Yes-No-Cancel".
Is Entering "DirectUIHWND" Possible? Yes! If You Know What's Going on Under the Hood
So, the point of interacting with MSN Messenger 6.0 is focused on whether it is possible to hook into "DirectUIHWND" and obtain data from inside successfully. General speaking, it is nearly impossible to hook and interact with a wrapper window such as "DirectUIHWND" if you have no documentation of its inside Windows message handler, COM interface description, and some global data structure. My first reaction to this window inside MSN Messenger 6.0 is that hooking into MSN Messenger 6.0 will be futile for I have no knowledge of the inside code. But, from my R&D experience, I have the following after a second thought:
|Condition 1
|MSN Messenger 6.0 can save chat contents in RTF format
|Result 1
|It has RichEditCtrl like the code handler inside
|Condition 2
|Due to the antitrust lawsuit, the MS Office Team is not permitted to use code written by the MS Windows Team --> the MSN Messenger Team will not have the RICHEDIT source code
|Premise 1
|MSN Messenger does not have enough resources to write RICHEDIT because the code is complicated
|Condition 3
|When I was doing my consulting on Excel Add-ins for MS, somebody told me that following:
MS Excel XP's Task Pane is purchased from a third-party company with the source code, which shocked me because it (Task Pane) seems like a 10-day job for any C++ guru
|Result 2
|The MSN Messenger Team buys/reuses a library-based RICHEDIT
|Condition 4
|MSN Messenger 6.0 loads RICHEDIT20.DLL dynamically whenever there is at least one chat window
|Condition 5
|In MSN Messenger 6.0, text is drawn "upon" the background image, Windowless Rich Edit seems more suitable to do that
|General Result
|MSN Messenger 6.0 uses the Windowless RICHEDIT
Figure 4: Rough Logic Deduction on MSN Messenger 6.0 of Jeff's
The above logic deduction, of course, is not strict and has logic defects. But, Condition 4 is critical, and is the strongest proof that the MSN Messenger Team does use their colleagues' work. Anyway, as I have mentioned, it may give you some idea when working on other programs.
(Something FYI: As you might know, richedit20.dll is a target of Worm.Nimda to launch itself when a user starts the MS Office family application. Besides, before the release a new version accompanying OfficeXP, it has a serious buffer overflow problem, which leads to system takeover by a remote user thru some IM chatting. Now, it is being protected, by default, by "Protected Storage" NT Service. An attempt to overwrite this will fail unless you stop this NT Service first.)
Click here for a larger image.
Figure 5. "Protected Storage" NT Service Will Prohibit Any Attempt to Overwrite Important DLL files in the System Directory
Now, use Depends to open "richedit20.dll". By default, this file is in the "%SystemRoot%\system32" directory. (If you are using Win2k, it may be in C:/WinNT/System32; or, if you are using WinXP, C:/Windows/System32.) The exported table follows:
|Ordinal ^
|Entry Point
|2 (0x0002)
|1 (0x0001)
|3 (0x0003)
|2 (0x0002)
|4 (0x0004)
|0 (0x0000)
|5 (0x0005)
|5 (0x0005)
|6 (0x0006)
|3 (0x0003)
|7 (0x0007)
|4 (0x0004)
|8 (0x0008)
|6 (0x0006)
|9 (0x0009)
|7 (0x0007)
|10 (0x000A)
|8 (0x0008)
Figure 6. Export Variables and Functions from Riched20.DLL by Depends
(FYI: You may wonder that how diligent I am that I type such a table from Depends. I am not. What I did was use a home-made tool hooking into Depends and stole the text inside the SysListView32. And with a later article I put on my writing schedule, I will show you. Check other articles of mine in CodeGuru.com's author list; it will be Text-Grabber or something like that.)
It is really lucky for us that riched20.dll just exports less than a dozen of variables and functions. Besides, after I checked it with "> Dumpbin -Exports Riched20.DLL", it has no forward function, which is good news for our hookers; and by default, riched20.dll is not available in the "Known DLL" part inside the Registry (refer to Chapters 19 & 20 of Programming Application for MS Windows 2000 by Jeffrey Richter, 1999, MS Press, for details). All these facts decided we only need to hook this riched20.dll.
Note: I am not saying that "forward function" and "Known DLL" will prevent us from hooking; you can always hook that Windows API on the final DLL. Now, we do not have these two problems, which means we only need to take care a single riched20.dll, leading to less work.
Besides, you may be surprised when I tell you the loading code in MSN Messenger 6.0 is like the following (pseudo code):
HMODULE hRichEditLib = ::LoadLibrary(_T("RICHED20.DLL"));
instead of a more robust one:
TCHAR szLibPath[MAX_PATH];
//Hope the guy's windows is not installed on Mars
UINT uRet = ::GetSystemDirectory(szLibPath, MAX_PATH]);
if(uRet == 0) err;
szLibPath[uRet] = TCHAR('\0');
::lstrcat(szLibPath, _T("\RichEd20.DLL"));
HMODULE hRichEditLib = ::LoadLibrary(szLibPath);
Note Again: I am not saying that the MSN Messenger Team's code is wrong. It just gives your code a chance to fake the RichEd20.DLL in the same directory as MSN Messenger 6.0 instead of writing API hooking code.
Okay, now start your VC++ 6.0 or VS.NET, create a new Win32 DLL project called "riched20.dll" (case insensitive), change its setting to Unicode, and export EXACTLY the same things as shown in Figure 5 in the riched20.def file, like this:
LIBRARY "Riched20" DESCRIPTION 'RichEdit Ver 2 & 3 DLL' EXPORTS IID_IRichEditOle @2 PRIVATE IID_IRichEditOleCallback @3 PRIVATE CreateTextServices @4 PRIVATE IID_ITextServices @5 PRIVATE IID_ITextHost @6 PRIVATE IID_ITextHost2 @7 PRIVATE REExtendedRegisterClass @8 PRIVATE RichEdit10ANSIWndProc @9 PRIVATE RichEditANSIWndProc @10 PRIVATE
You need go to the Platform SDK (now called MS SDK) directory, locate the file "TextServ.h", and copy the following from it to compose a new file titled "MyTextServ.h" in your project, like this:
#ifndef _TEXTSERV_H #define _TEXTSERV_H #ifdef __cplusplus struct PARAFORMAT2 : _paraformat //Copied from RichOle.h, //RichEdit.h... { LONG dySpaceBefore; ... }; #else // Regular C-style typedef struct _paraformat2 { UINT cbSize; ... } PARAFORMAT2; #endif // C++ //... more enum, struct and constant definition copied // from RichOle.h struct CHANGENOTIFY { DWORD dwChangeType; void * pvCookieData; }; #define TXTBIT_RICHTEXT 1 #define TXTBIT_MULTILINE 2 ... class ITextServices : public IUnknown { public: //@cmember Generic Send Message interface virtual HRESULT TxSendMessage( UINT msg, WPARAM wparam, LPARAM lparam, LRESULT *plresult) = 0; //more virtual functions .... }; class ITextHost : public IUnknown { public: //@cmember Get the DC for the host virtual HDC TxGetDC() = 0; //@cmember Release the DC gotten from the host virtual INT TxReleaseDC(HDC hdc) = 0; ... //more functions ... }; //+--------------------------------------------------------------- // Factories //---------------------------------------------------------------- // Text Services factory STDAPI CreateTextServices( IUnknown *punkOuter, ITextHost *pITextHost, IUnknown **ppUnk); typedef HRESULT (STDAPICALLTYPE * PCreateTextServices)( IUnknown *punkOuter, ITextHost *pITextHost, IUnknown **ppUnk); #endif // _TEXTSERV_H
You may find it frustrating to make this fake header compiled into our module. You have to dig several header files and copy-and-paste the constant, enum, or struct you need. Do not include "RichOle.h" or "RichEdit.h"!!! We are making a fake RichEd20.DLL; that's why you must make sure to add those defined in these header files into our "MyTextServ.h". Besides, NEVER NEVER change the member function order in the class!!! That will ruin the Vtbl we rely on for COM interface hooking.
Change your riched20.h file and let it export these:
#define RICHED20_API __declspec(dllexport) extern "C" RICHED20_API GUID IID_IRichEditOle; extern "C" RICHED20_API GUID IID_IRichEditOleCallback; extern "C" RICHED20_API GUID IID_ITextServices; extern "C" RICHED20_API GUID IID_ITextHost; extern "C" RICHED20_API GUID IID_ITextHost2; #include <unknwn.h> #include "mytextserv.h" extern "C" HRESULT WINAPI CreateTextServices( IUnknown *punkOuter, ITextHost *pITextHost, IUnknown **ppUnk); extern "C" LRESULT WINAPI REExtendedRegisterClass(HINSTANCE hInstance); extern "C" LRESULT WINAPI RichEdit10ANSIWndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); extern "C" LRESULT WINAPI RichEditANSIWndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
(FYI: The last three exported functions are not used by MSN Messenger 6.0, so you can omit them completely. I keep them here just for compatibility.)
Change your riched20.cpp file accordingly:
RICHED20_API GUID IID_IRichEditOle = { 0x00020D00, 0x0, 0x0, { 0xC0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46 } }; RICHED20_API GUID IID_IRichEditOleCallback = { 0x00020D03, 0x0, 0x0, { 0xC0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46 } }; RICHED20_API GUID IID_ITextServices = { 0x8d33f740, 0xcf58, 0x11ce, {0xa8, 0x9d, 0x00, 0xaa, 0x00, 0x6c, 0xad, 0xc5}}; RICHED20_API GUID IID_ITextHost = { 0xc5bdd8d0, 0xd26e, 0x11ce, {0xa8, 0x9e, 0x00, 0xaa, 0x00, 0x6c, 0xad, 0xc5}}; RICHED20_API GUID IID_ITextHost2 = { 0xc5bdd8d0, 0xd26e, 0x11ce, {0xa8, 0x9e, 0x00, 0xaa, 0x00, 0x6c, 0xad, 0xc5}}; typedef HRESULT (__stdcall *lpCreateTextServices)(IUnknown *punkOuter, ITextHost *pITextHost, IUnknown **ppUnk); typedef LRESULT (__stdcall *lpREExtendedRegisterClass) (HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); typedef LRESULT (__stdcall *lpRichEdit10ANSIWndProc) (HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); typedef LRESULT (__stdcall *lpRichEditANSIWndProc) (HWND hWnd,UINT Msg, WPARAM wParam, LPARAM lParam); #define NEW_DLL_NAME _T("\\RichEd20.Dll") //You MUST dynanically load the DLL HRESULT WINAPI CreateTextServices(IUnknown *punkOuter, ITextHost *pITextHost, IUnknown **ppUnk) { TCHAR szLib[MAX_PATH]; //255 is enough DWORD dw = GetSystemDirectory(szLib, MAX_PATH); if(dw == 0) return 0; szLib[dw] = TCHAR('\0'); ::lstrcat(szLib, NEW_DLL_NAME); HMODULE hLib = LoadLibrary(szLib); if(!hLib) return 0; lpCreateTextServices _CreateTextServices = (HRESULT (__stdcall *)(IUnknown*, ITextHost*, IUnknown**)) ::GetProcAddress(hLib, "CreateTextServices"); if(!_CreateTextServices) return 0; HRESULT hr = (_CreateTextServices)(punkOuter, pITextHost, ppUnk); //We cache this COM interface ITextServices* lpTx; ((IUnknown*)(*ppUnk))->QueryInterface(IID_ITextServices, (void**)(&lpTx)); if(lpTx) MessageBox(NULL, _T("Interface Hooked"), _T("Indeed"), MB_OK); //::FreeLibrary(hLib); //NOT FREE IT!!! return hr;
} //Note: //Protected Storage System Service Protects "RICHEDIT20.DLL" //under %SystemRoot%System32 LRESULT WINAPI REExtendedRegisterClass(HINSTANCE hInstance) { return 0; } LRESULT WINAPI RichEdit10ANSIWndProc(HWND hWnd,UINT Msg, WPARAM wParam, LPARAM lParam) { return 0; } LRESULT WINAPI RichEditANSIWndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { return 0; }
Making this DLL compliable may take you some time before you put everything correctly. After you produce this fake riched20.dll, copy it to the MSN Messenger 6.0 directory. Launch MSN Messenger 6.0, start a chat, and you will see for that each chat window, the message box will pop up six times. This means that in each chat window, there are six windowless rich edit controls working on your behalf. After a few experiments, I know the first one is the address area that shows the chatter's e-mail address and nickname. The second is the chat contents area and the fourth is where you input the words. The others have no direct user interaction functionality, so we omit them in the following discussion.
COM Interface Hooked!?
Up to now, code gurus should have understand the point all our working are around, just as I did in my MessengerSpy++, where I get the chat area window handle and interact with it. This time, I hooked my class into the windowless rich edit, and "embody it;" that is, you get the physically existing interface pointer. This "embodiment" must be done before the COM interface pointer returned to the Application (MSN Messenger) and my module must be a DLL module injected into that application.
In this way, my code can leverage the COM interface harmlessly (hopefully no race condition, no sync contradiction, and so on after carefully designing the code) with the application. What's cool is that you can go one step further and change the Vtbl of the interface—make the application's call into your hooking function first instead of calling into the stolen COM interface method. The following figures will explain the difference between a normal COM interface method call and a hooked interface method call.
Click here for a larger image.
Figure 7. Overview of COM Interface Hook Applied to MSN Messenger 6.0
If you are still not clear about this, refer the above figure. See, the MSN Messenger 6.0 implemented the ITextHost interface that is passed to riched20.dll and a ITextService interface pointer is returned. Because our fake Riched20.dll takes the middle position, we now own the interfaces' pointer—on one hand, you can query the ITextService for the RTF data and even set the RTF data (which is the prerequisite condition of dynamic interact with MSN Messenger 6.0 chatting). On the other hand, you can "WriteProcessMemory" and modify the interface Virtual Table (which is the counterpart of Windows Message Hooking if you do it on TxSendMessage").
The COM Interface Hook has one fatal shortcoming compared with API hooking and Message Hooking: You can make API hooking and unhooking in runtime freely by modifying the process image in memory. You also can hook and unhook a Windows Message by simply calling "SetWindowsHookEx" and "UnhookWindowsHookEx" freely. NOTE, freely means you can hook at any time, including the target process having been started for some time. The COM Interface Hook just cannot accept this. YOU MUST CATCH THE COM INTERFACE POINTER WHEN IT IS GIVEN BIRTH OR YOU LOSE IT FOREVER.
This characteristic means that a COM Interface Hooking program must be running before the target program creates the interface pointer and sometimes it must keep an eye on the creation of the target process if the target process does all interface creation stuff at the very beginning. This means you may need install "process monitor driver" written with DDK or other possible way like we put a fake DLL this time. As to the COM interface method hooking, it may be, under certain circumstances, extremely difficult to maintain program stablility without triggering a deadlock or race condition. So be careful.
Besides, most of the time, you may need to hook CoCreateIntance(Ex) directly to get the interface pointer, which requires your grasping API hooking tech first. As a fast link collection place, you may find my "Key Stroke Logeer and More, Part 3" gives a long list of resource concerning this topic. This topic will be discussed again, later in this series.
Communication Solution—Between MSN Messenger 6.0 and Your Program
Click here for a larger image.
Figure 8. Communication between MSN Messenger and Your Program. Note the hidden Window's functionality
Now, let's talk about the communication between your program and the fake riched20.dll. You can refer my previous articles, MessengerSpy++ and Key Stroke Logger and More Series, Part 1 for the usage of MMF when coping with variable length unidirectional data transmission. Although you can launch a "listening" thread inside this fake DLL and receive a command from your program, it may be complicated and error prone. As with my using Windows User Message in MessengerSpy++, this time I take the same approach with a little modification.
Because I am not using "SetWindowsHookEx" on a MSN Messenger 6.0 Chat window (well, you can use it, but it does not make much sense, like we use it in MSN Messenger 5.0 where I use it to cope with windows handles), I create a hidden window and this hidden window, just like COM STA, takes care of your Query Synchronization, Command Processing, and Redirection. And the code will be very compact; 30 lines is enough. "Simple is Beautiful." The code excerpt is below:
BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: InitializeRecv(TRUE); //Init the hidden window break; case DLL_THREAD_ATTACH: break; case DLL_THREAD_DETACH: break; case DLL_PROCESS_DETACH: InitializeRecv(FALSE); break; } return TRUE; } BOOL InitializeRecv(BOOL bInitialize) { if(bInitialize) { //Create Window.... RegisterClassEx(&wcex) g_hRecvWnd = CreateWindow(...); } else { if(!::IsWindow(g_hRecvWnd)) return FALSE; ::PostMessage(::g_hRecvWnd, WM_CLOSE, 0, 0); ::g_hRecvWnd = NULL; } return TRUE; } LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_DESTROY: PostQuitMessage(0); break; case WM_YOUR_COMMAND_QUERY_CHAT_AS_TEXT: BSTR bstrChat; g_lpTextService[wParam]->TxGetText(&bstrChat); WriteChatTextToMMF(bstrChat); SysFreeString(&bstrChat); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; }
You may now be bored due to the theoretical stuff I preached such a long way. Me too. So, I'll stop it here, and let you try the accompanying demo program. Use the folder "Riched20 Ver1", and copy the compiled DLL to your MSN Messenger 6.0' folder. Make sure you do that before you begin to chat. Now, start a chat with a friend (or yourself using WinXP/2003). I add a timer to the hidden window, so every 10 seconds it pop up a message box showing what is in the "To-Send" edit box area.
Also, try to put some emotional icon with the text. To keep all simple, I just save the first emotional icon to your C drive root directory as a bitmap file. By nature, the emotional icon is a WMF file, but, if I show you its original size image here, I guess you would like to use a bitmap more.
Figure 9. A Bitmap Grabbed from MSN Messenger 6.0 as an emotional icon (size: 19 X 19 pixels, always)
Figure 10. A WMF grabbed from MSN Messenger 6.0 as an emotional icon. Notice its size is much larger than its bitmap counterpart (size: 200 X 200 pixels, always)
Following is the code extracting an emotional icon from MSN Messenger 6.0. It is somewhat similar to MessengerSpy++ but this time, the embedded object is in WMF format instead of BMP format.
//Say, now, you have ITextServices* pointer g_lpIText already BSTR bstr; HRESULT hr = ((ITextServices*)::g_lpIText->TxGetText(&bstr); if(FAILED(hr)) err; //Process the text you got ::SysFreeString(bstr); //I only deal with the first embedded emotional icon IRichEditOle* pReo = NULL; g_lpIText->TxSendMessage(EM_GETOLEINTERFACE, 0, (LPARAM)(LPVOID*)&pReo, &lr); if(lr == 0) return; //how many images do we have? LONG nNumber = pReo->GetObjectCount(); //Your Image's Number //remember to pReo->Release(); when everything is settled if(nNumber == 0) return; REOBJECT* ro = new REOBJECT; ro->cbStruct = sizeof(REOBJECT); //deal with first image hr = pReo->GetObject(0, ro, REO_GETOBJ_ALL_INTERFACES); if(FAILED(hr)) err; IDataObject* lpDataObject; hr = (ro->poleobj)->QueryInterface(IID_IDataObject, (void **)&lpDataObject); if(FAILED(hr)) err; //I was stuck here for a while //ParseDataObject(lpDataObject); STGMEDIUM stgm; // out FORMATETC fm; // in fm.cfFormat = CF_METAFILEPICT; // Clipboard format fm.ptd = NULL; // Target Device = Screen fm.dwAspect = DVASPECT_CONTENT; // Level of detail = Full content fm.lindex = -1; // Index = Not appliciple fm.tymed = TYMED_MFPICT; hr = lpDataObject->GetData(&fm, &stgm); if(FAILED(hr)) err; //Metafile handle. The tymed member is TYMED_MFPICT. HMETAFILEPICT hMetaFilePict = stgm.hMetaFilePict; LPMETAFILEPICT pMFP = (LPMETAFILEPICT) GlobalLock (hMetaFilePict); int cx = 19; // pMFP->xExt; // it is always 19 X 19 int cy = 19; // pMFP->yExt; HWND hWnd = ::GetDesktopWindow(); //You are using true color display anyway HDC hDC = ::GetDC(hWnd); HDC hMemDC = ::CreateCompatibleDC(hDC); HBITMAP hMemBmp = ::CreateCompatibleBitmap(hDC, cx, cy); HBITMAP hPrevBmp = (HBITMAP)::SelectObject(hMemDC, hMemBmp); //Draw on Mem DC ::PlayMetaFile(hMemDC, pMFP->hMF); //If you want just save WMF anyway, just do that CopyMetaFile(pMFP->hMF, _T("C:\\fromMSN.wmf")); //If you want to save as BMP, go on TCHAR szFilename[64]; wsprintf(szFilename, _T("c:\\fromMSN.bmp")); //Hope you have C driver HANDLE hFile = ::CreateFile(szFilename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if(hFile == INVALID_HANDLE_VALUE) err; DWORD dwWritten; //need file header BITMAPFILEHEADER bmfh; bmfh.bfType = 0x4d42; // 'BM' int nColorTableEntries = 0; // true color only int nSizeHdr = sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * nColorTableEntries; bmfh.bfSize = 0; bmfh.bfReserved1 = bmfh.bfReserved2 = 0; bmfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * nColorTableEntries; ::WriteFile(hFile, (LPVOID)&bmfh, sizeof(BITMAPFILEHEADER), &dwWritten, NULL); BITMAP bm; //get bitmap information ::GetObject(hMemBmp, sizeof(bm), &bm); int nBitCount = bm.bmBitsPixel; //Warning! True Color! BITMAPINFOHEADER* lpBMIH = (LPBITMAPINFOHEADER) new char[sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * nColorTableEntries]; lpBMIH->biSize = sizeof(BITMAPINFOHEADER); lpBMIH->biWidth = bm.bmWidth; lpBMIH->biHeight = bm.bmHeight; lpBMIH->biPlanes = 1; lpBMIH->biBitCount = nBitCount; lpBMIH->biCompression = BI_RGB; lpBMIH->biSizeImage = 0; lpBMIH->biXPelsPerMeter = 0; lpBMIH->biYPelsPerMeter = 0; lpBMIH->biClrUsed = nColorTableEntries; lpBMIH->biClrImportant = nColorTableEntries;
//Compute Image Size DWORD dwCount =((DWORD) lpBMIH->biWidth * lpBMIH->biBitCount) / 32; if(((DWORD) lpBMIH->biWidth * lpBMIH->biBitCount) % 32) dwCount++; dwCount *= 4; dwCount = dwCount * lpBMIH->biHeight; //Use Virtual Memory API instead of new-delete LPVOID lpImage = ::VirtualAlloc(NULL, dwCount, MEM_COMMIT, PAGE_READWRITE); BOOL result = GetDIBits(hMemDC, (HBITMAP)hMemBmp, 0L, // start scan line (DWORD)bm.bmHeight, // # of scan lines (LPBYTE)lpImage, // address for bitmap bits (LPBITMAPINFO)lpBMIH, // address of bitmapinfo (DWORD)DIB_RGB_COLORS // use rgb for color table ); ::WriteFile(hFile, lpBMIH, sizeof(BITMAPINFOHEADER), &dwWritten, NULL); ::WriteFile(hFile, lpImage, dwCount, &dwWritten, NULL); ::VirtualFree(lpImage, 0, MEM_RELEASE); ::CloseHandle(hFile); //Restore DC ::SelectObject(hMemDC, hPrevBmp); ::DeleteObject(hMemBmp); ::DeleteDC(hMemDC); ::ReleaseDC(hWnd, hDC); ::GlobalUnlock(hMetaFilePict); //do not forget COM household work today ro->poleobj->Release(); //GetObject Called AddRef so //Release here delete ro;
You may wonder: Hi, Jeff, how do you know it is in WMF format? Unfornately, I am not a prophet, and I did try several ways to reveal what the MSN Messenger team hid behind the scene. Luckily, I got it. Following is my code:
void ParseDataObject(IDataObject* lpDataObject) { DWORD dwCF[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 0x0080, 0x0081, 0x0082, 0x0083, 0x008E}; DWORD dwTM[] = {1, 2, 4, 8, 16, 32, 64, 0}; int dimCF = sizeof(dwCF)/sizeof(dwCF[0]); int dimTM = sizeof(dwTM)/sizeof(dwTM[0]); for(int i = 0; i < dimCF; i++) { for(int j = 0; j < dimTM; j++) { FORMATETC fm; // in fm.cfFormat = dwCF[i]; // Clipboard format fm.ptd = NULL; // Target Device = Screen fm.dwAspect = DVASPECT_CONTENT; fm.lindex = -1; // Index = Not appliciple fm.tymed = dwTM[j]; STGMEDIUM stgm; // out HRESULT hr = lpDataObject->GetData(&fm, &stgm); if(FAILED(hr)) continue; PopMsg(_T("I caught it %d, %d"), i, j); } } }
The const value I used is extracted from <winuser.h> and <ObjIdl.h> like the following, by the way:
#define CF_TEXT 1 #define CF_BITMAP 2 #define CF_METAFILEPICT 3 #define CF_SYLK 4 ... enum tagTYMED { TYMED_HGLOBAL = 1, TYMED_FILE = 2, TYMED_ISTREAM = 4, ...
You will find the header files in Platform SDK are a golden mine that provide tons of invaluable information uncovered in documents. One joke for you: One of my classmates when I was bending on my course in EE someday told me in the end he read thru all the header files of Borland C++ 3.5 and found he felt himself completely when coding, "hi, just like when you crammed the Simplified Oxford Dictionary, your feeling on GRE verbal part," he added.
Thru ParseDataObject, there are two ways to extract the emotional icon—either you treat it as a CF_UNICODETEXT (actually, it is just the key-shortcut for the emotional icons), or you take it as a WMF.
In the end, there is a new kind of embedded object now in MSN Messenger 6.0—the "File Transmission Object". When you send a file to a chatter friend, you will have a thumbnail of the file (if it is a image Windows can recognize) you are sending there with a progress bar beneath. We will defer this object handling until later.
Hook COM Interface Method
From our C++ people. COM Interface is just a C++ class (yes, I know COM is language neutral, but taking it like an ordinary C++ class here make more sense). Derived from IUnknown, it definitely has a Virtual Table (abbr. VTBL) because a base class has a virtual function already. I know almost of you have experience with it already, but I do not think a lot of people are clear how the VTBL exists in the memory.
Actually, different C++ compilers use different ways to do it (I do not know how some other popular compilers such as Delphi and C++ Builder do this, but I guess they take similar approach as Visual C++. But, one thing is they all run on MS Windows. As far as I know, at least a kind of C++ compile for DSP chip programming put a vtbl pointer after the class member while MSVC put a vtbl pointer the first place). And what we are talking about here is specific to MSVC on Win32 platform, so all pointers here are 4 bytes long. With the following code (you will find it in VtblStory1 folder in the accompanying demo):
class classA { public: virtual int method1() { return 11; } virtual int method2() { return 12; } virtual int method3() { return 13; } }; class classB: public classA { public: virtual int method1() { return 21; } int m; int n; }; class classC : public classB { public: int method1(int a, short b) { return 31; } }; void testVtabl() { classC* pC = new classC; classB* pB = pC; int y = pB->method1(); //31 classB bb; bb.m = 31; bb.n = 32; LPVOID pBB = &bb; LPVOID pBB2 = &(bb.m); LPDWORD* lpVtabl = (LPDWORD*)&bb; HANDLE hSelf = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ::GetCurrentProcessId()); MEMORY_BASIC_INFORMATION mbi; if(VirtualQueryEx(hSelf, (LPVOID)(*lpVtabl), &mbi, sizeof(mbi)) != sizeof(mbi)) err; PVOID pvRgnBaseAddress = mbi.BaseAddress; DWORD dwOldProtect1, dwOldProtect2; if(!::VirtualProtectEx(hSelf, pvRgnBaseAddress, 4, PAGE_EXECUTE_READWRITE, &dwOldProtect1)) err; BOOL bStridePage = FALSE; //Check if Vtbl Strike 2 Pages LPBYTE lpByte = (LPBYTE)pvRgnBaseAddress; lpByte += 4096; //in Win32, 4k/page if((DWORD)lpByte < (DWORD)lpVtabl + 4) //We explain later bStridePage = TRUE; PVOID pvRgnBaseAddress2 = (LPVOID)lpByte; if(bStridePage) if(!::VirtualProtectEx(hSelf, pvRgnBaseAddress2, 4, PAGE_EXECUTE_READWRITE, &dwOldProtect2)) err; //Swap classB's method1 & method2 pointer DWORD dw; memcpy((LPVOID)&dw, (LPVOID)(*lpVtabl), 4); memcpy((LPVOID)(*lpVtabl), (LPVOID)(*lpVtabl + 1), 4); memcpy((LPVOID)(*lpVtabl + 1), (LPVOID)&dw, 4); //recover page property DWORD dwFake; ::VirtualProtectEx(hSelf, pvRgnBaseAddress, 4, dwOldProtect1, &dwFake); if(bStridePage) ::VirtualProtectEx(hSelf, pvRgnBaseAddress2, 4, dwOldProtect2, &dwFake); //Compiler sometimes addicts to optimization y = bb.method1(); //still 21 y = bb.method2(); //22 //Unfortunatly Compile takes place one step earlier; you will //not see effect return; }
Click here for a larger image.
Figure 11. Virtual Table of C++ base and derived class
In MSVC++, if a class self or base class(es) have a virtual function, its first 4 bytes of class layout inside the memory is the pointer to Vtbl, followed by the member variable and then the member function. Take the figure as an example: pBB points to an instance of classB, goes to the memory window. The first 4 bytes are "2C 50 42 00" (remember that Intel chips are little endian), followed by "1F 00 00 00". It is the m we just assigned to 31 (0x1F), then "20 00 00 00", which is n we assigned 32 (0x20).
Now, go to virtual memory 0x 0042 502C, and you have the right-most memory window. Umm, how do I say, from 0042502C to 00425038, it is classB's virtual table area, starting form the first virtual method—method1, it is "28 10 40 00", which is different from the base class—classA's method1 which lies in 0x0042503C. It makes sense; when you call a classB's method1, you enter its method1. On the other hand, because classB does not implement method2, when you call method2 in a classB instance, you enter classA's method2.
When you scroll the vertical scrollbar, you will see classC's vtbl lies in higher consecutive memory (the left bottom memory window). By comparing the data in this area carefully, I bet you are clear the layout of vtbl now.
Please note: Vtbl is always being put inside read-only pages together with whatever const you declared in C++, and if you try to write to it, your program will be terminated by system and a GP error box will pop up, which means you must call VirtualProtectEx to modify the page property to PAGE_EXECUTE_READWRITE before swapping the pointers of method1 and method2 in classB's Vtbl.
Also note: There is no proof that the Vtbl of a class lies in a single page. You must make sure all your write operations are conducted in areas you have modified. In our code above, there are three virtual methods totally, so classA, classB, classC (they are consecutive) vtbl each takes 3 * 4 byte, and that explains why " if((DWORD)lpByte < (DWORD)lpVtabl + 4)", see, lpByte points to classB's Vtbl, and I want to make sure classB's Vtbl place is modified before we continue.
Now you may ask: "Hi, I changed classB's Vtbl, okay, then I expect '(y = bb.method1()) == 22' and '(y = bb.method2()) == 21'. How come I still get 21 and 22? The answer is: The compiler has computed the function entry and hard-coded it in the binary. In other words, when program starts, it didn't use Vtbl at all because the compiler has decided which function to call in compile time. Sigh, too smart compiler...
So what on earth we can do to make a program have a look at the Vtbl before turning a member function? Component-based program. For, to a component, it has no knowledge of what member function will be called in runtime, it must use Vtbl to decide which member function to call. Okay, let's make such a scenario: (I do not teach COM/ATL; you must have experience with this to continue.)
Create a COM ATL DLL project (you will find its source code in the Plus folder in the accompanying demo), use everything default setting, need proxy/stub bound, add a Simple Object, call it Sum, make it a Custom Interface (you can use Dual but you have to modify offset later in the code), and add two methods until you get following in your Sum.cpp:
STDMETHODIMP CSum::method1() { PopMsg(_T("method1")); return S_OK; } STDMETHODIMP CSum::method2() { PopMsg(_T("method2")); return S_OK; }
Do not be concerned about PopMsg; it just calls WinAPI MessageBox. Then, add the third method:
STDMETHODIMP CSum::RadarIt() { LPDWORD* lpVtabl = (LPDWORD*)this; HANDLE hSelf = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ::GetCurrentProcessId()); MEMORY_BASIC_INFORMATION mbi; if(VirtualQueryEx(hSelf, (LPVOID)(*lpVtabl), &mbi, sizeof(mbi) != sizeof(mbi)) err; PVOID pvRgnBaseAddress = mbi.BaseAddress; DWORD dwOldProtect1, dwOldProtect2; if(FALSE == ::VirtualProtectEx(hSelf, pvRgnBaseAddress, 4, PAGE_EXECUTE_READWRITE, &dwOldProtect1)) err; //make sure all Vtbl areas are set LPBYTE lpByte = (LPBYTE)pvRgnBaseAddress; lpByte += 4096; //in Win32 4k/page, I am too lazy to call API BOOL bStridePage = FALSE; if((DWORD)lpVtabl + 2 * 4 > (DWORD)lpByte) bStridePage=TRUE; PVOID pvRgnBaseAddress2 = (LPVOID)lpByte; if(bStridePage) if(FALSE == VirtualProtectEx(hSelf, pvRgnBaseAddress2, 4, PAGE_EXECUTE_READWRITE, &dwOldProtect2)) err; //Vtbl has five functions; they are //Add Release QueryInterface Method1 Method2 //swap 3rd <--> 4th //That is swap Method1 and Method2 DWORD dw; memcpy((LPVOID)&dw, (LPVOID)(*lpVtabl + 3), 4); memcpy((LPVOID)(*lpVtabl + 3), (LPVOID)(*lpVtabl + 4), 4); memcpy((LPVOID)(*lpVtabl + 4), (LPVOID)&dw, 4); //Recover Page Property DWORD dwFake; ::VirtualProtectEx(hSelf, pvRgnBaseAddress, 4, dwOldProtect1, &dwFake); if(bStridePage) ::VirtualProtectEx(hSelf, pvRgnBaseAddress2, 4, dwOldProtect2, &dwFake); return S_OK; }
Hi, a little thing, I am too lazy to call API to get the page size though you should do it when you are serious. Let's focus on the main topic. ISum is derived from IUnknown that already has AddRef, Release, and QueryInterface virtual-ed. So, our method1 and method2 take the offset 3 and 4 (DWORD unit, that is 4 bytes). Remember that we swap method1 and method2 when RadarIt is called.
Now, make a MFC dialog project (you will find the code in the Pop folder in the accompanying demo), everything default, import the DLL's type library, add a button on the dialog so you can push it, and do the following:
void CPopDlg::OnButton1() { CoInitialize(NULL); PLUSLib::ISum* pSum; hr = ::CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, iid, (LPVOID*)&pSum); pSum->method1(); //Pop up "method1" pSum->RadarIt(); pSum->method1(); //Pop up "method2" ::CoUninitialize(); }
The first time you call pSum->method1(), you will see the "method1" message box; but after you call RadarIt, you call method1; you will get the "method2" message box instead of the "method1" message box. Make sense?
Okay, one step further. Now, change the ATL DLL's code as following:
STDMETHODIMP CSum::method2() { PopMsg(_T("method2")); RadarIt(); //Recover Original Vtbl method1(); //to call method1() RadarIt(); // return S_OK; }
Try out Pop Dialog; now, you see that method2 takes method1's position and actually method2 wrapped method1—whenever you call method1, you enter method2 first and then method1. COM interface method is hooked. Think this way: Your code injected into the target program, grabbed the COM interface pointer it uses, hooked the interface method, and ... all calls go to your code first, and you can do modification and whatever, then up to you, pass it down to the original interface method. Just one thing to remember: The interface can only be hooked when it is given birth.
That's all for today, and we will continue talking about MSN Messenger 6.0 and COM Hooking in the next article. Take care when playing with the demo program; the timer will pop up a message box every 10 seconds. And you have to exit MSN Messenger 6.0 before deleting or removing our fake riched20.dll, the same as before you copy it to MSN Messenger 6.0 directory.
As to what's "Coming Soon," I will give an integrated, all ways mentioned above, way to make a tool like my previous "MessengerSpy++ for MSN Messenger/Window Messenger"; and I think guru-level readers can skip it to my third article unless they want to copy-and-paste my code. Later, I will try to show you how to make MS Office 2K/XP and MSN Messenger 6.0 interact with each other, how to take advantage of MSN IM/Network to construct a P2P tunnel (without your server, that's the point!) to interchange data or make remote inter-program communication.
In the end, thank you all for your patience to read such a long article and I hope you enjoy it.
DownloadsDownload Demo Project Source (all the source code + exe, MFC DLL static linked) - 771 Kb
Version History
|Release Date
|article close
|Spare night time to put all basic together to www.codeguru.com readers after being asked in dozens of mails
|Sept 29, 2003
|Interface Method Hooked Implemented
|Sept 8, 2003
|Grab rtf, icons, ole objects from MSN IM6
|July 7, 2003
|ITextService caught in fake DLL, busy...zzz... plus submission of "Keystroke" series in CodeGuru.com
|July ?, 2003
|Brutal force injection API failed on MSN IM6; it blocked contact list intentionally. But time and socket system are taken over by inject code.


text:
Farewell to Summer With Outdoor Supper
Easy grilled pizza (the appetizer) and Cornish hens
"Not too long until fall," my husband, David, mused on our run yesterday morning. At first I thought he was rushing things a bit, but his observation piqued my awareness. I looked down. Sure enough, here and there on the gravel road was a smattering of red and yellow leaves.
See also: Planning a family reunion?
I'm not deceived. I've lived long enough to know there are still a few scorchers in the offing, but I also know fall's knockin' on summer's back door. While the weather's still holding, why not celebrate? And while it's still easy, use the grill.
Start with grilled pizza. Never tried it? You'll like it. I promise. With the grill's direct and enveloping heat, grilled pizza cooks up faster and crisps up better than oven-baked. You can make your own pizza dough. It's just five minutes in the food processor and a couple of unattended hours rising time. But these days pizza dough is also widely available in one-pound balls, making fresh pizza almost as simple (and more superb) than store-bought crust.
- If you're using fresh dough — store-bought or homemade — I offer a few tips:
- Resist the temptation to punch down and knead risen dough (and don't aggravate the grocery store stuff either). In its relaxed, supple state it's ripe for stretching. Knead it and you're stuck waiting for it to relax again.
- Stretch pizza dough into long rectangles rather than rounds. It's easier (as is cooking and cutting).
- Get the grill good and hot, but then turn down the heat once the stretched dough is put on the grill.
- Whether you need them or not, grill four pizzas at a time. Leftovers make a wonderful meatless lunch. To reheat, pop in the microwave to take off the chill and then warm them in a 300-degree (toaster) oven to re-crisp.
- Keep it simple. Don't make sauce or prepare toppings. Rather, spread them with flavored ricotta, and top with roasted or fried peppers and olives (both of which you'll find in the grocery store's Italian aisle.
While everyone nibbles pizza, crank up the heat again and lay on the Cornish hens. Butterflied, they cook more quickly and evenly. Once the hens have browned — 4 to 5 minutes per side should do it — dial back the heat and brush with reduced balsamic vinegar, a quick way to impressively flavor and color the birds.
If the birds are big and appetites are small, consider a half hen per person. Or grill one for each with leftovers to enjoy the next day.
You have your choice of side dishes — what doesn't go with chicken! Grill veggies during the high-heat phase in the open spaces. With all the grilling, you may prefer to cook vegetables indoors while the birds finish up over low heat. Green beans, broccoli, steam-sautéed spinach, corn and cherry tomatoes all work well.
Dessert is utterly simple. Sugar sliced end-of-season peaches served with a dollop of light sour cream or yogurt and a sprinkling of candied pecans or walnuts (which you'll likely find in the produce section).
Dine al fresco and enjoy the last of the warm weather. Since the days are getting shorter you might need a few candles, and you probably ought to grab a light sweater. Chilly nights are back.
Grilled Pizzas With Ricotta, Roasted Peppers and Olives
Serves 4 as a main course and up to 8 as an appetizer
1 cup part-skim ricotta, thinned with 2 tablespoons milk
2 large garlic cloves, minced
2 tablespoons chopped fresh basil, plus extra leaves for garnish
Salt and ground black pepper
1 pound bag store-bought pizza dough
1 jar (12 ounces) fried peppers or roasted red peppers packed in oil, cut into thin strips
16 pitted kalamata olives, halved
4 ounces (about 1 cup) grated sharp provolone cheese
1/4 cup grated Parmesan cheese (about 1/2 cup)
Turn all burners of a gas grill on high or build a medium-hot charcoal fire. Mix ricotta, milk, garlic, basil and a generous sprinkling of salt and pepper in a medium bowl; set aside.
Without punching or kneading dough (which makes stretching more difficult), turn dough onto a lightly floured surface. Using a dough scraper or sharp knife, quarter dough crosswise. Working one at a time, stretch each portion into a rustic 12- by 3 1/2- to 4-inch rectangle; transfer to a large cornmeal-coated baking sheet.
Reduce grill heat to medium; lift stretched pieces of dough onto grill. Cover and cook until bottoms are spotty brown, moving them around to ensure even cooking and piercing puffing dough as necessary, 3 to 4 minutes. Return pizza crusts, grilled side up, to baking sheet, topping with a portion of each in the following order: ricotta mixture, peppers, olives, provolone. Return pizzas to grill; cover and continue to grill until pizza bottoms are spotty brown, 3 to 4 minutes longer. Transfer to a cutting board; sprinkle with Parmesan, cut into pieces and serve.
Grilled Balsamic-Glazed Butterflied Cornish Hens
Serves 4 to 8
4 Cornish hens, 1 1/2 to 2 pounds each, rinsed, dried, split down the backbone with kitchen shears, opened up and pressed flat to butterfly
4 teaspoons each: kosher salt, freshly ground black pepper and Italian seasonings
1/2 cup balsamic vinegar
Prepare Cornish hens as described above. Mix salt, pepper, and Italian seasonings; sprinkle over both sides of Cornish hens. If time permits; let stand on a wire rack over rimmed baking sheet up to 2 hours or refrigerate overnight.
Meanwhile, bring vinegar to simmer in a medium skillet over medium-high heat; reduce to thin syrup (about 1/4 cup), just a few minutes.
When ready to cook, heat a gas grill igniting all burners on high for at least 10 minutes. Place hens on hot rack; cover and grill, skin side down, until impressively marked, 4 to 5 minutes. Turn birds over and continue to grill until well brown on remaining side, 4 to 5 minutes longer. Reduce heat to low and brush skin side with reduced balsamic; cover and continue to grill 7 to 8 minutes. Turn birds over brush with remaining vinegar and continue to cook until birds are impressively brown and cooked through, 7 to 8 minutes longer. Remove from grill and let rest a few minutes. Serve.
Sugared Peaches With Sour Cream and Honey-Roasted Pecans
Serves 6
6 medium peaches, peeled and pitted
3 tablespoons sugar
1/2 teaspoon almond extract
6 heaping tablespoons sour cream
6 tablespoons honey-roasted pecan or walnut pieces (from a 3.5 ounce packet)
Mix peaches, sugar and almond extract in a medium bowl; let stand a few minutes for sugar to dissolve and peaches to give off a little liquid. Spoon peaches into goblets; top with sour cream and pecans. Serve.
Join the Discussion0 | Add Yours
Please leave your comment below.
You must be logged in to leave a comment.

subset: commoncrawl


text:
The Big Day: Wedding Day or the Day After?
Many of us expect the biggest day of our lives to be the day we finally tie the knot! So many life-long expectations, dreams and aspirations are finally coming true. To have the "perfect wedding day" is often seen as the ultimate goal, and "bridezilla" is a humorous term often used for this phenomenon. However, it seems that the focus on being the center of attention, or on having the perfect party, really serves as a distraction from the true focus, which is this new eternal union.
Planning so many things that have to work out just right takes up great time and energy: the music, food, guests, seating arrangements, flowers, pictures, transportation, the wedding dress, hair styling, groomsmen and maids-of-honor (and their clothing), make-up, timing, the photographer…well, the list could go on and on.. It is, after all, a day that has been dreamed of or down-right planned for years in advance. The trimmings are abundant.
Amidst all the hoopla, however, comes amazing stress as the details of the "BIG EVENT" take over. It's not uncommon for that very stress to cause hurt feelings when things don't quite work as anticipated. Often the bride-to-be feels so overwhelmed and busy that she forgets to "check in" and see how others are feeling—her fiancée included. On the actual wedding day, running around during the event, the bride may not be able to even enjoy it! Many brides confirm that the wedding itself was exhausting, and that they felt quite empty and let down afterwards, almost like "that was it?"
All this expectation linked to having one perfect day (not to mention the honeymoon), can be expensive and start the marriage out with unnecessary stress (including debt). Simplifying the costs as well as the details will allow both the bride and groom to be able to focus on the spirit of the occasion, and the days after the "Big Day." Simplifying can allow time to truly enjoy and visit with their guests and their families, instead of creating empty details for the "perfect" future wedding album.
For the success and longevity of a thriving and loving marriage, how important is the actual wedding day…really? President Spencer W. Kimball answers this question with great clarity and wisdom. He was once interviewing a young man returning from his mission and asked him what he was looking forward to the most when he got home. The young man answered, "I've been praying to marry the one I love." Clearly this returning missionary had a girl back home with whom he was deeply in love and had planned on getting married.
President Kimball's loving and wise response to this young man (and to all of us) was: "Don't pray to marry the one you love, but pray to love the one you marry." It's not that the young man's prayer was wrong, by any means, but that his prayer would be answered the day he got married, and the days thereafter.
It is the month, or the year, after the wedding, when those rose-colored glasses are gone, that we truly learn to love. It is the morning when we wake up to a crying baby after sleeping only two hours that we make a marriage union real. It is when you don't really feel loving, but choose to act loving towards your new spouse that a marriage is truly born … not the wedding day or the honeymoon afterwards.
Praying continuously for an increase of love towards our spouse throughout our lifetime and acting upon those promptings is the real recipe for success. It is in the daily effort of thinking and praying about improving our spouse's life that true love is found and grows.
So, it is true that the day of our wedding is a milestone in our eternal progression, a great day to celebrate with family and friends our eternal union. However, the greatest days start after the wedding day and continue for the remainder of our lives with daily, weekly, monthly and yearly deposits of loving prayers and actions.
Elia Gourgouris Ph.D.
Dr. Elia Gourgouris is a nationally known speaker, relationship coach, and the president of LDSCoaching.com. With over 20 years of experience, he has inspired thousands of individuals and couples to find greater happiness and fulfillment, both in their careers and their personal lives. He holds a degree from UCLA and a Ph.D. in psychology. Dr. Gourgouris speaks to groups around the country regarding women's issues, self esteem, communications skills, and relationships. He is also a favorite presenter at both BYU and BYU-ID Education Weeks, and Time Out for Women conferences. He is the author of "DTR: What You Need to Know Before You Get Married" and he has an upcoming CD entitled "The Multi-Platinum Marriage: Going from Just Surviving to Thriving!" He and his wife, Sona, live near Boulder, Colorado, with their children.


text:
Since 2005, Textile Waste Solutions of Santa Barbara has been working with the city of Santa Maria, Calif., to reduce the number of textiles in landfills. According to the company's website, the partnership has recycled an average of 1.2 million pounds of textiles per year. The company, founded by Christopher Mkpado in 1995, plans on building more partnerships with California cities.
To encourage more recycling, the company lists 10 reasons why recycling textiles makes sense.
Top 10 Reasons to Recycle Textiles


text:
MIAMI - Live, WrestleMania 28 gave the fans in Sun Life Stadium a little bit of everything -- some head-scratching confusion, the end of an era, tremendous scientific wrestling, lots of Mommas, and an epic encounter that won't soon be forgotten.
The match between John Cena and The Rock had been a year in the making, and the crowd in Miami was more than amped -- they were electrifying.
The WWE's addition of personalized introductions for both men was a nice touch and added that much more hype to the match. Surprisingly, there we a good portion of Cena fans in attendance. It was nowhere near split, but probably 70/30 for The Rock.
"The Great One" showed the Survivor Series was no fluke, as he hadn't missed a beat. Rocky took the action to Cena with the high-octane offense. The crowd really got on Cena when he bailed out of the ring at one point. Perhaps a sign of pressure? Frustration? You be the judge.
The false finishes had the fans on their toes, but Cena made the cardinal sin of becoming complacent and attempting to one-up his adversary. It burned him in the end, because he ate a Rock Bottom and lost the match.
The sheer disappointment was evident on Cena's face, as he stormed up the ramp directly after the match. He sat there with his hands in his head like he just lost the biggest match of his life, but that nightmare became a reality, as The Rock stood tall in front of his hometown fans. After the PPV ended, The Rock circled the ring, thanked the fans, and even signed some autographs. He then approaches a fan holding a sign reading, "If Cena loses, we riot." I have to agree with The Rock, who quipped, "I don't think that's gonna happen."
As for the rest of the show, it had its moments live:
The Masked Writer doubles as a Masked Photographer in Miami. Be on the alert -- he might be standing next to you right now and you may not know it!


text:
DuPont™ Innovalight™ Silicon inks enable superior Selective Emitter solar cells
DuPont™ Innovalight™ has developed a portfolio of patented technologies and materials that allow crystalline silicon cell manufacturers to produce solar cells with higher conversion efficiencies.
DuPont™ Innovalight™ provides innovative silicon inks and licenses proprietary high-efficiency solar cell process technology to solar cell manufacturers which enables an immediate increase in solar cellconversion efficiency for customers.
DuPont™ Innovalight™'s selective emitter platform is the latest low cost cell design enabled by DuPont™ Innovalight™ silicon ink that allows cell manufacturers to quickly break through the efficiency ceiling.
The proprietary material is comprised of silicon nano-particles formulated into a screen printable ink. The result of over 100 engineering-years of ink development, DuPont™ Innovalight™ has optimized silicon ink powder and formulation properties to maximize the conversion efficiency of the DuPont™ Innovalight™ selective emitter platform. Additionally, the material was formulated to be compatible with low-cost industry standard screen printers - the same tool cell manufacturers use for printing metal contacts.
DuPont™ Innovalight™ continues to innovate and are working on an extensive roadmap for use of Silicon technologies and doping technologies to create advanced high efficiency solar architectures using standard industry tools and processes.
Low Cost
Manufacturing cost on a per watt basis is lowered
Quick Path to High Efficiency
Fast evaluation and quick ramp-up of production lines
Existing manufacturing lines can be retrofitted with Silicon Ink printing
Low Risk
Screen printers are low cost, low risk equipment with a well-established supply base and service infrastructure
Flexible Implementation
In response to the strong demand for faster access and deployment of new technology for high efficiency cells and modules, DuPont has a new commercial offering for customers to implement selective emitter technology using the DuPont™ Innovalight™ selective emitter technology platform.
- Flexible licensing terms to meet customers preference (by the factory, by the line, by the cell, by the kg)
- Full/partial technology deployment by DuPont or customer self-deployment option


text:
Most of our lumber is sold using "Board Measure" (BM) except where otherwise noted. The "Board Foot" (Bd. Ft. or BF) is the unit of measure. This is a measurement of Volume and each BF contains 144 cubic inches. All measurements are based on the net measure of the rough board. Nothing is added for shrinkage. All of our rough lumber is in random widths and lengths. Only the rough thickness is a constant and prices will vary from one thickness to another. In other words, a pile of 1" thick lumber may contain boards 5" wide and wider x 6' long and longer. These minimums are determined by the grade and the mixture of sizes in any given pile will be different. You will need to inquire for specific lengths and widths at time of ordering.
Here is the formula for calculating BF using the rough sawn dimensions of the board.
Length (feet) x Width (inches) x Thickness (inches)12
All of our veneers are measured and sold by the Square Foot (SF). The sizes vary depending on species, log size and inventory on hand.
Some of our solid species such as D4S stock, squares and burls are more conveniently measured and sold by the pound (lb.), lineal foot (ln. ft.), lineal inch (ln. in.) or per piece (pc. or each). These will be identified along with the price where necessary.


text:
|View Poll Results: sola - Episode 7 Rating
|Perfect 10
|9 out of 10 : Excellent
|8 out of 10 : Very Good
|7 out of 10 : Good
|6 out of 10 : Average
|5 out of 10 : Below Average
|4 out of 10 : Poor
|3 out of 10 : Bad
|2 out of 10 : Very Bad
|1 out of 10 : Painful
|Voters: 88. You may not vote on this poll
|Thread Tools
|2007-05-19, 16:20
|Link #81
Yuuki Aoi
Join Date: Jul 2004
As for "genuinely remorseful," that reminds me too much of the fake confessions that are an integral part of the present-day Chinese justice system. I prefer to think that simple forgiveness may help elicit real remorse and change later.
Mainly, though, I don't want to treat characters in popular fiction as if they were models for real-world actions. I prefer to keep it as a fantasy world.
|2007-05-19, 16:33
|Link #82
Waiting for more taiyuki!
Join Date: Jan 2004
Yep, forgiveness does not mean letting them go scott free. Punishment should reflect what society deems adequate for the crime.
Oh Takeshi, can we borrow your sword now?
So what anime is popular on TV now? Didn't Skane say that this wasn't?
|2007-05-19, 18:38
|Link #88
Yuuki Aoi
Join Date: Jul 2004
The top new show on the 2channel board (i.e., among otaku) is Nanoha StrikerS, then Lucky Star, then Gurren Laggen, Hayate, Xenoglossia, and Ookiku Furikabutte. Gurren Laggen and Hayate are daytime shows, but the others are late-night shows.
There are threads in the General Anime section with more details about TV Ratings and 2channel popularity.
|2007-05-19, 20:49
|Link #90
Anime Snark
Join Date: May 2006
Location: Singapore
Age: 36
Woah, woah. Last Boss? When did sola become a Shounen series? Continuing on this light-hearted track, these revelations about Aono makes me wonder how the heck will Yorito "score" with Aono in the game. Especially if some of the theories about her being an unhinged murderess is true.
Would the protagonist's internal thoughts go something like this?
"Hmm... nee-san will break my ankles if I choose to go out with Mana tonight. Should I-
a) Sneak out of the house.
b) Keep nee-san company.
c) Knock nee-san out with medication."
~~~~ ~~~~
Are there going to be "Bad Ends" like the infamous "School Days" game? Given that Aono has the second-biggest prominence after Matsuri in the promotional material, I am fairly confident that there is actually an Aono-route in the upcoming game. So how the heck will that play out?
At this point, there is very little material for a turning point in Aono's redemption (without resorting to self-sacrifice). I can sympathise with Aono, and I feel that it is okay to do so. I will only be worried if a person empathises with Aono.
I am just at a loss on how Aono can have a genuinely happy ending at this point (without resorting to plot handwaving).
|2007-05-19, 21:28
|Link #92
Join Date: Mar 2006
I think it's still too early to peg Aono as the "bad guy" until we get some answers as to what exactly is going on with Yorito. Shows like this love to play with viewers heads, pointing them in one direction and then dropping something different on them in the next episode. It's possible the scenes where just shown like that to give you the wrong impression.
Sola is certainly undernoticed/appreciated, but that's not a bad thing. I certainly didn't expect much when it first aired, but each episode continued to surprise and well...here we are. I think Omni had a similar view when he did his synopsis of the spring shows.
|2007-05-19, 21:38
|Link #93
Waiting for more taiyuki!
Join Date: Jan 2004
But this is ep. 7 out of 13 with 2 additonal DVD only episodes. They can't play with us for too much longer and have a real resolution. Pretty soon they have to answer those questions and fill in the plot holes to get to a resolution by the end of the TV run. This should be the last episode to mess with our heads imo.
Thanks Kaoru Chujo for the info.
Last edited by orion; 2007-05-19 at 21:48.
|2007-05-19, 21:51
|Link #95
Senior Member
Join Date: Dec 2003
That's true. Though by the looks of it, all major conflicts and the majority of plot-points are in place, so episode 7 did it's job as the middle episode quite well. Only thing left now, outside of a few necessary flashbacks, is to watch the characters specific situations run their course alongside the major themes.
And on another note, if either of those extra episodes end up being the true final episode, then the wait is honestly a price I'm willing to pay. The more meat to this show, the better. =)
|2007-05-20, 02:35
|Link #97
Lost in my dreams...
Join Date: Jun 2006
Age: 32
You know, Skane - that question about Aono's path in the game really got me thinking, and i agree - i don't see how in the world Yorito could "score" with her at this point. Unless she never goes bonkers if you go after her path and choose not to run after Matsuri for example. I think "School Days notorious ending style" is indeed a possibility.
And this
|2007-05-20, 02:45
|Link #98
sensei no pet
Join Date: Apr 2007
Location: Virginia, USA
This episode is why I was holding back on the 10 ratings. Don't say I didn't warn you.
I can't hate Aono. Well, really I can't hate anybody, in a pacifistic zen-like way. But all this talk of wanting to punish Aono for hurting Matsuri and Yorito... There's no need. Aono already has been justly punished: Because of what she's done, Yorito will never love her the way she wants him to. She created her own hell the moment she decided to manipulate Yorito's mind. That's why I feel sorry for her.
I was wondering if we'd get to see Nagasaki's famous mountain lifts in this show. (Sola is a secret a promotional tool for the tourism board.) Also another good shot of the megane bridge. And now I now why Yorito and Aono live in such a large house -- to store all of Aono's clothes. (So how come Matsuri had to borrow from Mana? This could be Matsuri's secret from episode 3. And the real reason Aono kicked her out.)
More flashback fashions. The sandals they're wearing have flat soles. And it looks like Aono is wearing stockings?
When I saw the milk carton I thought there'd be a picture of Matsuri with the words "Have you seen this girl?"
The question isn't if we'll have to deal with more cranial knots, but whether any of the next 5 episodes will give us a break.
And I'm really thinking I need to find a copy of School Days. Is it wrong for me to want to play a game for the bad endings? (I keep killing myself in the aforementioned game because Ciel-sensei is ~so~ cute.)
|2007-05-20, 03:03
|Link #99
Join Date: Mar 2007
Location: Canada
|2007-05-20, 03:08
|Link #100
Lost in my dreams...
Join Date: Jun 2006
Age: 32
"I tried mindraping/framing and murder out of jealousy, but things didn't turn out my way. Poor me " < This is basically the Aono's situation i am supposed to feel sorry for.
As things stand now, i am incapable of feeling sorry for her, and she has dug herself a hole in that aspect. The moment you are ready to kill other people just to get what you want, you can stop looking for compassion in my way. Nothing short of self-sacrifice can redeem her in my eyes as of now. Actions speak louder than words, and attempted murder by manipulation Yorito is about as loud as it can get.
*goes to find a rusty, sunlight-imbued pitchfork*
|Thread Tools


text:
generates a three-dimensional plot of a surface representing an array of height values.
generates a plot of the surface with heights zi at positions {xi,yi}.
plots the surfaces corresponding to each of the datai.
Details and Options
- ListPlot3D has the same options as Graphics3D, with the following additions and changes:
Axes True whether to draw axes BoundaryStyle Automatic how to draw boundary lines for surfaces BoxRatios {1,1,0.4} bounding 3D box ratios ClippingStyle Automatic how to draw clipped parts of the surface ColorFunction Automatic how to determine the color of surfaces ColorFunctionScaling True whether to scale arguments to ColorFunction DataRange Automatic the range of and values to assume for data Filling None filling under the surface FillingStyle Opacity[0.5] style to use for filling InterpolationOrder None the polynomial degree in each variable of surfaces used in joining data points MaxPlotPoints Automatic the maximum number of points to include Mesh Automatic how many mesh lines in each direction to draw MeshFunctions {#1&,#2&} how to determine the placement of mesh lines MeshShading None how to shade regions between mesh lines MeshStyle Automatic the style for mesh lines Method Automatic the method to use for interpolation and data reduction NormalsFunction Automatic how to determine effective surface normals PerformanceGoal $PerformanceGoal aspects of performance to try to optimize PlotLegends None legends for surfaces PlotRange {Full,Full,Automatic} the range of or other values to include PlotRangePadding Automatic how much to pad the range of values PlotStyle Automatic graphics directives to specify the style for the surface PlotTheme $PlotTheme overall theme for the plot RegionFunction (True&) how to determine whether a point should be included ScalingFunctions None how to scale individual coordinates TextureCoordinateFunction Automatic how to determine texture coordinates TextureCoordinateScaling True whether to scale arguments to TextureCoordinateFunction VertexColors Automatic colors to assume at each point VertexNormals Automatic effective normals to assume at each point
- In ListPlot3D[array], array must be a rectangular array. Each element can be either a single real number representing a value, or an triple.
- There will be holes in the surface corresponding to array elements that do not represent explicit height values.
- ListPlot3D[array] by default takes the and coordinate values for each data point to be successive integers starting at 1.
- The elements of array can also be triples {x11,y11,z11}, specifying heights zij at explicit positions {xij,yij}. The connectivity of the surface in this case is still taken to follow the 2D array.
- In the default case with no explicit and given, the setting DataRange->{{xmin,xmax},{ymin,ymax}} specifies the ranges of coordinate values to use.
- With the default setting DataRange->Automatic, ListPlot3D[{{a11,a12,a13},…,{an1,an2,an3}}] will assume that the data being given is {{x1,y1,z1},…}, rather than an ×3 array of height values.
- ListPlot3D[list,DataRange->All] always takes list to represent an array of height values.
- Possible settings for ScalingFunctions include:
sz scale the z axis {sx,sy} scale x and y axes {sx,sy,sz} scale x, y and z axes
- Each scaling function si is either a string "scale" or {g,g-1}, where g-1 is the inverse of g.
- For ListPlot3D[array], Mesh->Full draws a mesh that crosses at the position of each data point.
- array can be a SparseArray object.
- The arguments supplied to functions in MeshFunctions and RegionFunction are , , and . Functions in ColorFunction and TextureCoordinateFunction are by default supplied with scaled versions of these arguments.
- The setting for VertexColors must be an array or list with the same structure as the coordinate data.
- An explicit setting for VertexColors overrides colors determined from ColorFunction.
- ListPlot3D returns Graphics3D[data].
- Themes that affect 3D surfaces include:
"DarkMesh" dark mesh lines "GrayMesh" gray mesh lines "LightMesh" light mesh lines "ZMesh" vertical mesh lines "ThickSurface" add thickness to surfaces "FilledSurface" add filling below surfaces
Examplesopen allclose all
Basic Examples (3)
Scope (18)
Options (105)
Applications (5)
Properties & Relations (14)
Possible Issues (2)
Neat Examples (2)
See Also
ListPointPlot3D ListSurfacePlot3D Plot3D ListDensityPlot ListContourPlot ListContourPlot3D ListSliceContourPlot3D ListSliceDensityPlot3D ListLinePlot ReliefPlot ArrayPlot
Related Guides
Related Links
Introduced in 1988
(1.0)| Updated in 2017


text:
When it comes to Justin Bieber and Selena Gomez's on-off affair, I don't have a clue what's happening week to week. They're on, they're off, they're blocking phones, they're at the same hotel, whew! So much drama for two young kids. But I have confirmation that at this very second -- well, at least at the second these photos were taken -- the pair were ON. So much so that it's kind of shudder-inducing, truth be told. But, hey, I won't be a love scrooge. It's holiday PDA, everyone! Gather round ...
On Friday, the Biebs and Selena were photographed kissing in a Salt Lake City airport. And, I mean, they weren't just kissing. It appears that Selena had something stuck in her throat and Biebs was valiantly trying to remove it with his tongue. I hope she's okay.
The pair apparently flew to Utah for a ski trip and may even have been joined by Taylor Swift and Harry Styles. I'd love to be a fly on the wall of that chalet! (Well, not really.)
At the airport, Selena sat on Justin's lap while the pair literally lapped at each other. A witness told RadarOnline: "They were very affectionate with each other. They were kissing on and off and they just sat together and talked and looked totally in love."
Thanks, source (coughpaparazzicough), for that info. Quite apparent from the photos! We'll see how long this particular reunion lasts. I get the feeling these two just love the ups and downs (so to speak) of a relationship full of angst drama. Trust me, kids, it gets old fast!
Sorry, I'm getting a little scroogey again. What I meant to say was, awwwww. Young love. So sweet!
Do you think these two will be together long?
Image via Getty


text:
Authors: Mungler and AgiEditor: Agi
The Cathode Ray Tube (CRT) has been the standard for PCs from the start - from the ancient, tiny green screens to today�??s high powered 20" + SVGA behemoths. However, while the image quality of CRT's has continued to improve, there are other areas that are still a concern: mainly power consumption, physical size and radiation emissions. Thanks to standards like TCO'95, the issues of power consumption and emissions are being addressed. These standards are evolving as quickly as the technology. The issue of size, though, has not been as easy to overcome. In fact, as monitor prices continue to fall, users are purchasing larger and larger monitors. The downside of this new purchasing power is that these big monitors consume an immense amount of the space on your desk.
There is a solution that addresses all three of the concerns listed above: Thin Film Transistor Liquid Crystal Display (TFT LCD). These types of computer monitors are compact and yet meet the strictest of standards imposed on today's VDU�??s (Visual Display Units). The only problem is, with these new technologies comes a whole new set of issues and somewhat confusing specifications. This article will attempt to explain the meaning of various technical specifications, their importance when identifying a good display model and how these things affect the gaming experience.
So what is a TFT LCD
The Austrian botanist Fredreich Rheinizer discovered liquid crystal back in 1888. "Liquid crystal" is neither a liquid nor a solid crystal; instead it remains in a state much like oil. In the mid-1960s, scientists demonstrated that liquid crystals could change the properties of light passing through it when you apply an electric field. Prototype displays were made soon after, but were too unstable for any kind of mass production. However, a British researcher proposed using a stable, liquid crystal type material (biphenyl). LCDs then went on to be featured in things like calculator screens and other gadgets displays.
How do they work
Instead of making an image by firing a scanning beam of electrons at tiny phosphor dots on the inside of the screen (like CRTs), LCDs use a strong backlight as their light source, and then control how much of this light is allowed to reach the dots (pixels) by selectively blocking the path of that light. If you then place coloured glass over each pixel you can create colour images on the display.
Thin Film Transistors (TFT LCD) are an extension of the older Liquid Crystal Display technology. TFT LCD has a sandwich-like structure with liquid crystal filled between two glass plates. TFT Glass has as many TFTs as the number of pixels displayed, but these transistors are lighter than in previous LCD models. Liquid crystals then move according to the difference in voltage between the Colour Filter Glass and the TFT Glass.
Liquid crystals remain transparent unless a voltage is placed across them. While the voltage is applied, the portion of the display under its influence remains opaque. The speed at which the liquid crystal can be switched on and off is relatively slow (this will be discussed later). But in 1992, Thorn EMI announced a new type of LCD called FLCD (ferroelectric LCDs). This technology switches more quickly and also has the ability for each pixel to remain in whatever state it�??s in without the need for the switching voltage to be constantly applied. This greatly increases battery life for portable equipment such as laptops and PDA�??s. While FLCDs hold much promise, not much of that technology has been seen on the open market as of yet. The sensitivity of the FLCD screens to shock and vibration, which dislodges the crystals, is believed to have been solved and the contrast ratio is also improving as the elimitation of defects increase.
The future for ferroelectric LCDs is still uncertain, but if a higher contrast and wider viewing angle is achieved, they will compete with current LCDs. What is the response time for the FLCD? The FLCD has the advantage that the on and off switching times are the same, unlike other displays and the response time is a staggering 70µsec!!! at normal projector working temperatures. But I digress :)
The Major Issues with TFT LCD
For most users, TFT LCD monitors more than meet the requirements of everyday computing; coping adequately with the Windows environment, photos, video, documents, etc. For gamers, however, it's not quite that straightforward. While CRTs have issues with size, emissions and power consumption, TFT LCD�??s have a different set of issues - issues that directly affect aspects of PC gaming. These concerns include native resolution, non-native resolution, response time, dot pitch and a few more.
Native ResolutionThe Cathode Ray Tube of regular CRT monitors extends from the glass you see in the front of the screen, into a cone shape to the back of the unit and makes up the bulk of the monitor (in size as well as weight). At the very back of the tube is an electron gun. This gun fires a stream of electrons towards the screen. Magnets steer the electrons from the guns. These magnets are what you adjust with your control panel buttons on the monitor, allowing you to fill the viewable area of the screen. On the other side of the glass is a masking layer of three colored phosphers, the stream of electrons hits this layer and shines either red, green or blue. These primary colours combine to make your image.
TFT LCD monitors work on a completely different principle allowing manufacturers to significantly reduce the screen in both size and weight, while keeping the same viewable area. These screens have a fixed amount of pixels built into the display and are therefore designed to operate at a specific resolution; this is termed the native resolution of the display. Native Resolution is not usually a problem with everyday tasks...but can cripple the gaming experience. Why? If the video card does not have enough horsepower to run a game at the native resolution, the game will turn into a dull slideshow. One solution to get the game to run at an acceptable frame rate (at the native resolution) is to reduce various graphical details options. Considering today�??s photorealistic games, however, the resultant image quality is rarely satisfying to the people that want to play these games. So one thing to consider when choosing a TFT LCD is whether your system can handle the images at its native resolution.
Non-native ResolutionIf the computer does not have enough horsepower to run at the native resolution, another option available is to change the resolution of the display to something less than the native resolution. This may seem to be a simple answer but this usually uncovers another weakness of TFT LCD�??s...most do not display acceptable images at anything other than their native resolution. Two things can happen when a screen is not in its native resolution; either the screen size may be reduced and black bars are added or the image is stretched to fill the screen. If you have a screen with a native resolution of 1024x768 and you play a game at 800x600 then there may be nice black bars round the edge of your screen of 306432 pixels that are simply not being used. Much like watching a letterbox format movie (really really widescreen) on a standard size TV. Alternatively, the screen may use pixel doubling or �??zooming�??. This is where the non-native screen resolution is mapped onto the native resolution. You will not have any black bars round the edge of the screen, but each pixel being output by the video card may be mapped onto several pixels on the actual screen. This gives rise to fuzzy edges on objects, even when the picture is completely static. This situation should be avoided as much as possible. Make sure you look for an TFT LCD monitor that is the right size for your uses and for your computer.
Response TimeSince their debut, a major black mark against TFT LCD�??s in the gaming world is their relatively slow response times, hinted at earlier. The screens simply do not refresh at the same rate as the CRT cousins and this leads to ghosting and jagged pixel effects on you display. Ghosting is when the previous image displayed on the screen can still be seen for moments after the image has changed. For instance, in any first person shooter style game, if you look around quickly, a hazy after image of walls etc will trail the actual image on the screen. The image below shows that effect.
(Far Cry image taken with digital camera)
In the photo below, the enlarged version of the picture above, you can just about see the effects of ghosting on the edge of the wall bleeding into the light area to it's right. However, it's difficult to completely convey this effect in static images.
(enlarged view showing ghosting)
CRT�??s have a refresh rate measured in Hz. The higher this number, the faster the screen refreshes the display. A CRT monitor redraws the whole display every pass, picking up even the smallest changes at a much faster rate than the human eye. Having a refresh rate that is too low, the CRT monitor will appear to flicker. In the States, the sine wave used by electric utilities is 60Hz. That's why CRT monitors will flicker when run at 60Hz. (for more on the effects of Refresh Rate and VSYNC, click here and here) TFT LCD�??s are slightly different and are not hindered by the flicker that plagues CRT monitors. Consequently, TFT LCD�??s typically operate at 60Hz and when the image on the screen changes, only the affected pixels on the display are altered. The rest remain unchanged. But changing pixels takes time to alter from one state to another, this is commonly called rising and falling. The time the pixels take to rise or fall is then referred to as the response time. The smaller the response time, the faster the pixels can change and this will result in less of the ghosting effect. This is, in my opinion, the greatest issue with TFT LCD�??s and gaming. This response time is given in milliseconds on the specifications for the display. However, gray-to-gray response time is much more important for determining if an LCD is the best for gaming, rather than rising and falling response time. Unfortunately, grey-to-grey response time is rarely given on sales specifications.
Viewable AreaOne continuous criticism of CRT monitors was the screen size. Invariably CRT monitors would be sold according to their tube size. So the �??18�?� monitor�?� refers to the size of the cathode ray tube in the monitor. The viewable area of the screen may be significantly different from 19�?�. The viewable area may range from 15�?� to 17.1�?� depending on the manufacturer of the CRT in question. When referring to LCD�??s and TFT 19�?� is 19�?�. The viewable area of an 17�?� CRT is equivalent to a 15�?� LCD or TFT screen. So if you are perfectly happy with the size of your screen and just wish for it to take up less space. You should go for the next size down (i.e. if you have a 19" CRT desk hog, look at buying the next size down, a 17" or 15" LCD/TFT)
There are a few more minor issues that may or may not affect your buying decision.
Next Page
Table of Contents
Page 1:
Page 2:
Graphical Design by
Mohsin Ali
Website Layout by Universal
PHP Programming by Network
Additional HTML Programming by Moddin.Net


text:
THE SQL Server Blog Spot on the Web
One of the things that I enjoy about working for Quest Software is that we give back copiously to the community. From activities and offerings like SQLServerPedia, to our free posters mailed anywhere in North America (and don't forget the free hi-res PDFs for the rest of the world),
Don't forget that free DVDs of our virtual conferences featuring me, along with Buck Woody (blog | twitter) and Brent Ozar (blog | twitter) will be mailed anywhere in North America free of charge, now available at this link.
If you would like to receive an email when updates are made to this post, please register here
Subscribe to this post's comments using
We are play a games in grouping and you are get the more chance in this games we are play the more games https://setclockwindows.com/ when you are alone in this house to play the more games in this website.


text:
Do you need to find information about a historical person, place, or event for a school or research project? If so, the Smithtown Library's research databases can help you get started. Click on one of the history related topics below to see all the resources available.
NOTE: To access these databases remotely from home you will need a valid Smithtown Library card and a library account password. If you would like to check the status of your card or need help establishing a library account password please contact the library's main reference desk at 631-360-2480 x 198.