FireMonkey Application with Multiple Monitors - firemonkey

I need to get the Screen Scale for the monitor my application is being displayed on. The multi-platform run-time TPlatFormServices has a service called IFMXScreenService that returns the Screen Scale (GetScreenScale).
The problem is it only returns it for the primary monitor. I need to get it for whichever monitor my application is being run on.
Below is my C++ code for getting the IFMXScreenService. How do I modify it to get the screen service for my active monitor?
if (TPlatformServices::Current->SupportsPlatformService(__uuidof(IFMXScreenService)) == true)
{
pScreenService_ = TPlatformServices::Current->GetPlatformService(__uuidof(IFMXScreenService));
}

For now, I just cheated under Windows and added the following routine to my base form class:
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// TFormBase::GetScaleFactor()
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
double TFormBase::GetScaleFactor(void)
{
double fScale = 0.0;
#ifdef WIN32
TPoint objTopLeft = TPoint(Left,Top);
HMONITOR hMonitor = MonitorFromPoint(objTopLeft,MONITOR_DEFAULTTONULL);
if (hMonitor != NULL)
{
DEVICE_SCALE_FACTOR nScaleFactor;
if (GetScaleFactorForMonitor(hMonitor,&nScaleFactor) == S_OK)
{
fScale = static_cast<double> ((static_cast<int> (nScaleFactor))) / 100.0;
}
}
if (fScale == 0.0)
{
fScale = ScreenService->ScreenScale;
}
#else
fScale = ScreenService->ScreenScale;
#endif
return fScale;
}

I don't know how in C++ Builder, in Delphi I do this as follows
function GetScreenshotZoom(APoint: TPoint): Single; overload;
var
Monitor: HMonitor;
DpiX, DpiY: Cardinal;
begin
if TOSVersion.Check(6, 3) then
begin
Monitor := Winapi.MultiMon.MonitorFromPoint(APoint, MONITOR_DEFAULTTONEAREST);
GetDPIForMonitor(Monitor, MDT_Default, DpiX, DpiY);
Result := DpiX / StandardDpi;
end
else
Result := 1;
end;

uses Winapi.ShellScaling, Winapi.ShlObj, Winapi.MultiMon;
function GetScreenScale(sc:integer): Single;
var
Monitor: HMonitor;
Scale: DEVICE_SCALE_FACTOR;
p:TPoint;
begin
result:=1;
p.X:=Screen.Displays[sc].BoundsRect.Left;
p.Y:=Screen.Displays[sc].BoundsRect.Top;
Monitor:=MonitorFromPoint(p,MONITOR_DEFAULTTONULL);
if (GetScaleFactorForMonitor(sc,Scale)=S_OK) then
begin
result:=(100.0 / Integer(Scale));
if result=0 then
result:=1;
end;
end;

Related

Where are positions of desktop shortcuts stored?

Where are the positions of Windows desktop shortcuts stored?
I am asking about the screen positions of the icons not the actual icons themselves. I know the icons themselves are stored in various DLLs, EXEs etc. The positions are clearly stored in some non-volatile store because they persists through re-boots.
My end goal is to write an app to display, and optionally re-arrange icons on my desktop.
I know this is possible because many available apps do this (e.g., "WinTidy").
I find much talk about "Windows Shell Bags". An interesting article about these are in http://williballethin.com.forensics/shellbags, but that only addresses directories not shortcuts. These are in the registry at various places including
`HKEY_CURRENT_USER/Software/Microsoft/Windows/Shell/Bags/1/Desktop`
`HKEY_USERS/.DEFAULT/Software/Microsoft/Windows/Shell/Bags/1/Desktop`
I wrote a program to extract these but the format of the key values is incomprehensible.
Any body know where and how they are stored?
UPDATE 6/3/20
++++++++++++++++++++++++++++++++++++++
I just switched over to a Win10 64-bit machine and find the solution below no longer works. I believe because of a change in the desktop internals. I figured out how to do this. See "WIN10 ADDENDUM" at the end of this answer.
++++++++++++++++++++++++++++++++++++++
I finally figured out how to do what I want (display and re-arrange desktop icons). My original question concerned locating, reading and writing to the file where the icon info is stored, but this is not a useful approach. Here is what I learned:
Explorer.exe displays desktop items in a giant ListView covering the whole desktop with ListView items corresponding to each visible icon. At startup, Explorer reads info from some arcane file and populates the ListView. On exit, it re-writes that file from the ListView. So modifying the file would not help because it would be overwritten on exit.
The right way to manipulate desktop items is to directly manipulate items in the ListView. Any changes are immediately visible on change, and are saved on exit. To access the items, we can use several Windows messages: LVM_GETITEM, LVM_GETITEMCOUNT, LVM_GETITEMPOSITION and LVM_SETITEMPOSITION. These messages are fairly simple to use with one complication: some require pointers to parameter structures. These structures must be in Explorer's address space not my app's, so some trickery is needed. Here's how to do it. I use LVM_GETITEMPOSITION as an example, which requires a pointer to a POINT structure.
Declare a POINT structure in your app.
Allocate a mirror structure in Explorer's address space using API VirtualAllocEx().
Send LVM_GETITEMPOSITION to Explorer specifying a pointer to this structure.
Read back the result into your app's POINT using API ReadProcessMemory(). This function can read memory across different address spaces.
I have prototyped these operations and they work as I wanted. My code is quite long but I will post excerpts as soon as I clean it up.
UPDATE 10/4/2019 ------------------------------------
CODE EXCERPTS
A set of commonly used utility functions was created to make code more compact and readable. These are named "exp*()" and are included at the end. A reference can be found at http://ramrodtechnology.com/explorer. Much of the basic technique herein was shamelessly stolen from https://www.codeproject.com/Articles/5570/Stealing-Program-s-Memory
Setup
// COMMONLY USED VARS
HANDLE hProcess; // explorer process handle
HWND hWndLV; // explorer main window
// SET UP CONVENIENCE VARS
hProcess = expGetProcessHandle(); // get explorer process handle
if( !hProcess ) exit( 1 );
hWndLV = expGetListView(); // get main ListView of Desktop
if( !hWndLV ) exit( 1 );
Function to Print All Item Names
//# Process a list view window and print item names
int
printAllNames()
{
int ok,icount,indx;
LVITEM item; // point in app space
LVITEM *_pitem; // point in exp space
char text[512];
char *_ptext;
int nr,nwrite; // count of bytes read/written
printf( "\n" );
// ALLOC ITEMS IN EXP SPACE
_pitem = expAlloc( sizeof(LVITEM) );
_ptext = expAlloc( sizeof(text ) );
printf( " NAME\n" );
printf( " ==================================\n" );
icount = expGetItemCount();
for( indx=0; indx<icount; indx++ ) { // for each item in LV
// SETUP ITEM IN EXP SPACE
memset( &item, 0, sizeof(LVITEM) ); // preclear
item.iItem = indx; // index of item to read
item.iSubItem = 0; // sub index (always 0)
item.mask = LVIF_TEXT; // component to read
item.pszText = _ptext; // buffer to recv text
item.cchTextMax = sizeof(text); // size of buffer
// WRITE ITEM REQ TO EXP SPACE
ok = WriteProcessMemory( hProcess, _pitem, &item, sizeof(LVITEM), &nwrite );
// SEND MESSAGE TO GET ITEM INTO EXP SPACE
ok = SendMessage( hWndLV, LVM_GETITEM, indx, (LPARAM)_pitem );
// READ EXP TEXT INTO APP SPACE
memset( &item, 0, sizeof(LVITEM) );
ok = ReadProcessMemory( hProcess, _pitem, &item, sizeof(POINT), &nr );
ok = ReadProcessMemory( hProcess, _ptext, &text, sizeof(text), &nr );
// PRINT RESULT
printf( " %s\n", text );
}
ok = expFree( _pitem );
ok = expFree( _ptext );
return( TRUE );
//r Returns TRUE on success, FALSE on error
}
Function To Print All Item Positions
//# Process a list view window and print position
int
printAllPositions()
{
int ok,icount,indx,nr;
POINT pt; // point in app space
POINT *_ppt; // point in exp space
icount = expGetItemCount();
_ppt = expAlloc( sizeof(POINT) );
if( !_ppt ) return( FALSE );
printf( " X Y\n" );
printf( "---- ----\n" );
for( indx=0; indx<icount; indx++ ) { // for each item in LV
ok = SendMessage( hWndLV, LVM_GETITEMPOSITION, indx, (LPARAM)_ppt );
ok = ReadProcessMemory( hProcess, _ppt, &pt, sizeof(POINT), &nr );
printf( "%4d %4d\n", pt.x, pt.y );
}
ok = expFree( _ppt );
return( TRUE );
//r Returns TRUE on success
}
Function To Move Item
See 'expSetItemPosition' below. UPDATED 10/6/19
Explorer Utility Functions
// EXPLORER UTILITY FUNCTIONS
//# Allocate a block of memory in explorer space
void *
expAlloc(
int size) // size of block
{
void *p;
p = VirtualAllocEx(
hProcess,
NULL,
size,
MEM_COMMIT,
PAGE_READWRITE );
return( p );
//r Returns addr of memory in EXPLORER space or NULL on error
}
//# Free virtual memory in EXPLORER space
int
expFree(
void *p) // pointer to free
{
int ok;
ok = VirtualFreeEx( hProcess, p, 0, MEM_RELEASE );
return( ok );
//r Returns TRUE on success, else FALSE
}
static int aBiggest; // biggest area so far
static HWND hWndBiggest; // hWnd with biggest area
//# Find main list view of explorer
HWND
expGetListView()
{
//n Approach: Enumerate all child windows of desktop and find largest.
//n This will be the main explorer window.
HWND hWndDesktop;
hWndDesktop = GetDesktopWindow();
if( !hWndDesktop ) return( NULL );
aBiggest = -1; // init
hWndBiggest = NULL; // init
EnumChildWindows( hWndDesktop, CallbackDesktopChild, 0 );
return( hWndBiggest );
//r Returns hWnd of largest explorer list view
}
//# Callback for EnumChildWindows
BOOL CALLBACK CallbackDesktopChild(
HWND hWnd,
LPARAM dwUser)
{
//n Get size of child. If biggest, save hWnd.
int i,w,h,a;
char classname[MAXPATH+1];
RECT rect;
i = GetClassName( hWnd, classname, MAXPATH ); // get class
if( stricmp( classname, "SysListView32" ) ) { // not a list view?
return( TRUE ); // skip it
}
// CALC SIZE
i = GetWindowRect( hWnd, &rect );
w = rect.right - rect.left;
h = rect.bottom - rect.top;
// CHECK IF BIGGEST
a = w * h;
if( a > aBiggest ) { // is biggest?
aBiggest = a;
hWndBiggest = hWnd;
}
return( TRUE ); // TRUE to continue enumeration
}
//# Get process handle of explorer.exe
HANDLE
expGetProcessHandle()
{
//n Approach: take process snapshot and loop through to find "explorer.exe"
//n Needs tlhelp32.h and comctl32.lib
int i,stat;
PROCESSENTRY32 pe;
HANDLE hSnapshot;
char *name;
HANDLE h;
// TAKE A SNAPSHOT
hSnapshot = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
if( !hSnapshot ) return( NULL );
// LOOP THROUGH PROCESSES AND FIND "explorer.exe"
for( i=0;;i++ ) {
pe.dwSize = sizeof( PROCESSENTRY32 );
if( i == 0 ) stat = Process32First( hSnapshot, &pe );
else stat = Process32Next ( hSnapshot, &pe );
if( !stat ) break; // done or error?
name = pe.szExeFile;
if( !stricmp( name, "explorer.exe" ) ) { // matches?
h = OpenProcess( PROCESS_ALL_ACCESS, FALSE, pe.th32ProcessID );
return( h );
}
}
return( NULL );
//r Returns explorer process handle or NULL on error
}
//# Get count of items in explorer list view
int
expGetItemCount()
{
int count;
count = SendMessage( hWndLV, LVM_GETITEMCOUNT, 0, 0 );
return( count );
//r Returns count of item
}
//# Get position of list view icon by index
int
expGetItemPosition(
int indx, // index of item
int *x, // ptr to int to recv x
int *y) // ptr to int to recv y
{
int i,ok,icount;
char classname[MAXPATH+1];
POINT pt; // point in app space
POINT *_ppt; // point in exp space
int nr; // count of bytes read
//int w,h;
i = GetClassName( hWndLV, classname, MAXPATH );
// GET COUNT OF ITEMS IN LIST VIEW
icount = expGetItemCount();
if( indx < 0 || indx >= icount ) return( FALSE );
// ALLOC POINT IN EXP SPACE
_ppt = expAlloc( sizeof(POINT) );
if( !_ppt ) return( FALSE );
// SEND MESSAGE TO GET POS INTO EXP SPACE POINT
ok = SendMessage( hWndLV, LVM_GETITEMPOSITION, indx, (LPARAM)_ppt );
if( !ok ) return( FALSE );
// READ EXP SPACE POINT INTO APP SPACE POINT
ok = ReadProcessMemory( hProcess, _ppt, &pt, sizeof(POINT), &nr );
if( !ok ) return( FALSE );
ok = expFree( _ppt );
if( !ok ) return( FALSE );
if( x ) *x = pt.x;
if( y ) *y = pt.y;
//r Returns TRUE on success
return( TRUE );
}
//# Move item
int
expSetItemPosition(
char *name, // icon name
int x, // new x coord
int y) // new y coord
{
int ok,indx;
LPARAM lParam;
indx = expGetItemIndex( name );
if( indx < 0 ) return( FALSE );
lParam = MAKELPARAM( x, y );
ok = SendMessage( hWndLV, LVM_SETITEMPOSITION, indx, lParam );
if( !ok ) return( FALSE );
return( TRUE );
//r Returns TRUE on success
}
WIN10 ADDENDUM
6/19/20
++++++++++++++++++++++++++++++++++
Under Win10, the solution is much more complicated. You must use various COM objects and interfaces, e.g. IShellWindows, etc. (God, I hate COM). I did not create a library but rather offer a complete working program below. I compiled this using MSVC 2019. Error checking has been omitted for clarity (but you should do it).
// icons.cpp - Display (and optionally move) desktop icons
#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <ShlObj.h>
#include <atlbase.h>
int
main(int argc,char** argv)
{
CComPtr<IShellWindows> spShellWindows;
CComPtr<IShellBrowser> spBrowser;
CComPtr<IDispatch> spDispatch;
CComPtr<IShellView> spShellView;
CComPtr<IFolderView> spView;
CComPtr<IShellFolder> spFolder;
CComPtr<IEnumIDList> spEnum;
CComHeapPtr<ITEMID_CHILD> spidl;
CComVariant vtLoc(CLSID_ShellWindows);
CComVariant vtEmpty;
STRRET str;
int count=0;
HRESULT hr;
long lhWnd;
// INITIALIZE COM
CoInitialize(NULL);
// GET ShellWindows INTERFACE
hr = spShellWindows.CoCreateInstance(CLSID_ShellWindows);
// FIND WINDOW
hr = spShellWindows->FindWindowSW(
&vtLoc, &vtEmpty, SWC_DESKTOP, &lhWnd, SWFO_NEEDDISPATCH, &spDispatch);
// GET DISPATCH INTERFACE
CComQIPtr<IServiceProvider>(spDispatch)->
QueryService(SID_STopLevelBrowser, IID_PPV_ARGS(&spBrowser));
spBrowser->QueryActiveShellView(&spShellView);
spShellView->QueryInterface(IID_PPV_ARGS(&spView) );
hr = spView->GetFolder(IID_PPV_ARGS(&spFolder));
// GET ENUMERATOR
spView->Items(SVGIO_ALLVIEW, IID_PPV_ARGS(&spEnum)); // get enumerator
// ENUMERATE ALL DESKTOP ITEMS
for (; spEnum->Next(1, &spidl, nullptr) == S_OK; spidl.Free()) {
// GET/PRINT ICON NAME AND POSITION
char* name;
POINT pt;
spFolder->GetDisplayNameOf(spidl, SHGDN_NORMAL, &str);
StrRetToStr(&str, spidl, &name);
spView->GetItemPosition(spidl, &pt);
printf("%5d %5d \"%s\"\n", pt.x, pt.y, name);
#define MOVE_ICON
#ifdef MOVE_ICON
// OPTIONAL: MOVE *SINGLE* SELECTED ITEM
{
if( !_stricmp(name, "ICON_NAME_TO_MOVE") ) {
PCITEMID_CHILD apidl[1] = { spidl };
int numitems = 1;
// SET pt TO NEW POSITION HERE
hr = spView->SelectAndPositionItems(numitems, apidl, &pt, 0);
}
}
#endif
count++;
}
CoUninitialize(); // release COM
fprintf(stderr, "enumerated %d desktop icons\n", count);
fprintf(stderr, "Press any key to exit...\n");
_getch();
exit(0 );
}

How do I call the default windows screen to choose a certificate?

THTTPRIO component, in HTTPWebNode property, when you click in ClientCertificate, Delphi opens a form to choose the certificate and load it's information in the component's propertys. Is this a windows screen? If it is, how can I use it? Today I'm using SecureBlackBox to load the certificates in a combobox, but I'd like to know if is possible to use this screen.
Thanks
UPDATE
I was able to show the dialog using the ms function CryptUIDlgSelectCertificateFromStore, using JWAPI. Now I'm having problems with the result of the function, the PCCERT_CONTEXT structure.
var
P: Pointer;
Context: PCCERT_CONTEXT;
Issuer: DATA_BLOB;
function GetDataBlobText(Data: DATA_BLOB): string;
begin
SetString(Result, PAnsiChar(Data.pbData), Data.cbData div SizeOf(AnsiChar));
end;
begin
P := CertOpenSystemStore(0, 'MY');
Context := CryptUIDlgSelectCertificateFromStore(P, 0, PChar('test'), nil, CRYPTUI_SELECT_ISSUEDTO_COLUMN, 0, nil);
if Context <> nil then
begin
Issuer := Context.pCertInfo.Issuer;
ShowMessage((GetDataBlobText(Issuer)));
end;
end;
The result in ShowMessage is:
UPDATE2
Thanks #RbMm.
To get string of ASN encoding fields (Issuer and Subject)
var
P: Pointer;
Context: PCCERT_CONTEXT;
Subject: DATA_BLOB;
SubjectStr: string;
size : Cardinal;
begin
P := CertOpenSystemStore(0, PAnsiChar('MY'));
Context := CryptUIDlgSelectCertificateFromStore(P, 0, 'test', 'select certificate',
CRYPTUI_SELECT_ISSUEDTO_COLUMN, 0, nil);
if Context <> nil then
begin
Subject := Context.pCertInfo.Subject;
size := CertNameToStr(X509_ASN_ENCODING or PKCS_7_ASN_ENCODING, #Subject, CERT_X500_NAME_STR, 0, 0);
SetString(SubjectStr, PAnsiChar(Subject.pbData), size);
CertNameToStr(X509_ASN_ENCODING or PKCS_7_ASN_ENCODING, #Subject, CERT_X500_NAME_STR, PAnsiChar(SubjectStr), size);
Result := SubjectStr;
end;
To get the string of raw data block (SerialNumber):
var
SerialNumber: CRYPT_INTEGER_BLOB;
size : Cardinal;
s: PWideChar;
ss: string;
begin
SerialNumber := Context.pCertInfo.SerialNumber;
CryptBinaryToStringW(SerialNumber.pbData, SerialNumber.cbData, CRYPT_STRING_HEX, nil, size);
s := AllocMem(SizeOf(Char) * size);
CryptBinaryToStringW(SerialNumber.pbData, SerialNumber.cbData, CRYPT_STRING_HEX, s, size);
ss := s;
showmessage(ss);
FreeMem(s, SizeOf(Char) * size);
all data blobs in certificate is encoded. so you need decode it. in general by using CryptDecodeObjectEx api. however for Issuer ( i.e. CERT_NAME_BLOB) decode you can use also CertNameToStrW. only after converts an encoded name in a CERT_NAME_BLOB structure to a null-terminated character string you can print it. code example on c/c++:
void PrintIssuer(PCCERT_CONTEXT Context)
{
CERT_NAME_BLOB Issuer = Context->pCertInfo->Issuer;
// option #1
if (ULONG len = CertNameToStrW(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &Issuer, CERT_X500_NAME_STR, 0, 0))
{
PWSTR sz = (PWSTR)alloca( len * sizeof(WCHAR));
if (CertNameToStrW(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &Issuer, CERT_X500_NAME_STR, sz, len))
{
DbgPrint("%S\n", sz);
}
}
// option #2
PCERT_NAME_INFO pcni;
ULONG size;
if (CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, X509_NAME, Issuer.pbData, Issuer.cbData,
CRYPT_DECODE_ALLOC_FLAG, 0, &pcni, &size))
{
if (DWORD cRDN = pcni->cRDN)
{
PCERT_RDN rgRDN = pcni->rgRDN;
do
{
if (DWORD cRDNAttr = rgRDN->cRDNAttr)
{
PCERT_RDN_ATTR rgRDNAttr = rgRDN->rgRDNAttr;
do
{
DbgPrint("ObjId = %s\n", rgRDNAttr->pszObjId);
switch (rgRDNAttr->dwValueType)
{
case CERT_RDN_PRINTABLE_STRING:
DbgPrint("Value = %s\n", rgRDNAttr->Value.pbData);
break;
}
} while (rgRDNAttr++, --cRDNAttr);
}
} while (rgRDN++, --cRDN);
}
LocalFree(pcni);
}
}
and output
CN=***
ObjId = 2.5.4.3
Value = ***
(the string after CN= and Value = is the same)
you can note that "2.5.4.3" is szOID_COMMON_NAME or "CN". so first api is append CN= before Issuer name. second variant return you name as is and additional ObjId = 2.5.4.3
convert SerialNumber to printable string can next way:
CRYPT_INTEGER_BLOB SerialNumber = Context->pCertInfo->SerialNumber;
DWORD cb = 0;
if (CryptBinaryToStringW(SerialNumber.pbData, SerialNumber.cbData, CRYPT_STRING_HEX, 0, &cb))
{
PWSTR sz = (PWSTR)alloca( cb * sizeof(WCHAR));
if (CryptBinaryToStringW(SerialNumber.pbData, SerialNumber.cbData, CRYPT_STRING_HEX, sz, &cb))
{
DbgPrint("%S\n", sz);
}
}

SHGetImageList returns corrupted folder icon on Windows 7

I've got a problem with default icons for folder on Windows 7.
I'm used to get icons with next code
DWORD flags = directory ? FILE_ATTRIBUTE_DIRECTORY : FILE_ATTRIBUTE_NORMAL;
...
HICON largeIcon = NULL;
SHGetFileInfo(filename.c_str(), flags, &shfi, sizeof(SHFILEINFO),
SHGFI_USEFILEATTRIBUTES | SHGFI_SYSICONINDEX);
IImageList* imageList = NULL;
HRESULT hResult = SHGetImageList(SHIL_JUMBO, IID_IImageList, (void**)&imageList);
if (!imageList || hResult != S_OK)
return NULL;
hResult = imageList->GetIcon(shfi.iIcon, ILD_TRANSPARENT, &largeIcon);
if (hResult != S_OK)
return NULL;
icon = LoadBitmapFromHicon(largeIcon);
DestroyIcon(shfi.hIcon);
DestroyIcon(largeIcon);
But it returns corrupted icon for folder. Another ones is fine.
I've applied grey filter over it. Original one looks the same.
http://i.stack.imgur.com/IM7pm.png
PS. I looks fine on W8.
Have the same problem. But I use workaround:
function ExtractFolderIcon: HICON;
var
Info: TSHStockIconInfo;
Lib: HMODULE;
begin
Info.cbSize := SizeOf(Info);
OleCheck(SHGetStockIconInfo(SIID_FOLDER, SHGSI_ICONLOCATION, Info));
Lib := LoadLibrary(Info.szPath);
if Lib = 0 then RaiseLastOSError;
try
Result := LoadImage(Lib, MAKEINTRESOURCE(-Info.iIcon), IMAGE_ICON, 256, 256, LR_DEFAULTCOLOR);
finally
FreeLibrary(Lib);
end;
end;
Left image was received with your "classic" way, right with workaround:

Jump to listbox item by typing first few characters

I have a list of items (potentially large) from which the user must select one. I'd like to allow the user to type the first few letters of the desired item to jump to the correct place in the list. By default, each keypress jumps to the first item starting with that letter, so you can't type the first several letters. Is there any straightforward way to do this? Any CodeProject or other such example?
I've looked for hours, and found any number of samples for IAutocomplete, but that won't help here because I need to guarantee that the result is in the list.
The only way I can think to do this is to derive from CListBox, capture the keystrokes myself, find the item, run a timer so that new keystrokes after a sufficient pause will start a new search... since I'm not an MFC jock, this is daunting. Any tips much appreciated.
One clarifying note: my ultimate goal is actually to get this keyboard behavior for a ComboBox of DropDownList style (i.e. no edit box). The lack of an edit box rules out most autocomplete code, and the need for ComboBox functionality means I can't use CListCtrl by itself.
After much unnecessary pain, I've discovered that the real correct answer is simply to use LBS_SORT. Simply by specifying this style, the basic vanilla listbox supports the incremental search keyboard shortcut style I wanted. Without LBS_SORT (or CBS_SORT for a combobox), you get the irritating and almost-useless jump-to-first-letter-only behavior. I didn't try LBS_SORT because my list contents were added in sorted order anyway.
So the dozen or so hours of investigating custom controls, etc., all for naught because the Microsoft documentation makes no mention of this important behavioral difference in the description of LBS_SORT!!
Thanks to everyone who contributed.
I've implemented such a functionality in core Win32. Heres the code.
Somewhere in your message loop that processes the list box insert:
switch(message)
{
case WM_CHAR:
if(HandleListBoxKeyStrokes(hwnd, wParam) == FALSE)
return FALSE;
....
Heres the code (propably not fully complete):
/* ======================================================================== */
/* ======================================================================== */
#define RETURNr(a, b) // homegrown asserts
BOOLEAN HandleListBoxKeyStrokes(HWND hwnd, UINT theKey)
{
#define MAXCHARCACHEINTERVALL 600.0 // Max. milisecs time offset to consider as typed 'at once'
static char sgLastChars[255] = {'0'};
static double sgLastCharTime = 0.;
static HWND sgLasthwnd = NULL;
if(GetSecs() - sgLastCharTime > MAXCHARCACHEINTERVALL ||
sgLasthwnd != hwnd)
*sgLastChars = 0;
if(theKey == ' ' && *sgLastChars == 0)
return TRUE;
sgLastCharTime = GetSecs();
sgLasthwnd = hwnd;
AppendChar(sgLastChars, toupper(theKey));
if(strlen(sgLastChars) > 1)
{
LONG l = GetWindowLong(hwnd, GWL_STYLE);
Char255 tx;
GetClassName(hwnd, tx, sizeof(tx));
if( (! stricmp(tx, "Listbox") &&
! (l & (LBS_EXTENDEDSEL | LBS_MULTIPLESEL)) ) ||
(! stricmp(tx, "ComboBox") && // combo Box support
l & CBS_DROPDOWNLIST &&
! (l & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE)) ) )
{
long Count, l, BestMatch = - 1, BestMatchOff = 0;
long LBcmdSet[] = {LB_GETCOUNT, LB_GETTEXTLEN , LB_GETTEXT};
long CBcmdSet[] = {CB_GETCOUNT, CB_GETLBTEXTLEN, CB_GETLBTEXT};
long *cmdSet = (! stricmp(tx, "ComboBox")) ? CBcmdSet : LBcmdSet;
RETURNr((Count = SendMessage(hwnd, cmdSet[0], 0, 0)) != LB_ERR, 0);
for(int i = 0; i < Count; i++)
{
RETURNr((l = SendMessage(hwnd, cmdSet[1], i, 0)) != LB_ERR, TRUE);
RETURNr( l < sizeof(tx), TRUE);
RETURNr((l = SendMessage(hwnd, cmdSet[2], i, (LPARAM)&tx)) != LB_ERR, TRUE);
strupr(tx);
if(! strncmp(tx, sgLastChars, strlen(sgLastChars)))
{
SelListBoxAndNotify(hwnd, i);
return FALSE;
}
char *p;
if(p = strstr(tx, sgLastChars))
{
int off = p - tx;
if(BestMatch == -1 || off < BestMatchOff)
{
BestMatch = i;
BestMatchOff = off;
}
}
}
// If text not found at start of string see if it matches some part inside the string
if(BestMatch != -1)
SelListBoxAndNotify(hwnd, BestMatch);
// Nothing found - dont process
return FALSE;
}
}
return TRUE;
}
/* ======================================================================== */
/* ======================================================================== */
void SelListBoxAndNotify(HWND hwnd, int index)
{
// i am sorry here - this is some XVT-toolkit specific code.
// it has to be replaced with something similar for native Win32
WINDOW win = xvtwi_hwnd_to_window(hwnd);
WINDOW parent = xvt_vobj_get_parent(win);
xvt_list_set_sel(win, index, 1);
EVENT evt;
memset(&evt, 0, sizeof(evt));
evt.type = E_CONTROL;
evt.v.ctl.id = GetDlgCtrlID(hwnd);
evt.v.ctl.ci.v.lbox.dbl_click = FALSE;
xvt_win_dispatch_event(parent, &evt);
}
/* ======================================================================== */
/* ======================================================================== */
double GetSecs(void)
{
struct timeb timebuffer;
ftime(&timebuffer);
return (double)timebuffer.millitm +
((double)timebuffer.time * 1000.) - // Timezone needed for DbfGetToday
((double)timebuffer.timezone * 60. * 1000.);
}
/* ======================================================================== */
/* ======================================================================== */
char AppendChar(char *tx, char C)
{ int i;
i = strlen(tx);
tx[i ] = C;
tx[i + 1] = 0;
return(C);
}
Can you use a CListView CListCtrl instead? They work like that by default.

Audio Unit and Writing to file

I'm creating real-time audio sequencer app on OS X.
Real-time synth part is implemented by using AURenderCallback.
Now I'm making function to write rendered result to Wave File (44100Hz 16bit Stereo).
Format for render-callback function is 44100Hz 32bit float Stereo interleaved.
I'm using ExtAudioFileWrite to write to file.
But ExtAudioFileWrite function returns error code 1768846202;
I searched 1768846202 but I couldn't get information.
Would you give me some hints?
Thank you.
Here is code.
outFileFormat.mSampleRate = 44100;
outFileFormat.mFormatID = kAudioFormatLinearPCM;
outFileFormat.mFormatFlags =
kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
outFileFormat.mBitsPerChannel = 16;
outFileFormat.mChannelsPerFrame = 2;
outFileFormat.mFramesPerPacket = 1;
outFileFormat.mBytesPerFrame =
outFileFormat.mBitsPerChannel / 8 * outFileFormat.mChannelsPerFrame;
outFileFormat.mBytesPerPacket =
outFileFormat.mBytesPerFrame * outFileFormat.mFramesPerPacket;
AudioBufferList *ioList;
ioList = (AudioBufferList*)calloc(1, sizeof(AudioBufferList)
+ 2 * sizeof(AudioBuffer));
ioList->mNumberBuffers = 2;
ioList->mBuffers[0].mNumberChannels = 1;
ioList->mBuffers[0].mDataByteSize = allocByteSize / 2;
ioList->mBuffers[0].mData = ioDataL;
ioList->mBuffers[1].mNumberChannels = 1;
ioList->mBuffers[1].mDataByteSize = allocByteSize / 2;
ioList->mBuffers[1].mData = ioDataR;
...
while (1) {
//Fill buffer by using render callback func.
RenderCallback(self, nil, nil, 0, frames, ioList);
//i want to create one sec file.
if (renderedFrames >= 44100) break;
err = ExtAudioFileWrite(outAudioFileRef, frames , ioList);
if (err != noErr){
NSLog(#"ERROR AT WRITING TO FILE");
goto errorExit;
}
}
Some of the error codes are actually four character strings. The Core Audio book provides a nice function to handle errors.
static void CheckError(OSStatus error, const char *operation)
{
if (error == noErr) return;
char str[20];
// see if it appears to be a 4-char-code
*(UInt32 *)(str + 1) = CFSwapInt32HostToBig(error);
if (isprint(str[1]) && isprint(str[2]) && isprint(str[3]) && isprint(str[4])) {
str[0] = str[5] = '\'';
str[6] = '\0';
} else
// no, format it as an integer
sprintf(str, "%d", (int)error);
fprintf(stderr, "Error: %s (%s)\n", operation, str);
exit(1);
}
Use it like this:
CheckError(ExtAudioFileSetProperty(outputFile,
kExtAudioFileProperty_CodecManufacturer,
sizeof(codec),
&codec), "Setting codec.");
Before you can do any sort of debugging, you probably need to figure out what that error message actually means. Have you tried passing that status code to GetMacOSStatusErrorString() or GetMacOSStatusCommentString()? They aren't documented so well, but they are declared in CoreServices/CarbonCore/Debugging.h.

Resources