使用 .Net 4.0 时出现堆栈不平衡错误,但在使用 .Net 2.0 时不调用堆栈不平衡错误



我遇到了一个奇怪的问题:我的应用程序是2009/2011的一个非常旧的VS项目。今天我把它转换成VS 2012项目,能够很好地编译和运行。该应用程序由一个 C# 部件和一个 C/C++ DLL 组成,如下所示引用:

class clsBMS_KommInternal
{
    // initialization and deinitialization
    [DllImport("bms.dll", EntryPoint = "InitBMS_SM", SetLastError = true, CharSet = CharSet.Auto)]
    internal unsafe static extern bool _InitBMS_SM(UInt32* p_dwGroesse, ushort** pp_ptrBase, UInt32* p_dwError);
    [DllImport("bms.dll", EntryPoint = "DeInitBMS_SM", SetLastError = true, CharSet = CharSet.Auto)]
    internal unsafe static extern bool _DeInitBMS_SM(UInt32* p_dwError);
    [DllImport("bms.dll", EntryPoint = "InitHeaderSM", SetLastError = true, CharSet = CharSet.Auto)]
    // internal unsafe static extern bool _InitHeaderSM(UInt32* p_dwError);
    internal unsafe static extern bool _InitHeaderSM(UInt32* p_dwError);
    [DllImport("bms.dll", EntryPoint = "GetBasePointerBMS_SM", SetLastError = true, CharSet = CharSet.Auto)]
    internal unsafe static extern Byte* _GetBasePointerBMS_SM();
    [DllImport("bms.dll", EntryPoint = "GetBasePointerBMS_SMHeader", SetLastError = true, CharSet = CharSet.Auto)]
    internal unsafe static extern Byte* _GetBasePointerBMS_SMHeader();
    // Claim and release Mutex to GDB
    [DllImport("bms.dll", EntryPoint = "ClaimMutexBMS_SM", SetLastError = true, CharSet = CharSet.Auto)]
    internal static extern bool _ClaimMutexBMS_SM(UInt32 dwTimeout);
    [DllImport("bms.dll", EntryPoint = "ReleaseMutexBMS_SM", SetLastError = true, CharSet = CharSet.Auto)]
    internal static extern bool _ReleaseMutexBMS_SM();
}

如果我使用旧的项目设置(使用 .Net Framework 2.0),应用程序运行良好。但后来我想包含另一个 C# 程序集,这需要我切换到 .Net Framework 4.0。

现在我的代码失败了:

 UInt32 dwGroesse;
 UInt32 dwFehler;
 int iFehler;
 ushort* pBase;
 // Zuerst die allgemeine Initialisierung
 clsBMS_KommInternal._InitHeaderSM(&dwFehler);

Visual Studio给出以下消息:

PInvokeStackImbalance occurred
Message: Managed Debugging Assistant 'PInvokeStackImbalance' has detected a problem in 'D:WolfgangDocumentsVisual Studio 2015ProjectsBPara_CSWindowsFormsApplication1binx86DebugWindowsFormsApplication1.vshost.exe'.
Additional information: Ein Aufruf an die PInvoke-Funktion "WindowsFormsApplication1!FlexCommInternal.BMS_KommDLLImport.clsBMS_KommInternal::_InitHeaderSM" hat das Gleichgewicht des Stapels gestört. Wahrscheinlich stimmt die verwaltete PInvoke-Signatur nicht mit der nicht verwalteten Zielsignatur überein. Überprüfen Sie, ob die Aufrufkonvention und die Parameter der PInvoke-Signatur mit der nicht verwalteten Zielsignatur übereinstimmen.

对于这里的非德语读者,我会说,C# 替换了 中方法的正确签名。Net2.0 中有一个错误的 .净4.0

但是为什么?我该如何更正此错误?

问候沃尔夫冈

插件:

bms 库的源代码:

// bms.cpp : Definiert den Einstiegspunkt für die DLL-Anwendung.
//
#include "stdafx.h"
#include <stdio.h>
#include <winbase.h>
#include "include/bms.h"
//*****************************************************************************
//
// Defines
//
//*****************************************************************************
// Name des SharedMemory fuer die Bundmutter-Station
#define SHMEM_BPARA_ID "B-PARA-SM"
// Name des SharedMemory fuer die Header-Struktur
#define SHMEM_BPARA_HEADER_ID "B-PARA-SM_HEADER"
// Name des Mutex fuer den exclusiven Zugriff
#define SHMEM_BPARA_MUTEX "MUTEX_BPARA_SM"

#define TIMEOUT_MUTEX_BPARA_SM INFINITE 

//*****************************************************************************
//
// globale Variable
//
//*****************************************************************************
HANDLE hMutexSM    = 0 ; 
HANDLE hSMHeader    = 0 ;
HANDLE hSMBPARA       = 0 ;
// Zeiger auf das SharedMemory fuer die Header-Struktur
// ToDo: Typ definieren
psBMSDLLHEADER pSMBParaHeader = 0 ;
LPVOID pSharedMemBPara;   // Zeiger auf den Shared-Memory-Bereich
// internal functions
static BOOL CheckOffset(DWORD dwOffsetHighByte);
static BOOL CreateMutexBMS(DWORD* p_dwError);
static BOOL OpenMutexBMS(DWORD* p_dwError);



BOOL APIENTRY DllMain( HANDLE hModule, 
                      DWORD  ul_reason_for_call, 
                      LPVOID lpReserved
                      )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}
HANDLE OpenSharedMemory(DWORD DesiredAccess,BOOL bInheritHandle,LPCTSTR lpName,VOID ** location)
{
    HANDLE hMapFile;
    LPVOID pBuf;
    hMapFile = OpenFileMapping(
        FILE_MAP_ALL_ACCESS,   // read/write access
        bInheritHandle,                 // do not inherit the name
        lpName);               // name of mapping object 
    if (hMapFile == NULL || hMapFile == INVALID_HANDLE_VALUE) 
    { 
        return NULL;
    }
    pBuf = (LPVOID) MapViewOfFile(hMapFile,   // handle to map object
        FILE_MAP_ALL_ACCESS, // read/write permission
        0,                   
        0,                   
        0);           
    if (pBuf == NULL) 
    { 
        return NULL;
    }
    *location = pBuf;
    return hMapFile;
} 
HANDLE CreateSharedMemory(DWORD flProtect,DWORD MaximumSizeHigh,DWORD MaximumSizeLow,LPCTSTR lpName,VOID ** location)
{
    HANDLE hMapFile;
    VOID *pBuf;
    hMapFile = CreateFileMapping(INVALID_HANDLE_VALUE, NULL,flProtect, MaximumSizeHigh,MaximumSizeLow,lpName);
    if (hMapFile == NULL || hMapFile == INVALID_HANDLE_VALUE) 
    { 
        return NULL;
    }
    pBuf = (LPVOID) MapViewOfFile(hMapFile,   // handle to map object
        FILE_MAP_ALL_ACCESS, // read/write permission
        0,
        0,
        0);
    if (pBuf == NULL) 
    { 
        return NULL;
    }
    *location = pBuf;
    return hMapFile;
}

//****************************************************************************
//
//  static BOOL InitHeaderSM()
//  
//  SharedMemory fuer Header anlegen bzw. laden
//  
//  Parameter:
//      keiner
//
//  Return:
//      TRUE, wenn alles ok,
//      FALSE, bei Fehler
//
//****************************************************************************
BOOL InitHeaderSM(DWORD* p_dwError)
{
    LPVOID  pHeader;

    * p_dwError = kBMSSuccess;
    // Gibt es bereits ein SharedMemory fuer den Header , so bekomme ich hier das Handle zurueck
    hSMHeader = OpenSharedMemory(PAGE_READWRITE, FALSE, SHMEM_BPARA_HEADER_ID, (void **)&pHeader);
    // Es gab kein SharedMenory
    if(NULL == hSMHeader)
    {
        // SharedMenory explizit anlegen und initialisieren
        hSMHeader= CreateSharedMemory(PAGE_READWRITE, 0, sizeof(sBMSDLLHEADER), SHMEM_BPARA_HEADER_ID, (void **)&pHeader);
        // wenn SharedMenory vorhanden, initialisieren
        if(NULL != hSMHeader && NULL != pHeader)
        {
            //            ClaimMutexBMS(INFINITE);
            {
                memset(pHeader,0x00,sizeof(sBMSDLLHEADER));
                pSMBParaHeader = (psBMSDLLHEADER) pHeader;
                pSMBParaHeader->dwVersion   = SHMEM_BMS_SM_VERSION + sizeof(sBMSDLLHEADER);  // momentan gueltige Version der BMS
                pSMBParaHeader->dwClients ++;
            }
            //            ReleaseMutexBMS();
        }
        else
        {
            // error creating shared memory
            pHeader = NULL;
            pSMBParaHeader = (psBMSDLLHEADER)pHeader;
            if(NULL != p_dwError)
            {
                *p_dwError = kBMSErrorNotCreated;
            }
            return FALSE;
        }
    }
    else
    {
        // es gibt bereits ein SharedMemory fuer den Header
        // Zeiger auf den Header des SharedMenory wurde bereits durch
        // OpenSharedMemory gesetzt
        pSMBParaHeader = (psBMSDLLHEADER)pHeader;
        //        ClaimMutexBMS(INFINITE);
        {
            pSMBParaHeader->dwClients ++;
        }
        //        ReleaseMutexBMS();
        return TRUE;
    }
    pSMBParaHeader = (psBMSDLLHEADER)pHeader;
    return TRUE;

}
//****************************************************************************
//
//  static BOOL DeInitHeaderSM()
//  
//  SharedMemory entladen, bzw. loeschen
//  
//  Parameter:
//      keiner
//
//  Return:
//      TRUE, wenn alles ok,
//      FALSE, bei Fehler
//
//****************************************************************************
BOOL DeInitHeaderSM(DWORD* p_dwError)
{
    BOOL bErg;
    bErg  =TRUE;
    *p_dwError = kBMSSuccess;
    //    ClaimMutexBMS(INFINITE);
    {
        pSMBParaHeader->dwClients --;
        if(pSMBParaHeader->dwClients == 0)
        {
            if (hSMBPARA != 0)
            {
                CloseHandle(hSMBPARA);
                hSMBPARA = 0;
                pSMBParaHeader = NULL;
            }
            CloseHandle(hSMHeader);
            hSMHeader = 0;
            pSMBParaHeader = NULL;
        }
    }
    //    ReleaseMutexBMS();

    return bErg;
}
//****************************************************************************
//
//  Die Funktion erzeugt den shared memory-Bereich für die BMS und 
//  initialisiert mit NULL.
//  
//    Parameter:
//    p_dwSize - Pointer zur Größenangabe für die BMS 
//    pp_bBase - Pointer zur Rückgabe des Base Pointers
//    p_dwError - Pointer zur Rückgabe des Error-Codes
//    
//    Return:
//    BOOL - TRUE bei erfolgreichem Öffnen bzw. Erzeugung und Initialisierung, 
//           FALSE bei einem Fehler. siehe ErrorCode in p_dwError.
//      
//****************************************************************************
BOOL InitBMS_SM(DWORD* p_dwSize, BYTE** pp_bBase, DWORD* p_dwError)
{
    BOOL bErg = FALSE;
    if(NULL != p_dwError)
        *p_dwError = kBMSSuccess;
    // Ueberpruefen auf Parameter
    if(NULL == p_dwSize || NULL == pp_bBase)
    {
        if(NULL != p_dwError)
            *p_dwError = kBMSErrorInvalidParam;
        return bErg;
    }
    //    ClaimMutexBMS(INFINITE);
    // Gibt es bereits ein SharedMemory, so bekomme ich hier das Handle zurueck
    hSMBPARA= OpenSharedMemory(PAGE_READWRITE, FALSE, SHMEM_BPARA_ID, &pSharedMemBPara);
    // Es gab kein SharedMenory
    if(NULL == hSMBPARA)
    {
        // SharedMenory explizit anlegen und initialisieren
        hSMBPARA= CreateSharedMemory(PAGE_READWRITE, 0, *p_dwSize, SHMEM_BPARA_ID, &pSharedMemBPara);
        // wenn SharedMenory vorhanden, initialisieren
        if(NULL != hSMBPARA && NULL != pSharedMemBPara)
        {
            // Zeiger auf den Header des SharedMenory Durch Aufruf abgedeckt 

            // Adresse der BMS zurueck geben 
            *pp_bBase = (BYTE*)pSharedMemBPara;
            // Header-Infos beschreiben
            pSMBParaHeader->dwClients       = 1;                  // ich habe es angelegt, also 1
            pSMBParaHeader->dwUsers         = 1;                  // InitBMS wird gerade ausgefuehrt
            pSMBParaHeader->dwGroesse       = *p_dwSize;          // Groesse, so wie es der User moechte
            // initialize BMS (beginning after header of sharedmem)
            memset((BYTE*)pSharedMemBPara,0,*p_dwSize);
            bErg =TRUE;
        }
        else
        {
            // error creating shared memory
            *pp_bBase = NULL;
            if(NULL != p_dwError)
            {
                *p_dwError = kBMSErrorNotCreated;
            }
            bErg = FALSE;
        }
    }
    else
    {
        // es gibt bereits ein BMS
        // Zeiger auf den Header des SharedMenory wurde bereits durch
        // OpenSharedMemory gesetzt
        // Versionskontrolle
        if (pSMBParaHeader->dwVersion != SHMEM_BMS_SM_VERSION + sizeof(sBMSDLLHEADER))
        {
            if(NULL != p_dwError)
                *p_dwError = kBMSErrorInvalidDLLVersion;
            bErg = FALSE;
        }
        else
        {
            // aktuelle Groesse des BMS-Speichers zurueckgeben
            *p_dwSize = pSMBParaHeader->dwGroesse;
            // Anzahl Benutzer um eins erhoehen
            pSMBParaHeader->dwUsers++;
            *pp_bBase = (BYTE*)pSharedMemBPara;
            if(NULL != p_dwError)
                *p_dwError = kBMSErrorInitAlreadyDone;
            bErg = TRUE;
        }
    }
    //    ReleaseMutexBMS();
    return bErg;
}
//****************************************************************************
//
//  BOOL DeInitBMS(DWORD* p_dwError)
//  
//  Das BMS-Sharedmemory freigeben
//  
//  Parameter:
//    p_dwError - Pointer zur Rückgabe des Error-Codes
//    
//    Return:
//    BOOL - TRUE bei erfolgreichem Öffnen bzw. Erzeugung und Initialisierung, 
//           FALSE bei einem Fehler. siehe ErrorCode in p_dwError.
//****************************************************************************
BOOL DeInitBMS_SM(DWORD* p_dwError)
{
    BOOL bErg;
    * p_dwError = kBMSSuccess;
    //    ClaimMutexBMS(INFINITE);
    pSMBParaHeader->dwUsers--;
    if (pSMBParaHeader->dwUsers == 0 )
    {
        CloseHandle(hSMBPARA);
        hSMBPARA = NULL;
        pSMBParaHeader = NULL;
    }
    bErg = TRUE;
    //    ReleaseMutexBMS();
    return bErg;
}
//****************************************************************************
//
//  BYTE* GetBasePointerBMS()
//  
//  Den Zeiger auf den Anfang des SharedMemory zurueckgeben
//  
//  Parameter:
//      keiner
//
//  Return:
//      Byte-Zeiger
//
//
//****************************************************************************
BYTE* GetBasePointerBMS_SM()
{
    return (BYTE*) pSharedMemBPara;
}
//****************************************************************************
//
//  BYTE* GetBasePointerBMSHeader()
//  
//  Den Zeiger auf den Anfang des SharedMemory zurueckgeben
//  
//  Parameter:
//      keiner
//
//  Return:
//      Byte-Zeiger
//
//
//****************************************************************************
BYTE* GetBasePointerBMS_SMHeader()
{
    return (BYTE*) pSMBParaHeader;
}

// Read or write Bytes, Words, DWords or many Bytes from BMS_SM
BYTE ReadByteBMS_SM                      (DWORD dwOffset, DWORD* dwError)
{
return 0;
}
WORD ReadWordBMS_SM                      (DWORD dwOffset, DWORD* dwError)
{
return 0;
}
DWORD ReadDwordBMS_SM                    (DWORD dwOffset, DWORD* dwError)
{
return 0;
}
BOOL ReadManyBMS_SM                      (DWORD dwOffset, BYTE* p_bTarget, DWORD dwLength, DWORD* p_dwError)
{
return 0;
}
BOOL ReadManyBMS_SMDownward             (DWORD dwOffset, BYTE* p_bTarget, DWORD dwLength, DWORD* p_dwError)
{
return 0;
}
BOOL WriteByteBMS_SM                     (DWORD dwOffset, BYTE bWert, DWORD* dwError)
{
return 0;
}
BOOL WriteWordBMS_SM                     (DWORD dwOffset, WORD wWert, DWORD* dwError)
{
return 0;
}
BOOL WriteDwordBMS_SM                    (DWORD dwOffset, DWORD dwWert, DWORD* dwError)
{
return 0;
}
BOOL WriteManyBMS_SM                     (DWORD dwOffset, BYTE* p_bSource, DWORD dwLength, DWORD* p_dwError)
{
return 0;
}
BOOL WriteManyBMS_SMDownward            (DWORD dwOffset, BYTE* p_bSource, DWORD dwLength, DWORD* p_dwError)
{
return 0;
}

//****************************************************************************
//
//  Die Funktion erzeugt ein Mutex für den Zugriff auf das shared memory GDB.
//  
//    Parameter:
//    p_dwError - Pointer zur Rückgabe des Error-Codes
//    
//      Return:
//      TRUE - Mutex erfolgreich erzeugt
//      FALSE - Fehler beim Erzeugen
//      
//****************************************************************************
BOOL CreateMutexBPARA_SM(DWORD* p_dwError)
{
    // requests MUTEX_ALL_ACCESS access to the existing mutex object
    hMutexSM = CreateMutex(NULL, FALSE, SHMEM_BPARA_MUTEX);
    if(NULL == hMutexSM)
    {
        if(NULL != p_dwError)
            *p_dwError = kBMSErrorMutexNotCreated;
        return FALSE;
    }
    return TRUE;
}
//****************************************************************************
//
//  Die Funktion öffnet das Mutex für den Zugriff auf das shared memory GDB.
//  Das Mutex muss zuvor mit CreateMutexGDB erzeugt werden. Andernfalls wird 
//  FALSE zurückgegeben.
//  
//    Parameter:
//    p_dwError - Pointer zur Rückgabe des Error-Codes
//    
//      Return:
//      TRUE - Mutex erfolgreich geöffnet
//      FALSE - Mutex nicht geöffnet
//      
//****************************************************************************
BOOL OpenMutexBPARA_SM(DWORD* p_dwError)
{
    // The first two parameters are ignored (see RTX 6.0 documentation)
    hMutexSM = OpenMutex(NULL, FALSE, SHMEM_BPARA_MUTEX);
    if(NULL == hMutexSM)
    {
        if(NULL != p_dwError)
            *p_dwError = kBMSErrorMutexNotOpened;
        return FALSE;
    }
    return TRUE;
}
//****************************************************************************
//
//  Die Funktion wartet auf die Freigabe des Mutex für den Zugriff auf die GDB. 
//  
//    Parameter:
//    dwTimeout - Maximale Wartezeit in ms. Nach Verstreichen der Zeit wird
//                die Mutexanforderung abgebrochen.
//    
//      Return:
//      TRUE - Mutex erfolgreich angefordert
//      FALSE - Mutex nicht angefordert
//      
//****************************************************************************
BOOL ClaimMutexBMS_SM(DWORD dwTimeout)
{
    DWORD dwWaitResult;
    DWORD dwError;
    if(NULL == hMutexSM)
    {
        if(!OpenMutexBPARA_SM(&dwError))
            CreateMutexBPARA_SM(&dwError);
    }
    dwWaitResult = WaitForSingleObject(hMutexSM, dwTimeout);
    switch (dwWaitResult) 
    {
        // The thread got mutex ownership.
    case WAIT_OBJECT_0: 
        return TRUE;
        // Cannot get mutex ownership due to time-out.
    case WAIT_TIMEOUT: 
        return FALSE; 
        // Got ownership of the abandoned mutex object.
    case WAIT_ABANDONED: 
        return FALSE; 
    }
    return FALSE;
}
//****************************************************************************
//
//  Die Funktion gibt das Mutex für den Zugriff auf die GDB wieder frei.
//  
//    Parameter:
//      keine
//    
//      Return:
//      TRUE - Mutex freigegeben
//      FALSE - Mutex nicht freigegeben
//      
//****************************************************************************
BOOL ReleaseMutexBMS_SM()
{
    if(NULL != hMutexSM)
    {
        return ReleaseMutex(hMutexSM);
    }
    else
        return FALSE;
}

C++ DLL 代码的默认调用约定是 __cdecl

另一方面,PInvoke 使用的默认调用约定是 StdCall ,即 本机代码__stdcall(这是用于调用 Win32 API 的调用约定)。

因此,您有一个调用约定不匹配:基本上,调用方代码和被调用代码在诸如如何在堆栈上传递参数、谁负责从堆栈中弹出参数等问题上存在分歧,从而导致堆栈不平衡错误,这在您的情况下是正确检测到的。

如果不想修改本机 C++ DLL,则应在 PInvoke 声明中显式指定调用约定,并添加CallingConvention=CallingConvention.Cdecl规范,例如:

[DllImport("bms.dll", ..., CallingConvention=CallingConvention.Cdecl)
...

最新更新