Generating PDF with Quick Reports behind a Delphi Web Server - web-services

I have a Delphi web server providing some web services*. One of them is supposed to generate and return a PDF report.
The PDF creation is done with a QReport that is then exported into a PDF file with the ExportToFilter procedure.
The routine works fine when called from within an application, but when called behind a TIdTCPServer, it hangs and never finishes. Debugging it, I got tho the hanging point:
(note: I'm home right now and I don't have the source code. I'll try to reproduce quickrpt.pas' source as accurrate as I can remember).
procedure TCustomReport.ExportToFilter(TQRDocumentFilter filter);
...
AProgress := TQRFormProgress.Create(Application); // Hangs on this line
AProgress.Owner := QReport;
if ShowProgress then AProgress.Show;
QReport.Client := AProgress;
...
Searching the web, I found in this page (1) the suggestion to set ShowProgress to False, and edit the code so that it does not create the progress form when ShowProgress is set to false (apparently, this is due to QReport not being threadsafe).
So, I edited the code, and now I have this:
procedure TCustomReport.ExportToFilter(TQRDocumentFilter filter);
...
if ShowProgress then
begin
AProgress := TQRFormProgress.Create(Application);
AProgress.Owner := QReport;
AProgress.Show;
QReport.Client := AProgress
end;
...
Now, the report comes out. But then the service gets to an Invalid Pointer Exception (which I can't trace). Following calls to the service complete successfully, but when I shut down the service** it starts whining again with Invalid Pointer Exceptions, then the "MyServer has commited an invalid action and must be closed" windows message, then again a couple of times more, then just the pointer exception, then comes to error 216 (which as far as I could find out, is related to Windows access permissions).
Thanks!
Update (jan 5): Thanks Scott W. for your answer. Indeed, after some research, I found another suggestion that only the main thread can access some components. So I set the QR code back to normal and called the main method from a Synchronize call inside a TThread (so that way the main thread would handle it). But I still get the same error.
You mention you were able to generate PDF as a service with QR 4. Maybe that's why it's not working for me, since I'm using QR 3. On the other hand, you don't mention if you're doing that behind a TIdTCPServer (which is my case, providing web services) or if you run it by itself (for instance, during a batch process).
Anybody knows whether my QR version might be the problem? Thanks!
* Running Delphi 7 and QuickReport 3 on a Windows XP SP2. The server is based on Indy.
** I have two versions of the server: a Windows application and a Windows Service. Both call the same inner logic, and the problem occurs with both versions.
Update (mar 8): After all, my problem was that my printing routine was in another dll, and the default memory management module is somewhat crappy. Setting the first uses of my .dpr to be ShareMem overrides the memory management module with Borland's implementation, and solved my problem.
uses
ShareMem, ...
(1): http://coding.derkeiler.com/Archive/Delphi/borland.public.delphi.thirdpartytools.general/2006-09/msg00013.html

I'm guessing that QReport.Client is used somewhere later in the code, and with your modified code no longer assigning it to AProgress, you end up with an error.
Are you sure that you have to modify the QuickReport source? I have used QuickReport in a Windows Service to generate a PDF file and then attach to email message and all worked fine without having to modify the QR source. I don't recall exactly which settings had to be made, but it was done with Delphi 6 and QR 4.06.

Related

Using wh_shell hook for custom windows-shell(explorer.exe replacement program) C++

So I have spent that past week and a half working on code to simply setup the hook procedure for wh_shell for a program that will replace explorer.exe in the registry and will run as the main desktop program. There seems to be very little information and sources for using this outside of just the windows API which is a bit undescriptive and doesn't explain everything to a great detail. For some reason I just cant get it to work, no matter if I run it inside of explorer.exe, or if I replace the register and make it the default shell. I'm going to ask a couple of things in this post because I think that if you can answer one of these questions you likely have the answer to more.
So first I just have a question about hooks in general: When I run the SetWindowsHookEx(...) function -resource below- it says for var lpfn that a dll is not necessary if the hook is only used to monitor the current process. Now obviously when monitoring events such as window_created, those are events within a different processes which makes me think that the hookproc has to be within a DLL(which is how ive programmed so far). But this is questionable to me because when u are running SetWindowsHookEx(...) the process I wish to monitor do not yet exist until the user decides to start them. Do these processes notify the system when wh_shell events are being done so that I my hook doesnt need to get placed into every process upon creation, or is it more like when I run SetWindowsHookEx(...) with wh_shell that it will place a hook in all processes when the are created. The second resource states that the system just calls the hookproc when these things happen, so then do I even need a DLL, or what process does it need to be hooked to because I dont think it needs to be hooked into everything.
So second I have a question regarding setting my process as default shell - see resources - the resource states any process that registers itself as the default shell(which I assume is just modifying the registry to my process, if not and there is more please let me know) needs to call the SystemsParameterInfo(...) function. So first, does this func need to be called before running SetWindowsHookEx(...) or is there some expected spot it should be elsewhere in my code? Then in regards to the other variables it doesnt specify for, just curious what the recommended would be to set them as, like what are they set as for explorer.exe, and maybe a few other examples(including things NOT to do).
Finally for the sake of testing, using the console will be the most helpful to me here. The console will be used for input to run functions and commands for now(like open the register and swap back the shell to explorer.exe). If my hookproc is within a DLL, I need it to output some messages, I dont want to muddle the same console and I also dont even know if it will output to the same console, so what might be a recommended or potential solution for outputs(again this is temporary and for testing so it doesnt have to be perfect or even great)?
Also I would think windows 11 shouldn't be an issue, but I havent tested on windows 10 system...
I havent included any code as Im pretty sure most of this stuff can be answered without it and that its so few lines of code that its not like typical questions where its like examine my code and help me, maybe some example code you can show me would be really helpful.
Thankyou!
SetWindowsHookEx(...)
https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowshookexa
defaultShell
https://learn.microsoft.com/en-us/windows/win32/winmsg/about-hooks#wh_shell
regards to WH_SHELL section
Testing Environment:
Windows 11 vm running in Hyper-V Manager
You haven't mentioned an important parameter in your description and that is the last argument of SetWindowsHookEx, the thread id.
When it is set to 0 then ..
'[in] Specifies the identifier of the thread with which the hook procedure is to be associated. If this parameter is zero, the hook procedure is associated with all existing threads running in the same desktop as the calling thread.'
That said, then like everything in Windows programming nothing is as what the documentation states, its as if the documentation is a wish-list Microsoft will like Windows to be when it grows up.
Actually even if you manage to get everything right you will see that the shell messages you will get will be VERY few compared to what the documentation states.
I am working on the same issue and when I get it going I will post the results here.

Unexpected IConnectionPointImpl::Unadvise call on Windows Embedded Compact 7

We have a bigger software running on Win CE6 without problems. The core functionality is implemented in a COM server DLL that provides connection points. The COM client program registers event handlers for the connection points on program startup to get status notifications etc. On program exit it unregisters the handlers by calling the corresponding IConnectionPointImpl::Unadvise methods.
Now, we are porting the program to run on Win EC 7. The new Board Support Package (BSP) for Win EC 7 works well. There are also different versions with different options, created at different times with different sources from Microsoft, but our software always show the same issue.
On program startup, ~10s after launch, IConnectionPointImpl::Unadvise is called unexpectedly on all registered event handlers. We only have one method in our source code that calls IConnectionPointImpl::Unadvise and this is definitely not executed.
The issue appears ~95%, but sometimes the program starts and runs without problems. We cannot use the Debugger because of the size of the program, the performance is very poor.
We guess, that the COM runtime calls the IConnectionPointImpl::Unadvise methods for some reasons. But we have no idea, how to prevent this.
Has anybody observed the same issue? Is there a solution/workaround available? Thanks.
So we finally found how solve this problem.
We remove our dependency on MarshalByReObject and replace it by a proper implementation of ISerializable.
That allow us to load properly inside custom AppDomain our assembly and events are not loose anymore.
But this has a side effect on path where assembly a configuration file are loaded. To solve this we also implement an AppDomain.AssemblyResolve event which allow us to redirect the loading in a proper place.
I hope this can help you ;)

WMVCore crashes when WMIsContentProtected methos was called

My team is developing an application that is capable of recording videos and exporting the video into .wmv format file.
The export function is done via a method that utilizes windows API method derived from WMVCore.dll library.
However, on some PCs, this export function gets into crashes and according to the dump file taken from the crash, it appears that the last method called was WMVCore.WMIsContentProtected method. The call stack from the dump file is like below:
379fe8d8 65aa0cdb 299f94f0 00025800 379fe8f4
WMVCORE!WMIsContentProtected+0x4bc3 379fe970 6102e07e 1cae3f20
299f94f0 379fece8 WMVCORE!WMCreateWriterPushSink+0x1e278
It is really frustrating getting this crash without any more information out of the dump file. I also thought that the library might be corrupted on that specific PC, but there is no way to re-install the library as it comes with Windows Media Player. Any suggestion would be appreciated.
The code simply called WMVCore.WMCreateWriterPushSink which is followed by WMVCore.WMIsContentProtected. The OS version is Windows 7 Enterprise 64 bit. The application is a web based application on IE, in which case when the crash happens, IE would also crash and stop. The version of the WMVCore.dll is 12.0.7601.17514 and the question here is whether anyone has experienced the same issue and if yes, what sort of things can be tried/done to prevent this crash from happening?

Implementing custom windows authentication package

I'm building a custom authentication subpackage for MSV1_0 for Windows 7. I've used the msvsubauth sample in from the Windows SDK and I have 2 questions regarding some problems I'm facing with that:
When I'm trying just to make sure that the routine get's invoked and set the Auth0 property in the registry to my package and add a simple code at the end of the Msv1_0SubAuthenticationRoutine that creates a file:
//
// Cleanup up before returning.
//
Cleanup:
hTestFile = CreateFile(
TEXT("C:\\lsa\\lsa.txt"),
GENERIC_READ|GENERIC_WRITE, 0,
NULL, CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL, NULL);
if(hTestFile != INVALID_HANDLE_VALUE) {
CloseHandle(hTestFile);
}
return Status;
} // Msv1_0SubAuthenticationRoutine
Apparently the package gets invoked because when I enter my password I get an error message from windows "the parameter is incorrect" which is a good sign. But why I'm getting that error? when the exactly same code is executed from a separate .exe file it runs perfectly and creates the test text file. I've checked the permissions and set "full control" for "everyone". Any ideas? the SDK doesn't exactly mention what kind of isolation LSA is creating for code within auth packages.
The second problem is testing the AP. Currently with every change I rebuild the library, copy it to a test VM and then to the System32 folder and reboot it. Is there an easier way to do that?
Thank in advance!
Debugging in Winlogon and LSASS makes for the most time consuming debugging.
To ease your debugging, you could write a proxy AP that exports the same functions. When loaded, you proxy_ap would
Copy the real AP from a known location to a temp locationand.
LoadLibrary that DLL, GetProcAddress of everything, and forward any calls it receives to that newly loaded DLL.
Watch for changes in the directory where the original AP was copied from
When a change occurs (and if your AP changed) FreeLibrary and goto step 2
But you need to keep a tight grip on what happens on your development target, because handling the dll switch while dealing with requests comming from many threads can become a worse nightmare that what you are trying to solve.
LogonUI.exe starts a new instance every time, but LSASS.exe is long lived.
+Have a look at CVSNT source code (http://cvsnt.sourcearchive.com/). They have a pretty nice AP that implements su. Run the sample in the local system account with psexec -s (from Microsoft/SysInternals pstools suite)
Perhaps your problem is Everyone only includes Authenticated users? This is just a guess.
I suggest you use Process Monitor to monitor for Access Denied messages or for your path. It is fantastic for debugging permission/path problems of all kinds.
http://technet.microsoft.com/en-us/sysinternals/bb896645.aspx
If you experience the issue at the "Unlock Workstation" or "change Password" screens, and it doesn't prevent you logging in, this should be easy to do - set it running, reproduce the problem, log back in and hey presto.
Otherwise you might have to resort to tricks like executing that code path only for certain user accounts, on the Nth try, etc.

Is MsiOpenProduct the correct way to read properties from an installed product?

Given an MSI product code I want to get the upgrade code (among other properties) from an already installed product. I have tried this by calling the MsiOpenProduct method, followed by MsiGetProductProperty(). An (abbreviated) example looks like this:
MSIHANDLE handle = NULL;
MsiOpenProduct(strProductCode,&handle);
CString strUpgradeCode;
MsiGetProductProperty(handle,_T("UpgradeCode"), strUpgradeCode.GetBuffer(GUID_LENGTH), &dwSize);
strUpgradeCode.ReleaseBuffer();
MsiCloseHandle(handle);
This gets me the desired value, and judging from the MSDN documentation this seems like a valid way to do this:
The MsiOpenProduct function opens a
product for use with the functions
that access the product database. The
MsiCloseHandle function must be called
with the handle when the handle is no
longer needed.
However the call to MsiOpenProduct() pops up the "Windows installer is preparing the installation..." dialog. The call to MsiCloseHandle() makes it disappear again.
This left me wondering:
What does the call to MsiOpenProduct() do under the hood? I do not want to trigger any actions, I just want to read properties.
I don't mind the dialog popping up, as this is only for unit test code as long as this has no side effects. And as there are many unit tests that do this, it must still work when opening and closing handles in rapid succession.
Although I stumbled over the MsiGetProductInfo method, there seems to be no way to get the upgrade code. Am I right?
Is MsiOpenProduct the correct way to read properties like the upgrade code?
MsiOpenProduct should be fine So long as you don't run any sequences or actions, it won't do anything. If you want to silence the dialog, you can with careful use of either MsiSetInternalUI() or MsiSetExternalUI().
Another approach you can take, as long as the ProductCode and UpgradeCode are safely static (i.e. as long as they aren't changed by transforms), is to locate the database using MsiGetProductInfo() and call MsiOpenDatabase() on that. The difference is that MsiOpenProduct() (or similarly MsiOpenPackage) applies the transforms that were used at installation time and prepares a session, whereas MsiOpenDatabase() does neither.
There is a comprehensive answer with information on how to get the UpgradeCode using Powershell or VBScript and WMI here: How can I find the Upgrade Code for an installed MSI file?
Below is a quick, basic example using VBScript / COM automation (MSI API, not WMI) and the approach discussed by OP (using the OpenProduct method - the COM equivalent to the Win32 installer function).
As discussed in my comment above, I will just add this little VBScript to do the same as OP does in C++. Note that Windows Installer can be accessed via WMI (Win32_Product object), COM automation and Win32 C++ installer functions.
For some reason the UpgradeCode for a package appears to not be available directly from the COM API or the Win32 API. Very strange indeed, especially since it is an input parameter to functions like Installer.RelatedProducts - it is not clear in the documentation that the actual call should be RelatedProducts(UpgradeCode), but looking in the msi.IDL you see: StringList* RelatedProducts([in] BSTR UpgradeCode);
The WMI option works, but so does this OpenProduct call demonstrated below (which is significantly faster and appears safe - WMI is completely read-only as far as I know though - but heaven knows what they are doing "under the hood". Are they spinning up a session object? Or are they reading from a WMI database? WMI does "feels" safer somehow).
The beauty of the below method is that it will apply all transforms that were applied to the product in question at installation time. If you want to write to disk instead of showing message boxes and can't be bothered looking up the docs, here is a similar VBScript that writes package info to a desktop text file: How can I find the product GUID of an installed MSI setup? - quite a bit down the page, just copy a couple of lines and you are message box free).
Note! The script below will create one log file per opened MSI if automatic logging is enabled on the system. As it stands the script will only open one MSI before it exists though (the Exit For construct).
On Error Resume Next ' This "tersified" script has no error handling
Const msiUILevelNone = 2
Set installer = CreateObject("WindowsInstaller.Installer")
Set products = installer.ProductsEx("", "", 7)
installer.UILevel = msiUILevelNone ' Suppress GUI (MSI progress dialog)
'Iterate over all MSI packages on the box
For Each product In products
' productcode = product.ProductCode
' name = product.InstallProperty("ProductName")
' version = product.InstallProperty("VersionString")
' pkgcode = product.InstallProperty("PackageCode")
Set session = installer.OpenProduct(product.ProductCode)
upgradecode = session.ProductProperty("UpgradeCode")
MsgBox upgradecode
Set session = Nothing ' Important, close the session (doesn't work in Javascript btw)
Exit For ' End after one iteration (so you don't get a million message boxes)
' Alternatively something like: If i > 4 Then Exit For
Next
Set installer = Nothing
MsgBox "Finished"
I have tried to look in the C++ Win32 installer functions for any other way to retrieve the UpgradeCode, but I can't see anything obvious. The session approach should work in C++ as well, but I am a little apprehensive about the release of handles and resources. I am not properly potty-trained with C++, but know more than enough to be dangerous. Fire In The Hole. Etc...
I wonder if the OP retrieved all packages on the box, or just a single one. I wonder if the timing issues and concurrent session object problems seen with Javascript would strike in C++ as well? I will give it a go I think - someday.
For the information you want, it sounds like you can just call ::MsiGetProductInfo(). ::MsiOpenDatabase() is a very slow operation while ::MsiGetProductInfo() is (IIRC) more on par with registry look ups.