IDEs drive me nuts, including the Delphi IDE. Searching for Margin or Setting only reveals the margin and gutter setting, not the formatter settings.
So you have to remember this is in the “Formatter”, especially the “Line Breaks” node:
–jeroen
Posted by jpluimers on 2021/06/17
IDEs drive me nuts, including the Delphi IDE. Searching for Margin or Setting only reveals the margin and gutter setting, not the formatter settings.
So you have to remember this is in the “Formatter”, especially the “Line Breaks” node:
–jeroen
Posted in Delphi, Development, Software Development | Leave a Comment »
Posted by jpluimers on 2021/06/16
Do not call SYS.DBMS_SESSION.SET_NLS with an instance of the [Archive.is] TOraStoredProc Class, as under the hood, it will translate the call to this:
Class:Ora.TOraStoredProc;Component:sprSetNls;Flag:tfQPrepare,Text:Prepare: begin SYS.DBMS_SESSION.SET_NLS(:PARAM, :VALUE); end; :PARAM(VARCHAR[22],IN)='NLS_NUMERIC_CHARACTERS' :VALUE(VARCHAR[4],IN)=''.,''
The above is a translation of the bold portions in this call (note it contains the an instantiation of an [Archive.is] TOraSession Class as you need one; examples further down assume this session instance to exist):
var MainOraSession: TOraSession; DbmsSessionSetNlsOraStoredProc: TOraStoredProc; begin MainOraSession := TOraSession.Create(Self); try MainOraSession.Name := 'MainOraSession'; MainOraSession.Username := 'FOO'; MainOraSession.Server := 'BAR'; MainOraSession.LoginPrompt := False; MainOraSession.Options.UseOCI7 := True; MainOraSession.Open(); DbmsSessionSetNlsOraStoredProc := TOraStoredProc.Create(Self); try DbmsSessionSetNlsOraStoredProc.Name := 'DbmsSessionSetNlsOraStoredProc'; DbmsSessionSetNlsOraStoredProc.StoredProcName := 'SYS.DBMS_SESSION.SET_NLS'; DbmsSessionSetNlsOraStoredProc.Session := MainOraSession; DbmsSessionSetNlsOraStoredProc.Debug := True; with DbmsSessionSetNlsOraStoredProc.ParamData.Add do begin DataType := ftString; Name := 'PARAM'; ParamType := ptInput; Value := nil; end; with DbmsSessionSetNlsOraStoredProc.ParamData.Add do begin DataType := ftString; Name := 'VALUE'; ParamType := ptInput; Value := nil; end; DbmsSessionSetNlsOraStoredProc.ParamByName('PARAM').AsString := sParam; DbmsSessionSetNlsOraStoredProc.ParamByName('VALUE').AsString := sValue; DbmsSessionSetNlsOraStoredProc.Prepare(); DbmsSessionSetNlsOraStoredProc.ExecProc(); finally DbmsSessionSetNlsOraStoredProc.Free(); end; finally MainOraSession(); end; end;
It will result in an Oracle error during the Prepare of the statement:
ORA-06550: line 2, column 36: PLS-00103: Encountered the symbol ":" when expecting one of the following: ( - + case mod new not null continue avg count current exists max min prior sql stddev sum variance execute forall merge time timestamp interval date pipe
In stead, take your TOraPackage object and make a call like this:
var DbmsSessionOraPackage: TOraPackage; begin DbmsSessionOraPackage := TOraPackage.Create(Self); try DbmsSessionOraPackage.Name := 'DbmsSessionOraPackage'; DbmsSessionOraPackage.Debug := True; DbmsSessionOraPackage.Session := dbSession; DbmsSessionOraPackage.PackageName := 'SYS.DBMS_SESSION'; DbmsSessionOraPackage.ExecProcEx('SET_NLS', ['PARAM', 'NLS_NUMERIC_CHARACTERS', 'VALUE', '''.,''']); finally DbmsSessionOraPackage.Free(); end; end;
This then results in this in the SQL monitoring (note quoting quotes is different in SQL than Delphi):
Class:Ora.TOraSQL;Component:;Flag:tfQExecute,Text:begin SYS.DBMS_SESSION.SET_NLS(:PARAM, :VALUE); end; :PARAM(VARCHAR[22],IN)='NLS_NUMERIC_CHARACTERS' :VALUE(VARCHAR[4],IN)=''.,''
instead of this:
Class:Ora.TOraStoredProc;Component:sprSetNls;Flag:tfQPrepare,Text:Prepare: begin SYS.DBMS_SESSION.SET_NLS(:PARAM, :VALUE); end; :PARAM(VARCHAR[22],IN)='NLS_NUMERIC_CHARACTERS' :VALUE(VARCHAR[4],IN)=''.,''
I am still a sort of baffled why this is a problem. But using the TOraPackage works.
One thing to remember is that an
TOraSessioninstance does not allow you to get to the underlyingTOCIConnectioninstance, which does allow setting NLS information directly; see for instance the old code at [WayBack] OraClasses.pas in xinhaining-dianjianyiqi-tongxunchengxu | source code search engine.This is because the underlying connection can be both
OCIandDirectdepending on theTOraSession.Options.Directvalue: [WayBack] About Connection.Ping method when Direct Mode – Devart Forums.
The odd thing is that single-parameter calls on SYS.DBMS_SESSION.SET_ROLE (which can be tricky, see [WayBack] Introducing Database Security for Application Developers) work fine, so no alternative (like a plain [WayBack] SET ROLE) is needed:
var DbmsSessionSetRoleOraStoredProc: TOraStoredProc; begin DbmsSessionSetRoleOraStoredProc := TOraStoredProc.Create(Self); try DbmsSessionSetRoleOraStoredProc.Name := 'DbmsSessionSetRoleOraStoredProc'; DbmsSessionSetRoleOraStoredProc.StoredProcName := 'SYS.DBMS_SESSION.SET_ROLE'; DbmsSessionSetRoleOraStoredProc.Session := dbSession; with DbmsSessionSetRoleOraStoredProc.ParamData.Add do begin DataType := ftString; Name := 'ROLE_CMD'; ParamType := ptInput; Value := 'EXAMPLE'; end; DbmsSessionSetRoleOraStoredProc.ParamByName('ROLE_CMD').AsString := 'EXAMPLEROLE'; DbmsSessionSetRoleOraStoredProc.Prepare; DbmsSessionSetRoleOraStoredProc.ExecProc; finally DbmsSessionSetRoleOraStoredProc.Free(); end; end;
results in this log:
Class:Ora.TOraStoredProc;Component:StrdPSetRole;Flag:tfQExecute,Text:begin SYS.DBMS_SESSION.SET_ROLE(:ROLE_CMD); end; :ROLE_CMD(VARCHAR[10],IN)='EXAMPLEROLE'
Calling anything on DBMS_APPLICATION_INFO gives you an exception.
On the .NET side of DevArt, you can use
–jeroen
Posted in Database Development, Delphi, Development, OracleDB, Software Development | Leave a Comment »
Posted by jpluimers on 2021/06/16
I got this when taking over maintenance of some legacy code that called a stored procedure in a :
--------------------------- Debugger Exception Notification --------------------------- Project LegacyProject.exe raised exception class Exception with message 'Need Oracle 8 Call Interface'. --------------------------- Break Continue Help ---------------------------
The problem with finding more information about this, is that there are virtual no hits on Oracle 8 Call Interface, OCI 8 or Oracle Call Interface 8.
A good document is [WayBack] OCI: Introduction and Upgrading (found via [Archive.is] “OCI 7” – Google Search), but it never mentions 8, only these terms occur:
The error happens on the first instance of at least an [Archive.is] TOraStoredProc Class or [Archive.is] TOraPackage Class that prepares or executes a stored procedure.
One problem is that this is an instance of a plain [WayBack] Exception Class , not a [WayBack] EDatabaseError Class or [Archive.is] EDAError Class.
What it means is that the UseOCI7 sub-property was set to True in your TOraSession property Options:
- [Archive.is] TOraSession Class
- [Archive.is] TOraSession.Options Property
- [Archive.is] TOraSessionOptions.UseOCI7 Property
var MainOraSession: TOraSession; begin MainOraSession := TOraSession.Create(Self); MainOraSession.Name := 'MainOraSession'; MainOraSession.Username := 'ExammpleUser'; MainOraSession.Server := 'ExampleTnsName'; MainOraSession.LoginPrompt := False; MainOraSession.Options.UseOCI7 := True;One thing to remember is that an
TOraSessioninstance does not allow you to get to the underlyingTOCIConnectioninstance, which does allow setting NLS information directly; see for instance the error at [WayBack] Not connected to oracle error – Devart Forums and old code at [WayBack] OraClasses.pas … | source code search engine and [WayBack] Ora.pas in … | source code search engine; UniDac is relatively similar [WayBack] UniDAC v3.7 …:OraClassesUni.pas (another explanation is in [WayBack] Setting the Client Info for the TOraAlerter extra session – Devart Forums).This is because the underlying connection can be both
OCIandDirectdepending on theTOraSession.Options.Directvalue: [WayBack] About Connection.Ping method when Direct Mode – Devart Forums.
After the first occurrence, you will not get this error again, you get way more spurious errors, especially on stored procedures in packages, where the stored procedure has 2 or more parameters. There you get errors like this one for TOraStoredProc:
ORA-06550: line 2, column 36: PLS-00103: Encountered the symbol ":" when expecting one of the following: ( - + case mod new not null <an identifier> <a double-quoted delimited-identifier> <a bind variable> continue avg count current exists max min prior sql stddev sum variance execute forall merge time timestamp interval date <a string literal with character set specification> <a number> <a single-quoted SQL string> pipe <an alternatively-quoted string literal with character set speci.
When logging using a [Archive.is] TOraSQLMonitor Class instance (use the [Archive.is] TCustomDASQLMonitor.OnSQL Event with the [Archive.is] TOnSQLEvent Procedure Reference signature) you can get the underlying information of the TOraStoredProc (see the example code below that raises this):
Class:Ora.TOraStoredProc;Component:sprSetNls;Flag:tfQPrepare,Text:Prepare: begin SYS.DBMS_SESSION.SET_NLS(:PARAM, :VALUE); end; :PARAM(VARCHAR[22],IN)='NLS_NUMERIC_CHARACTERS' :VALUE(VARCHAR[4],IN)=''.,''
It fails equally when doing any DBMS_APPLICATION_INFO calls, for which the .NET version of ODAC has a special [WayBack] SetDbmsApplicationInfo Method in the [WayBack] OracleConnection Class. Too bad the Delphi version omits that.
When using TOraPackage, you can get different errors, like for instance this EDatabaseError:
Exception class EDatabaseError with message 'Parameter 'VALUE' not found'.
It basically means that through OCI, the parameter list could not be obtained, so the error is caught on the Delphi side instead of the Oracle side.
Using a TOraPackage is easier at design time, as it allows you to select the [Archive.is] TOraPackage.PackageName Property property using the underlying TOraSession. Somehow a TOraStoredProc does not allow you to select the [Archive.is] TOraStoredProc.StoredProcName Property at design time.
Related:
–jeroen
Posted in Database Development, Delphi, Development, OracleDB, Software Development | Leave a Comment »
Posted by jpluimers on 2021/06/15
Since I keep forgetting about this, and there is the Embarcadero documentation for it has had no examples added in Delphi 2007 or later: [WayBack] Delphi Basics : FindCmdLineSwitch command
Example code : Saerch the program parameters for 3 switches begin
// Before running this code, use the Run/parameters menu option
// to set the following command line parameters : /def abc /ghi
ShowMessage(CmdLine); // Show the execution command + parameters
// How many parameters were passed?
ShowMessage(‘There are ‘+IntToStr(ParamCount)+’ parameters’);
// Scan for parm1, parm2 and parm3 parameters
if FindCmdLineSwitch(‘abc’)
then ShowMessage(‘abc found’)
else ShowMessage(‘abc NOT found’);
if FindCmdLineSwitch(‘def’)
then ShowMessage(‘def found’)
else ShowMessage(‘def NOT found’);
if FindCmdLineSwitch(‘ghi’)
then ShowMessage(‘ghi found’)
else ShowMessage(‘ghi NOT found’);
end;
Show full unit code “C:\Program files\Borland\Delphi7\Projects\Project1.exe” /def abc /ghi
There are 3 parameters
abc NOT found
def found
ghi found
Related: [WayBack] delphi – How to read value of ParamStr with different deliminators? – Stack Overflow
Unchanged Embarcadero Delphi 2007 documentation:
For comparison, the Delphi 10.2 Rio documentation:
FindCmdLineSwitch determines whether a string represents a command-line switch, based on the first character of the string.
Switchis a command-line parameter to search for.Charsis the set of characters that distinguish switches from other parameters. IfSwitchis omitted, it defaults to ‘-‘ on Unix based systems, and to ‘/’ and ‘-‘ on Windows based systems.IgnoreCasedetermines whether case-insensitive character matching is used. The default is True. This function can also be used to retrieve parameter values in addition to finding command-line switches, for that overload.Valuereturns the value associated with the specified switch.SwitchTypesdetermines how parameter values are parsed:
Constant Value clstValueNextParam -p ValueclstValueAppended -p Valueor -p:ValueThe
clstValueNextParamparameter specifies that values are separated from the switch by a space character. TheclstValueAppendedparameter specifies that the values are appended immediately, following the switch (no space) or after a colon.SwitchTypesis a set type parameter and the default is to allow values of both parameter types to be returned–[clstValueNextParam, clstValueAppended]. Any single switch type may be specified as well. An emptySwitchTypeparameter, [], will result in no values being returned. Pass theSwitchTypesparameter to exclude any of these switch types (Switchmay be 1 or more characters in length.)
The really odd thing? The really versatile TCommandParser class got ditched after Delphi XE6: On the Delphi TCommandParser class for parsing command-lines and arguments (via: Suggestions for how to define command line parameters – Stack Overflow).
–jeroen
Posted in Delphi, Development, Software Development | Leave a Comment »
Posted by jpluimers on 2021/06/10
I have been bitten by this a few times too much, so time to write it down:
When porting old Delphi code from the Delphi 7 and older era, the options were stored .dof (options) and .cfg (configuration) files.
More modern Delphi versions try to translate these files when generating the .dproj file, in a similar way as they try to upgrade older .dproj files to newer formats.
Many things can go wrong, including these ones I have bumped into multiple times:
DCC_DebugInformation having a 0 or false value, but not handling these values correctly (I remember problems around the Delphi XE5 era with this: When the Delphi XE5 commandline compiler fails with error F1026: File not found: ‘False.dpr’)DCC_OutputDependencies not being adhered to (no .d file is being emitted by the compiler), though for the .drc files, DCC_OutputDRCFile works fine)DCC_DebugInformation having a 1 value (Limited Debug information) instead of being absent (Debug information)DCC_SymbolReferenceInfo having a 1 value (Definitions Only) instead of being absent (Reference info)–jeroen
Posted in Delphi, Development, Software Development | 1 Comment »
Posted by jpluimers on 2021/06/09
For cleanup of .dproj files, I want to know the bit flags that TargetPlatforms can have.
For future reference empiric values for the flags that build the TargetedPlatforms (not to be confused with PlatformTargets) element content in the main PropertyGroup of a .dproj file in a table.
This might help creating an XSD for a .dproj file (Source: Reminder to self: make a start for an XSD that validates Delphi dproj files).
Absent cells means I have no idea if the values are relevant or what they could be.
Input for those is more than welcome.
| Bit# | TargetedPlatforms bit flag value |
Platform and $(Platform)value |
Meaning (dropdown value of “Select Platform” dialog) |
|---|---|---|---|
| 0 | 1 |
Win32 |
32-bit Windows |
| 1 | 2 |
Win64 |
64-bit Windows |
| 2 | 4 | ||
| 3 | 8 | ||
| 4 | 16 | ||
| 5 | 32 | ||
| 6 | 64 | ||
| 7 | 128 | ||
| 8 | 256 | ||
| 9 | 512 | ||
| 10 | 1024 |
iOSDevice64 |
iOS Device 64-bit |
| 11 | 2048 | ||
| 12 | 4096 |
($Platform) values still to cover:
AndroidLinux64OSX32iOSDevice32iOSSimulatorThere is only one place for TargetedPlatforms in the .dproj file: at the XPath /Project/PropertyGroup/TargetedPlatforms.
For getting the XPath, I used Notepad++ as described in my earlier blog post Getting the path of an XML node in your code editor.
It has the combined flags, so:
3 means Win32 and Win64 are enabled1025 means Win32 and iOSDevice64 are enabledThe Platform value (and thus $(Platform) value) is the one used in for example these elements or attributes:
/Project/PropertyGroup/Platform as currently selected platform/Project/PropertyGroup/@Condition as selectivity expression, for instance
<PropertyGroup Condition="('$(Platform)'=='Win64' and '$(Cfg_1)'=='true') or '$(Cfg_1_Win64)'!=''"><PropertyGroup Condition="('$(Platform)'=='Win64' and '$(Base)'=='true') or '$(Base_Win64)'!=''">/Project/ProjectExtensions/BorlandProject/Platforms/Platform (all having for the value property with the Platform value) having a content of either True or False.
.dproj file to contain information for platforms that are not visible yet.The actual values of Platform also play a role in these places:
/Project/PropertyGroup/Base_Win64 containing the base settings for the Win64 platform so they can be derived for the Debug or Release builds./Project/PropertyGroup/@Condition for instance <PropertyGroup Condition="'$(Base_Win64)'!=''">/Project/ProjectExtensions/BorlandProject/Deployment/DeployFile/Platform/@Name for instance <Platform Name="iOSSimulator">Even worse: there are unneeded nodes present for bits in TargetPlatforms and /Project/ProjectExtensions/BorlandProject/Platforms/Platform being absent or having a content False for /Project/ProjectExtensions/BorlandProject/Platforms/Platform/@value other than the enabled bits in TargetPlatforms, for instance:
/Project/ProjectExtensions/BorlandProject/Deployment/DeployFile/Platform/Project/ProjectExtensions/BorlandProject/Deployment/ProjectRoot/@Platform/Project/ProjectExtensions/BorlandProject/Deployment/DeployClass/Platform/@Name (and the parent DeployClass subtrees)/Project/ProjectExtensions/BorlandProject/Platforms/Platform@valueSome examples of superfluous nodes when TargetPlarforms has a value of 1 (corresponding to Platform having a value of Win32:
<ProjectRoot Platform="Android" Name="$(PROJECTNAME)"/> <ProjectRoot Platform="Linux64" Name="$(PROJECTNAME)"/> <ProjectRoot Platform="OSX32" Name="$(PROJECTNAME).app"/> <ProjectRoot Platform="Win32" Name="$(PROJECTNAME)"/> <ProjectRoot Platform="Win64" Name="$(PROJECTNAME)"/> <ProjectRoot Platform="iOSDevice32" Name="$(PROJECTNAME).app"/> <ProjectRoot Platform="iOSDevice64" Name="$(PROJECTNAME).app"/> <ProjectRoot Platform="iOSSimulator" Name="$(PROJECTNAME).app"/>
Also, non relevant platforms are included in this node:
<Platforms> <Platform value="Win32">True</Platform> <Platform value="Win64">False</Platform> </Platforms>
The Deployment section is even worse; see for instance [WayBack] delphi – How manage or clean deploy section in dproj files? – Stack Overflow.
–jeroen
Posted in Conference Topics, Conferences, Delphi, Development, Event, Software Development | 1 Comment »
Posted by jpluimers on 2021/06/08
Too bad the whole mechanism involving TRegGroups.UnregisterModuleClasses is not documented anywhere
It is the underlying storage to support TClassFinder, which was introduced in Delphi 6, documented on-line in Delphi 2007, documented slightly in Delphi 2009, and since Delphi 2010 only one line of documentation was added (including the unchanged “instatiated”):
This is class
Classes.TClassFinder.
The
TClassFinderallows the list of registered persistent classes to be retrieved. Objects instatiated from persistent classes are those that can be stored (serialised) beyond the operation of the current application.
TClassFinderallows registered persistent classes to be retrieved.The
TClassFinderallows the list of registered persistent classes to be retrieved. Objects instatiated from persistent classes are those that can be stored (serialised) beyond the operation of the current application.
Back to TRegGroups.UnregisterModuleClasses: it takes a HMODULE parameter and ultimately gets called through the (since Delphi 2007) on-line documented [WayBack] Classes.UnRegisterModuleClasses Function
procedure UnRegisterModuleClasses(Module: HMODULE);Call UnRegisterModuleClasses to unregister all object classes that were registered by the module with the handle specified by the Module parameter. When a class is unregistered, it can’t be loaded or saved by the component streaming system.
After unregistering a class, its name can be reused to register another object class.
To get more context about the access violation, I used both the stack trace and a debugging watch for GetModuleName(Module) (using the [WayBack] SysUtils.GetModuleName Function).
In order to see which classes were registered by what module, I set a breakpoint at in TRegGroup.AddClass (which can be called through various code paths):
procedure TRegGroup.AddClass(AClass: TPersistentClass); begin FGroupClasses.Add(AClass); end;
HModuleThat gave me the class, but I also needed the HModule for a class, so I did a windows get module of currently executing code – Google Search, giving me these links, all C/C++ related:
I currently use the following trick (inspired from this forum):
const HMODULE GetCurrentModule() { MEMORY_BASIC_INFORMATION mbi = {0}; ::VirtualQuery( GetCurrentModule, &mbi, sizeof(mbi) ); return reinterpret_cast<HMODULE>(mbi.AllocationBase); }Is there a better way to do this that doesn’t look so hacky?
If you are using a Microsoft linker, you can take advantage of
a pseudovariable which the linker provides.EXTERN_C IMAGE_DOS_HEADER __ImageBase; #define HINST_THISCOMPONENT ((HINSTANCE)&__ImageBase)The pseudovariable
__ImageBase
represents the DOS header of the module, which happens to
be what a Win32 module begins with.
In other words, it’s the base address of the module.
And the module base address is the same as itsHINSTANCE.
Here you already see some confusion: there is HINSTANCE and HMODULE. That’s a historic thing, as described by Raymond Chen in [WayBack] What is the difference between HINSTANCE and HMODULE? | The Old New Thing:
They mean the same thing today, but at one time they were quite different.
It all comes from 16-bit Windows.
In those days, a “module” represented a file on disk that had been loaded into memory, and the module “handle” was a handle to a data structure that described the parts of the file, where they come from, and where they had been loaded into memory (if at all). On the other hand an “instance” represented a “set of variables”.
One analogy that might (or might not) make sense is that a “module” is like the code for a C++ class – it describes how to construct an object, it implements the methods, it describes how the objects of the class behave. On the other hand, an “instance” is like a C++ object that belongs to that class – it describes the state of a particular instance of that object.
In C# terms, a “module” is like a “type” and an instance is like an “object”. (Except that modules don’t have things like “static members”, but it was a weak analogy anyway.)
GetModuleNameSearching for delphi “__ImageBase” – Google Search then got me [WayBack] c++ – Get DLL path at runtime – Stack Overflow with a nice Delphi related answer by [WayBack] Ian Boyd:
For Delphi users:
SysUtils.GetModuleName(hInstance); //Works; hInstance is a special global variable SysUtils.GetModuleName(0); //Fails; returns the name of the host exe process SysUtils.GetModuleName(GetModuleFilename(nil)); //Fails; returns the name of the host exe processIn case your Delphi doesn’t have SysUtils.GetModuleName, it is declared as:
...
This reassured my use of [WayBack] SysUtils.GetModuleName code was OK:
function GetModuleName(Module: HMODULE): string; var ModName: array[0..MAX_PATH] of Char; begin SetString(Result, ModName, GetModuleFileName(Module, ModName, Length(ModName))); end;
HInstance in DelphiThe example from Ian Boyd also brought back memories from long ago about the [WayBack] HInstance Variable – Delphi in a Nutshell [Book]:
Posted in Conference Topics, Conferences, Delphi, Development, Event, History, Software Development | Leave a Comment »
Posted by jpluimers on 2021/06/03
I wnder who maintains [WayBack] GExperts · GitHub:
GExperts has 4 repositories available. Follow their code on GitHub.
It has not been updated for about 5 years, but contains some important bits.
–jeroen
Posted in Delphi, Development, GExperts, Software Development | 1 Comment »
Posted by jpluimers on 2021/06/02
A long time ago, I wrote about Delphi prebuild/prelink/postbuild events.
In practice, especially that you cannot inherit build events, then add new ones (), it is convenient to put all you need in a script, for instance a batch file.
Passing parameters to that script, then in my experience, is easiest to do as environment variables.
That way you can do this in a script:
> %temp%\build.txt echo ~dp0=%~dp0 >> %temp%\build.txt echo $(BDS)=%BDS% >> %temp%\build.txt echo $(INPUTDIR)=%INPUTDIR% >> %temp%\build.txt echo $(PROJECTDIR)=%PROJECTDIR% >> %temp%\build.txt echo $(OUTPUTDIR)=%OUTPUTDIR% >> %temp%\build.txt echo $(Platform)=%Platform% if "%OUTPUTDIR:~0,2%"==".." set OUTPUTDIR=%INPUTDIR%%OUTPUTDIR% >> %temp%\build.txt echo $(OUTPUTDIR)=%OUTPUTDIR%
Passing the parameters and calling the script results in this build event contents:
set Platform=$(Platform) set INPUTDIR=$(INPUTDIR) set PROJECTDIR=$(PROJECTDIR) set OUTPUTDIR=$(OUTPUTDIR) call ..\post-build-event.bat
The IDE then shows this fragment in the project output:
Target PostBuildEvent: set Platform=Win32&&set INPUTDIR=D:\Versioned\TestRepostitory\Test Project With Spaces\&&set PROJECTDIR=D:\Versioned\TestRepostitory\Test Project With Spaces&&set OUTPUTDIR=..\EXE Output Directory With Spaces\&&call ..\post-build-event.bat
The above batch file also shows things:
OUTPUTDIR build parameter: check if the first two characters are .. (trick via [WayBack] Check if Batch variable starts with “…” – Stack Overflow).set assignments&& success operatorINPUTDIR and OUTPUTDIR end with a backslash, but PROJECTDIR does not (Delphi prebuild/prelink/postbuild events explains more about the parameters).Note:
BDS does not end in a backslash%~dp0 does end in a backslashWith this trick now you can copy BPL files for the right platform:
xcopy /y "%BDS%\Redist\%Platform%\rtl250.bpl" "%OUTPUTDIR%"
Note that the LibSuffix (with value 250 for Delphi 10.2 Tokyo) is not available as environment variable, nor build parameter. The environment variable ProductVersion however (with value 19.0 for Delphi 10.2 Tokyo) is. So technically, you could make a mapping.
–jeroen
Posted in Conference Topics, Conferences, Delphi, Development, Event, Software Development | Leave a Comment »
Posted by jpluimers on 2021/06/01
In addition to [WayBack] c++ – DLL Load Library – Error Code 126 – Stack Overflow while debugging LoadLibrary error 126
Windows dll error 126 can have many root causes. The most useful methods I have found to debug this are:
- Use dependency walker to look for any obvious problems (which you have already done)
- Use the sysinternals utility [WayBack] Process Monitor – Windows Sysinternals | Microsoft Docs from Microsoft to trace all file access while your dll is trying to load. With this utility, you will see everything that that dll is trying to pull in and usually the problem can be determined from there.
Search for the first entry NAME NOT FOUND after your library is being loaded.
It indicates the module that cannot be found which indirectly causes error number 126 (ERROR_MOD_NOT_FOUND in [WayBack] System Error Codes (0-499) – Windows applications | Microsoft Docs ) in [WayBack] LoadLibrary:
ERROR_MOD_NOT_FOUND
- 126 (0x7E)
The specified module could not be found.
This is why I upvoted the very relevant comment:
… when I looked at the rows surrounding my dll being loaded I saw MSVCP140D.dll was giving a result of
NAME NOT FOUND. Turns out the machine that couldn’t load my dll doesn’t have the ‘D’ version of MSVCP140.dll. Everything worked when I built my dll for release! – [WayBack] Pakman.
–jeroen
Posted in Delphi, Development, Software Development, Windows Development | Leave a Comment »