How can I access to global IDirect3DDevice9 object in firemonkey 3D form directly ? is it possible ?
Yes, you can have access to DirectX interfaces using these properties:
TCustomDX9Context.SharedDevice: IDirect3DDevice9; |
TCustomDX9Context.Direct3D9Obj: IDirect3D9 ;
To access the current context of your application use: 'Form.Context :TContext3D'. TCustomDX9Context is a descendant of TContext3D, you have to test first which context device your application is currently using. It could be one of these classes:
TCustomDX9Context //Windows
TCustomDX10Context //Windows
TCustomContextOpenGL //Mac
Good luck!
Related
I am trying to disable CORS in TEdgeBrowser and found a lot of solutions by using ICoreWebView2EnvironmentOptions because TEdgeBrowser is implement by WebView2.
In Microsoft's document, the sample code seems to used for Visual C++ and C++ Builder is not applicable:
auto options = Microsoft::WRL::Make<CoreWebView2ExperimentalEnvironmentOptions>();
Here is the code I have tried in C++ Builder 11:
_di_ICoreWebView2EnvironmentOptions *m_WV2_EnvOpt = new _di_ICoreWebView2EnvironmentOptions();
m_WV2EnvOpt->put_AdditionalBrowserArguments(L"--disable-web-security");
It would fail in m_WV2EnvOpt->put_AdditionalBrowserArguments().
I have no idea and maybe I got the wrong way. Can someone help me?
_di_ICoreWebView2EnvironmentOptions is a typedef for DelphiInterface holding a ICoreWebView2EnvironmentOptions* pointer. You don't use new on DelphiInterface itself, you new a class that implements the interface, eg:
class TCoreWebView2EnvironmentOptionsImpl : public ICoreWebView2EnvironmentOptions
{
// implement IUnknown and ICoreWebView2EnvironmentOptions as needed...
};
_di_ICoreWebView2EnvironmentOptions m_WV2_EnvOpt = new TCoreWebView2EnvironmentOptionsImpl;
However, WRL objects are not used this way.
In this case, the WebView2 library exposes ICoreWebView2EnvironmentOptions as a COM object, so you can use CoCreateInstance() to instantiate it (the CLSID for the WebView2 library is 26D34152-879F-4065-BEA2-3DAA2CFADFB8, and the IID for ICoreWebView2EnvironmentOptions is 2FDE08A8-1E9A-4766-8C05-95A9CEB9D1C5), eg:
_di_ICoreWebView2EnvironmentOptions m_WV2_EnvOpt;
CoCreateInstance(LIBID_WebView2, NULL, CLSCTX_INPROC_SERVER, IID_ICoreWebView2EnvironmentOptions, (LPVOID*)&m_WV2_EnvOpt);
However, that doesn't help you in this situation, because you would have to provide the created ICoreWebView2EnvironmentOptions object when creating the WebView2 object, and AFAIK TEdgeBrowser simply does not allow you to do that.
You could try setting the WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS environment variable before the WebView2 object is created, but AFAIK this method does not support the --disable-web-security option.
Trying to use MS Speech API v11 with a Japanese engine (MS Haruka) in Delphi 10.3.
I have a sample app with a form and a button. The click handler goes:
uses SpeechLib11_TLB; // Imported from "Microsoft Speech Object Library" v.B.0
procedure TForm1.Button1Click(Sender: TObject);
var
v: ISpeechVoice;
begin
v := CoSpVoice.Create();
//5.4 only but won't hurt
v.Voice := v.GetVoices('language=411', '').Item(0);
v.Speak('時間', SVSFDefault);
end;
That causes an error, "Catastrophic failure" (HRESULT 0x8000FFFF, E_UNEXPECTED). The code that I think should be equivalent works in a C++ project:
#include <windows.h>
#import "libid:d3c4a7f2-7d27-4332-b41f-593d71e16db1" rename_namespace("SAPI") //v11
//#import "libid:C866CA3A-32F7-11D2-9602-00C04F8EE628" rename_namespace("SAPI") //v5.4
int wmain()
{
CoInitializeEx(0, COINIT_APARTMENTTHREADED);
{
SAPI::ISpeechVoicePtr v;
v.CreateInstance(__uuidof(SAPI::SpVoice));
//Needed for 5.4 only, but won't hurt
SAPI::ISpeechObjectTokensPtr voices(v->GetVoices(L"language=411", L""));
v->Voice = voices->Item(0);
v->Speak(L"時間", SAPI::SVSFDefault);
}
CoUninitialize();
return 0;
}
That works and speaks. So the SAPI per se is not broken on the machine. The platform is Win32 in both projects, not Win64. The Japanese voice is the default one (no need to set it explicitly).
Same result with SAPI 5.4 proper (not OneCore), although the Japanese voice is not the default one and I had to add a couple of lines to set it as the default.
Further debugging reveals that on the Delphi side, as much as calling the v.Voice property getter immediately after the first line causes the same error, E_UNEXPECTED. Meanwhile, the Voice setter works if you pass it a valid voice token object from GetVoices(). It looks as if the voice object initializes itself to its defaults correctly in C++, but somehow skips that in the Delphi project.
Requesting v.Voice right after construction works though in Delphi with SAPI 5.4. Calling Speak() still throws a E_UNEXPECTED.
What could be the difference in process/threadwide execution context between C++ and Delphi? It's not the thread locale. The COM thread model is apartment in both.
The same Delphi code works with an English phrase and an English voice (MS Helen). So whatever init failure there might be, it's probably specific to Haruka.
The SAPI 11 runtime is available here. The language data for TTS are here.
Another data point. I've rewritten the SAPI logic in Delphi to use SAPI 5.4 OneCore instead (not SAPI 5.4 proper). Unlike 5.4 and 11, it doesn't expose an IDispatch-based interface, and it's somewhat clumsier specifically in Delphi, but Japanese TTS works. The question, as initially posed, is still unanswered, but at least there's a workaround. I'll write up an answer, but I won't accept it.
However, it's not the custom vs. dual distinction that's to blame. I've changed the logic to use custom interfaces instead of automation ones with SAPI 5.4 proper (the typelib defines both), still got E_UNEXPECTED from Speak(). There's no error info.
Here's another beautiful data point: SAPI 5.4 TTS with automation based API works and talks as expected in a Delphi console app. So it's not even Delphi specific, it's somehow VCL specific. What is it with Delphi GUI? Needless to say, I immediately retested the C++ snippet in a C++ GUI application, with a form and a button. The C++ one talks.
Not an answer, but a workaround.
Windows 10 comes with two flavors of 32-bit SAPI. There's SAPI 5.4 proper (in system32\speech), and also SAPI 5.4 OneCore (in system32\speech_onecore). The latter, even though it's superficially the same, exposes a different typelib - there's no automation support, all interfaces are custom instead of dual. What's more important, when you download the Japanese TTS voice in the Windows 10 Settings app, you end up with 3 voices under OneCore (Sayaka, somehow, missing) and only one, Haruka, under 5.4 proper.
Delphi can consume custom interfaces in a typelib, but the methods look somewhat clumsier. Also, the enumeration of voices in the automation API is cleaner. Anyway, here goes.
uses SpeechLib54Core_TLB; // This time it's OneCore
procedure TForm1.Button1Click(Sender: TObject);
var
v: ISpVoice;
cat: ISpObjectTokenCategory;
toks: IEnumSpObjectTokens;
tok: ISpObjectToken;
sno: LongWord;
hr: HResult;
n: Cardinal;
begin
v := CoSpVoice.Create();
hr := v.GetVoice(tok);
tok.GetCategory(cat);
cat.EnumTokens('language=411', '', toks); //411 means Japanese
toks.GetCount(n);
if n = 0 then
exit; // No Japanese voices installed
toks.Item(0, tok); //Take the first one - typically Ayumi
v.SetVoice(tok);
v.Speak('時間', 0, sno);
end;
Note that passing a Japanese string literal to a COM method works without an explicit cast to a wide string.
I want my application to always run using the real gpu on nVidia Optimus laptops.
From "Enabling High Performance Graphics Rendering on Optimus Systems", (http://developer.download.nvidia.com/devzone/devcenter/gamegraphics/files/OptimusRenderingPolicies.pdf):
Global Variable NvOptimusEnablement (new in Driver Release 302)
Starting with the Release 302 drivers, application developers can
direct the Optimus driver at runtime to use the High Performance
Graphics to render any application–even those applications for which
there is no existing application profile. They can do this by
exporting a global variable named NvOptimusEnablement. The Optimus
driver looks for the existence and value of the export. Only the LSB
of the DWORD matters at this time. A value of 0x00000001 indicates
that rendering should be performed using High Performance Graphics. A
value of 0x00000000 indicates that this method should be ignored.
Example Usage:
extern "C" { _declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; }
The problem is that I want to do this using Delphi. From what I've read Delphi does not support export of variables even though some hacks exists. I did try a few of them but couldn't make it work.
In the same nvidia document I read that forcing the proper GPU can be accomplished via linking statically to one of a handful listed dlls. But I don't want to link to dlls I'm not using. (Why the opengl.dll is not one of them is beyond me.) A simple exported variable seems much cleaner.
From what I've read Delphi does not support export of variables.
That statement is incorrect. Here's the simplest example that shows how to export a global variable from a Delphi DLL:
library GlobalVarExport;
uses
Windows;
var
NvOptimusEnablement: DWORD;
exports
NvOptimusEnablement;
begin
NvOptimusEnablement := 1;
end.
I think your problem is that you wrote it like this:
library GlobalVarExport;
uses
Windows;
var
NvOptimusEnablement: DWORD=1;
exports
NvOptimusEnablement;
begin
end.
And that fails to compile with this error:
E2276 Identifier 'NvOptimusEnablement' cannot be exported
I don't understand why the compiler doesn't like the second version. It's probably a bug. But the workaround in the first version is just fine.
I'm not a Delphi expert, but AFAIK it is possible to link to static libraries implemented in C from Delphi. So I'd simply create a small stub library, just providing this export, which is statically linked into your Delphi program. This adds the very export you need.
I have this line of code in my Delphi app:
sh := CoShellWindows.Create;
When run through a Citrix session, this raises an exception "Not enough storage is available to complete this operation."
Can someone confirm my suspicion that I can't access this through Citrix? I'm running in Seamless mode if that makes any difference. Maybe there's something I need to change on the published icon to make it work?
I am guessing that there is no "Shell" in Citrix to create.
Thanks
EDIT
The CoShellWindows is simply a class which creates an object which implements the IShellWindows interface. This interface is then used to iterate through it's items looking for an instance of Internet Explorer (or more specifically, an item which implements the IWebBrowser2 interface).
There are a few other use case scenarios using the CoShellWindows, but all ultimately are used to interact with the IWebBrowser2 interface (Internet Explorer 8). My requirement is to obtain this IWebBrowser2 object.
The call, behind the scenes is calling the Windows API CoCreateInstance with the following parameters:
rclsid = {9BA05972-F6A8-11CF-A442-00A0C90A8F39} (CLSID of
IShellWindows)
pUnkOuter = null (nil)
dwClsContext = CLSCTX_ALL (I've tried various combinations of these
flags)
riid = {85CB6900-4D95-11CF-960C-0080C7F4EE85} (IID of IShellWindows)
ppv = a variable declared as type IShellWindows
eg:CoCreateInstance(CLASS_ShellWindows, nil, CLSCTX_ALL, IID_IShellWindows, sh)
Your exception "Not enough storage is available to complete this operation." should really read "Shell does not exist so no instance can be created"
Basically you are correct in your assumption that there is no shell to create in Citrix.
What are you using the shell for? as if you provide more information we may well be able to offer a full work around.
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.