仅在Windows XP上诊断失败的P/Invoke调用需要帮助
本文关键字:Invoke 调用 帮助 XP Windows 诊断 失败 仅在 | 更新日期: 2023-09-27 18:10:00
我定义了以下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大师,所以不要想当然。这个定义方式有什么明显的错误吗?是否还有更多封送处理要进行(手动(?我觉得我可能错过了一些显而易见的东西。
EDIT:根据要求,这里分别是.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#代码。
[StructLayout(..., Pack = 1)]
C代码中没有可见的#pragma包,本地代码中的结构包实际上是1的可能性很低。默认值为8,与[StructLayout]的默认值相同。最低限度的健全性检查是,C#中结构类型的Marshal.SizeOf((返回的值与C代码中的SizeOf((完全相同。当出现不匹配并且确实可能出现随机AV时,它将无法正常工作。
并使用调试器对AV进行诊断。Project+Properties,Debug选项卡,勾选非托管代码调试选项,这样您就可以调试C#和C代码。在C函数的第一条语句上设置断点。并检查传递的结构指针的调试视图是否与您在C#代码中分配的数据匹配。故障通常位于结构末端附近。Debug+Exceptions,勾选Win32异常的抛出框,以便在AV异常发生时停止调试器。
最终,问题与证据所建议的完全不同(特别是考虑到我对p/Invoke的了解程度(。事实上,问题是使用这个声明的结果:
__declspec( thread ) BOOL dotnetCalcs = FALSE;
在进入代码后,我发现它在以下行失败:
if (dotnetCalcs && (!bComingFromDotNet))
成员"dotnetCalcs"被声明为线程本地存储,经过一些研究,它似乎是XP上已知的失败。我发现的一个例子线索是MSDN页面末尾的评论:
http://msdn.microsoft.com/en-us/library/9w1sdazb(v=vs.80(.aspx
关于延迟加载的部分适用于此实例,因为有问题的DLL是由于DllImport而加载的。
感谢所有回应的人,我很抱歉引起了大家的关注。但最终,建立一个专门的调试站的麻烦证明是值得的。
修复:
用对TLS方法的调用替换__declspec(线程(方法。在DllMain((中,我建立了一个TLS索引并全局保存它,用TlsSetValue((设置初始值。然后,所有后续的请求者都使用该索引通过TlsGetValue((检索TLS值。每当值发生变化时,只需再次使用TlsGetValue((来设置值。始终记得使用LPVOID进行强制转换,因为这就是这些类型的使用。