none
在Dll裡釋放記憶體,執行時會出現的"呼叫已使堆疊失去平衡"有辦法解決嗎? RRS feed

  • 問題

  • 請問在Dll裡釋放IntPtr的記憶體,執行時會出現的"呼叫已使堆疊失去平衡"有辦法解決嗎?

    Dll:

    extern "C" __declspec(dllexport) void DeleteTestClass( CTestClass* instance )
    {
        delete instance;
        instance = NULL;
    }

     

    主程式:

    public class DFF
            {
                [System.Runtime.InteropServices.DllImport("T.dll",
                EntryPoint = "?DoSomething@CTestClass@@QAEHH@Z",
                CallingConvention = System.Runtime.InteropServices.CallingConvention.ThisCall)]
                public static extern int TestThisCalling(IntPtr ths, int i);

     
                [System.Runtime.InteropServices.DllImport("T.dll")]
                public static extern IntPtr CreateTestClass();


                [System.Runtime.InteropServices.DllImport("T.dll")]
                public static extern void DeleteTestClass(IntPtr instance);
            }

            public Form1()
            {
                InitializeComponent();
            }

            private void button1_Click(object sender, EventArgs e)
            {
                IntPtr instancePtr = DFF.CreateTestClass();
                int res = DFF.TestThisCalling(instancePtr, 9);
                label1.Text = Convert.ToString(res);
                DFF.DeleteTestClass(instancePtr);//呼叫已使堆疊失去平衡
            }

     

    謝謝!!

    2010年9月17日 上午 06:35

解答

  • 已經可以了! 分享一下結果!

    方法1:

     

    //加入__stdcall
    extern "C" MY36_API CMy36* __stdcall CreateMy36()
    extern "C" MY36_API void __stdcall DeleteMy36(CMy36* instance)

     

    // 36.cpp : 定義 DLL 應用程式的匯出函式。
    #include "stdafx.h"
    #include "36.h"
    
    MY36_API int fnMy36(void)
    {
    	return 42;
    }
    
    
    CMy36::CMy36()
    {
    	this->m_i = 100;
    	return;
    }
    
    CMy36::~CMy36()
    {
    	
    }
    
    extern "C" MY36_API CMy36* __stdcall CreateMy36()
    {
    	return new CMy36();
    }
    
    extern "C" MY36_API void __stdcall DeleteMy36(CMy36* instance)
    {
    	delete instance;
    }
    
    // 36.h
    #ifdef MY36_EXPORTS
    #define MY36_API __declspec(dllexport)
    #else
    #define MY36_API __declspec(dllimport)
    #endif
    
    
    class MY36_API CMy36 {
    public:
    	CMy36(void);
    	~CMy36(void);
    	// TODO: 在此加入您的方法。
    	int m_i;
    	
    };
    
    MY36_API int fnMy36(void);
    
    extern "C" MY36_API CMy36* __stdcall CreateMy36();
    extern "C" MY36_API void __stdcall DeleteMy36(CMy36* instance);

    //-------------------------------------------------------------------------------------------------------------------------//

    public class DLLWrapper
        {
          
          [System.Runtime.InteropServices.DllImport("36.dll")]
          public static extern IntPtr CreateMy36();
    
    
          [System.Runtime.InteropServices.DllImport("36.dll")]
          public static extern void DeleteMy36(IntPtr instance);
           
        }
    
        public Form1()
        {
          InitializeComponent();
        }
    
        private void button1_Click(object sender, EventArgs e)
        {
          IntPtr instancePtr = DLLWrapper.CreateMy36();
          DLLWrapper.DeleteMy36(instancePtr);
          instancePtr = IntPtr.Zero;  
        }

     

    //--------------------------------------------------------------------------------------------------------//

    方法2:(下策)

    在VS2010功能表的偵錯->例外狀況->Managed Debugging Assistants->PInvokeStaticImbalance的擲回選項取消掉

     

    謝謝!!

    • 已標示為解答 大雄 Uriel 2010年9月20日 上午 05:05
    2010年9月20日 上午 04:46

所有回覆

  • 用解決方案在 Form1 的專案中加入 DFF 專案,然後執行這個解決方案,就能同時除錯。

    通常是你在 dll 內已釋放了這個指標,然後你還要去存取。


    論壇是網友平等互助 保證解答請至 微軟技術支援服務
    提問時,錯誤情境描述與錯誤訊息很重要,情境描述包含你做了什麼,預期的結果與實際發生的結果。一個最爛的問法範例:「我的電腦電腦怎麼不能開機?」誰知道你家是不是沒電還是你根本找不到電源鈕。
    2010年9月17日 下午 04:44
  • >>用解決方案在 Form1 的專案中加入 DFF 專案,然後執行這個解決方案,就能同時除錯。

    請問這樣加入是否是正確呢?

    因為我不知道要怎麼在WindowsFormsApplication1專案(Form1專案) 中加入visual C++項目,所以在WindowsFormsApplication1方案(解決方案 ) 直接加入T專案(DLLorDFF 專案 ) !

    請問還要設定什麼嗎?如專案相依性

    >>然後執行這個解決方案,就能同時除錯。

    當我執行WindowsFormsApplication1方案(解決方案 ) 的時候,T專案 沒有自動將T.dll複製到\WindowsFormsApplication1\WindowsFormsApplication1\bin\Debug 路徑下,能讓他自動複製到該路徑嗎?

    但是有個問題是我照上面圖進行執行的時候還是有出現的"呼叫已使堆疊失去平衡"!!

    是我哪裡操做錯了嗎?

    感謝!

    2010年9月18日 上午 04:39
  • delete instance;

    這一行程式碼所刪除的instance指標必須是一個動態配置的結果(例如透過new或malloc取得的位址), 不可以是靜態配置的物件的位址

    2010年9月18日 上午 06:28
  • >>這一行程式碼所刪除的instance指標必須是一個動態配置的結果(例如透過new或malloc取得的位址), 不可以是靜態配置的物件的位址

    原程式:

    extern "C" __declspec(dllexport) CTestClass* CreateTestClass(void)
    {
    	return new CTestClass();
    }
    extern "C" __declspec(dllexport) void DeleteTestClass( CTestClass* instance )
    {
    	delete instance;
    	instance = NULL;
    }
    
    2010年9月18日 上午 08:26
  • "呼叫已使堆疊失去平衡"只會出現在DEBUG的時候,如果直接點選應用程式來執行是不會有問題的!
    2010年9月18日 上午 08:29
  • CTestClass類別的建構函式和解構函式的寫法也可能會有影響
    2010年9月19日 上午 07:33
  • 警告訊息:

    "其他資訊: 對 PInvoke 函式 'WindowsFormsApplication1!WindowsFormsApplication1.Form1+DFF::DeleteTestClass' 的呼叫已使堆疊失去平衡。這可能是因為 Managed PInvoke 簽章和 Unmanaged 目標簽章不相符。請確認 PInvoke 簽章的呼叫慣例及參數與目標 Unmanaged 簽章是否相符。

     

    // dllmain.cpp : 定義 DLL 應用程式的進入點。
    #include "stdafx.h"
    #define PINVOKELIB_EXPORTS
    #include "T.h"
    
    BOOL APIENTRY DllMain( HMODULE 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;
    }
    
    CTestClass::CTestClass()
    {
    	m_member = 1;
    }
    
    CTestClass::~CTestClass()
    {
    }
    
    int CTestClass::DoSomething( int i )
    {
    	return i*i + m_member;
    }
    
    extern "C" PINVOKELIB_API CTestClass* CreateTestClass(void)
    {
    	return new CTestClass();
    }
    
    extern "C" PINVOKELIB_API void DeleteTestClass( CTestClass* instance )
    {
    	delete instance;
    	instance = NULL;
    }

    //---------------------------------------------------------------------------------------------//

    //T.h
    #ifdef PINVOKELIB_EXPORTS
    #define PINVOKELIB_API __declspec(dllexport)
    #else
    #define PINVOKELIB_API __declspec(dllimport)
    #endif
    
    class PINVOKELIB_API CTestClass
    {
    public:
    	CTestClass( void );
    	int DoSomething( int i );
    	~CTestClass( void );
    
    private:
    	int m_member;
    };
    
    extern "C" PINVOKELIB_API CTestClass* CreateTestClass();
    extern "C" PINVOKELIB_API void DeleteTestClass( CTestClass* instance );

     

    2010年9月19日 上午 07:57
  • 看起來很正常, 從詳細的錯誤訊息來看, 有可能是製作成的DLL和.NET程式使用的DLL的版本不一致, 您可以先用C/C++程式使用製作好的DLL, 看看是否正常?
    2010年9月20日 上午 03:35
  • 已經可以了! 分享一下結果!

    方法1:

     

    //加入__stdcall
    extern "C" MY36_API CMy36* __stdcall CreateMy36()
    extern "C" MY36_API void __stdcall DeleteMy36(CMy36* instance)

     

    // 36.cpp : 定義 DLL 應用程式的匯出函式。
    #include "stdafx.h"
    #include "36.h"
    
    MY36_API int fnMy36(void)
    {
    	return 42;
    }
    
    
    CMy36::CMy36()
    {
    	this->m_i = 100;
    	return;
    }
    
    CMy36::~CMy36()
    {
    	
    }
    
    extern "C" MY36_API CMy36* __stdcall CreateMy36()
    {
    	return new CMy36();
    }
    
    extern "C" MY36_API void __stdcall DeleteMy36(CMy36* instance)
    {
    	delete instance;
    }
    
    // 36.h
    #ifdef MY36_EXPORTS
    #define MY36_API __declspec(dllexport)
    #else
    #define MY36_API __declspec(dllimport)
    #endif
    
    
    class MY36_API CMy36 {
    public:
    	CMy36(void);
    	~CMy36(void);
    	// TODO: 在此加入您的方法。
    	int m_i;
    	
    };
    
    MY36_API int fnMy36(void);
    
    extern "C" MY36_API CMy36* __stdcall CreateMy36();
    extern "C" MY36_API void __stdcall DeleteMy36(CMy36* instance);

    //-------------------------------------------------------------------------------------------------------------------------//

    public class DLLWrapper
        {
          
          [System.Runtime.InteropServices.DllImport("36.dll")]
          public static extern IntPtr CreateMy36();
    
    
          [System.Runtime.InteropServices.DllImport("36.dll")]
          public static extern void DeleteMy36(IntPtr instance);
           
        }
    
        public Form1()
        {
          InitializeComponent();
        }
    
        private void button1_Click(object sender, EventArgs e)
        {
          IntPtr instancePtr = DLLWrapper.CreateMy36();
          DLLWrapper.DeleteMy36(instancePtr);
          instancePtr = IntPtr.Zero;  
        }

     

    //--------------------------------------------------------------------------------------------------------//

    方法2:(下策)

    在VS2010功能表的偵錯->例外狀況->Managed Debugging Assistants->PInvokeStaticImbalance的擲回選項取消掉

     

    謝謝!!

    • 已標示為解答 大雄 Uriel 2010年9月20日 上午 05:05
    2010年9月20日 上午 04:46
  • 感謝各位的幫忙

    沒有想到只有差__stdcall ,因為DllImport的CallingConvention 預設為 CallingConvention.StdCall,所以加__stdcall 比較完整!

    謝謝!!

    2010年9月20日 上午 05:05