Pascal: Internal variables in DLL - variables

For example in Pascal, if I had a library that I'm compiling to DLL:
library Blah;
procedure AddToNum;
begin
Num := Num + 1;
end;
procedure PrintNum;
begin
WriteLN(Num);
end;
Exports AddToNum;
Exports PrintNum;
var
Num: Integer;
begin
Num := 0
end.
Ideally, the user could just call the AddToNum and PrintNum routines and it would do as such. However, you actually have to pass in arguments, and that means the user has to keep track of ALL of the variables I'd be using. What is the ideal method to do this? Pointers or something?
I'm looking for the variable to be the same between functions calls, much like some sort of "global"

Move your DLL code (the actual code that runs) into a separate unit (for instance, DLLCode.pas), declare the variable at the top of the implementation section, and have your .DPR file just use that unit. All of the actual code goes in DLLCode.pas, and visibility of the variable follows the normal Pascal scoping rules.
Here's a sample DLL (DLLSample.dpr and DLLCode.pas), and a test console application that uses it. All of the code compiles and properly executes under Delphi 2007 and Windows 7 64-bit.
DllSample.dpr:
library DLLSample;
{ Important note about DLL memory management: ShareMem must be the
first unit in your library's USES clause AND your project's (select
Project-View Source) USES clause if your DLL exports any procedures or
functions that pass strings as parameters or function results. This
applies to all strings passed to and from your DLL--even those that
are nested in records and classes. ShareMem is the interface unit to
the BORLNDMM.DLL shared memory manager, which must be deployed along
with your DLL. To avoid using BORLNDMM.DLL, pass string information
using PChar or ShortString parameters. }
uses
SysUtils,
Classes,
DLLCode in 'DLLCode.pas';
{$R *.res}
begin
end.
DllCode.pas:
unit DLLCode;
interface
procedure AddToNum; stdcall;
procedure PrintNum; stdcall;
exports
AddToNum,
PrintNum;
implementation
// Num is only visible from here down, and starts with a value of zero when the
// DLL is first loaded. It keeps it's value until the DLL is unloaded, which is
// typically when your app is exited when using static linking, or when you
// FreeLibrary() when dynamically linking.
var
Num: Integer = 0;
procedure AddToNum;
begin
Inc(Num); // Same as Num := Num + 1;
end;
procedure PrintNum;
begin
WriteLn(Num);
end;
end.
DllTestApp.dpr:
program DLLTestApp;
{$APPTYPE CONSOLE}
uses
SysUtils;
// Links to the procedures in the DLL. Note that the DLL has to be
// in the same folder, or located somewhere on the Windows PATH. If
// it can't be found, your app won't run.
procedure AddToNum; external 'DLLSample.dll';
procedure PrintNum; external 'DllSample.dll';
begin
PrintNum; // Print initial value
AddToNum; // Add to it twice
AddToNum;
PrintNum; // Print new value
ReadLn; // Wait for Enter key
end.
This outputs:
0
2
in a console window and waits for you to hit Enter to close it.

Related

What does $$ (double dollar) mean in Oracle SQL?

This Create package query executes in a SQLPlus util cli without error -
create or replace package PACKAGE2 as
$if $$install_ad_zd_sys $then
procedure LOG( X_MODULE varchar2, X_LEVEL varchar2, X_MESSAGE varchar2);
function LITERAL(X_VALUE varchar2) return varchar2;
else procedure LOG_VER;
$end
end;
/
Just want to know what does double dollar mean here?
Is it a sqlplus specific keyword?
I tried running this whole block as part of jdbc but it gives error 'Invalid column index'. So, I am suspecting the sign is specific to sqlplus, but not able to find the meaning of it.
This is how I am executing the above sql in java jdbc
plSqlstatement = connection.prepareCall(sqlBuffer.toString());
//sqlBuffer contains the whole create package block
//like sqlBuffer.append("CREATE OR REPLACE
//PACKAGE ....").append("/n").append(..next Line) and so on.
It's an inquiry directive:
An inquiry directive provides information about the compilation environment.
An inquiry directive typically appears in the boolean_static_expression of a selection directive, ...
... which is how you are using it, as it's within the $if conditional compilation directive.
You would need to assign a value, which you don't seem to be doing in either execution. But it will compile anyway, through SQL*Plus or JDBC (as in this db<>fiddle).
If you are getting an error from your JDBC call then you need to look at how you are running it - particularly if you are making get/set calls for arguments, as the error message suggests - since there are no arguments to set or retrieve.

DllMain/DllMainCRTStartup does not execute in DLL

The initialization code in a DLL build with gnat is not running automatically when imported. I did a MCVE that consists on:
division.ads
with System;
with Interfaces.C;
package Division is
--Neither of these work
procedure DllMainCRTStartup ;
pragma Export (StdCall, DllMainCRTStartup , "DllMainCRTStartup"); --Edited as noticed by Brian
-- procedure DllMain
-- pragma Export (StdCall, DllMain , "DllMain ");
function Div (A : in INTEGER; B : in INTEGER) return INTEGER;
pragma Export (C, Div, "MyDivision");
-- --If I put this, it does not compile... maybe a wrong linkage option set?
-- procedure AdaInit;
-- pragma Import (C, AdaInit, "adainit");
end Division;
division.adb
with text_io;
package body Division is
procedure DllMainCRTStartup is begin --DllMain or DllMainCRTStartup
text_io.put("INIT CODE YEAH!!!*************!"); --This does not execute :(
--AdaInit;
end DllMainCRTStartup ;
function Div(A : in INTEGER; B : in INTEGER) return INTEGER is
X : INTEGER := A/B;
begin
return X;
end Div;
end Division;
and the gpr:
library project Proj_Name is
for Library_Name use "math";
for Object_Dir use "obj";
for Source_Dirs use ("src");
for Library_Dir use "lib";
for Library_Interface use ("Division");
for Library_Kind use "dynamic";
for Library_Options use ("-LC:\GNAT\2015\lib\gcc\i686-pc-mingw32\4.9.3\adalib",
"-LC:\GNAT\2015\lib\gcc\i686-pc-mingw32\4.9.3\adalib\libgnat");
end Proj_Name;
I am testing the dll from python, with ctypes. I import it with ctypes.CDLL and I'm able to use MyDivision. However, the init code does not run when importing the dll, as the text_io is not executed.
On the other hand, if I add the AdaInit procedure to the code I get something like this when compiling:
undefined reference to `adainit'
Thank you very much!
I’m not sure how you know that the initialization code isn’t being run?
I’m running on macOS, but the Ada aspects should be similar. I wrote this package spec/body as a simpler version of yours:
package Division is
function Div (A : in INTEGER; B : in INTEGER) return INTEGER;
pragma Export (C, Div, "MyDivision");
end Division;
with Ada.Text_IO;
package body Division is
function Div(A : in INTEGER; B : in INTEGER) return INTEGER is
X : INTEGER := A/B;
begin
return X;
end Div;
procedure Test_For_Elaboration is
begin
Ada.Text_IO.Put_Line ("hello world!");
end Test_For_Elaboration;
begin
Test_For_Elaboration;
end Division;
with this simpler GPR
library project Proj_Name is
for Library_Name use "math";
for Object_Dir use "obj";
for Source_Dirs use ("src");
for Library_Dir use "lib";
for Library_Interface use ("Division");
for Library_Kind use "dynamic";
end Proj_Name;
and tested with this C code:
#include <stdio.h>
extern int MyDivision(int, int);
int main()
{
printf("42 / 2 => %d\n", MyDivision(42, 2));
return 0;
}
and the result was
$ ./caller
hello world!
42 / 2 => 21
so clearly, for me, library elaboration was called without my having to do anything.
The reason is that you specified Library_Interface in your project file, which means you’re building a stand-alone library, which
is a library that contains the necessary code to elaborate the Ada units that are included in the library. A stand-alone library is a convenient way to add an Ada subsystem to a more global system whose main is not in Ada since it makes the elaboration of the Ada part mostly transparent.
You can specify a stand-alone dynamic library which is not automatically initialized, using
for Library_Auto_Init use "false";
in which case you need to call the library’s initialization procedure yourself; it's called {library-name}init (in your case, mathinit). But then you need to call it from your main program; it’d need to be declared, in C
extern void mathinit();

Package got invalidated in oracle 11g [duplicate]

I am using oracle 10g and toad 11.5. I am trying to call an api from an anonymous block.
If I recompile the api after adding dbms_output.put_line and then try to execute the anonymous block, it shows error as:
"ORA-06508: PL/SQL: could not find program unit being called".
However if I end current session and open a new session, then the anonymous block will execute with out the error.
Due to this issue, i am made to reconnect the session everytime i make a change to API.
Can anyone help if this issue can be resolved by making any configurations in toad or database level.
I suspect you're only reporting the last error in a stack like this:
ORA-04068: existing state of packages has been discarded
ORA-04061: existing state of package body "schema.package" has been invalidated
ORA-04065: not executed, altered or dropped package body "schema.package"
ORA-06508: PL/SQL: could not find program unit being called: "schema.package"
If so, that's because your package is stateful:
The values of the variables, constants, and cursors that a package
declares (in either its specification or body) comprise its package
state. If a PL/SQL package declares at least one variable, constant,
or cursor, then the package is stateful; otherwise, it is stateless.
When you recompile the state is lost:
If the body of an instantiated, stateful package is recompiled (either
explicitly, with the "ALTER PACKAGE Statement", or implicitly), the
next invocation of a subprogram in the package causes Oracle Database
to discard the existing package state and raise the exception
ORA-04068.
After PL/SQL raises the exception, a reference to the package causes
Oracle Database to re-instantiate the package, which re-initializes
it...
You can't avoid this if your package has state. I think it's fairly rare to really need a package to be stateful though, so you should revisit anything you have declared in the package, but outside a function or procedure, to see if it's really needed at that level. Since you're on 10g though, that includes constants, not just variables and cursors.
But the last paragraph from the quoted documentation means that the next time you reference the package in the same session, you won't get the error and it will work as normal (until you recompile again).
seems like opening a new session is the key.
see this answer.
and here is an awesome explanation about this error
Based on previous answers. I resolved my issue by removing global variable at package level to procedure, since there was no impact in my case.
Original script was
create or replace PACKAGE BODY APPLICATION_VALIDATION AS
V_ERROR_NAME varchar2(200) := '';
PROCEDURE APP_ERROR_X47_VALIDATION ( PROCESS_ID IN VARCHAR2 ) AS BEGIN
------ rules for validation... END APP_ERROR_X47_VALIDATION ;
/* Some more code
*/
END APPLICATION_VALIDATION; /
Rewritten the same without global variable V_ERROR_NAME and moved to procedure under package level as
Modified Code
create or replace PACKAGE BODY APPLICATION_VALIDATION AS
PROCEDURE APP_ERROR_X47_VALIDATION ( PROCESS_ID IN VARCHAR2 ) AS
**V_ERROR_NAME varchar2(200) := '';**
BEGIN
------ rules for validation... END APP_ERROR_X47_VALIDATION ;
/* Some more code
*/
END APPLICATION_VALIDATION; /
I recompiled the package specification, even though the change was only in the package body. This resolved my issue

Can I introspect the name of the main.main package?

This is a fairly niche problem, but I'm currently trying to write a conventions-based settings storage library with golang. It would be a great API boon if I could programmatically determine the running package name that wants to store something (eg "github.net/author/projectname/pkg") calling my library function.
With Python a similar thing could be achieved with the inspect module, or even with __main__.__file__ and a look at the file system.
You can get similar information if you use the following functions:
runtime.Caller
runtime.FuncForPC
The code may look like this:
pc, file, line, ok := runtime.Caller(1)
if !ok { /*failed*/ }
println(pc, file, line, ok)
f := runtime.FuncForPC(pc)
if f == nil { /*failed*/ }
println(f.Name())
If I put the above code (with the 1st line changed into runtime.Caller(0)) into a (randomly chosen) Go library which I have installed in GOROOT, it prints:
134626026 /tmp/go-build223663414/github.com/mattn/go-gtk/gtk/_obj/gtk.cgo1.go -4585 true
github.com/mattn/go-gtk/gtk.Init
Or it prints:
134515752 /home/user/go/src/github.com/mattn/go-gtk/example/event/event.go 12 true
main.main
The filename on the 1st line, and the 2nd line, seem to contain the information you are looking for.
There are two problems:
It may give incorrect result if functions are automatically inlined by the compiler
For any function F defined in package main, the function name is just main.F. For example, if runtime.Caller(0) is called from main(), the function name is main.main even if the main() function is defined in a Go file found in GOROOT/src/github.com/mattn/go-gtk/.... In this case, the output from runtime.Caller is more useful than the output from runtime.FuncForPC.

how do we call a c function from an sql script

how do we call a C function from an SQL script?
int get_next_fbill_b2kId_seq_num(b2kIdType seq_val,bankIdPtrType bank_id)
{
validate_dc_alias(dcAlias);
tbaDateType sysDate;
tbaGetSystemDateTime(sysDate,NULL,NULL); /* returns in TBA date format */
sysDate[10] = EOS;
get_seq_value(next_num_char, 0, FBILL_B2KID_SRL_NUM,bank_id,TBAFATAL);
m_sprintf (seq_val, "%s%s%s", dcAlias, sysDate+8,next_num_char);
return(SUCCESS);
}
This is my function defined in a cxx file.
I want to call this in an SQL script.
How can I do this?
I assume the OP uses Oracle because he/she writes about PL/SQL.
It is possible to call an external c procedure. http://www.shutdownabort.com/quickguides/c_extproc.php
If your function is plain C, you need to create an executable and invoke it via ! or HOST.
If you are in a .Net environment, you can also create a .Net assembly containing your code, and call your procedure as if it was a PL/SQL procedure.