Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Keep reference to generic server function delegates so they don't get GCed and crash the application #70

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/SapNwRfc/Internal/Interop/RfcInterop.cs
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ public virtual IntPtr AppendNewRow(IntPtr tableHandle, out RfcErrorInfo errorInf
public delegate RfcResultCode RfcServerFunction(IntPtr connectionHandle, IntPtr functionHandle, out RfcErrorInfo errorInfo);

[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)]
public delegate RfcResultCode RfcFunctionDescriptionCallback(string functionName, RfcAttributes attributes, ref IntPtr funcDescHandle);
public delegate RfcResultCode RfcFunctionDescriptionCallback(string functionName, RfcAttributes attributes, out IntPtr funcDescHandle);

[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate void RfcServerErrorListener(IntPtr serverHandle, in RfcAttributes clientInfo, in RfcErrorInfo errorInfo);
Expand Down
49 changes: 39 additions & 10 deletions src/SapNwRfc/SapServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -166,20 +166,44 @@ public static void InstallGenericServerFunctionHandler(SapConnectionParameters p
InstallGenericServerFunctionHandler(new RfcInterop(), parameters, action);
}

// Keep a reference to the generic server function delegates so they don't get GCed
private static readonly RfcInterop.RfcServerFunction ServerFunction = HandleGenericFunction;
private static readonly RfcInterop.RfcFunctionDescriptionCallback GenericMetadata = HandleGenericMetadata;

#pragma warning disable SA1306 // Field names should begin with lower-case letter
private static bool GenericServerFunctionHandlerInstalled;
private static (RfcInterop Interop, SapConnectionParameters Parameters, Action<ISapServerConnection, ISapServerFunction> Action) GenericServerFunctionHandler;
#pragma warning restore SA1306 // Field names should begin with lower-case letter

private static void InstallGenericServerFunctionHandler(RfcInterop interop, SapConnectionParameters parameters, Action<ISapServerConnection, ISapServerFunction> action)
{
RfcResultCode resultCode = interop.InstallGenericServerFunction(
serverFunction: (IntPtr connectionHandle, IntPtr functionHandle, out RfcErrorInfo errorInfo)
=> HandleGenericFunction(interop, action, connectionHandle, functionHandle, out errorInfo),
funcDescPointer: (string functionName, RfcAttributes attributes, ref IntPtr funcDescHandle)
=> HandleGenericMetadata(interop, parameters, functionName, out funcDescHandle),
out RfcErrorInfo installFunctionErrorInfo);
GenericServerFunctionHandler = (interop, parameters, action);

if (!GenericServerFunctionHandlerInstalled)
{
RfcResultCode resultCode = interop.InstallGenericServerFunction(
serverFunction: ServerFunction,
funcDescPointer: GenericMetadata,
out RfcErrorInfo installFunctionErrorInfo);

resultCode.ThrowOnError(installFunctionErrorInfo);
resultCode.ThrowOnError(installFunctionErrorInfo);

GenericServerFunctionHandlerInstalled = true;
}
}

private static RfcResultCode HandleGenericFunction(RfcInterop interop, Action<ISapServerConnection, ISapServerFunction> action, IntPtr connectionHandle, IntPtr functionHandle, out RfcErrorInfo errorInfo)
private static RfcResultCode HandleGenericFunction(IntPtr connectionHandle, IntPtr functionHandle, out RfcErrorInfo errorInfo)
{
(RfcInterop interop, _, Action<ISapServerConnection, ISapServerFunction> action) = GenericServerFunctionHandler;
if (interop == null || action == null)
{
errorInfo = new RfcErrorInfo
{
Code = RfcResultCode.RFC_EXTERNAL_FAILURE,
};
return RfcResultCode.RFC_EXTERNAL_FAILURE;
}

IntPtr functionDesc = interop.DescribeFunction(
rfcHandle: functionHandle,
errorInfo: out RfcErrorInfo functionDescErrorInfo);
Expand Down Expand Up @@ -211,8 +235,14 @@ private static RfcResultCode HandleGenericFunction(RfcInterop interop, Action<IS
}
}

private static RfcResultCode HandleGenericMetadata(RfcInterop interop, SapConnectionParameters parameters, string functionName, out IntPtr funcDescHandle)
private static RfcResultCode HandleGenericMetadata(string functionName, RfcAttributes attributes, out IntPtr funcDescHandle)
{
funcDescHandle = IntPtr.Zero;

(RfcInterop interop, SapConnectionParameters parameters, _) = GenericServerFunctionHandler;
if (interop == null || parameters == null)
return RfcResultCode.RFC_NOT_FOUND;

RfcConnectionParameter[] interopParameters = parameters.ToInterop();

IntPtr connection = interop.OpenConnection(
Expand All @@ -222,7 +252,6 @@ private static RfcResultCode HandleGenericMetadata(RfcInterop interop, SapConnec

if (connectionErrorInfo.Code != RfcResultCode.RFC_OK)
{
funcDescHandle = IntPtr.Zero;
return connectionErrorInfo.Code;
}

Expand Down