1

次の 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

最初の 2 つの "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_GetValue() で呼び出されるこのメソッド F_GetValue2() も含めます。これはマネージ コードに戻って実行されているように見えますが、そうではありません。このメソッドは別の目的で存在します。私の XP の問題の場合に呼び出されないことを保証することはできません。(dotnetCalcs && (!bComingFromDotNet)) が true である必要があるためです。

もう 1 つ、そこで呼び出されるもう 1 つのメソッドは F_DoGetValue() であり、一連のパラメーターがそれに渡されます。その方法は膨大なので、ここには掲載しません。ただし、Formula を解析し、学習したものを使用して、解析された文字列を使用してデータベースからデータをフェッチし、Double メンバー fReturn をチェーンに戻して、最終的に元に戻すまでチェーンを戻すと言えば十分です。マーシャリングによる C# コード。

4

2 に答える 2