Posted by: Christian Wimmer in: JEDI Windows Security Code Lib
I found this question in the borland mailinglist:
… I get my dialog and all runs well – *except* I’ve lost theming
on this particular dialog. This means the application’s main windows shows
up correctly themed, the progress dialog does not. I’ve already added the
XP manifest to the progress dialog, too, but to no avail so far.
A theme is only applied if an EXE or DLL file has a manifest that explicitly enables theme support. If Windows cannot find this manifest, the process is only shown with the regular window design.
However the Vista elevation is done by creating a COM object and offers its methods through a seperate process. A seperate process is necessary because the same process cannot get assigned the ncessary administrator token. In our case a service in the service container svchost.exe has the task to host the COM object. The contained service is called dllhost.exe (service name: “COMSysApp”) and does not run until requested. It does not matter whether the service is deactivated or not because it is started directly by svchost.
The COM method and the dialog will run in the process space of the newly elevated dllhost.exe. And because there is no manifest information in this file you cannot get a themed dialog layout.
So how can you get a themed dialog anyhow?
The task is done by using the basic principles of COM. COM knowns Callback interfaces and other stuff. By using a Callback interface you can turn around the server – client principle.
If you prefer to read code instead of text you should study this code excerpts.
Define a callback interface for your client and add it to the server’s TypeLib. Delphi will generate code for it automatically in a file called <Name>_TLB.pas (TLB Header). Do not create the interface by using the Delphi’s ActiveX expert. Just add it manually.
IMyCallback interface is known to both the server and the client, because we use the TLB Header in the client and the server. The client will implement and the server will receive a pointer to it and then call the included method ACallBackMethod everytime it wishes to do so.
The server implements a method to allow a callback interface to be passed to it.
Of course you have to implement both interfaces: IServerInt and IMyCallback. However IServerInt is implemented on server side and IMyCallback is only implemented on the client side.
On client side (namely your application) write..
Install the COM object anywhere before you’re going to use it.
After all implement the callback method…
…and anywhere you like, call the method to use our Callback interface.
I’m using TComObject and TComObjectFactory on the client side , because this way works for console applications, too. If you need the possibilities of TAutoIntfObject, you will have to write a standalone COM server with TApplication. However this is not necessary here. The method ACallBackMethod will be run in the client’s process space rather than the elevated helper process. You can check whether the callback method runs in the correct process (elevated or not?) with the following code.
Second way, still on the client side:
If you already have a form class, you can add the interface to it.
Instead of creating the COM object, you pass a self-pointer to the method parameter.
Be aware that in a multi thread environment, you have to make sure that resources in that specific form are accessed only by one thread at a time.
Eventually the server side calls the methods within the callback interface:
TServerIntImpl = class(TAutoObject, IServerInt)
procedure DoStuffThatNeedsCallBack(const Callback: IMyCallback;…) safecall;
procedure TServerIntImpl.DoStuffThatNeedsCallBack(const Callback: IMyCallback;…)
The callback method will be called in the context of the client. So it will not be elevated. This is because there is no ordinary method call here. COM will pack the function stack and all its parameter into a stream, send it to the client, unpack it and restore a proper function stack to be executed. This happens in the client process of course.
Get more information about COM callbacks here.