Reputation: 19
I need to implement this DLLImport in C#
const char* PegaSolicitacao(const char* CNPJ,
const char* CPF,
const char* CRM,
const char* UF_CRM,
const char* DT_EMISSAO );
The Dll could be found at this link https://farmaciapopular-portal-homologacao.saude.gov.br/farmaciapopular-portal/gbas/GBASMSB_2-Client.rar
Inside the .RAR \GBASMSB_2-Client\Ofd SDK 0.2 Windows.zip -> gbasmsb_library.dll
The only way that i got a return was with this code:
[DllImport(@"gbasmsb_library.dll")]
public static extern char PegaSolicitacao(string CNPJ,
string CPF,
string CRM,
string UF_CRM,
string DT_Emissao);
var Teste = PegaSolicitacao("31617905000139",
"99999999484",
"30828",
"SP",
DateTime.Today.ToString("d"));
But the return is suposed to be a string not a char. When I tried return a string in the DLLImport the system breaks, if I try to return a char[] I got a exception telling me about Marshaling.
Im, new at C# and never worked with MarshalAs, but looking at the Forum I tried some option like:
[DllImport(@"gbasmsb_library.dll", CharSet = CharSet.Ansi)]
[return: MarshalAs(UnmanagedType.LPTStr)]
public static extern char[] PegaSolicitacao([MarshalAs(UnmanagedType.LPArray)]char[] CNPJ,
[MarshalAs(UnmanagedType.LPArray)]char[] CPF,
[MarshalAs(UnmanagedType.LPArray)]char[] CRM,
[MarshalAs(UnmanagedType.LPArray)]char[] UF_CRM,
[MarshalAs(UnmanagedType.LPArray)]char[] DT_Emissao);
and some other variants too, but I cant find the right option.
Upvotes: 1
Views: 3585
Reputation: 28111
Using the DLL
I've used DLL Export Viewer to see the exported functions. A quick google search resulted in these C export definitions:
const char* IdentificaEstacao();
const char* PegaSolicitacao( const char* CNPJ, const char* CPF, const char* CRM, const char* UF_CRM, const char* DT_EMISSAO );
const char* PegaConfirmacao( const char* CNPJ, const char* NU_AUTORIZACAO, const char* NU_CUPOM_FISCAL );
Because of its simplicity, I decided to start with IdentificaEstacao
which should return an identifier that identifies the station.
I've tried all kind of return MarshalAs
, CharSet
and CallingConvention
values, but couldn't get it working with an import that returns a string
type. So let's change the return type to IntPtr
instead:
[DllImport("gbasmsb_library.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr IdentificaEstacao();
Now, when calling that function, you'll get back an IntPtr
that points to a memory address. When checking the content of that memory location (Debug > Windows > Memory > Memory1 while pausing on a breakpoint), you can see a single-byte, null-terminated string (looks like Base64 data).
I tried freeing it with one of the Marshal.Free...
methods, but that didn't work. I've called the same method several times and each time we're getting back the same memory address in the IntPtr
which makes me guess that they're using a global allocated string that should not be freed by the caller (that might also be the reason why the string
return type doesn't work).
With the code below, we're able to get the station identifier:
var ptr = IdentificaEstacao();
var stationIdentifier = Marshal.PtrToStringAnsi(ptr);
Let's change the signature of the other import in the same way:
[DllImport("gbasmsb_library.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr PegaSolicitacao(
[MarshalAs(UnmanagedType.LPStr)] string CNPJ,
[MarshalAs(UnmanagedType.LPStr)] string CPF,
[MarshalAs(UnmanagedType.LPStr)] string CRM,
[MarshalAs(UnmanagedType.LPStr)] string UF_CRM,
[MarshalAs(UnmanagedType.LPStr)] string DT_Emissao);
And make this test call:
var ptr = PegaSolicitacao("31617905000139",
"99999999484",
"30828",
"SP",
DateTime.Today.ToString("d"));
This again returns a pointer to a static string (calling it multiple times returns the same memory address), so you can just get the result by calling Marshal.PtrToStringAnsi(ptr);
again.
As an additional test, I've ran this in a tight loop and there seems to be no memory leak, so this should be a pretty safe way of calling the imported function(s).
Note that I have changed the CallingConvention
from StdCall
to Cdecl
so we can use string
as input parameters without getting the unbalanced stack exception.
Using the EXE
I've also noticed that the archive contains an executable gbasmsb_gbas.exe
which can perform the same functions.
gbasmsb_gbas.exe --i
gives you the station identifier, while gbasmsb_gbas.exe --solicitacao 99999999484 31617905000139 30828 SP 12/03/2019
returns the request info.
Calling the EXE and parsing the output is also a possible integration path that is less prone to breaking changes for future updates of that external library.
Upvotes: 3