Showing HTML Help (chm) from interactive service on XP - windows-services

I have a C++ App than can optionally run as Windows Service on XP and interacts with the Desktop (yes, I know it's bad practice but it's been around for a long time!)
Retrofitting html help to it I've discovered HtmlHelp() doesn't work from a service. I've tried running hh.exe using CreateProcess() and ShellExecute() with no success. On the other hand, running Write using CreateProcess works fine, so there must be something different about hh.exe. No amount of googling has shed any light. How can I fire up a chm file from a service?
PROCESS_INFORMATION ProcInfo;
STARTUPINFO si;
memset(&si, '\0', sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
//si.lpDesktop = "winsta0\\default"; // <-- doesn't make any difference
char *helpcmd = "hh.exe c:\\help\myhelpfile.chm";
BOOL bSuccess = ::CreateProcess(NULL, helpcmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &ProcInfo);

Finally found an answer to this:
Under the registry key:
HKLM\SOFTWARE\Microsoft\HTMLHelp\1.x\HHRestrictions
Make a new DWORD value key:
EnableNonInteractiveUser
And set the value to 1.
This will allow an interactive service process on XP to show HTML Help.
If your service process is already running you may need to restart it before this will work (the need to do this or not depends on how your program run-time system starts the HTML Help viewer - cached results mean that it might not work until restart.)
At this stage its unknown to me if the same change will work for Vista, Win7 or Win8.

Related

Why is COMMON_APPDATA returned as a null string on Windows XP

One of my users at a large university (with, I imagine, the aggressive security settings that university IT departments general have on their computers) is getting an empty string returned by Windows XP for CSIDL_COMMON_APPDATA or CSIDL_PERSONAL. (I'm not sure which of these is returning the empty string, because I haven't yet examined his computer to see how he's installed the software, but I'm pretty sure it's the COMMON_APPDATA...)
Has anyone encountered this or have suggestions on how to deal with this?
Here's the Delphi code I'm using to retrieve the value:
Function GetSpecialFolder( FolderID: Integer):String;
var
PIDL: PItemIDList;
Path: array[0..MAX_PATH] of Char;
begin
SHGetSpecialFolderLocation(Application.Handle, FolderID, PIDL);
SHGetPathFromIDList(PIDL, Path);
Result := Path;
end; { GetSpecialFolder }
ShowMessage(GetSpecialFolder(CSIDL_COMMON_APPDATA)); <--- This is an empty string
Edit:
Figuring out this API made me feel like I was chasing my tail - I went in circles trying to find the right call. This method and others similar to it are said to be deprecated by Microsoft (as well as by a earlier poster to this question (#TLama?) who subsequently deleted the post.) But, it seems like most of us, including me, regularly and safely ignore that status.
In my searches, I found a good answer here on SO from some time ago, including sample code for the non-deprecated way of doing this: what causes this error 'Unable to write to application file.ini'.
If you want to find out why an API call is failing you need to check the return values. That's what is missing in this code.
You need to treat each function on its own merits. Read the documentation on MSDN. In the case of SHGetSpecialFolderLocation, the return value is an HRESULT. For SHGetPathFromIDList you get back a BOOL. If that is FALSE then the call failed.
The likely culprit here is SHGetSpecialFolderLocation, the code that receives the CSIDL, but you must check for errors whenever you call Windows API functions.
Taking a look at the documentation for CSIDL we see this:
CSIDL_COMMON_APPDATA
Version 5.0. The file system directory that contains application data for all users. A typical path is C:\Documents and Settings\All
Users\Application Data. This folder is used for application data that
is not user specific. For example, an application can store a
spell-check dictionary, a database of clip art, or a log file in the
CSIDL_COMMON_APPDATA folder. This information will not roam and is
available to anyone using the computer.
If the machine has a shell version lower than 5.0, then this CSIDL value is not supported. That's the only documented failure mode for this CSIDL value. I don't think that applies to your situation, so you'll just have to see what the HRESULT status code has to say.

Delphi: MAPILOGON ErrorCode 1

I tried with many versions of MAPISend, but I everytime got error in one place.
The MAPILogon returns with errorcode 1.
dwRet := MapiLogon(Handle,
nil,
nil,
MAPI_DIALOG or MAPI_NEW_SESSION,
0, #MAPI_Session);
I tried with "MAPISend component", this code:
http://prog.hu/tudastar/60044-6/Delphi-Email+csatolt+file+thunderbird.html
and 2 of others.
Interesting, that Acrobat Reader CAN use the MAPI with Attach to email function, and the "Send" "In Mail" context menu also working.
I don't understand why it isn't working, in my machine (Win7) it is working fine.
Then machines where I failed have WinXP OS, and they used Thunderbird.
What I can do to successfully logon into MAPI?
THanks:
dd
I think I found the problem.
The problem, that Delphi needs a Registry value named MAPI under
"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Messaging Subsystem"
If this not present, it send 1 error code to you what is the base of the confusion.
This value must be string. The sysadmin wrote DWORD, and this caused the problem.
Thanks for your help:
dd
I am using RapWare components, http://www.rapware.nl/
Hth's.
Stanko.
I'm not sure about any Delphi-specific issues, but you don't need to call MAPILogon before calling MAPISendMail. If you do, I wouldn't check the return value. That's why Acrobat Reader was working and your SMAPI client was not.

How to redirect registry access of a dll loaded by my program

I have got a dll that I load in my program which reads and writes its settings to the registry (hkcu). My program changes these settings prior to loading the dll so it uses the settings my program wants it to use which works fine.
Unfortunately I need to run several instances of my program with different settings for the dll. Now the approach I have used so far no longer works reliably because it is possible for one instance of the program to overwrite the settings that another instance just wrote before the dll has a chance to read them.
I haven't got the source of the dll in question and I cannot ask the programmer who wrote it to change it.
One idea I had, was to hook registry access functions and redirect them to a different branch of the registry which is specific to the instance of my program (e.g. use the process id as part of the path). I think this should work but maybe you have got a different / more elegant.
In case it matters: I am using Delphi 2007 for my program, the dll is probably written in C or C++.
As an alternative to API hooking, perhaps you could use RegOverridePredefKey API.
Instead of hooking the registry access for the dll, you can use an inter-process lock mechanism for writing the values to the registry for your own app. The idea being that the lock acquired by instance1 isn't released until its dll "instance" has read the values, so that when instance2 starts it won't acquire the lock until instance1 has finished. You'd need a locking mechanism that works between processes for this to work. For example mutexes.
To create mutexes:
procedure CreateMutexes(const MutexName: string);
//Creates the two mutexes checked for by the installer/uninstaller to see if
//the program is still running.
//One of the mutexes is created in the global name space (which makes it
//possible to access the mutex across user sessions in Windows XP); the other
//is created in the session name space (because versions of Windows NT prior
//to 4.0 TSE don't have a global name space and don't support the 'Global\'
//prefix).
const
SECURITY_DESCRIPTOR_REVISION = 1; // Win32 constant not defined in Delphi 3
var
SecurityDesc: TSecurityDescriptor;
SecurityAttr: TSecurityAttributes;
begin
// By default on Windows NT, created mutexes are accessible only by the user
// running the process. We need our mutexes to be accessible to all users, so
// that the mutex detection can work across user sessions in Windows XP. To
// do this we use a security descriptor with a null DACL.
InitializeSecurityDescriptor(#SecurityDesc, SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDacl(#SecurityDesc, True, nil, False);
SecurityAttr.nLength := SizeOf(SecurityAttr);
SecurityAttr.lpSecurityDescriptor := #SecurityDesc;
SecurityAttr.bInheritHandle := False;
CreateMutex(#SecurityAttr, False, PChar(MutexName));
CreateMutex(#SecurityAttr, False, PChar('Global\' + MutexName));
end;
To release a mutex, you'd use the ReleaseMutex API and to acquire a created mutex, you'd use the OpenMutex API.
For CreateMutex see: http://msdn.microsoft.com/en-us/library/ms682411(VS.85).aspx
For OpenMutex see: http://msdn.microsoft.com/en-us/library/ms684315(v=VS.85).aspx
For ReleaseMutex see: http://msdn.microsoft.com/en-us/library/ms685066(v=VS.85).aspx
dirty method: open the dll in a hexeditor and change/alter the registry-path from the original hive to any other you want to use or have your proper settings.

ZeroConf/Bonjour Code that works in Delphi 7 not working in 2009

I have the following declaration for DNSServiceRegister:
function DNSServiceRegister
(
var sdRef: TDNSServiceRef;
const flags: TDNSServiceFlags;
const interfaceIndex: uint32_t;
const name: PUTF8String; //* may be NULL */
const regType: PUTF8String;
const domain: PUTF8String; //* may be NULL */
const host: PUTF8String; //* may be NULL */
const port: uint16_t;
const txtLen: uint16_t;
const txtRecord: Pointer; //* may be NULL */
const callBack: TDNSServiceRegisterReply; //* may be NULL */
const context: Pointer //* may be NULL */
): TDNSServiceErrorType; stdcall; external DNSSD_DLL;
In my Bonjour framework I have the following response to an announced service being made active (i.e. to actually start announcing itself, via Bonjour):
procedure TAnnouncedService.Activate;
var
flags: Cardinal;
name: UTF8String;
svc: UTF8String;
pn: PUTF8String;
ps: PUTF8String;
begin
fPreAnnouncedServiceName := ServiceName;
inherited;
if AutoRename then
flags := 0
else
flags := kDNSServiceFlagsNoAutoRename; { - do not auto-rename }
if (ServiceName <> '') then
begin
name := ServiceName;
pn := PUTF8String(name);
end
else
pn := NIL;
svc := ServiceType;
ps := PUTF8String(svc);
CheckAPIResult(DNSServiceRegister(fHandle,
flags,
0 { interfaceID - register on all interfaces },
pn,
ps,
NIL { domain - register in all available },
NIL { hostname - use default },
ReverseBytes(Port),
0 { txtLen },
NIL { txtRecord },
DNSServiceRegisterReply,
self));
TBonjourEventHandler.Create(fHandle);
end;
This is more verbose than I think it strictly needs to be, certainly it was working perfectly well in Delphi 7 in a much less verbose form. I have expanded a lot of operations into explicit steps to facilitate debugging, e.g. to be able to identify any implicit transforms of string payloads that may be occuring "under the hood" in Delphi 2009.
Even in this untidy expanded form this code compiles and works perfectly well in Delphi 7, but if I compile and run with Delphi 2009 I get no announcement of my service.
For example, if I run this code as part of a Delphi 7 application to register a _daap._tcp service (an iTunes shared library) I see it pop-up in a running instance of iTunes. If I recompile the exact same application without modification in Delphi 2009 and run it, I do not see my service appearing in iTunes.
I get the same behaviour when monitoring with the dns-sd command line utility. That is, service code compiled with Delphi 7 behaves as I expect, compiled in Delphi 2009 - nothing.
I am not getting any errors from the Bonjour API - the DNSServiceRegisterReply callback is being called with an ErrorCode of 0 (zero), i.e. success, and if I supply a NIL name parameter with AutoRename specified in the flags then my service is allocated the correct default name. But still the service does not show up in iTunes.
I am at a loss as to what is going on.
As you might be able to tell from the expansion of the code, I have been chasing potential errors being introduced by the Unicode implementation in Delphi 2009, but this seems to be leading me nowhere.
The code was originally developed against version 1.0.3 of the Bonjour API/SDK. I've since updated to 1.0.6 in case that was somehow involved, without any success. afaict 1.0.6 merely added a new function for obtaining "properties", which currently supports only a "DaemonVersion" property for obtaining the Bonjour version - this is working perfectly.
NOTE: I'm aware that the code as it stands is not technically UTF8-safe in Delphi 7 - I have eliminated explicit conversions as far as possible so as to keep things as simple as possible for the automatic conversions that Delphi 2009 applies. My aim now is to get this working in Delphi 2009 then work backward from that solution to hopefully find a compatible approach for earlier versions of Delphi.
NOTE ALSO: I originally also had problems with browsing for advertised services, i.e. identifying an actual iTunes shared library on the network. Those issues were caused by the Unicode handling in Delphi 2009 and have been resolved. My Delphi 2009 code is just as capable of identifying an actual iTunes shared library and querying it's TXT records. It's only this service registration that isn't working.
I must be missing something stupid and obvious.
Does anyone have any ideas?!
UPDATE
Having returned to this problem I have now discovered the following:
If I have a pre-D2009 and a D2009+ IDE open (e.g D2006 and D2010) with the same project loaded into both IDE's concurrently:
Build and run under 2006: It works - my service announcement is picked up by iTunes
Switch to D2010 and run (without building): It does a minimal compile, runs and works.
Do a full build in D2010: It stops working
Switch back to D2006 and run (without building): It doesn't work
Do a full build in D2006: It works again
Does this give anyone any other ideas?
The answer to this is mind boggling. On the one hand I made a completely stupid, very simple mistake, but on the other hand it should never - as far as I can see - have worked in ANY version of Delphi!
The problem was nothing what-so-ever to do with the Unicode/non-unicodeness of any strings, but was actually due to a type mismatch in the PORT parameter.
I was passing in the result of ReverseBytes(Port) - that parameter expected a uint16_t, i.e. a Word value. My Port property was however declared (lazily) as an Integer!!
Once I fixed this and had Port declared as a Word, it now works on both D2007- and D2009+ versions of Delphi.
Very weird.
I can only think that some other edge-case behaviour of the compiler that might have somehow affected this was changed when Unicode support was introduced.
Based on the information that we have available here, the situation is this:
When calling the DLL with your code in Delphi 2007, it gives one result.
When calling the same DLL with your code in Delphi 2009, it gives another result.
The suspicion is, that it is related to the Delphi 2009 compiler.
Logically, the difference must therefore be, that Delphi 2009 sends different values as parameters. In order to make the debugging truly Delphi-independent, you therefore need to create a dummy DLL, which reports the values it gets. Other Delphi-dependent methods may be applied, like looking at the disassembly of the function-call into the DLL, and debugging it so that we know exactly what values are passed, and how, to the DLL, in both compilers.
I can't find the declaration instruction for the vars "ServiceName" and "ServiceType" in your code sample.
Assuming a String type (thus a unicode string), I guess (yes... no D2009 available to test this) lazy typecasting could be an issue:
name := ServiceName;
Why not use the following?
name := PAnsiChar(AnsiString(ServiceName))
Anyhow... just my 2 cts.
BTW:
I always use the pre defined "EmptyStr", "EmptyWideStr" ... so the test would look like:
if (ServiceName <> EmptyStr) then
which should be safe and avoid the confusion of types.
On the other side, Delphi may interpret the '' as an ANSIChar like the following declaration do:
const
MyParagraphChar = 'ยง';
Not sure... I'm confused - should go home now ;)
If the DLL is not written using Delphi 2009, you may want to use something else than PUTF8String. The Delphi 2009 Utf8String type is different from Delphi 2007's UTF8String type.
If the DLL was written using C/C++, I strongly suggest to use PAnsiChar() instead of PUtf8String.

Find out what process registered a global hotkey? (Windows API)

As far as I've been able to find out, Windows doesn't offer an API function to tell what application has registered a global hotkey (via RegisterHotkey). I can only find out that a hotkey is registered if RegisterHotkey returns false, but not who "owns" the hotkey.
In the absence of a direct API, could there be a roundabout way? Windows maintains the handle associated with each registred hotkey - it's a little maddening that there should be no way of getting at this information.
Example of something that likely wouldn't work: send (simulate) a registered hotkey, then intercept the hotkey message Windows will send to the process that registered it. First, I don't think intercepting the message would reveal the destination window handle. Second, even if it were possible, it would be a bad thing to do, since sending hotkeys would trigger all sorts of potentially unwanted activity from various programs.
It's nothing critical, but I've seen frequent requests for such functionality, and have myself been a victim of applications that register hotkeys without even disclosing it anywhere in the UI or docs.
(Working in Delphi, and no more than an apprentice at WinAPI, please be kind.)
One possible way is to use the Visual Studio tool Spy++.
Give this a try:
Run the tool (for me, it's at C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\Tools\spyxx_amd64.exe or you can download it). Note: there is spyxx.exe (32-bit version) and spyxx_amd64.exe (64-bit version) - if you don't see anything in 64-bit use the 32-bit version (ie.catches messages only in same architecture)
In the menu bar, select Spy -> Log messages... (or hit Ctrl + M)
Check All Windows in System in the Additional Windows frame
Switch to the Messages tab
Click the Clear All button
Select WM_HOTKEY in the listbox, or check Keyboard in Message Groups (if you're OK with more potential noise)
Click the OK button
Press the hotkey in question (Win + R, for example)
Select the WM_HOTKEY line in the Messages (All Windows) window, right click, and select Properties... in the context menu
In the Message Properties dialog, click the Window Handle link (this will be the handle for the window that received the message)
Click the Synchronize button on the Window Properties dialog. This will show the window in the main Spy++ window treeview (if it's windows itself or some popup application it shows nothing).
On the Window Properties dialog, select the Process tab
Click the Process ID link. This will show you the process (In my Win + R case: EXPLORER)
Your question piqued my interest, so I've done a bit of digging and while, unfortunately I don't have a proper answer for you, I thought I'd share what I have.
I found this example of creating keyboard hook (in Delphi) written in 1998, but is compilable in Delphi 2007 with a couple of tweaks.
It's a DLL with a call to SetWindowsHookEx that passes through a callback function, which can then intercept key strokes: In this case, it's tinkering with them for fun, changing left cursor to right, etc. A simple app then calls the DLL and reports back its results based on a TTimer event. If you're interested I can post the Delphi 2007 based code.
It's well documented and commented and you potentially could use it as a basis of working out where a key press is going. If you could get the handle of the application that sent the key strokes, you could track it back that way. With that handle you'd be able to get the information you need quite easily.
Other apps have tried determining hotkeys by going through their Shortcuts since they can contain a Shortcut key, which is just another term for hotkey. However most applications don't tend to set this property so it might not return much. If you are interested in that route, Delphi has access to IShellLink COM interface which you could use to load a shortcut up from and get its hotkey:
uses ShlObj, ComObj, ShellAPI, ActiveX, CommCtrl;
procedure GetShellLinkHotKey;
var
LinkFile : WideString;
SL: IShellLink;
PF: IPersistFile;
HotKey : Word;
HotKeyMod: Byte;
HotKeyText : string;
begin
LinkFile := 'C:\Temp\Temp.lnk';
OleCheck(CoCreateInstance(CLSID_ShellLink, nil, CLSCTX_INPROC_SERVER, IShellLink, SL));
// The IShellLink implementer must also support the IPersistFile
// interface. Get an interface pointer to it.
PF := SL as IPersistFile;
// Load file into IPersistFile object
OleCheck(PF.Load(PWideChar(LinkFile), STGM_READ));
// Resolve the link by calling the Resolve interface function.
OleCheck(SL.Resolve(0, SLR_ANY_MATCH or SLR_NO_UI));
// Get hotkey info
OleCheck(SL.GetHotKey(HotKey));
// Extract the HotKey and Modifier properties.
HotKeyText := '';
HotKeyMod := Hi(HotKey);
if (HotKeyMod and HOTKEYF_ALT) = HOTKEYF_ALT then
HotKeyText := 'ALT+';
if (HotKeyMod and HOTKEYF_CONTROL) = HOTKEYF_CONTROL then
HotKeyText := HotKeyText + 'CTRL+';
if (HotKeyMod and HOTKEYF_SHIFT) = HOTKEYF_SHIFT then
HotKeyText := HotKeyText + 'SHIFT+';
if (HotKeyMod and HOTKEYF_EXT) = HOTKEYF_EXT then
HotKeyText := HotKeyText + 'Extended+';
HotKeyText := HotKeyText + Char(Lo(HotKey));
if (HotKeyText = '') or (HotKeyText = #0) then
HotKeyText := 'None';
ShowMessage('Shortcut Key - ' + HotKeyText);
end;
If you've got access to Safari Books Online, there is a good section about working with shortcuts / shell links in the Borland Delphi 6 Developer's Guide by Steve Teixeira and Xavier Pacheco. My example above is a butchered version from there and this site.
Hope that helps!
After some research, it appears that you'd need to get access to the internal structure that MS uses to store the hotkeys. ReactOS has a clean room implementation that implements the GetHotKey call by iterating an internal list and extracting the hotkey that matches the parameters to the call.
Depending on how close ReactOS' implementation is to the MS implementation, you may be able to poke around in memory to find the structure, but that's over my head...
BOOL FASTCALL
GetHotKey (UINT fsModifiers,
UINT vk,
struct _ETHREAD **Thread,
HWND *hWnd,
int *id)
{
PHOT_KEY_ITEM HotKeyItem;
LIST_FOR_EACH(HotKeyItem, &gHotkeyList, HOT_KEY_ITEM, ListEntry)
{
if (HotKeyItem->fsModifiers == fsModifiers &&
HotKeyItem->vk == vk)
{
if (Thread != NULL)
*Thread = HotKeyItem->Thread;
if (hWnd != NULL)
*hWnd = HotKeyItem->hWnd;
if (id != NULL)
*id = HotKeyItem->id;
return TRUE;
}
}
return FALSE;
}
I presume this thread on sysinternals was asked by someone related to this question, but I thought I'd link to it anyway to keep the two together. The thread looks very intriguing, but I suspect that some deep dive spelunking would need to happen to figure this out without access to the MS internals.
Off the top of my head, you might try enumerating all windows with EnumWindows, then in the callback, send WM_GETHOTKEY to each window.
Edit: Apparrently I was wrong about that. MSDN has more information:
WM_HOTKEY is unrelated to the WM_GETHOTKEY and WM_SETHOTKEY hot keys. The WM_HOTKEY message is sent for generic hot keys while the WM_SETHOTKEY and WM_GETHOTKEY messages relate to window activation hot keys.
Note: Here is a program purporting to have the functionality you are looking for. You could try decompiling it.
Another thread mentions a global NT level keyboard hook:
Re-assign/override hotkey (Win + L) to lock windows
maybe you can get the handle of the process that called the hook that way, which you can then resolve to the process name
(disclaimer: I had it in my bookmarks, haven't really tried/tested)
I know you can intercept the stream of messages in any window within your own process - what we used to call subclassing in VB6. (Though I do not remember the function, perhaps SetWindowLong?) I am unsure if you can do this for windows outside your own process. But for the sake of this post lets assume you find a way to do that. Then you can simply intercept the messages for all top level windows, monitor for the WM_HOTKEY message. You wouldn't be able to know all the keys right off the bat, but as they were pressed you could easily figure out what application was using them. If you persisted your results to disk and reloaded each time your monitor application was run you could increase the performance of your application over time.
This doesn't exactly answer the part of the question that is about the Windows API, but it answers the part of the question that is about a list of global hotkeys and the applications that "own" them.
The free Hotkey Explorer at http://hkcmdr.anymania.com/ shows a list of all global hotkeys and the applications that own them. This just has helped me figure out why an application-specific shortcut key stopped working and how to fix it (by reconfiguring the registered global hotkey in the app that had it registered), within a few seconds.

Resources