C# C“DLLImport convert”;常量字符*”;串
我需要在C中实现这个DLLImport# 可以在此链接中找到Dll 在.RAR\GBASMSB_2-Client\Ofd SDK 0.2 Windows.zip中->GBASMSB_library.dll 我得到报税单的唯一方法是使用以下代码:C# C“DLLImport convert”;常量字符*”;串,c#,marshalling,dllimport,C#,Marshalling,Dllimport,我需要在C中实现这个DLLImport# 可以在此链接中找到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,
[DllImport(@"gbasmsb_library.dll")]
public static extern char PegaSolicitacao(string CNPJ,
string CPF,
string CRM,
string UF_CRM,
string DT_Emissao);
但是返回的是字符串而不是字符。 当我尝试在dlliport中返回字符串时,系统会中断,如果我尝试返回char[],我会收到一个异常,告诉我有关封送的信息 我是C#的新人,从未与马歇尔斯合作过,但在论坛上我尝试了以下几种选择:
[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);
还有其他一些变体,但我找不到正确的选项。使用DLL 我使用了DLL导出查看器来查看导出的函数。快速的谷歌搜索产生了以下C导出定义:
const char*identificataestacao();
const char*PegaSolicitacao(const char*CNPJ,const char*CPF,const char*CRM,const char*UF\u CRM,const char*DT\u EMISSAO);
const char*PegaConfirmacao(const char*CNPJ,const char*NU_autorizao,const char*NU_CUPOM_财政);
由于它的简单性,我决定从identificataestacao
开始,它应该返回一个标识站点的标识符
我尝试了所有类型的返回marshallas
、CharSet
和CallingConvention
值,但无法使用返回字符串的导入。因此,让我们将返回类型改为IntPtr
:
[DllImport(“gbasmsb_library.dll”,CharSet=CharSet.Ansi,CallingConvention=CallingConvention.Cdecl)]
公共静态外部IntPtr identificataestacao();
现在,当调用该函数时,您将返回一个指向内存地址的IntPtr
。当检查该内存位置的内容时(在断点上暂停时,调试>窗口>内存>内存1),您可以看到一个以null结尾的单字节字符串(看起来像Base64数据)
我试着用Marshal.Free…
方法之一释放它,但没有成功。我多次调用相同的方法,每次我们在IntPtr
中返回相同的内存地址,这使我猜测它们使用的是全局分配的字符串,调用方不应释放该字符串(这也可能是string
返回类型不起作用的原因)
使用下面的代码,我们可以获得站点标识符:
var ptr=identificataestacao();
var stationIdentifier=Marshal.PtrToStringAnsi(ptr);
让我们以相同的方式更改另一个导入的签名:
[DllImport(“gbasmsb_library.dll”,CharSet=CharSet.Ansi,CallingConvention=CallingConvention.Cdecl)]
公共静态外部IntPtr PegaSolicitacao(
[Marshallas(UnmanagedType.LPStr)]字符串CNPJ,
[Marshallas(UnmanagedType.LPStr)]字符串CPF,
[Marshallas(UnmanagedType.LPStr)]字符串CRM,
[Marshallas(UnmanagedType.LPStr)]字符串UF_CRM,
[Marshallas(UnmanagedType.LPStr)]字符串DT_Emissao);
并进行此测试调用:
var ptr=PegaSolicitacao(“31617905000139”,
"99999999484",
"30828",
“SP”,
DateTime.Today.ToString(“d”);
这再次返回一个指向静态字符串的指针(多次调用它会返回相同的内存地址),因此您可以通过调用Marshal.PtrToStringAnsi(ptr)获得结果代码>再次
作为一个附加测试,我在一个紧密的循环中运行了它,并且似乎没有内存泄漏,因此这应该是调用导入函数的一种非常安全的方法
请注意,我已将调用约定
从StdCall
更改为Cdecl
,因此我们可以使用字符串
作为输入参数,而不会出现不平衡堆栈异常
使用EXE
我还注意到,归档文件包含一个可执行文件gbasmsb_gbas.exe
,它可以执行相同的功能
gbasmsb_gbas.exe--i
提供站点标识符,而gbasmsb_gbas.exe--requestacao 9999999 484 31617905000139 30828 SP 12/03/2019
返回请求信息
调用EXE并解析输出也是一种可能的集成路径,它不太容易中断更改,以便将来更新该外部库。返回的const char*
是如何分配的?我无权访问DLL的源代码,这是一个由巴西政府制作的第三方动态链接库,有任何文档吗?常量char*
只是一个指针。当它是一个参数时,封送处理程序可以处理它,但作为返回值,它需要知道如何处理该内存,否则它会泄漏。@sidprasher将处理封送部分,但可能也会导致内存泄漏,因为指针可能需要释放,但要释放,你需要知道它是如何分配的。你需要阅读DLL的文档。您需要了解返回的字符串是如何分配的。谢谢,我将尝试并报告返回的DLL结果和EXE结果之间存在差异。也许这是一个编码问题。
var Teste = PegaSolicitacao("31617905000139",
"99999999484",
"30828",
"SP",
DateTime.Today.ToString("d"));
[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);