-
Notifications
You must be signed in to change notification settings - Fork 10
Passing and returning arguments
When calling PL/SQL Developer's callbacks you need to pass arguments' values from the managed C# code to the unmanaged, and then receive the result from the unmanaged code — either through out arguments or return value. Marshalling makes this happen. You can refer to Callback concepts for the links on articles about marshalling.
According to "PL/SQL Developer Plug-In interface Documentation", the number of data types is limited to 3:
...Boolean (32 bit), Integer (32 bit) and zero terminated strings.
So these need to be marshalled and this article explains how.
No custom marshalling is required to pass and receive a 32-bit integer to and from unmanaged code. Using int datatype and default marshalling does all the job.
This example briefly demonstrates the case of passing an integer:
// C++ signature: void IDE_SplashCreate(int ProgressMax)
// Delphi signature: Delphi procedure IDE_SplashCreate(ProgressMax: Integer)
// Declaration
public delegate void IdeSplashCreate(int progressMax);
private IdeSplashCreate splashCreateCallback;
// Usage
splashCreateCallback(100);
int maxProgress = 100;
splashCreateCallback(maxProgress);And this one the case of returning an integer:
// C++ signature: int IDE_GetWindowConnection();
// Delphi signature: IDE_GetWindowConnection: Integer;
// Declaration
public delegate int IdeGetWindowConnection();
private IdeGetWindowConnection getWindowConnectionCallback;
// Usage
int connectionIndex = getWindowConnectionCallback();Booleans need to be marshalled as UnmanagedType.Bool.
This example briefly demonstrates the case of passing and returning a boolean:
// C++ signature: BOOL IDE_WindowHasEditor(BOOL CodeEditor);
// Delphi signature: procedure IDE_WindowHasEditor(CodeEditor: Bool);
// Declaration
[return: MarshalAs(UnmanagedType.Bool)]
public delegate void IdeWindowHasEditor([MarshalAs(UnmanagedType.Bool)] bool codeEditor);
private IdeWindowHasEditor windowHasEditorCallback;
// Usage
bool canSave = windowHasEditorCallback(true);
// or
bool notCode = false;
canSave = windowHasEditorCallback(notCode);Strings are tricky. Method to use depends on whether string is an argument or a function return value:
- Passing a string as an argument either in or out requires no custom marshalling —
stringdata type and default marshalling works OK. - Receiving a string as a function return value,
IntPtrand custom marshalling is required.
Using a string data type for a function return value works only in some cases, is unstable — it may work on one machine, but the same call may crash the IDE on another machine. Thus IntPtr is the recommended data type for function return values.
Passing a string as an incoming argument:
// C++ signature: int SQL_Execute(char *SQL);
// Delphi signature: function SQL_Execute(SQL: PChar): Integer;
// Declaration
public delegate int SqlExecute(string sql);
private SqlExecute sqlExecuteCallback;
// Usage
int error = sqlExecuteCallback("select 'x' from dual");Receiving a string as an out argument:
// C++ signature: void IDE_GetConnectionInfo(char **Username, char **Password, char **Database);
// Delphi signature: procedure IDE_GetConnectionInfo(var Username, Password, Database: PChar);
// Declaration
public delegate void IdeGetConnectionInfo(out string username, out string password, out string db);
private IdeGetConnectionInfo getConnectionInfoCallback;
// Usage
// IMPORTANT: Allocate out string before call.
string user = "";
string password = "";
string db = "";
getConnectionInfoCallback(out user, out password, out db);Receiving a string as return value:
// C++ signature: char* SYS_RootDir();
// Delphi signature: function SYS_RootDir: PChar;
// Declaration
public delegate IntPtr SysRootDir();
// Usage
IntPtr dirPathPtr = rootDirCallback();
string rootDir = Marshal.PtrToStringAnsi(dirPathPtr);Note that instead of string a pointer to a string is used and then explicit custom marshalling of IntPtr with Marshal.PtrToStringAnsi.
Know issue: there might be issues with this approach when passing national characters. For instance, cyrillic "абвгд" or baltic "ąšų" or whatever non-latin. Latin letters work OK.
Introduction
Main concepts
- Anatomy of the plug-in
- Exporting DLL functions
- Using callbacks
Misc
- Using menu items
- Accepting commands in the command window
- Validating DLL exports
- Working with color preferences
- Referencing external C# assemblies