COM call working on XP crashes Win7 application - windows-7

Sorry about kind of vague information about the background of this question, first the actual type library I am working with is subject to a non disclosure agreement, second I do not have direct access to the system where the problem is happening.
I have something like the following TLB for the COM interface of a device I am talking to in Free Pascal:
Device = interface(IDispatch)
// ...
function GetSubsystem: Subsystem; safecall;
property Subsystem: Subsystem read GetSubsystem;
// ...
end;
Subsystem = interface(IDispatch)
// ...
function GetSensors: Sensors; safecall;
property Sensors: Sensors read GetSensors;
// ...
end;
Sensors = interface(IDispatch)
// ...
function GetListItem(index: OleVariant): Sensor; safecall;
property ListItem[index: OleVariant]: Sensor read GetListItem; default;
// ...
end;
Sensor = interface(IDispatch)
// ...
function GetValue: Double; safecall;
property Value: Double read GetValue;
// ...
end;
If I read (according to the manufacturer's manual) a value from one of the sensors on Windows XP 32bit as follows
d := MyDevice.Subsystem.Sensors['ID23'].Value;
this works fine.
On Windows 7 64 bit, the manufacturer still has a 32bit COM interface, and all other calls from my 32bit exectuable work fine - just the one above makes my executable crash. It does not even raise an exception, it simply crashes. The same happens with
var o: oleVariant;
// ...
o = 'ID23';
d := MyDevice.Subsystem.Sensors[o].Value;
As I do not have access to said system directly, debugging is quite tedious and it took me a while to isolate the problem to exactly this call.
The interesting thing is that other programming languages using this same COM interface on Win7 64bit do not show this problem.
Does anyone have advice on how to approach this issue under the given circumstances?

I can only give you some pointers to try/be wary of:
FPC 3.0 supports SEH exceptions for 64-bit, but not for 32-bit
FPC seems to have some problem with default properties of dispatch interfaces. Try to use getlistitem() directly.

Related

Is it safe to use WM_COPYDATA between 64-and 32-bit (WOW64) apps?

There is a good SO Q/A session on the general use of WM_COPYDATA messages here, and a 'discussion' about whether or not this will work between apps of different 32/64-bitness here. However, the latter seems to be focussed on possible misuse of the 'data pointer' being passed. So, I'm raising a new question here.
I am working on getting two Windows apps to communicate/synchronize with each other and, as a first-round approach, I'm using Windows Messaging to implement this. Everything seems OK for now … but I'm using the WM_COPYDATA message to pass info between the apps.
My question: Is this approach guaranteed to be safe when the two apps have different (32/64) bitness? I've done some tests using the code below with all four possible combinations of 32 vs 64 builds between 'client' and 'server', and all work as expected; but is this just because I'm getting 'lucky' results (from possible undefined behaviour), or does the WOW64 system (especially when server is 64-bit and client is 32) take care of all the necessary marshalling?
If anyone can confirm that it is guaranteed to work, I would very much appreciate an 'official' link/reference confirming that.
Shared header file:
static const WPARAM nmIdFilePath = 0x00001100;
struct nmLockInfoType {
char filePathID[1024];
// More elements will be added later!
};
static const nmLockInfoType nmLockInfoDefault = {
"<<<Uninitialised Image Data Path>>>",
//...
};
extern nmLockInfoType nmLockInfo; // MUST be provided by each app!
///nmLockInfoType nmLockInfo = nmLockInfoDefault; // Use this code to instatiate it (once, somewhere)!
Server program code (inside the handler for a RegisterWindowMessage(L"HANDSHAKE"); message):
//...
COPYDATASTRUCT cds;
cds.dwData = nmIdFilePath; // Pre-defined ID
cds.cbData = sizeof(nmLockInfoType);
cds.lpData = &nmLockInfo; // Pre-defined structure (see above)
//...
// Send a copy of the "Welcome Pack" data structure to the client app ...
::SendMessage(clientLock, WM_COPYDATA, WPARAM(m_hWnd), LPARAM(&cds)); // "clientLock is the HWND of the client app's MainWnd
Client Program code:
BOOL MyFrame::OnCopyData(CWnd* pWnd, COPYDATASTRUCT* pCopyDataStruct)
{
switch (pCopyDataStruct->dwData)
{
case nmIdFilePath:
memcpy(&nmLockInfo, pCopyDataStruct->lpData, pCopyDataStruct->cbData);
return nmsSucceeded; // This is NON_ZERO so evaluates to TRUE
// Other cases here ...
}
return CMDIFrameWnd::OnCopyData(pWnd, pCopyDataStruct);
}
I'm particularly concerned about the case when the client is 32-bit but the server is 64-bit; in such a case, it would be sending a 64-bit data address to a 32-bit app (albeit, a WOW64 app). Does the in-built 'marshalling' handle this in WOW64 situations?
It's safe only when we follow the rule how to use it. Please refer the remarks of WM_COPYDATA message from below:
The data being passed must not contain pointers or other references to
objects not accessible to the application receiving the data.
While this message is being sent, the referenced data must not be
changed by another thread of the sending process.
The receiving application should consider the data read-only. The
lParam parameter is valid only during the processing of the message.
The receiving application should not free the memory referenced by
lParam. If the receiving application must access the data after
SendMessage returns, it must copy the data into a local buffer.
For example, if we are trying to passing the data type: ULONG_PTR, then the data copy maybe not function well when pass it from 64-bit application to 32-bit application. Because it is 32-bit on 32-bit application and 64-bit on 64-bit application.
You can test it via modify the code below:
struct nmLockInfoType {
char filePathID[1024];
ULONG_PTR point64_32;
// More elements will be added later!
};
The scenario mentioned above, which should be safe as the result you tested. Feel free to let me know if you still have concern about.
In-addition, below is an helpful document about developing 64-bit application for your reference:
Common Visual C++ 64-bit Migration Issues

VCL/LCL – a form in DLL – no Application taskbar window, cannot minimize the main form

I have one problem, and I tried to search a solution but can't achieve what I want. Sorry if that is actually simple, please just point me to correct way of how to do it.
So! I have a C program that is a loader. It must call my DLL written in Delphi or Lazarus (Free Pascal). The DLL is actually a standalone GUI application: during debugging I conditionally compile it as EXE and it working.
My build script compiles it as DLL with one entry point that must execute it just as it works standalone. I expect exactly the same behavior, but I can do some things different (especially setting the Application icon) if needed.
Loader is a console-style program but compiled without a console – no windows, no anything. It just loads DLL and calls a function.
Problem is that when I build even empty default project with one form as an EXE – it will actually have "master" Application (.Handle <> 0) window in taskbar. So I can set its title independently from main form caption.
But when the same thing is inside a DLL – there is no Application window (.Handle = 0), the title will be the form caption, but the most important bug: a form cannot be minimized!
In Delphi 7 it goes background under other windows (but taskbar thing stays!); in Lazarus it just minimizes to nowhere (hided, no way to restore anymore); both without any minimizing animation.
Other than that, my application seems to behave normally. This is only issue I have.
OK, I know that forms in libraries is a bad thing to do, but:
I’m fine to instantiate "another" VCL completely independent from host’s instance, maybe even in different thread.
There is no VCL in my particular host application! For me, it must work exactly as it will in EXE alone…
I searched something about Application.Handle in DLL, and now understand than I need to pass a handle to host’s Application object, so DLL will be joined with others host forms, but I have none! It’s even not Delphi… (and Application:=TApplication.Create(nil); didn’t help either)
Anything of following will probably help me:
A) How to instruct VCL to create a normal Application object for me? How it does it when in EXE, maybe I can copy that code?
B) How to create a suitable master window from C (proper styles, etc.) to pass it’s handle to DLL? Also, I believe, in Free Pascal there is no direct access to TApplication handle value, so I couldn’t probably assign it.
C) How to live without a taskbar window, but have my form (good news: my program has only one form!) to minimize correctly (or just somehow…)?
I now you all love to see some code, so here it is:
// default empty project code, produces valid working EXE:
program Project1;
uses Forms, Unit1 in 'Unit1.pas' {Form1};
{$R *.res}
begin
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.
+
// that's how I tried to put it in a DLL:
library Project1;
uses Forms, Unit1 in 'Unit1.pas' {Form1};
{$R *.res}
function entry(a, b, c, d: Integer): Integer; stdcall;
begin
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.Run;
Result := 0;
end;
exports
entry;
begin
end.
I specially crafted entry() function to be callable with rundll32, just for testing.
Also, I tried to put the body directly to "begin end." initialization section – same wrong behavior.
// To call a DLL, this can be used:
program Project1;
function entry(a, b, c, d: Integer): Integer; stdcall; external 'Project1.dll';
begin
entry(0, 0, 0, 0);
end.
Also, CMD-command "rundll32 project1.dll entry" will run it instantly. (Yeah, that way I might get a handle that Rundll gives me, but it isn’t what I want anyway.)
Last notes: (a) the DLL must be compiled in Lazarus; actually first thing I thought that it is a bug in LCL, but now when tested in Delphi7 I see the same; and since Delphi case is more simpler and robust, I decided to put here that; (b) my C loader doesn’t call LoadLibrary, it uses TFakeDLL hack (that OBJ file was tweaked to work without Delphi wrapper) and loads my DLL from memory (so I don’t have a handle to DLL itself), but otherwise their behavior is the same.
Okay, thanks to #Sertac Akyuz, I tried with .ShowModal:
// working Delphi solution:
library Project1;
uses Forms, Dialogs, SysUtils, Unit1 in 'Unit1.pas' {Form1};
{$R *.res}
function entry(a, b, c, d: Integer): Integer; stdcall;
begin
Result := 0;
Application.Initialize;
Form1 := TForm1.Create(nil);
try
Form1.ShowModal;
except
on e: Exception do
ShowMessage(e.message);
end;
Form1.Free;
end;
exports
entry;
begin
end.
There is still no Application window (taskbar title equal to form caption), but now my form can be successfully minimized (with system animation). Note that for EXE compilation I have to go default way with Application, because when I tried to create the form like this – it started to minimize to desktop (iconify) instead of the taskbar.
It works perfect in empty default Lazarus project too. But when I tried to implement it to my production program, it gave me "Disk Full" exception at .ShowModal!
That thing was frustrating me little earlier (and that’s why I got rid of modality altogether, tried it no more), but now I was determined enough to get the bottom of this.
And I found the problem! My build script doesn’t pass "-WG" ("Specify graphic type application") compiler option. Looks like something in LCL was using console-alike mode, and modality loop failed for some reason.
Then, I had another very confusing issue that I want to share. My form’s OnCreate was rather big and complex (even starting other threads), and some internal function give me access violation when tried to do some stuff with one of controls on the form. It looked like the control is not constructed yet, or the form itself…
Turns out that the actual call Form1:=TForm1.Create(nil); obviously will leave the global variable "Form1" unassigned during FormCreate event. The fix was simple: to add Form1:=Self; in the beginning of TForm1.FormCreate(Sender: TObject);
Now everything is working without any problems. I can even use other forms with a normal Form2.Show(); if I firstly add them to my entry() function, like Form2:=TForm2.Create(Form1);
(edit: minor note, if you would use Lazarus and try to run entry() function from any different thread than one that loaded DLL library itself – then you should put MainThreadID:=GetCurrentThreadId(); just above Application.Initialize;)
Yay, this question is solved!

How to determine programmatically the Windows' Performance settings with Delphi 2010

The following code is to fade my application on close.
procedure TfrmMain.btnClose1Click(Sender: TObject);
var
i : Integer;
begin
for i := 255 downto 0 do begin
frmMain.AlphaBlendValue := i;
application.ProcessMessages;
end;
Close;
end;
With Windows performance set to “Let Windows choose…”
When closing my Delphi app with the above code the fade is almost
instantaneous (maybe ¼ second at the most, if I blink I miss the
transition).
If I set the performance Option to ‘Adjust for best performance”
When exiting the same app the fade takes over 12 seconds.
Using the same code but commenting out the AlphaBlendValue change removes the delay.
I tested this out on both Delphi 2010 and DelphiXE2 and the results are the same.
This was tested on Windows 7 Ultimate 64bit if that makes any difference.
To say the least this behavior puzzles me.
I thought that the forms Alpha property was handled by the GPU and would therefore not be affected by Windows performance settings that should would be targeted at maximizing CPU performance.
So as far as this is concerned I'm not sure if this is a Windows 7 bug, a Delphi bug or just my lack of knowledge.
As far as a fix...
Is there a way to tell if Windows is running in crap graphics/max performance mode so that I can disable Alpha fade effects in my apps?
Edit for clarity:
While I would like to fix the fade what I am really looking for is a way to determine what the Windows performance setting is.
I am looking for how to determine a specific Windows setting - when you go into Windows Performance Options there are 3 tabs. On the first tab "Visual Effects" there are 3 canned options and a 4th option for 'Custom'. Minimally I am trying to determine if the option chosen is 'Adjust for best performance', if I could determine what the settings are on this tab even better.
Appreciate any help.
The fundamental problem with your code is that you are forcing 256 distinct updates irrespective of the performance characteristics of the machine. You don't have to use every single alpha blend value between 255 and 0. You can skip some values and still have a smooth fade.
You need to account for the actual graphics performance of the machine. Since you cannot predict that, you should account for real time in your fade code. Doing so will give you a consistent rate of fade irrespective of the performance characteristics of your machine.
So, here's a simple example to demonstrate tying the fade rate to real time:
procedure TfrmMain.btnClose1Click(Sender: TObject);
var
Stopwatch: TStopwatch;
NewAlphaBlendValue: Integer;
begin
Stopwatch := TStopwatch.StartNew;
while True do
begin
NewAlphaBlendValue := 255-(Stopwatch.ElapsedMilliseconds div 4);
if NewAlphaBlendValue>0 then
AlphaBlendValue := NewAlphaBlendValue
else
break;
end;
Close;
end;
The fade has a 1 second duration. You can readily adjust the mathematics to modify the duration to your requirements. This code will produce a smooth fade even on your low performing machine.
I would also comment that you should not use the global variable drmMain in a TfrmMain method. The TfrmMain method already has access to the instance. It is Self. And of course you can omit the Self. What's more the call to ProcessMessages is bad. That allows re-entrant handling of queued input messages. You don't want that to happen. So remove the call to ProcessMessages.
You actually ask about detecting the Adjust for best performance setting. But I think that's the wrong question. For a start you should fix your fade code so that the fade duration is independent of graphics performance.
Having done that you may still wish to disable the fade if the user has asked for lower quality appearance settings. I don't think you should look for one of the 3 canned options that you mention. They are quite possibly Windows version specific. Personally I would base the behaviour on the Animate windows when minimizing and maximizing setting. My rationale is that if the user does not want minimize and maximize to be animated, then presumably they don't want window close to be faded.
Here's how to read that setting:
function GetWindowAnimation: Boolean;
var
AnimationInfo: TAnimationInfo;
begin
AnimationInfo.cbSize := SizeOf(AnimationInfo);
if not SystemParametersInfo(SPI_GETANIMATION, AnimationInfo.cbSize,
#AnimationInfo, 0) then
RaiseLastOSError;
Result := AnimationInfo.iMinAnimate<>0;
end;
I think that most of the other settings that you may be concerned with can also be read using SystemParametersInfo. You should be able to work out how to do so by following the documentation.
Sorry for the tardy followup but it took me a while to figure out a working answer to my question and some of the issues behind it.
First, a thank you to David Heffernan for insight on a better way to handle the fade loop and information on the TStopWatch function from the Delphi's Diagnostics unit, much appreciated.
In regards to being able to determine the Windows' Performance settings...
When using the following un-optimized fade loop
procedure TfrmMain.btnFadeNCloseClick(Sender: TObject);
var
i : Integer;
begin
for i := 255 downto 0 do
frmMain.AlphaBlendValue := i;
Close;
end;
the actual Windows Performance Option settings causing the performance issue are "Enable desktop composition" and "Use visual styles on Windows and buttons". If both options are enabled there is no issue, if either setting is not enabled the loop crawls** (about 12 seconds on my system if the form is maximized).
Turns out that turning Aero Glass on or off affects these same 2 settings. So being able to detect if Aero Glass is on or not enables me to determine whether to not to enable the form effects, such as transition fades and other eye candy, in my apps. Plus now I can also capture that information in my bug reports.
**Note this appears to be an NVidia issue/bug, or at least an issue that is much more severe on systems with NVidia graphics cards. On 2
different NVidia systems (with recent, if not latest drivers) I
got similar results for a miximized form fade - less than
.001 seconds if Aero Glass is on, around 12 seconds if Aero Glass is
off. On a system with an Intel graphics card - less than .001 seconds
if Aero Glass is on, about 3.7 seconds if Aero Glass is off. Now
granted my test sampling is small, 3 NVidia systems (counting my
customer who initially reported the issue) and one non-NVidia system
but if I was using a decent NVidia graphic card I would not bother
turning Aero Glass off.
Below is the working code to detect if Aero Glass is enabled via Delphi:
This function has been tested on a Windows7 64 bit system and works with Delphi 2007, 2010 and Xe2 (32 & 64bit).
All of the various versions of the Delphi function below that I found on the net were broken - along with comments of people complaining about getting Access Violation errors.
What finally shed the light on fixing the bad code was Gerry Coll's response to: AccessViolationException in Delphi - impossible (check it, unbelievable...) which was about trying to fix AV errors in a function of the same type.
function ISAeroEnabled: Boolean;
type
_DwmIsCompositionEnabledFunc = function(var IsEnabled: Bool): HRESULT; stdcall;
var
Flag : BOOL;
DllHandle : THandle;
OsVersion : TOSVersionInfo;
DwmIsCompositionEnabledFunc: _DwmIsCompositionEnabledFunc;
begin
Result:=False;
ZeroMemory(#OsVersion, SizeOf(OsVersion));
OsVersion.dwOSVersionInfoSize := SizeOf(TOSVERSIONINFO);
if ((GetVersionEx(OsVersion)) and (OsVersion.dwPlatformId = VER_PLATFORM_WIN32_NT) and
(OsVersion.dwMajorVersion = 6) and (OsVersion.dwMinorVersion < 2)) then //Vista&Win7 only (no Win8)
begin
DllHandle := LoadLibrary('dwmapi.dll');
try
if DllHandle <> 0 then
begin
#DwmIsCompositionEnabledFunc := GetProcAddress(DllHandle, 'DwmIsCompositionEnabled');
if (#DwmIsCompositionEnabledFunc <> nil) then
begin
if DwmIsCompositionEnabledFunc(Flag)= S_OK then
Result:= Flag;
end;
end;
finally
FreeLibrary(DllHandle);
end;
end;
end;

How to programatically prevent Windows from hard disk drive spin down?

My program performs a task on the hard disk free space.
The task is quite long, it takes 1-2 hours.
The problem is that on laptop the hard disk may be turned off after few minutes when the user is inactive.
How do I programmatically prevent Windows from hard disk spin down (power off) ?
To prevent the system from entering idle mode you may try to use the SetThreadExecutionState function. This function informs the system that the application is in use and allows you to specify the thread's execution requirements. The usage can be like this, but I'm not sure if this affects also the disk power down timer:
type
EXECUTION_STATE = DWORD;
const
ES_SYSTEM_REQUIRED = $00000001;
ES_DISPLAY_REQUIRED = $00000002;
ES_USER_PRESENT = $00000004;
ES_AWAYMODE_REQUIRED = $00000040;
ES_CONTINUOUS = $80000000;
function SetThreadExecutionState(esFlags: EXECUTION_STATE): EXECUTION_STATE;
stdcall; external 'kernel32.dll' name 'SetThreadExecutionState';
procedure TForm1.Button1Click(Sender: TObject);
begin
if SetThreadExecutionState(ES_CONTINUOUS or ES_SYSTEM_REQUIRED or
ES_AWAYMODE_REQUIRED) <> 0 then
try
// execute your long running task here
finally
SetThreadExecutionState(ES_CONTINUOUS);
end;
end;
Or there is also available the new set of functions PowerCreateRequest, PowerSetRequest and PowerClearRequest designed for Windows 7, but the documentation is confusing and I haven't found any example of their usage at this time.
Or you can modify the power settings by PowerWriteACValueIndex or PowerWriteDCValueIndex functions with the GUID_DISK_SUBGROUP subgroup of power settings.
Windows does not allow applications to disable power control changes, because buggy applications were causing batteries to be drained. See http://blogs.msdn.com/oldnewthing/archive/2007/04/16/2148139.aspx
You can get notified when the system power status is about to be changed. See WM_POWERBROADCAST Messages.

SCardEstablishContext hangs as a service

Why might SCardEstablishContext hang, never to return, when called from a service?
I have code that works fine on lots of Windows installations. It accesses a Cherry keyboard's Smart Card reader (6x44) to read data on a smart card. It works fine on most PCs it has been tried on. However, on some PCs, running in Spain with Spanish Windows, the SCardEstablishContext function never returns. I cannot work out why this might be. I have logging either side of it, but the log entry after it does not appear. I cannot then shut it down (the worker thread is getting stuck), and have to kill it.
Exactly the same thread code works fine if run from an application, and not a service. Giving the service login settings of a user instead of system makes no difference.
I've installed Spanish XP on a machine here, but it works just fine. The far end has the same Winscard.dll version as I have here (both at XP SP3 status). No errors are shown in the event log.
How might I work out what is going wrong, and what might be fixing it? (Delphi code below)
// based on code by Norbert Huettisch
function TPCSCConnector.Init: boolean;
var
RetVar: LongInt;
ReaderList: string;
ReaderListSize: integer;
v: array[0..MAXIMUM_SMARTCARD_READERS] of string;
i: integer;
begin
Result := false;
FNumReaders := 0;
{$IFDEF MJ_ONLY}
LogReport(leInformation, 'About to call SCardEstablishContext');
{$ENDIF}
RetVar := SCardEstablishContext(SCARD_SCOPE_USER, nil, nil, #FContext);
{$IFDEF MJ_ONLY}
// never gets to report this (and logging known good etc)
LogReport(leInformation, 'SCardEstablishContext result = ' + IntToStr(RetVar));
{$ENDIF}
if RetVar = SCARD_S_SUCCESS then
begin
There may be different reasons why the API function appears to hang, like a deadlock, or an invisible message box or dialog waiting for user input. You should try to get a stacktrace using WinDbg.
You should also make sure that you are trying to reproduce the bug in the same environment. Important points might be whether Fast User Switching is active and whether other users are logged on, also that there are the same device drivers and services running.

Resources