C#DLLImport将“const char *”转换为字符串

问题描述 投票:0回答:1

我需要在C#中实现这个DLLImport

const char* PegaSolicitacao(const char* CNPJ,
                            const char* CPF,
                            const char* CRM,
                            const char* UF_CRM,
                            const char* DT_EMISSAO );

可以在此链接https://farmaciapopular-portal-homologacao.saude.gov.br/farmaciapopular-portal/gbas/GBASMSB_2-Client.rar找到Dll

在.RAR \ GBASMSB_2-Client \ Ofd SDK里面0.2 Windows.zip - > gbasmsb_library.dll

我得到回报的唯一方法是使用以下代码:

        [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"));

但是返回应该是字符串而不是字符。当我尝试在DLLImport中返回一个字符串时系统会中断,如果我尝试返回一个char [],我有一个异常告诉我有关Marshaling的事。

我是C#的新人,从未与MarshalAs合作过,但在论坛上我尝试了一些选项:

                [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);

和其他一些变种,但我找不到合适的选择。

c# marshalling dllimport
1个回答
2
投票

使用DLL

我使用DLL Export Viewer查看导出的函数。快速谷歌搜索产生了这些C导出定义:

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 );

由于它的简单性,我决定从IdentificaEstacao开始,它应返回一个标识该站的标识符。

我已经尝试了所有类型的返回MarshalAsCharSetCallingConvention值,但无法使用返回string类型的导入。所以我们将返回类型更改为IntPtr

[DllImport("gbasmsb_library.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr IdentificaEstacao();

现在,当调用该函数时,您将返回指向内存地址的IntPtr。检查该内存位置的内容(在断点处暂停时,调试> Windows>内存>内存1),您可以看到一个单字节,以空字符结尾的字符串(看起来像Base64数据)。

我尝试使用其中一种Marshal.Free...方法释放它,但这不起作用。我已经多次调用相同的方法,每次我们在IntPtr中返回相同的内存地址时,我猜他们正在使用全局分配的字符串,不应该被调用者释放(也可能是string返回类型不起作用的原因)。

使用下面的代码,我们可以获得电台标识符:

var ptr = IdentificaEstacao();
var stationIdentifier = Marshal.PtrToStringAnsi(ptr);

让我们以同样的方式更改其他导入的签名:

[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);

并进行此测试调用:

var ptr = PegaSolicitacao("31617905000139",
                           "99999999484",
                           "30828",
                           "SP",
                           DateTime.Today.ToString("d"));

这又返回一个指向静态字符串的指针(多次调用它返回相同的内存地址),所以你可以通过再次调用Marshal.PtrToStringAnsi(ptr);来获得结果。

作为一个额外的测试,我已经在一个紧凑的循环中运行它,似乎没有内存泄漏,所以这应该是一个非常安全的方式来调用导入的函数。

请注意,我已将CallingConventionStdCall更改为Cdecl,因此我们可以使用string作为输入参数,而不会获得不平衡的堆栈异常。

使用EXE

我还注意到该存档包含一个可执行的gbasmsb_gbas.exe,它可以执行相同的功能。

gbasmsb_gbas.exe --i为您提供电台标识符,而gbasmsb_gbas.exe --solicitacao 99999999484 31617905000139 30828 SP 12/03/2019返回请求信息。

调用EXE并解析输出也是一种可能的集成路径,不太容易破坏对外部库的未来更新的更改。

© www.soinside.com 2019 - 2024. All rights reserved.