我正在从C++/CLR背景中学习C#,方法是在C#中重写一个示例C++/CLR项目。
该项目是一个简单的GUI (使用Visual / Windows窗体),它执行对用C编写的DLL的调用(实际上,在NI LabWindows/CVI中,但这只是带有自定义库的ANSI C)。DLL不是由我编写的,我不能对它执行任何更改,因为它也在其他地方使用。
DLL包含使RFID设备执行某些功能(如读取/写入RFID标签等)的功能。在每个函数中,总是调用执行日志文件写入的另一个函数。如果日志文件不存在,则使用某个标头创建日志文件,然后追加数据。
问题是: C++/CLR项目运行良好。但是,在C# 1中,功能可以工作( RFID标签被正确地写/读等等)。但是没有关于日志文件的任何活动!
DLL导出的声明如下(当然还有更多的声明):
int __declspec(dllexport) __stdcall Magnetfeld_einschalten(char path_Logfile_RFID[300]);
int save_Logdatei(char path_Logdatei[], char Funktion[], char Mitteilung[]); save_Logdatei函数是在执行Magnetfeld_einschalten时调用的,如下所示:
save_Logdatei(path_Logfile_RFID, "Magnetfeld_einschalten", "OK");在C++/CLR项目中,我声明了如下函数:
#ifdef __cplusplus
extern "C" {
#endif
int __declspec(dllexport) __stdcall Magnetfeld_einschalten(char path_Logfile_RFID[300]);
#ifdef __cplusplus
}
#endif那么,对该函数的简单调用就可以工作了。
在C#项目中,声明如下:
[DllImport("MyDLL.dll", CallingConvention = CallingConvention.StdCall, EntryPoint = "Magnetfeld_einschalten", CharSet = CharSet.Ansi, ExactSpelling = false)]
private static extern int Magnetfeld_einschalten(string path_Logfile_RFID);而且,正如我所说的,虽然主要功能正在工作(在本例中,打开了RFID设备的磁场),但日志记录从未完成(因此,对save_Logdatei的内部DLL调用没有正确执行)。
表单构造函数中的相关代码如下:
pathapp = Application.StartupPath;
pathlog = string.Format("{0}\\{1:yyyyMMdd}_RFID_Logdatei.dat", pathapp, DateTime.Now);
//The naming scheme for the log file.
//Normally, it's autogenerated when a `save_Logdatei' call is made.
Magnetfeld_einschalten(pathlog);我遗漏了什么?我已经尝试过在DLL方法声明中使用unsafe --因为save_Logdatei中有一个File指针--但是它并没有产生任何区别。
===================================EDIT==================================
根据David的建议,我试图以一种容易测试的方式重现这个问题。为此,我创建了一个非常简单的动态链接库("test.dll"),我已经从定制的CVI库中完全剥离了它,所以即使没有CVI,它也应该是可复制的。我把它上传到这里了。无论如何,DLL的代码是:
#include <stdio.h>
int __declspec(dllexport) __stdcall Magnetfeld_einschalten(char path_Logfile_RFID[300]);
int save_Logdatei(char path_Logdatei[], char Funktion[], char Mitteilung[]);
int __declspec(dllexport) __stdcall Magnetfeld_einschalten(char path_Logfile_RFID[300])
{
save_Logdatei(path_Logfile_RFID, "Opening Magnet Field", "Success");
return 0;
}
int save_Logdatei(char path_Logdatei[], char Funktion[], char Mitteilung[])
{
FILE *fp; /* File-Pointer */
char line[700]; /* Zeilenbuffer */
char path[700];
sprintf(path,"%s\\20160212_RFID_Logdatei.dat",path_Logdatei);
fp = fopen (path, "a");
sprintf(line, "Just testing");
sprintf(line,"%s %s",line, Funktion);
sprintf(line,"%s %s",line, Mitteilung);
fprintf(fp,"%s\n",line);
fclose(fp);
return 0;
}C#代码也被简化了,我添加到标准表单项目中的唯一东西是Button 1(生成的按钮点击可以看到)。守则是:
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace TestDLLCallCSharp
{
public partial class Form1 : Form
{
public int ret;
public string pathapp;
public string pathlog;
[DllImport("test", CallingConvention = CallingConvention.StdCall, EntryPoint = "Magnetfeld_einschalten", CharSet = CharSet.Ansi, ExactSpelling = false)]
private static extern int Magnetfeld_einschalten(string path_Logfile_RFID);
public Form1()
{
pathapp = @"C:\ProgramData\test";
pathlog = string.Format("{0}\\20160212_RFID_Logdatei.dat", pathapp);
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
ret = Magnetfeld_einschalten(pathlog);
}
}
}可以看到,我避免了对日志文件使用自动命名方案(通常我使用日期),在dll和C#代码中,日志文件都是"20160212_RFID_Logdatei.dat“。我还避免使用app路径作为放置日志文件的目录,而是选择了一个在ProgramData中创建的名为test的文件夹。
同样,根本没有创建任何文件。
发布于 2016-02-12 11:09:53
这看起来像是调用代码中的一个简单的错误。而不是:
ret = Magnetfeld_einschalten(pathlog);你的意思是:
ret = Magnetfeld_einschalten(pathapp);在C#代码中,这两个字符串具有以下值:
pathapp == "C:\ProgramData\\test"
pathlog == "C:\ProgramData\\test\\20160212_RFID_Logdatei.dat"当您将pathlog传递给非托管代码时,它将执行以下操作:
sprintf(path,"%s\\20160212_RFID_Logdatei.dat",path_Logdatei);将path设置为
path == "C:\\ProgramData\\test\\20160212_RFID_Logdatei.dat\\20160212_RFID_Logdatei.dat"换句话说,您要将文件名追加到路径两次而不是一次。
发布于 2016-02-11 14:54:17
字符串采用Unicode格式,将其转换为byte[]。
Encoding ec = Encoding.GetEncoding(System.Threading.Thread.CurrentThread.CurrentCulture.TextInfo.ANSICodePage);
byte[] bpathlog = ec.GetBytes(pathlog);并将参数类型更改为byte[]
[DllImport("MyDLL.dll", CallingConvention = CallingConvention.StdCall, EntryPoint = "Magnetfeld_einschalten", CharSet = CharSet.Ansi, ExactSpelling = false)]
private static extern int Magnetfeld_einschalten(byte[] path_Logfile_RFID);对我来说很管用
JSh
发布于 2016-02-12 04:24:03
在C#中对P/Invoke进行了广泛的概述,并在平台调用教程- MSDN库中作了介绍。
问题在于您需要传递一个固定的char数组,而不是标准的char*。这是在字符串的默认编组中讨论的。
要点是,您需要从您的char[300]字符串构造一个,并传递该字符串而不是字符串.。
对于这种情况,指定了两种方法:
根据文档的说法,UnmanagedType.ByValTStr只在结构中有效,因此似乎不可能兼得这两个世界的优点。
https://stackoverflow.com/questions/35341625
复制相似问题