none
如何引用DLL RRS feed

  • 問題

  •  

    我拿到了 一組SDK的DLL

    可是我無法引用他,還有方法嗎??

     

    方法一:參考=>無法參考

    方法二:[DllImport("Pnxsdk.dll")] =>找不到進入點

    方法三:LoadLibrary=>//System.AccessViolationException: 嘗試讀取或寫入受保護的記憶體。這通常表示其他記憶體已損毀。 於 MyFun(Boolean )
     

    方法二:

    ===========================================================================

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Runtime.InteropServices;

    namespace Pnxsdk
    {
        public class iniDisplay
        {
            public static int OpenDisplay(bool pbRGBMode)
            {
                //擷取卡初始化前的初始化
                //撷取卡初始化前的初始化
                bool bb = (MP4Sys_SetDisplayMode(pbRGBMode)) == 0; if (!bb) { return 0; }
                //錯誤情況="在 DLL 'Pnxsdk.dll' 中找不到名稱為 'MP4Sys_SetDisplayMode' 的進入點。"
                //错误情况="在 DLL 'Pnxsdk.dll' 中找不到名称为 'MP4Sys_SetDisplayMode' 的进入点。"
                //擷取卡初始化
                //撷取卡初始化
                int iCount = MP4Sys_InitDSPs(); if (iCount == 0) { return iCount; }
                return iCount;
            }

            public static int CloseDisplay()
            {
                int ii = MP4Sys_DeInitDSPs(); return ii;
            }

            [DllImport("Pnxsdk.dll")]
            private static extern int MP4Sys_SetDisplayMode(bool bRGBMode);
            [DllImport("Pnxsdk.dll")]
            private static extern int MP4Sys_InitDSPs();
            [DllImport("Pnxsdk.dll")]
            private static extern int MP4Sys_DeInitDSPs();

        }
         
    }

     

    方法三:

    ===========================================================================

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Runtime.InteropServices;   // 用 DllImport 需用此 命名空間
    using System.Reflection;                // 使用 Assembly 類需用此 命名空間
    using System.Reflection.Emit;           // 使用 ILGenerator 需用此 命名空間

    namespace Pnxsdk
    {
        public class dld
        {
            /// <summary>
            /// 參數傳遞方式枚舉 ,ByValue 表示值傳遞 ,ByRef 表示址傳遞
            /// </summary>
            public enum ModePass
            {
                ByValue = 0x0001,
                ByRef = 0x0002
            }
           
            /// <summary>
            /// 原型是 :HMODULE LoadLibrary(LPCTSTR lpFileName);
            /// </summary>
            /// <param name="lpFileName">DLL 文件名 </param>
            /// <returns> 函數庫模塊的句柄 </returns>
            [DllImport("kernel32.dll")]
            static extern IntPtr LoadLibrary(string lpFileName);
            /// <summary>
            /// 原型是 : FARPROC GetProcAddress(HMODULE hModule, LPCWSTR lpProcName);
            /// </summary>
            /// <param name="hModule"> 包含需調用函數的函數庫模塊的句柄 </param>
            /// <param name="lpProcName"> 調用函數的名稱 </param>
            /// <returns> 函數指針 </returns>

            [DllImport("kernel32.dll")]
            static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);
            /// <summary>
            /// 原型是 : BOOL FreeLibrary(HMODULE hModule);
            /// </summary>
            /// <param name="hModule"> 需釋放的函數庫模塊的句柄 </param>
            /// <returns> 是否已釋放指定的 Dll</returns>

            [DllImport("kernel32",EntryPoint="FreeLibrary",SetLastError=true)]
            static extern bool FreeLibrary(IntPtr hModule);
            /// <summary>
            /// Loadlibrary 返回的函數庫模塊的句柄
            /// </summary>

            private IntPtr hModule=IntPtr.Zero;

            /// <summary>
            /// GetProcAddress 返回的函數指針
            /// </summary>
            private IntPtr farProc=IntPtr.Zero;

            /// <summary>
            /// 裝載 Dll
            /// </summary>
            /// <param name="lpFileName">DLL 文件名 </param>
            public void LoadDll(string lpFileName)
            {
                hModule=LoadLibrary(lpFileName);
                if(hModule==IntPtr.Zero)
                throw(new Exception(" 沒有找到 :"+lpFileName+"." ));
            }
                    
            //若已有已裝載Dll的句柄,可以使用LoadDll方法的第二個版本:
            public void LoadDll(IntPtr HMODULE)
            {
                if(HMODULE==IntPtr.Zero)
                throw(new Exception(" 所傳入的函數庫模塊的句柄 HMODULE 為空 ." ));
                hModule=HMODULE;
            }

            /// <summary>
            /// 獲得函數指針
            /// </summary>
            /// <param name="lpProcName"> 調用函數的名稱 </param>

            public void LoadFun(string lpProcName)
            { // 若函數庫模塊的句柄為空,則拋出異常
                if (hModule == IntPtr.Zero)
                    throw (new Exception(" 函數庫模塊的句柄為空 , 請確保已進行 LoadDll 操作 !"));
                // 取得函數指針
                if (lpProcName == "MP4Sys_SetDisplayMode")
                {
                    farProc = (IntPtr)0x00009DB0;
                }
                else
                {
                    farProc = GetProcAddress(hModule, lpProcName);
                }
                // 若函數指針,則拋出異常
                if (farProc == IntPtr.Zero)
                    throw (new Exception(" 沒有找到 :" + lpProcName + " 這個函數的入口點 "));
            }

            /// <summary>
            /// 獲得函數指針
            /// </summary>
            /// <param name="lpFileName"> 包含需調用函數的 DLL 文件名 </param>
            /// <param name="lpProcName"> 調用函數的名稱 </param>
            public void LoadFun(string lpFileName, string lpProcName)
            { // 取得函數庫模塊的句柄
                hModule = LoadLibrary(lpFileName);
                // 若函數庫模塊的句柄為空,則拋出異常
                if (hModule == IntPtr.Zero)
                    throw (new Exception(" 沒有找到 :" + lpFileName + "."));
                // 取得函數指針
                farProc = GetProcAddress(hModule, lpProcName);
                // 若函數指針,則拋出異常
                if (farProc == IntPtr.Zero)
                    throw (new Exception(" 沒有找到 :" + lpProcName + " 這個函數的入口點 "));
            }

            /// <summary>
            /// 卸載 Dll
            /// </summary>
            public void UnLoadDll()
            {
                FreeLibrary(hModule);
                hModule = IntPtr.Zero;
                farProc = IntPtr.Zero;
            }

            //Invoke方法的第一個版本:
            /// <summary>
            /// 調用所設定的函數
            /// </summary>
            /// <param name="ObjArray_Parameter"> 實參 </param>
            /// <param name="TypeArray_ParameterType"> 實參類型 </param>
            /// <param name="ModePassArray_Parameter"> 實參傳送方式 </param>
            /// <param name="Type_Return"> 返回類型 </param>
            /// <returns> 返回所調用函數的 object</returns>
            public object Invoke(object[] ObjArray_Parameter,Type[] TypeArray_ParameterType,ModePass[] ModePassArray_Parameter,Type Type_Return)
            {
                // 下面 3 個 if 是進行安全檢查 , 若不能通過 , 則拋出異常
                if(hModule==IntPtr.Zero)
                throw(new Exception(" 函數庫模塊的句柄為空 , 請確保已進行 LoadDll 操作 !"));
                if(farProc==IntPtr.Zero)
                throw(new Exception(" 函數指針為空 , 請確保已進行 LoadFun 操作 !" ) );
                if(ObjArray_Parameter.Length!=ModePassArray_Parameter.Length)
                throw(new Exception(" 參數個數及其傳遞方式的個數不匹配 ." ) );
                // 下面是創建 MyAssemblyName 對象並設置其 Name 屬性
                AssemblyName MyAssemblyName = new AssemblyName();
                MyAssemblyName.Name = "InvokeFun";
                // 生成單模塊配件
                AssemblyBuilder MyAssemblyBuilder =AppDomain.CurrentDomain.DefineDynamicAssembly(MyAssemblyName,AssemblyBuilderAccess.Run);
                ModuleBuilder MyModuleBuilder =MyAssemblyBuilder.DefineDynamicModule("InvokeDll");
                // 定義要調用的方法 , 方法名為「 MyFun 」,返回類型是「 Type_Return 」參數類型是「 TypeArray_ParameterType 」
                MethodBuilder MyMethodBuilder =MyModuleBuilder.DefineGlobalMethod("MyFun",MethodAttributes.Public| MethodAttributes.Static,Type_Return,TypeArray_ParameterType);
                // 獲取一個 ILGenerator ,用於發送所需的 IL
                ILGenerator IL = MyMethodBuilder.GetILGenerator();
                int i;
                for (i = 0; i < ObjArray_Parameter.Length; i++)
                {// 用循環將參數依次壓入堆棧
                    switch (ModePassArray_ParameterIdea)
                    {
                        case ModePass.ByValue:
                            IL.Emit(OpCodes.Ldarg, i);
                            break;
                        case ModePass.ByRef:
                            IL.Emit(OpCodes.Ldarga, i);
                            break;
                        default:
                            throw (new Exception(" 第 " + (i + 1).ToString() + " 個參數沒有給定正確的傳遞方式 ."));
                    }
                }

                if (IntPtr.Size == 4)
                {// 判斷處理器類型
                    IL.Emit(OpCodes.Ldc_I4, farProc.ToInt32());
                }
                else if (IntPtr.Size == 8)
                {
                    IL.Emit(OpCodes.Ldc_I8, farProc.ToInt64());
                }
                else
                {
                    throw new PlatformNotSupportedException();
                }

                IL.EmitCalli(OpCodes.Calli, CallingConvention.StdCall, Type_Return, TypeArray_ParameterType);
                IL.Emit(OpCodes.Ret); // 返回值
                MyModuleBuilder.CreateGlobalFunctions();
                // 取得方法信息
                MethodInfo MyMethodInfo = MyModuleBuilder.GetMethod("MyFun");
                try
                {
                    return MyMethodInfo.Invoke(null, ObjArray_Parameter);// 調用方法,並返回其值
                }
                catch ( Exception e)
                {
                    throw new Exception( e.Message ,e.InnerException);
                }
            }

            //Invoke方法的第二個版本,它是調用了第一個版本的:       
            /// <summary>
            /// 調用所設定的函數
            /// </summary>
            /// <param name="IntPtr_Function"> 函數指針 </param>
            /// <param name="ObjArray_Parameter"> 實參 </param>
            /// <param name="TypeArray_ParameterType"> 實參類型 </param>
            /// <param name="ModePassArray_Parameter"> 實參傳送方式 </param>
            /// <param name="Type_Return"> 返回類型 </param>
            /// <returns> 返回所調用函數的 object</returns>
            public object Invoke(IntPtr IntPtr_Function,object[] ObjArray_Parameter,Type[] TypeArray_ParameterType,ModePass[] ModePassArray_Parameter,Type Type_Return)
            {
            // 下面 2 個 if 是進行安全檢查 , 若不能通過 , 則拋出異常
                if(hModule==IntPtr.Zero)
                throw(new Exception(" 函數庫模塊的句柄為空 , 請確保已進行 LoadDll 操作 !"));
                if(IntPtr_Function==IntPtr.Zero)
                throw(new Exception(" 函數指針 IntPtr_Function 為空 !" ) );
                farProc=IntPtr_Function;
                return Invoke(ObjArray_Parameter,TypeArray_ParameterType,ModePassArray_Parameter,Type_Return);
            }
        }
    }

    測試方式:

    ===========================================================================

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Text;
    using System.Windows.Forms;
    using Pnxsdk;

    namespace test2
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }
            /// <summary>
            /// 創建一個 dld 類對像
            /// </summary>      
            //private dld myfun = new dld();

            public dld myfun = null;
            private bool bb1 = false;
            private void Form1_Load(object sender, EventArgs e)
            {
                //bb1 = true;
                //bool bb = true;
                //test1
                //int ii = Pnxsdk.iniDisplay.OpenDisplay(bb);
                //錯誤情況="在 DLL 'Pnxsdk.dll' 中找不到名稱為 'MP4Sys_SetDisplayMode' 的進入點。"
                //错误情况="在 DLL 'Pnxsdk.dll' 中找不到名称为 'MP4Sys_SetDisplayMode' 的进入点。"

                //this.textBox1.Text = ii.ToString();
                //test2
                test2();
                //System.AccessViolationException: 嘗試讀取或寫入受保護的記憶體。這通常表示其他記憶體已損毀。 於 MyFun(Boolean )
                //System.AccessViolationException: 尝试读取或写入受保护的记忆体。这通常表示其他记忆体已损毁。 于 MyFun(Boolean )
            }

            private void Form1_FormClosed(object sender, FormClosedEventArgs e)
            {
                if (bb1) {Pnxsdk.iniDisplay.CloseDisplay();}
            }

            private void test2()
            {
                myfun = new dld();
                myfun.LoadDll("Pnxsdk.dll"); // 加載 "Pnxsdk.dll"
                myfun.LoadFun("MP4Sys_SetDisplayMode"); // 調入函數 MP4Sys_SetDisplayMode, "_count@4" 是它的入口,可通過 Depends 查看

                object[] Parameters = new object[] { (bool)true }; // 實參為 0
                Type[] ParameterTypes = new Type[] { typeof(bool) }; // 實參類型為 int

                dld.ModePass[] themode = new dld.ModePass[] { dld.ModePass.ByValue }; // 傳送方式為值傳

                Type Type_Return = typeof(int); // 返回類型為 int

                // 彈出提示框,顯示調用 myfun.Invoke 方法的結果,即調用 count 函數
                try
                {
                    //return MyMethodInfo.Invoke(null, ObjArray_Parameter);// 調用方法,並返回其值
                    string ss = myfun.Invoke(Parameters, ParameterTypes, themode, Type_Return).ToString();

                    this.Text = " 這是您裝載該 Dll 後第 " + ss + " 次點擊此按鈕。 " + " 挑戰杯 ";
                }
                catch (Exception ee)
                {
                    this.Text = ee.Message;
                    this.textBox1.Text = ee.InnerException.ToString();
                }

                myfun.UnLoadDll();
            }
        }
    }

     

    2008年10月15日 下午 01:31

解答

  • 如果是找不到進入點,是函數名錯誤,不是找不到 dll

     

    開樓的程式碼太亂,不知道現在到底是怎樣,請適當濃縮。

     

    一次以單一會發生錯誤的函式為例,請把下列資訊明確列示:

    1. 廠商提供的 SDK 說明

    2. 你程式碼的宣告及呼叫

    3. Dependency Walker 看到的該函數名全名

     

    這樣大家比較好討論,我看這篇覺得很花...

    2008年10月16日 上午 11:51

所有回覆

  • 你那個 DLL 應該是 Win32 型的 DLL,用第二種方法應該就可以做到,不過你那個 DLL 要和程式檔放在同一個目錄。

     

    2008年10月15日 下午 01:37
    版主
  •  

    報告老大

    一樣ㄟ

    找不到進入點

    連這裡"\bin\Debug"我也把他丟進去試了

    一樣找不到進入點

     

    2008年10月15日 下午 01:44
  • 是 FILE_NOT_FOUND 嗎?

    如果是 ENTRY NOT FOUND 的話,那這個 DLL 你確定是寫給 Windows 平台上用的?

    或者 ... 你那個是 COM DLL 卻沒註冊?

     

    (從一開始你就沒講那個 DLL 是哪種類的... 如果不知道,可以用 OLE View 開那個 DLL 看看,如果能看到介面宣告的話,那它就是 COM DLL)

    2008年10月15日 下午 01:59
    版主
  •  

    我也不知道那個DLL是啥東西

    我只是跟他們經理要的

    但是看他的檔案描述是:TriMedia Manager - C Run Time Server for NDK4.0

    ps:"可以用 OLE View 開那個 DLL 看看",那個是vs2005內建的嗎,我找不到ㄟ
    2008年10月15日 下午 02:28
  • 可以用這個看:

    http://www.dotblogs.com.tw/regionbbs/archive/2008/10/09/5637.aspx

     

    如果匯出函數中有 DllRegisterServer(), DllUnregisterServer(), DllGetClassObject() 這些函數的話,代表這個 DLL 很有可能是 COM DLL,不然應該就是 Win32 DLL。

     

    但我認為你問提供這個 DLL 的廠商或人比較快吧 ...

     

    2008年10月15日 下午 02:33
    版主
  •  

    "DllRegisterServer(), DllUnregisterServer(), DllGetClassObject() "

    沒有

    "提供這個 DLL 的廠商或人比較快吧"

    他說不會,他只負責賣,我有問他有沒有SDK可是我忘了問他會不會.....

    他說會把問題後送,可是"好久阿...."

    等了好幾天了...沒消沒息的......

    快把那一張擷取卡丟了說.....
    2008年10月15日 下午 02:50
  • 1. 觀察DLL

    打開 Visual Studio 2005 Command Prompt , 在功能表的Visual Studio Tools (假設你用VS2005, 其它也都差不多)

    執行 dumpbin /exports 想要觀察的DLL(full path)

    看你要用的函式到底有沒有 export 出來吧 (個人猜想會不會只是沒有 function name, 這時需要對應impoort lib)

     

    2. 另外你執行測試程式的時候, 如果用IDE直接執行, 測試程式的路徑可能不是你所想的

    可以直接將DLL 擺在 \windows\system32 下 或是先找出你的執行路徑(透過 Application.StartupPath or Directory.GetCurrentDirectory())

    2008年10月16日 上午 01:54
  • 結果

    ==============================================================

    Microsoft (R) COFF/PE Dumper Version 8.00.50727.42
    Copyright (C) Microsoft Corporation.  All rights reserved.


    Dump of file f:\Pnxsdk.dll

    File Type: DLL

      Section contains the following exports for tmSDK.dll

        00000000 characteristics
        461DD4C8 time date stamp Thu Apr 12 14:42:16 2007
            0.00 version
               1 ordinal base
              56 number of functions
              56 number of names

        ordinal hint RVA      name

              1    0 00009DC0 MP4Sys_AllocMemoryForRealTimeVideo
              2    1 000096F0 MP4Sys_AutoBrightDynamicCtrl
              3    2 00009570 MP4Sys_AuxCodecCtrl
              4    3 000055C0 MP4Sys_CaptureIFrame
              5    4 00004160 MP4Sys_ChannelClose
              6    5 00004040 MP4Sys_ChannelOpen
              7    6 00009300 MP4Sys_ConfigAuxCodecMode
              8    7 00004F70 MP4Sys_ConfigCodecAttribute
              9    8 00008750 MP4Sys_ConfigLogoAttr
             10    9 00008BC0 MP4Sys_ConfigMosaicAttr
             11    A 00006600 MP4Sys_ConfigMotionDetecter
             12    B 00006C50 MP4Sys_ConfigOsdAttr
             13    C 00003D60 MP4Sys_DeInitDSPs
             14    D 00009F50 MP4Sys_FreeMemoryForRealTimeVideo
             15    E 00009880 MP4Sys_GetAuxCodecPictureHeader
             16    F 00004DC0 MP4Sys_GetBoardInfo
             17   10 00004CF0 MP4Sys_GetCapability
             18   11 00009C70 MP4Sys_GetChannelInfo
             19   12 0000A470 MP4Sys_GetDSPIndex
             20   13 000054A0 MP4Sys_GetFramesStatistics
             21   14 0000AA70 MP4Sys_GetInfo
             22   15 00004D20 MP4Sys_GetLastErrorNum
             23   16 0000B500 MP4Sys_GetLastSysError
             24   17 00004CD0 MP4Sys_GetSDKVersion
             25   18 00004EF0 MP4Sys_GetStreamType
             26   19 00003F90 MP4Sys_GetTotalEncChannels
             27   1A 00008FE0 MP4Sys_GetVideoPara
             28   1B 00008F10 MP4Sys_GetVideoSignal
             29   1C 00003730 MP4Sys_InitDSPs
             30   1D 00005AF0 MP4Sys_LoadYUVFromBmpFile
             31   1E 000068A0 MP4Sys_MotionAnalyzer
             32   1F 0000A6B0 MP4Sys_ReadBoardInfo
             33   20 0000A310 MP4Sys_ReadRealTimeVideo
             34   21 00004B10 MP4Sys_ReadStreamData
             35   22 0000A430 MP4Sys_RegisterImageStreamCallback
             36   23 00004B00 MP4Sys_RegisterLogRecordCallback
             37   24 00004AC0 MP4Sys_RegisterMessageNotifyHandle
             38   25 00004A80 MP4Sys_RegisterStreamReadCallback
             39   26 00004A70 MP4Sys_ResetDSP
             40   27 00004790 MP4Sys_RestoreOverlay
             41   28 00004DB0 MP4Sys_SDKErrorNum
             42   29 00005F80 MP4Sys_SaveYUVToBmpFile
             43   2A 0000B540 MP4Sys_SetAudioListen
             44   2B 00005720 MP4Sys_SetAudioPreview
             45   2C 00009DB0 MP4Sys_SetDisplayMode
             46   2D 00009AC0 MP4Sys_SetHorOffset
             47   2E 00009940 MP4Sys_SetLanguage
             48   2F 00004770 MP4Sys_SetOverlayColorKey
             49   30 0000A0B0 MP4Sys_SetRealTimeVideoAttr
             50   31 0000A510 MP4Sys_SetUserPassword
             51   32 000090D0 MP4Sys_SetVideoPara
             52   33 00005310 MP4Sys_SetVideoStandard
             53   34 0000B2D0 MP4Sys_UpdateDSPTime
             54   35 000047B0 MP4Sys_VideoCaptureCtrl
             55   36 00004260 MP4Sys_VideoPreviewCtrl
             56   37 0000A890 MP4Sys_WriteBoardInfo

      Summary

          34C000 .data
            2000 .rdata
            5000 .reloc
            1000 .rsrc
           16000 .text

    2008年10月16日 上午 02:01
  • 這樣表示有應該只是路徑問題

    現在你可以用方法2跟3

    之前應該只是DLL的路徑問題, 程式不知道 DLL在哪

    你可以看我前面建議的第二點

     

    2008年10月16日 上午 02:05
  • 該SDK一共有 Pnxsdk.dll,Pnxsdk.lib,Pnxman32.dll三支檔案

    類別庫

    ..\Pnxsdk\0.1\Pnxsdk0.2\

    ..\Pnxsdk\0.1\Pnxsdk0.2\bin\Debug\

    呼叫測試

    ..\Pnxsdk\0.1\test2

    ..\Pnxsdk\0.1\test2\bin\Debug

     

    以上的路徑我全部都有放這3支檔案

    2008年10月16日 上午 02:26
  • Key point 擺對路徑

    不是到處擺 請回頭看上面的建議 或是參考"心冷熱情熄" 建議的那些討論

    從 dumpbin 可以看出來有那些function

    只是找不到 DLL

    2008年10月16日 上午 07:03
  •  

    很好的建議

    基本上我也看過了該文章

    引用DLL的文章我找了5天看了N篇

    也了解到位置的重要

    只是還是找不到進入點

    所以不是不會放,是我給他通通放,至少去除了找不到該DLL的疑慮.

    2008年10月16日 上午 10:09
  • 如果是找不到進入點,是函數名錯誤,不是找不到 dll

     

    開樓的程式碼太亂,不知道現在到底是怎樣,請適當濃縮。

     

    一次以單一會發生錯誤的函式為例,請把下列資訊明確列示:

    1. 廠商提供的 SDK 說明

    2. 你程式碼的宣告及呼叫

    3. Dependency Walker 看到的該函數名全名

     

    這樣大家比較好討論,我看這篇覺得很花...

    2008年10月16日 上午 11:51
  • 请教一下.这个要怎么解决谢谢,我也有这样的问题

    2012年2月24日 上午 08:46