Mercurial > projects > dwt-win
changeset 191:528e7db39d9e
now really
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Mon, 17 Mar 2008 00:52:21 +0100 |
parents | 72ef824d4983 |
children | 135517d29564 |
files | dwt/program/Program.d |
diffstat | 1 files changed, 433 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwt/program/Program.d Mon Mar 17 00:52:21 2008 +0100 @@ -0,0 +1,433 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * Port to the D Programming language: + * Frank Benoit <benoit@tionex.de> + *******************************************************************************/ +module dwt.program.Program; + +import dwt.DWT; +import dwt.graphics.Image; +import dwt.graphics.ImageData; +import dwt.internal.win32.OS; + +import dwt.dwthelper.utils; +import dwt.dwthelper.Integer; +static import tango.text.convert.Utf; + +version(build){ + pragma(link, "shlwapi"); +} + +/** + * Instances of this class represent programs and + * their associated file extensions in the operating + * system. + */ +public final class Program { + char[] name; + char[] command; + char[] iconName; + char[] extension; + static const char[][] ARGUMENTS = ["%1"[], "%l", "%L"]; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + +/** + * Prevents uninitialized instances from being created outside the package. + */ +this () { +} + +static char[] assocQueryString (int assocStr, TCHAR[] key, bool expand) { + TCHAR[] pszOut = NewTCHARs(0, 1024); + uint[1] pcchOut; + pcchOut[0] = pszOut.length; + int result = OS.AssocQueryString(OS.ASSOCF_NOTRUNCATE, assocStr, key.ptr, null, pszOut.ptr, pcchOut.ptr); + if (result is OS.E_POINTER) { + pszOut = NewTCHARs(0, pcchOut [0]); + result = OS.AssocQueryString(OS.ASSOCF_NOTRUNCATE, assocStr, key.ptr, null, pszOut.ptr, pcchOut.ptr); + } + if (result is 0) { + if (!OS.IsWinCE && expand) { + int length_ = OS.ExpandEnvironmentStrings (pszOut.ptr, null, 0); + if (length_ !is 0) { + TCHAR[] lpDst = NewTCHARs (0, length_); + OS.ExpandEnvironmentStrings (pszOut.ptr, lpDst.ptr, length_); + return tango.text.convert.Utf.toString( lpDst[ 0 .. Math.max (0, length_ - 1) ] ); + } else { + return ""; + } + } else { + return tango.text.convert.Utf.toString( pszOut[ 0 .. Math.max (0, pcchOut [0] - 1)]); + } + } + return null; +} + +/** + * Finds the program that is associated with an extension. + * The extension may or may not begin with a '.'. Note that + * a <code>Display</code> must already exist to guarantee that + * this method returns an appropriate result. + * + * @param extension the program extension + * @return the program or <code>null</code> + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT when extension is null</li> + * </ul> + */ +public static Program findProgram (char[] extension) { + if (extension is null) DWT.error (DWT.ERROR_NULL_ARGUMENT); + if (extension.length is 0) return null; + if (extension.charAt (0) !is '.') extension = "." ~ extension; //$NON-NLS-1$ + /* Use the character encoding for the default locale */ + TCHAR[] key = StrToTCHARs (0, extension, true); + Program program = null; + if (OS.IsWinCE) { + void*[1] phkResult; + if (OS.RegOpenKeyEx ( cast(void*)OS.HKEY_CLASSES_ROOT, key.ptr, 0, OS.KEY_READ, phkResult.ptr) !is 0) { + return null; + } + uint [1] lpcbData; + int result = OS.RegQueryValueEx (phkResult [0], null, null, null, null, lpcbData.ptr); + if (result is 0) { + TCHAR[] lpData = NewTCHARs (0, lpcbData [0] / TCHAR.sizeof); + result = OS.RegQueryValueEx (phkResult [0], null, null, null, cast(ubyte*)lpData.ptr, lpcbData.ptr); + if (result is 0) program = getProgram ( TCHARzToStr( lpData.ptr ), extension); + } + OS.RegCloseKey (phkResult [0]); + } else { + char[] command = assocQueryString (OS.ASSOCSTR_COMMAND, key, true); + if (command !is null) { + char[] name = null; + if (name is null) name = assocQueryString (OS.ASSOCSTR_FRIENDLYDOCNAME, key, false); + if (name is null) name = assocQueryString (OS.ASSOCSTR_FRIENDLYAPPNAME, key, false); + if (name is null) name = ""; + char[] iconName = assocQueryString (OS.ASSOCSTR_DEFAULTICON, key, true); + if (iconName is null) iconName = ""; + program = new Program (); + program.name = name; + program.command = command; + program.iconName = iconName; + program.extension = extension; + } + } + return program; +} + +/** + * Answer all program extensions in the operating system. Note + * that a <code>Display</code> must already exist to guarantee + * that this method returns an appropriate result. + * + * @return an array of extensions + */ +public static char[] [] getExtensions () { + char[] [] extensions = new char[] [1024]; + /* Use the character encoding for the default locale */ + TCHAR[] lpName = NewTCHARs (0, 1024); + uint [1] lpcName; lpcName[0] = lpName.length; + FILETIME ft; + int dwIndex = 0, count = 0; + while (OS.RegEnumKeyEx ( cast(void*)OS.HKEY_CLASSES_ROOT, dwIndex, lpName.ptr, lpcName.ptr, null, null, null, &ft) !is OS.ERROR_NO_MORE_ITEMS) { + char[] extension = TCHARsToStr( lpName[0 .. lpcName[0] ]); + lpcName [0] = lpName.length; + if (extension.length > 0 && extension.charAt (0) is '.') { + if (count is extensions.length) { + char[][] newExtensions = new char[][]( extensions.length + 1024 ); + System.arraycopy (extensions, 0, newExtensions, 0, extensions.length); + extensions = newExtensions; + } + extensions [count++] = extension; + } + dwIndex++; + } + if (count !is extensions.length) { + char[][] newExtension = new char[][]( count ); + System.arraycopy (extensions, 0, newExtension, 0, count); + extensions = newExtension; + } + return extensions; +} + +static char[] getKeyValue (char[] string, bool expand) { + /* Use the character encoding for the default locale */ + TCHAR[] key = StrToTCHARs (0, string, true); + void* [1] phkResult; + if (OS.RegOpenKeyEx (cast(void*)OS.HKEY_CLASSES_ROOT, key.ptr, 0, OS.KEY_READ, phkResult.ptr) !is 0) { + return null; + } + char[] result = null; + uint [1] lpcbData; + if (OS.RegQueryValueEx (phkResult [0], null, null, null, null, lpcbData.ptr) is 0) { + result = ""; + int length_ = lpcbData [0] / TCHAR.sizeof; + if (length_ !is 0) { + /* Use the character encoding for the default locale */ + TCHAR[] lpData = NewTCHARs (0, length_); + if (OS.RegQueryValueEx (phkResult [0], null, null, null, cast(ubyte*)lpData.ptr, lpcbData.ptr) is 0) { + if (!OS.IsWinCE && expand) { + length_ = OS.ExpandEnvironmentStrings (lpData.ptr, null, 0); + if (length_ !is 0) { + TCHAR[] lpDst = NewTCHARs (0, length_); + OS.ExpandEnvironmentStrings (lpData.ptr, lpDst.ptr, length_); + result = tango.text.convert.Utf.toString ( lpDst[0 .. Math.max (0, length_ - 1) ] ); + } + } else { + length_ = Math.max (0, lpData.length - 1); + result = tango.text.convert.Utf.toString ( lpData[0 .. length_]); + } + } + } + } + if (phkResult [0] !is null) OS.RegCloseKey (phkResult [0]); + return result; +} + +static Program getProgram (char[] key, char[] extension) { + + /* Name */ + char[] name = getKeyValue (key, false); + if (name is null || name.length is 0) { + name = key; + } + + /* Command */ + char[] DEFAULT_COMMAND = "\\shell"; //$NON-NLS-1$ + char[] defaultCommand = getKeyValue (key ~ DEFAULT_COMMAND, true); + if (defaultCommand is null || defaultCommand.length is 0) defaultCommand = "open"; //$NON-NLS-1$ + char[] COMMAND = "\\shell\\" ~ defaultCommand ~ "\\command"; //$NON-NLS-1$ + char[] command = getKeyValue (key ~ COMMAND, true); + if (command is null || command.length is 0) return null; + + /* Icon */ + char[] DEFAULT_ICON = "\\DefaultIcon"; //$NON-NLS-1$ + char[] iconName = getKeyValue (key ~ DEFAULT_ICON, true); + if (iconName is null) iconName = ""; //$NON-NLS-1$ + + /* Program */ + Program program = new Program (); + program.name = name; + program.command = command; + program.iconName = iconName; + program.extension = extension; + return program; +} + +/** + * Answers all available programs in the operating system. Note + * that a <code>Display</code> must already exist to guarantee + * that this method returns an appropriate result. + * + * @return an array of programs + */ +public static Program [] getPrograms () { + Program [] programs = new Program [1024]; + /* Use the character encoding for the default locale */ + TCHAR[] lpName = NewTCHARs (0, 1024); + uint [1] lpcName; lpcName[0] = lpName.length; + FILETIME ft; + int dwIndex = 0, count = 0; + while (OS.RegEnumKeyEx (cast(void*)OS.HKEY_CLASSES_ROOT, dwIndex, lpName.ptr, lpcName.ptr, null, null, null, &ft) !is OS.ERROR_NO_MORE_ITEMS) { + char[] path = tango.text.convert.Utf.toString ( lpName[0 .. lpcName [0]]); + lpcName [0] = lpName.length ; + Program program = getProgram (path, null); + if (program !is null) { + if (count is programs.length) { + Program [] newPrograms = new Program [programs.length + 1024]; + System.arraycopy (programs, 0, newPrograms, 0, programs.length); + programs = newPrograms; + } + programs [count++] = program; + } + dwIndex++; + } + if (count !is programs.length) { + Program [] newPrograms = new Program [count]; + System.arraycopy (programs, 0, newPrograms, 0, count); + programs = newPrograms; + } + return programs; +} + +/** + * Launches the executable associated with the file in + * the operating system. If the file is an executable, + * then the executable is launched. Note that a <code>Display</code> + * must already exist to guarantee that this method returns + * an appropriate result. + * + * @param fileName the file or program name + * @return <code>true</code> if the file is launched, otherwise <code>false</code> + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT when fileName is null</li> + * </ul> + */ +public static bool launch (char[] fileName) { + if (fileName is null) DWT.error (DWT.ERROR_NULL_ARGUMENT); + + /* Use the character encoding for the default locale */ + auto hHeap = OS.GetProcessHeap (); + TCHAR[] buffer = StrToTCHARs (0, fileName, true); + int byteCount = buffer.length * TCHAR.sizeof; + auto lpFile = cast(wchar*) OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount); + OS.MoveMemory (lpFile, buffer.ptr, byteCount); + SHELLEXECUTEINFO info; + info.cbSize = SHELLEXECUTEINFO.sizeof; + info.lpFile = lpFile; + info.nShow = OS.SW_SHOW; + bool result = cast(bool) OS.ShellExecuteEx (&info); + if (lpFile !is null) OS.HeapFree (hHeap, 0, lpFile); + return result; +} + +/** + * Executes the program with the file as the single argument + * in the operating system. It is the responsibility of the + * programmer to ensure that the file contains valid data for + * this program. + * + * @param fileName the file or program name + * @return <code>true</code> if the file is launched, otherwise <code>false</code> + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT when fileName is null</li> + * </ul> + */ +public bool execute (char[] fileName) { + if (fileName is null) DWT.error (DWT.ERROR_NULL_ARGUMENT); + int index = 0; + bool append = true; + char[] prefix = command, suffix = ""; //$NON-NLS-1$ + while (index < ARGUMENTS.length) { + int i = command.indexOf (ARGUMENTS [index]); + if (i !is -1) { + append = false; + prefix = command.substring (0, i); + suffix = command.substring (i + ARGUMENTS [index].length , command.length ); + break; + } + index++; + } + if (append) fileName = " \"" ~ fileName ~ "\""; + char[] commandLine = prefix ~ fileName ~ suffix; + auto hHeap = OS.GetProcessHeap (); + /* Use the character encoding for the default locale */ + TCHAR[] buffer = StrToTCHARs (0, commandLine, true); + int byteCount = buffer.length * TCHAR.sizeof; + auto lpCommandLine = cast(TCHAR*)OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount); + OS.MoveMemory (lpCommandLine, buffer.ptr, byteCount); + STARTUPINFO lpStartupInfo; + lpStartupInfo.cb = STARTUPINFO.sizeof; + PROCESS_INFORMATION lpProcessInformation; + bool success = cast(bool) OS.CreateProcess (null, lpCommandLine, null, null, false, 0, null, null, &lpStartupInfo, &lpProcessInformation); + if (lpCommandLine !is null) OS.HeapFree (hHeap, 0, lpCommandLine); + if (lpProcessInformation.hProcess !is null) OS.CloseHandle (lpProcessInformation.hProcess); + if (lpProcessInformation.hThread !is null) OS.CloseHandle (lpProcessInformation.hThread); + return success; +} + +/** + * Returns the receiver's image data. This is the icon + * that is associated with the receiver in the operating + * system. + * + * @return the image data for the program, may be null + */ +public ImageData getImageData () { + if (extension !is null) { + SHFILEINFOW shfi; + int flags = OS.SHGFI_ICON | OS.SHGFI_SMALLICON | OS.SHGFI_USEFILEATTRIBUTES; + TCHAR[] pszPath = StrToTCHARs (0, extension, true); + OS.SHGetFileInfo (pszPath.ptr, OS.FILE_ATTRIBUTE_NORMAL, &shfi, SHFILEINFO.sizeof, flags); + if (shfi.hIcon !is null) { + Image image = Image.win32_new (null, DWT.ICON, shfi.hIcon); + ImageData imageData = image.getImageData (); + image.dispose (); + return imageData; + } + } + int nIconIndex = 0; + char[] fileName = iconName; + int index = iconName.indexOf (','); + if (index !is -1) { + fileName = iconName.substring (0, index); + char[] iconIndex = iconName.substring (index + 1, iconName.length ).trim (); + try { + nIconIndex = Integer.parseInt (iconIndex); + } catch (NumberFormatException e) {} + } + /* Use the character encoding for the default locale */ + TCHAR[] lpszFile = StrToTCHARs (0, fileName, true); + HICON [1] phiconSmall, phiconLarge; + OS.ExtractIconEx (lpszFile.ptr, nIconIndex, phiconLarge.ptr, phiconSmall.ptr, 1); + if (phiconSmall [0] is null) return null; + Image image = Image.win32_new (null, DWT.ICON, phiconSmall [0]); + ImageData imageData = image.getImageData (); + image.dispose (); + return imageData; +} + +/** + * Returns the receiver's name. This is as short and + * descriptive a name as possible for the program. If + * the program has no descriptive name, this string may + * be the executable name, path or empty. + * + * @return the name of the program + */ +public char[] getName () { + return name; +} + +/** + * Compares the argument to the receiver, and returns true + * if they represent the <em>same</em> object using a class + * specific comparison. + * + * @param other the object to compare with this object + * @return <code>true</code> if the object is the same as this object and <code>false</code> otherwise + * + * @see #hashCode() + */ +public override int opEquals(Object other) { + if (this is other) return true; + if ( auto program = cast(Program)other ) { + return name.equals(program.name) && command.equals(program.command) + && iconName.equals(program.iconName); + } + return false; +} + +/** + * Returns an integer hash code for the receiver. Any two + * objects that return <code>true</code> when passed to + * <code>equals</code> must return the same value for this + * method. + * + * @return the receiver's hash + * + * @see #equals(Object) + */ +public override hash_t toHash() { + return .toHash(name) ^ .toHash(command) ^ .toHash(iconName); +} + +/** + * Returns a string containing a concise, human-readable + * description of the receiver. + * + * @return a string representation of the program + */ +public char[] toString () { + return "Program {" ~ name ~ "}"; //$NON-NLS-1$ //$NON-NLS-2$ +} + +}