我定义了以下 P/Invoke:
[DllImport("helper.dll", CallingConvention=CallingConvention.Cdecl,
CharSet=CharSet.Ansi, EntryPoint="F_GetValue")]
private static extern Int32 _F_GetValue(String Formula, ref DATA_STRUCT Data,
ref DATA_KEY DefaultKeyBuf, ref Double Result);
此调用在 Windows Vista 及更高版本上成功,但在 Windows XP 上失败并出现以下内存异常:
A first chance exception of type 'System.AccessViolationException' occurred
in helper.dll
我尝试将前两个“ref”修饰符更改为 [In, Out] 但这并没有解决问题。
DATA_STRUCT 和 DATA_KEY 都是实例化和预填充的结构。
这是我正在调用的 C++ 方法定义:
int F_GetValue(const char* pFormula, DATA_STRUCT* pData,
DATA_KEY* pDefaultKeyBuf, double* freturn)
我不是 P/invoke 大师,所以不要假设任何事情。这个定义的方式有什么明显的错误吗?是否有更多的封送处理(手动)?我觉得我可能遗漏了一些明显的东西。
编辑:根据要求,这里分别是 .NET 中的结构定义、C++ F_GetValue() 方法和 C++ 结构定义:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public struct DATA_STRUCT
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public String DataDir;
[MarshalAs(UnmanagedType.U2)]
public UInt16 LTType;
[MarshalAs(UnmanagedType.U2)]
public UInt16 FOMType;
[MarshalAs(UnmanagedType.U2)]
public UInt16 ResultType;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 136)] // ( POS_BLOCK_SIZE + sizeof(int) + 4 )
public Byte[] posBlock;
public DATA_REC dataBuf;
[MarshalAs(UnmanagedType.U2)]
public UInt16 dataLen;
[MarshalAs(UnmanagedType.U2)]
public UInt16 keyNum;
public DATA_KEY keyBuf;
[MarshalAs(UnmanagedType.U2)]
public UInt16 TNTC;
[MarshalAs(UnmanagedType.I2)]
public Int16 status;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public struct DATA_KEY
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 17)]
public String LocName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 17)]
public String ParName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 13)]
public String DateTime;
}
int F_GetValue(const char* pFormula, DATA_STRUCT* pData, DATA_KEY* pDefaultKeyBuf, double* freturn)
{
if (pFormula[0] == 0) // return quickly if nothing to do
{
*freturn = blank;
pData->ResultType = sbit(DATA_BLANK);
pData->status = B_NO_ERROR;
return 0;
}
if ((_strnicmp(pFormula, "DOTNET_", 7) == 0) || (_strnicmp(pFormula, "(DOTNET_", 7) == 0)) // switch to/from dotnet
{
dotnetCalcs = (_strnicmp(pFormula + 7 + ((pFormula[0] == '(') ? 1 : 0), "ON", 2) == 0);
*freturn = dotnetCalcs ? 1 : 0;
pData->ResultType = sbit(DATA);
pData->status = B_NO_ERROR;
return strlen(pFormula);
}
BOOL bComingFromDotNet = (pData->dataLen == 65535);
if (dotnetCalcs && (!bComingFromDotNet))
{
return F_GetValue2(pFormula, pData, pDefaultKeyBuf, freturn);
}
if (pSharedMem->bClient && ! bServer)
{
if (FromServer(ACTION_OPEN,NULL) == B_NO_ERROR)
{
((CS_FORMULA *)pSharedMem->ClientServer)->nRecords = 1;
strcpy(((CS_FORMULA *)pSharedMem->ClientServer)->DataDir,pData->DataDir);
memcpy(&((CS_FORMULA *)pSharedMem->ClientServer)->Formula[0].DefaultKeyBuf,pDefaultKeyBuf,sizeof(DATA_KEY));
strcpy(((CS_FORMULA *)pSharedMem->ClientServer)->Formula[0].Formula,pFormula);
((CS_FORMULA *)pSharedMem->ClientServer)->iType = CSTYPE_FORMULA;
((CS_FORMULA *)pSharedMem->ClientServer)->iAction = ACTION_READ;
if (FromServer(ACTION_READ,NULL) == B_NO_ERROR)
{
*freturn = ((CS_FORMULA_RESULT *)pSharedMem->ClientServer)->Data[0].Data;
pData->ResultType = ((CS_FORMULA_RESULT *)pSharedMem->ClientServer)->Data[0].ResultType;
pData->status = ((CS_FORMULA_RESULT *)pSharedMem->ClientServer)->Data[0].status;
FromServer(ACTION_CLOSE,NULL);
return strlen(pFormula);
}
FromServer(ACTION_CLOSE,NULL);
}
*freturn = blank;
return 0;
}
else
{
BOOL bOpenTemporaryData = bComingFromDotNet || (pData->dataLen == 0);
if (bOpenTemporaryData)
{
DataOpenAndInitialize(pData,NULL);
}
int iReturn = F_DoGetValue(pFormula,pData,pDefaultKeyBuf,freturn);
if (bOpenTemporaryData)
DataExec(B_CLOSE,pData);
return iReturn;
}
}
typedef struct
{
char DataDir[MAX_PATH];
unsigned short LTType;
unsigned short FOMType;
unsigned short ResultType;
BTI_BYTE posBlock[POS_BLOCK_SIZE_];
DATA_REC dataBuf;
BTI_WORD dataLen;
BTI_WORD keyNum;
DATA_KEY keyBuf;
unsigned short TNTC;
BTI_SINT status;
} DATA_STRUCT;
typedef struct
{
char LocName[LP_SIZE];
char ParName[LP_SIZE];
char DateTime[13];
} DATA_KEY;
为了完整起见,我还包括了这个方法,F_GetValue2(),它在 F_GetValue() 中调用。虽然这看起来好像可以直接运行回托管代码,但它不会。这个方法的存在是为了不同的目的,我不能保证在我的 XP 问题的情况下它不会被调用,因为它需要 (dotnetCalcs && (!bComingFromDotNet)) 为真,但事实并非如此。
还有一件事,其中调用的另一个方法是 F_DoGetValue(),然后将一组参数传递给它。这个方法很大,所以我不会在这里发布。但只要说它解析 Formula 并使用它所学的调用更多的方法就足够了,这些方法使用解析的字符串从数据库中获取数据,将 Double 成员 fReturn 返回到链上,直到最终返回到通过封送处理的 C# 代码。