我现在已经在p / invoke问题上苦苦挣扎了一段时间。继续“尝试读取或写入受保护的内存”。所有的时间,我怀疑我的编组有点不对。
这是c签名:
long ft_show( /* show file-attributes */
const struct ft_admission *admis, /* I: transfer admission */
const struct ft_shwpar *par, /* IO: parameter-list */
struct ft_fileinfo *info, /* O: requested information */
struct ft_err *errorinfo, /* O: error information */
void *options /* I: options */
);
以下是相关结构(c)
struct ft_admission
{
char *remsys;
char *remadmis;
char *remaccount;
char *rempasswd;
};
struct ft_shwpar
{
int shwparvers;
char *fn;
char *mgmtpasswd;
char *fud;
int fudlen;
};
struct ft_fileinfo
{
int ftshowivers;
char fn[INFO_FN_LEN];
enum ft_ftype filetype;
enum ft_charset charset;
enum ft_rform recordform;
long recsize;
enum ft_available availability;
int access;
char account[ACC_LEN];
long size;
long maxsize;
char legalqual[LQ_LEN];
char cre_user[USER_LEN];
long cre_date;
char mod_user[USER_LEN];
long mod_date;
char rea_user[USER_LEN];
long rea_date;
char atm_user[USER_LEN];
long atm_date;
long long fsize;
long long fmaxsize;
};
struct ft_err
{
long main;
long detail;
long additional;
};
struct ft_options
{
int ftoptsvers;
int ftapivers;
};
我试图创建c#结构并调用如下:
[DllImport("ftapi.dll", EntryPoint = "ft_showdir")]
private static extern long ft_showdir(ft_admission admis, ref ft_shwpar par, ft_fileinfo[] buf, int bufsize, ref ft_err errorinfo, ft_options options);
public struct ft_admission
{
public String remsys;
public String remadmis;
public String remaccount;
public String rempasswd;
};
public struct ft_shwpar
{
public int shwparvers;
public String fn;
public String mgmtpasswd;
public IntPtr fud;
public int fudlen;
};
[StructLayout(LayoutKind.Sequential, Size = 1464, CharSet = CharSet.Ansi), Serializable]
public struct ft_fileinfo
{
public int ftshowivers;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 257)]
public String fn;
public ft_ftype filetype;
public ft_charset charset;
public ft_rform recordform;
public long recsize;
public ft_available availability;
public int access;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)]
public String account;
public long size;
public long maxsize;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 81)]
public String legalqual;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 68)]
public String cre_user;
public long cre_date;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 68)]
public String mod_user;
public long mod_date;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 68)]
public String rea_user;
public long rea_date;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 68)]
public String atm_user;
public long atm_date;
public long fsize;
public long fmaxsize;
};
public struct ft_err
{
public long main;
public long detail;
public long additional;
}
public struct ft_options
{
public int ftoptsvers;
public int ftapivers;
};
我已经在另一个调用中成功使用了ft_err和ft_options结构,所以我认为这些结构是正确编组的。
我认为问题是ft_fileinfo结构。我想在那里使用StringBuilder,但是框架中存在一个错误,阻止了StringBuilder在结构体内的正确编组。我尝试过这里描述的解决方法:http://support.microsoft.com/kb/327109 但没有成功。
以下是我尝试访问的api文档中提供的代码段(某些代码已被删除,因为它仅用于将信息打印到屏幕上):
/* sample3.c File management requests */
/*************************************************************/
static const char Sccsid[] = "@(#)sample3.c 2.33 2009/03/25";
/*************************************************************
Program call: sample3 <directory>
*************************************************************/
/* Include Files */
/*************************************************************/
#include <stdio.h> /* printf() */
#include <stdlib.h> /* exit() */
#include <string.h> /* strcat(), strcpy() */
#include <ftapi.h>
/*************************************************************/
/* Local Constants and Macros */
/*************************************************************/
#define BUFSIZE 200 /* number of ft_fileinfo structures */
/* in output buffer */
/*************************************************************/
/* Local Functions Declarations */
/*************************************************************/
static void error_print(struct ft_err *, char *);
static void printinfo(struct ft_fileinfo *info);
int main (int argc, char *argv[])
{
int i; /* Some local counter */
int count; /* Number of files */
char remotesys[201]; /* Address of remote systems */
char remoteadm[68]; /* Transfer admission */
char fud[STAT_FUD_LEN]; /* Further details */
struct ft_err errorinfo; /* Error information */
struct ft_admission admis; /* Admission parameters */
struct ft_shwpar par; /* Show directory parameters */
struct ft_options opt; /* Options */
struct ft_fileinfo buf[BUFSIZE]; /* Output buffer */
/* Check program arguments */
if (argc != 2)
{
printf("Call: %s <directory>\n", argv[0]);
exit(EXIT_FAILURE);
}
/* Initialize ft_transpar structure */
memset(&par, 0, sizeof(par));
/* Set version of the parameter list */
par.shwparvers = FT_SPARV2;
/* Set the name of the remote directory */
par.fn = argv[1];
/* Set buffer and buffer length for the further details */
par.fud = fud;
par.fudlen = sizeof(fud);
/* Set version of the ft_fileinfo structure in the */
/* output buffer. The version in the first structure */
/* is valid for all structtures in the output buffer. */
buf[0].ftshowivers = FT_SHOWIV2;
/* Get the name/address of the remote system and the */
/* transfer admission */
printf("Enter name of remote system: ");
scanf("%s", remotesys);
printf("Input transfer admission to remote system: ");
scanf("%s", remoteadm);
admis.remsys = remotesys;
admis.remadmis = remoteadm;
admis.remaccount = NULL;
admis.rempasswd = NULL;
/* Prepare the options structure */
opt.ftoptsvers = FT_OPTSV1;
opt.ftapivers = FT_APIV2;
/* Read the contents of the remote directory */
count = ft_showdir(&admis, &par, buf, BUFSIZE, &errorinfo,
&opt);
if (count == -1)
{
/* Error */
error_print(&errorinfo, par.fud);
}
/* Display result of request */
printf("There are %d entries in remote directory %s\n",
count, argv[1]);
/* If the output range was not large enough for all */
/* the information, only the data in the buffer is */
/* displayed */
if (count > BUFSIZE)
count = BUFSIZE;
for (i = 0; i < count; i++)
printinfo(&buf[i]);
return(0);
}
有人可以帮我在C#中实现同样的目标吗?甚至解释为什么必须这样,所以我们都可以从中学到一些东西:)
谢谢!
答案 0 :(得分:0)
第一个问题我看到你在C#结构声明中使用long类型。将其替换为int - C#long具有64位长度,而Win32 C中的long与int具有相同的大小 - 32位。
关于ft_fileinfo * info参数:对于默认编组无法处理的每个非平凡参数,请使用低级Marshal类函数。函数需要info指向大小sizeof(fileinfo)* bufsize的非托管内存块。将此参数定义为IntPtr:
[DllImport("ftapi.dll", EntryPoint = "ft_showdir")] private static extern long ft_showdir(ft_admission admis, ref ft_shwpar par, IntPtr buf, int bufsize, ref ft_err errorinfo, ft_options options);
分配非托管内存块:
IntPtr f = Marshal.AllocHGlobal(Marshal.Sizeof(typeof(ft_fileinfo)) * bufsize);
将此参数传递给非托管函数。函数返回后,使用Marshal.PtrToStructure方法bufsize次读取fileinfo结构,通过SizeOf(ft_fileinfo)递增IntPtr。不要忘记使用Marshal.FreeHGlobal释放非托管内存块。
你可以看到,Marshal类允许在C语言级别编写互操作性代码。
答案 1 :(得分:0)
您的DllImport属性中的ft_fileinfo之前似乎缺少 ref 。我通过以下缩减重现了您的问题。在C ++中定义入口点:
// OneEntryPoint.cpp : Defines the exported functions for the DLL application.
//
#include "stdafx.h"
#include <stdio.h>
extern "C"
{
struct AStruct
{
char fn[11];
};
__declspec(dllexport) void AnEntry(AStruct* someStruct)
{
printf("A character %c", someStruct->fn[255]);
}
}
并在C#中重现通话:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace ConsoleApplication2
{
public struct AStruct
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 255)]
public string fn;
}
class Program
{
[DllImport("OneEntryPoint", CallingConvention = CallingConvention.Cdecl)]
static extern void AnEntry(AStruct somestruct);
static void Main(string[] args)
{
try
{
AStruct fred;
fred.fn = "hello world";
Program.AnEntry(fred);
Console.WriteLine("Done");
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
}
运行此命令并获得AccessViolationException(尝试读取\ write protected memory)。将ref放入并且结构被正确编组并且没有错误。
答案 2 :(得分:0)
好的,我明白了。
当查看函数的c声明时,我们可以看到所有参数都是通过引用或通过指针发送的。所以我只是修改了pinvoke声明,以便所有参数都在ref。
[DllImport("ftapi.dll", EntryPoint = "ft_showdir", CallingConvention = CallingConvention.Cdecl)]
private static extern Int32 ft_showdir(ref ft_admission admis, ref ft_shwpar par, ref ft_fileinfo[] buf, int bufsize, ref ft_err errorinfo, ref ft_options options);