There is actually a "dirty trick" to accomplish this, but it is not for the faint of heart. It comes with several caveats and I would only recommend it if you feel that you absolutely need it.
The caveats are:
1) You need a "helper" routine written in a different .NET language, such as C#, to help pin the data for the garbage collector and potentially marshal it.
2) This helper routine gets a lot more complicated if there are pointers embedded in the data structures you intend to pass, because COBOL pointers in .NET are not the same kind of pointers you are accustomed to.
3) The following description will use an undocumented function of the COBOL runtime that may not continue to exist in its current form in future versions of the runtime.
4) This solution will not do any of the automatic marshaling of PIC X data from Unicode UTF-8 encoding to ANSI Code Page encoding that would normally be done by the Fujitsu NetCOBOL runtime if you did not provide the SuppressEncodingConversionAttribute attribute. You can perform this conversion manually if you need to. If your PIC X data is all ASCII, then you don't have to worry about this.
The outline of the solution is as follows:
1) You need to create a COBOL pointer in your calling program that points to the data structure you want to pass. For example:
01 MYVARP USAGE POINTER.
...
SET MYVARP TO ADDRESS OF TEST1.
2) You need to convert this COBOL pointer into an unmanaged memory pointer (COBOL pointers in Fujitsu NetCOBOL for .NET are not real memory pointers) that is pinned for the garbage collector. Pinning is required to make sure that the data isn't relocated by the garbage collector during your call to the unmanaged function. To accomplish this, you first need to define an integer redefines of the pointer from (1) in the calling program:
01 MYVARP-INT REDEFINES MYVARP PIC S9(9) COMP-5.
Then you need your helper routine to take this integer and convert it into a pinned unmanaged memory pointer. The following is a C# code snippet that does this:
using Fujitsu.COBOL;
using Fujitsu.COBOL.Runtime.OmeLib;
using System.Runtime.InteropServices;
...
// parm is assumed to be the int parameter that is
// the integer redefines of the COBOL pointer passed
// from the COBOL program
COBOLData srcData = hPointerManager.hPointerToCOBOLData((uint)parm);
GCHandle handle = GCHandle.Alloc(srcData.Block, GCHandleType.Pinned);
int result = (int)handle.AddrOfPinnedObject() + srcData.Offset;
// result now contains the pinned unmanaged memory
// pointer that you can pass to the unmanaged C
// function.
3) You need to call your unmanaged routine with the integer result computed in step 2. This integer result is, in fact, an unmanaged memory pointer. This call can be done inline from the C# code or from your COBOL code using the returned integer result from your C# helper routine.
4) You need to free up the GCHandle that was allocated in step 2 so that you are no longer pinning the data that you used to call your unmanaged routine. Pinning data without unpinning it means that the performance of the garbage collector will gradually degrade. To free the GCHandle, you simply need to call the "Free" method on the handle that was created in step 2. If you called the unmanaged C function directly from the C#, you could free the GCHandle directly after the call. Otherwise, you need to return the handle from the helper routine so that the calling COBOL program can free the handle.
There are a lot of more complicated variations on this solution that are needed in the case of embedded pointers or in the case that you need to perform other custom marshaling, such as character encoding conversions. The more complicated variations involve allocating unmanaged memory using Marshal.AllocCoTaskMem and copying the data from the managed data structure to the unmanaged memory area while performing the necessary marshaling. Doing this inevitably means creating a C# routine with "unsafe" extensions to do the necessary copying.
After wading through this description, you may well decide that it's not worth it...
Good luck!