I have a code exmple.
unit Unit1;
interface
uses
...
type
...
function Wow64EnableWow64FsRedirection(Wow64FsEnableRedirection: BOOL): BOOL; stdcall;
var
...
implementation
{$R *.dfm}
uses
...
function Wow64EnableWow64FsRedirection; external kernel32 name 'Wow64EnableWow64FsRedirection';
procedure LaunchOSK;
begin
Wow64EnableWow64FsRedirection(False);
try
ShellExecute(0, 'OPEN', PChar('osk.exe'), nil, nil, SW_SHOWNORMAL);
finally
Wow64EnableWow64FsRedirection(True);
end;
end;
end.
This code works fine in 64-bit OS. However, naturally when you try to use this in a 32-bit OS (XP) you get an error message (The procedure entry point Wow64Enable64FsRedirection could not be located in the dynamic link library kernel32.dll.). If you isolate Wow64EnableWow64FsRedirection and just run ShellExecute it launches fine.
I have beaten my head the past 2 days trying different things I have Googled and not one them of works as far as safely starting OSK in both 32 and 64 bit. Even a well known Delphi guru's suggestion does not work.
I do not need a lecture about disabling Wow64EnableWow64FsRedirection. Since it is temporary no harm no foul.
What I need is understanding how I can take this code and isolate the Wow64Enable64FsRedirection interface procedure and its accompanying implementation part and the necessary parts of the LaunchOSK procedure when the application is running on XP for example. Is this possible??? Compiler statements are useless, right?
If this is not possible then I will have to resort in producing 2 apps (one for XP and the other for 64 bit) which is a last resort.
Thanks in advance.
You can use dynamic loading and simply check whether the GetProcAddress function returns a valid function address.
Or you can use delayed loading and call the imported function only if the OS version and bitness matches necessary requirements.
Your code uses load time linking. The imported function must be present in the system or your executable will not start.
Instead you need to use run time linking. Use LoadLibrary and GetProcAddress to link to the function. If GetProcAddress fails to find the function then it isn't available and of course you don't need to call it because you are on a 32 bit OS.
The right way to do this though is to spawn a 64 bit process which can then launch the OSK process. And if ever you really do need to disable redirection do heed the documentation which tells you to use Wow64DisableWow64FsRedirection.
Finally it's always best to use CreateProcess to launch a process. There's nothing to be gained from involving the shell and file associations. Why ask the shell to call CreateProcess when you can do so directly?
OK, that's normally good advice, but once again, OSK is special. See Delphi - On Screen Keyboard (osk.exe) works on Win32 but fails on Win64.
Related
I´m writing a global keyboard hook in Delphi 2007 and Windows 10. I´ve found different signatures for a Delphi LowLevelKeyboardProc callback. Someones like this and windows documentation (https://learn.microsoft.com/es-es/previous-versions/windows/desktop/legacy/ms644985(v=vs.85))
LowLevelKeyboardProc() never is executed
prompt that lparam is a pointer to a TKBDLLHOOKSTRUCT record.
Other ones however (http://www.delphifaq.com/faq/delphi_windows_API/f512.shtml and https://www.swissdelphicenter.ch/en/showcode.php?id=1722), show
function KeyHookFunc(Code, VirtualKey, KeyStroke: Integer): LRESULT; stdcall;
completely ignoring the TKBDLLHOOKSTRUCT record pointer.
Which one is correct?
I'm guessing the first one is the correct one but I need to be sure. I´ve been using such signature but when I call another Function in the same unit (and DLL) passing lParam it causes Access Violation when accessing it. All functions and procedures within the DLL use stdcall
All of the callback declarations are correct, although the latter two links do not give examples of LowLevelKeyboardProc but KeyboardProc. IOW, they are not low level keyboard hooks.
Note the latter two wouldn't work in 64 bits due to Integer usage instead of WPARAM, LPARAM and LRESULT.
I need to set two environmental variables (when my application runs) for included 3rd party libraries.
The problem is that 'that way' it is not working, however
when I run console application, set these two variables and then run the application, everything is okay ...
how to configure these two vars correctly?
i use the procedure:
function SetEnvVarValue(const VarName,
VarValue: string): Integer;
begin
// Simply call API function
if SetEnvironmentVariable(PChar(VarName),
PChar(VarValue)) then
Result := 0
else
Result := GetLastError;
end;
It returns 0
Maybe the thing is, that i have the libraries being loaded on application startup.
When my application stars I set then the variables and I do it too late ...?
Further information
I have included two units in dpr:
ImageMagick in 'C:\Program Files (x86)\Borland\Delphi7\Lib\Magick\magick\ImageMagick.pas',
magick_wand in 'C:\Program Files (x86)\Borland\Delphi7\Lib\Magick\wand\magick_wand.pas';
And the Unit:
unit DoItFirst;
interface
uses
Windows, Sysutils;
var
s: string;
error: Integer;
function _putenv_s(const lpName, lpValue: PChar): BOOL; cdecl; external 'msvcrt.dll';
implementation
function GetEnvVarValue(const VarName: string): string;
var
BufSize: Integer; // buffer size required for value
begin
// Get required buffer size (inc. terminal #0)
BufSize := GetEnvironmentVariable(PChar(VarName), nil, 0);
if BufSize > 0 then
begin
// Read env var value into result string
SetLength(Result, BufSize - 1);
GetEnvironmentVariable(PChar(VarName),
PChar(Result), BufSize);
end
else
// No such environment variable
Result := '';
end;
initialization
_putenv_s(PChar('DYLD_LIBRARY_PATH'), PChar('g:\_projekty\ZBar Test\'));
_putenv_s(PChar('MAGICK_CODER_MODULE_PATH'), PChar('g:\_projekty\ZBar Test\modules\coders\'));
s := GetEnvVarValue('DYLD_LIBRARY_PATH');
s := GetEnvVarValue('MAGICK_CODER_MODULE_PATH');
end.
This unit is at the beginning of dpr file.
From what I can discern from your update, the third party library in question is ImageMagick. And the .pas wrappers for that library use load-time linking to the ImageMagick DLL.
When you modify the environment variables from a command interpreter, and then start your process, the ImageMagick DLL can see those environment variables. When you modify the environment variables in your process startup code, the the ImageMagick DLL cannot see those environment variables. Presumably because it has read the variables before your code modifies them.
What I would conclude from the above is that the ImageMagick DLL is reading the environment variables in its initialization.
Because you are using load-time linking, the DLL initialization happens before you have any opportunity to execute your code. I can think of the following ways to work around the issue:
Switch from load-time linking to run-time linking for the ImageMagick DLL. This will require you to modify the ImageMagick wrappers that you use. If you are unfamiliar with how to do this, then you might consult the JEDI source code for inspiration. Note that if you are using a modern Delphi, then you can simply modify the wrapper DLL to delay load the ImageMagick DLL. Add the delayed directive to the function declarations. This results in run time linking.
Move some of your code into a DLL so that you can load it with run-time linking. I'm imagining that you move any code that uses the ImageMagick wrapper into a DLL. That would allow you to keep on using the same wrapper, but still have the ImageMagick DLL loaded at process run-time rather than process load-time. You might even move your entire code into a DLL and then have an executable that did nothing more than load that DLL, and then call a single exported main function.
Use a separate launcher process. The launcher process prepares the environment, and then launches the real application.
Of these options, the first is by far the most preferable, in my opinion.
You may be changing the environment variables after they've been read by the third party libraries.
First up, you should set the environment variable as the first thing tour program does.
Even then, it may be that the third-party libraries could be reading that information in initialisation functions, possibly before your code even starts running.
If that's the case, I think the order of initialisation is deterministic (see here), depending on the order of your units in the dpr (project file).
If you want those variables set before the third-party libs look at them, you can create a DoMeFirst unit and do it in that unit's init code. Then make sure that's the first one in the project file.
If that doesn't work, another option may be to write a program which changes the environment and then calls your current program as a child.
in mine project i have a Windows application and a dll. I have wrote dll so:
library MyDLL;
uses
System.SysUtils,
System.Classes;
{$R *.res}
function Prova: string; export;
begin
result := 'prova';
end;
exports Prova;
begin
end.
and in main program i have called routine so:
unit FrmMain;
interface
uses
// declaration uses //
function Prova: string; external 'MyDLL.dll';
type
// declaration type //
implementation
begin
...
TAdvEdit1.Text := Prova; // [1] //
...
end;
end.
When i compile all project not is reported error, and status report SUCCESS, but application not start.
If i remove the line [1] then it works correctly. In general, application not start when i call the function Prova.
What i can solve this problem? Thanks very much.
The behaviour you describe is what happens when your application fails to load. When run from the debugger you get a silent failure. When run without a debugger you will see an error message, "Application failed to initialize...". This will give details.
In your case it seems that the likely cause is that the dependency of the DLL cannot be resolved by the library loader. That's why the app runs when the call to the external function is removed. When you remove that call, you also remove the dependency on the external DLL. Solve the problem by making sure the DLL can be loaded. For example place it in the same directory as the executable.
The silent failure from the debugger is rather frustrating. Once you have experienced it a few times you'll know what to do - run without the debugger to find out what's really going wrong.
I also recommend that you don't pass managed Delphi strings across module boundaries. That will force you to use the same compiler for both executable and DLL. If you are going to accept that constraint then you may as well use packages. As your code stands, it would need to use ShareMem to work. But I don't recommend that.
Your program and your DLL have separate memory manager. As a general rule memory allocated from DLL should not be used inside your application (the opposite is also true).
Where the allocation comes from? In Delphi "string" is managed type i.e. when you assign some text to a string variable (in your case result := 'prova'), Delphi (behind the scene) allocates memory for that string using DLL's memory manager. Then, for example, if you assign other text value within your main application, the reallocation uses application's memory manager which is bad i.e. app's MM is touching memory that it hasn't allocated itself.
To solve this issue you have to include "SimpleShareMem" (Delphi >= 2010 IIRC?) unit as the first unit of your USES clause in the application (.dpr file) AND in the DLL:
library MyDLL;
uses
SimpleShareMem, // **MUST BE THE FIRST UNIT**
System.SysUtils,
System.Classes;
...
program YourApp;
uses
SimpleShareMem, // **MUST BE THE FIRST UNIT**
// declaration uses //
An example of this approach can be found in "Start > Programs > Embarcadero RAD Studio > Samples > Delphi > RTL > SimpleShareMem"
You can also use PCHAR to transfers strings between DLL and the APP.
how to popup this form use delphi ?
is ShellExecuteEx or ShellExecute can do this?
From the command line you can do this. (at least, on Win7 for me...()
rundll32.exe van.dll,RunVAN
So, just wrap that in a suitable ShellExecute or similar call.
Note that the dialog is designed to pop up in the system tray. I don't know how you'd get it to appear somewhere more obvious.
Also, have a look at this thread. There's another way mentioned here that describes how to do it in a possibly more useful fashion:
https://groups.google.com/forum/?hl=en&fromgroups=#!topic/microsoft.public.development.device.drivers/nPn-PH3g_2Q
If you want to invoke this from your program, it's simpler just to skip the rundll32 call. You can load the DLL yourself and invoke the function. For example:
procedure RunVANW(hwnd: HWND; hinst: HINST; lpszCmdLine: LPSTR;
nCmdShow: Integer); stdcall; external 'van.dll';
procedure ShowViewAvailableNetworksDialog;
begin
RunVANW(0, 0, nil, 0);
end;
I would expect that this functionality is not available on older versions of Windows, and almost certainly will be modified on future versions of Windows. So you may prefer to write the DLL import using LoadLibrary and GetProcAddress, and switch behaviour at runtime depending on whether or not the RunVANW function is available.
I have just started working with RemObjects Pascal Script. and have been trying to follow the remobjects tutorial.
http://devcenter.remobjects.com/articles/?id={2FFC1EE9-F18D-4B11-9DE4-1BA0A79D0D04}
all was going fine up to the part you run
begin
ShowNewMessage('Show This !');
end.
where it claimed it does not know of it.
but i have it here
procedure Tmainwindow.ceCompile(Sender: TPSScript);
begin
Sender.AddMethod(Self, #Tmainwindow.ShowNewMessage,
'procedure ShowNewMessage(const Message: string);');
end;
procedure ShowNewMessage(const Message: string);
procedure Tmainwindow.ShowNewMessage(const Message: string);
begin
//ShowMessage('ShowNewMessage invoked:'#13#10+Message);
end;
added on the compile event as instructed... it all compiles in delphi but when i run the code from within my executable it says it dont exist.
secondly if i add any plugins to improve the function calls of the script i get this..
please help i realise i may be doing something silly here im new to rem objects.
Well, I tried building the example as shown on that page, and it compiled and ran correctly for me. Try using the example shown at the top of the page, under "The following code will compile and...". Just make sure to leave out the line that replaces the script text.
As for the plugins, it can't register your event types because they refer to object classes that haven't been registered yet. Unfortunately, the PS Plugin system doesn't have any way of automatically resolving dependencies, and the compiler's error message doesn't tell you which type it couldn't find. You'll need the debugger to help you resolve this. But a lot of the basics, including TObject (yes, you have to import it explicitly) are found in TPSImport_Classes.
i have the same Problem. That has nothing to do with the syntax, only with the inclusion of the Forms-Unit template for the script compiler.
Sry, i do not have a solution for that problem, because it even occurs when removing the OnMenuDrawItem and OnMenuAdvancedDrawItem events (which both make Problems).
I use BDS 2006, that might be the problem as it uses advanced Forms source code in comparison to what D7 used (which was the version RO PS was actually made for).
So, remove the Forms unit plugin for the compiler, which also includes the menus unit and try it again, that should "solve" your problem.