发布新日志

  • 在C#中显示加载C++的Dll

    2009-02-24 14:15:14

        class NativeMethod

        {

            public delegate int Operation(int m,int n);

     

            [DllImport("kernel32", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]

            public static extern IntPtr LoadLibraryExA(string filePath,IntPtr hFile,int flag);

     

            [DllImport("kernel32", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]

            public static extern Operation GetProcAddress(IntPtr hModule,string procName);

     

            [DllImport("kernel32", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]

            public static extern bool FreeLibrary(IntPtr hModule);

        }

     

        class Program

        {

            static void Main(string[] args)

            {

                IntPtr handle = NativeMethod.LoadLibraryExA(@"E:\Program Files\Microsoft Visual Studio\MyProjects\DllLib\Debug\DllLib",IntPtr.Zero,0);

                NativeMethod.Operation Add = NativeMethod.GetProcAddress(handle,"Add");

                int result = Add(2,3);

                NativeMethod.FreeLibrary(handle);

            }

        }

  • 【转载】模块间(dll, exe)使用导出变量、静态变量和外部变量的试验与结论

    2008-11-18 13:45:47

    // Dll头文件声明

    AFX_EXT_DATA int D1_nCount;

    extern int D1_nCountE;

    static int D1_nCountS = 0;

    AFX_EXT_API int D1_GetCount();

    // 结论:多模块调用时,或重复调用时,模块内的静态变量是唯一的,不会重复分配内存
    int nCount = D1_GetCount();
    nCount = D2_GetCount();
    nCount = D1_GetCount();

    // 结论:dll中声明的静态变量在每个使用的cpp下均有一份拷贝,多模块更是如此(静态变量在编译时会分别拷贝)
    nCount = D1_nCountS;
    D1_nCountS = 2;

    // 结论:dll中声明的外部变量不能用于其他模块,链接失败(基地址都不一样,肯定不行)
    nCount = D1_nCountE;
    D1_nCountE = 2;

    // 结论:dll中导出的数据到处都可以用,在模块内部只有一份拷贝,每个用到的模块各有一份拷贝
    nCount = D1_nCount;
    D1_nCount = 2;

    ---- 2008年6月2日

    以下后续试验得出了新的结论:

    // Dll头文件声明

    #ifdef DLL2_EXPORTS
    #define D2_API        __declspec(dllexport)
    #else
    #define D2_API        __declspec(dllimport)
    #endif

    D2_API extern int D2_nCount;

    // 结论:dll中导出变量在其所在模块为导出,在其他模块必须为导入
    // 结论:dll中导出的外部变量到处都可以用,在所有模块中都只有一份拷贝
    // 结论:dll中导出的变量到处都可以用,在所有模块中都只有一份拷贝,
    // 但是在其所在模块只能包含一次(否则为重定义),所以只能声明为外部变量
    nCount = D2_nCount;
    D2_nCount = 2;

    // 总结论:dll中的变量一般情况下是以函数接口形式导出,
    // 但在某些情况下可能需要在模块间直接使用,那么就有两种方法:
    // 如果需要该变量在每个模块都有一份拷贝,那么可以使用static声明,
    // 不过这种用途一般比较少。。还有就是使用__declspec(dllexport) extern声明,
    // 一般都是需要各个模块公用一份拷贝,注意在其他模块使用时需要用
    // __declspec(dllexport)导入该变量,否则就又是导出了,而且没有实现

  • dll_5 vtable 布局对显式使用dll导出类的影响 -- 转载

    2008-11-18 13:44:54

    根据我对vc的试验得出的结论如下:

    vc对vatabled的布局是如下确定的

    // This class is exported from the trydll2.dll
    class  __declspec(dllexport)   CTrydll2 {
    public:
     CTrydll2(void);
     virtual void diplay();
     virtual int fnp(int i);
     // TODO: add your methods here.
    }; 

    vtbale布局如下:

    display()//第零个元素

    fnp(int)//第一个元素

    、。。也就是根据声明的顺序来决定他们在 vatable中的顺序。

    如果要在客户端中正确使用则生民顺需要一致!!!

    一下是我得错误代码:

    ///////////////////////trydll2.h///////////////

    #ifdef TRYDLL2_EXPORTS
    #define TRYDLL2_API __declspec(dllexport)
    #else
    #define TRYDLL2_API __declspec(dllimport)
    #endif

    // This class is exported from the trydll2.dll
    class TRYDLL2_API CTrydll2 {
    public:
     CTrydll2(void);
     virtual void diplay();
     virtual int fnp(int i);
     // TODO: add your methods here.
    };

    //extern TRYDLL2_API int nTrydll2;//·ÅÔÚÕâÀï²»»áÖض¨Òå!!


    extern "C"
    {
    TRYDLL2_API int nTrydll2;
    TRYDLL2_API CTrydll2 * GetInstance();
    TRYDLL2_API int fnTrydll2(void);
    TRYDLL2_API int fnp(int para);
    };
    、、、、、、、trydll2.cpp、、、、、、、、、

    #include "stdafx.h"
    #include "trydll2.h"
    //..............................
    #include <iostream>
    using namespace std;
    ///..............................

    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;
    }


    // This is an example of an exported variable
    //TRYDLL2_API int nTrydll2=0;//½«µ¼ÖÂÖض¨Òå
    extern "C" TRYDLL2_API int nTrydll2=0;

    // This is an example of an exported function.
    TRYDLL2_API int fnTrydll2(void)
    {
     cout<<"fnTrydll2 \n";
     return 42;
    }

    TRYDLL2_API CTrydll2 * GetInstance()
    {
     CTrydll2 *p=new CTrydll2;
     return p;
    }
    TRYDLL2_API int fnp(int para)
    {
    cout<<"fnp:"<<para<<endl;
    return para=1;
    }
    // This is the constructor of a class that has been exported.
    // see trydll2.h for the class definition
    CTrydll2::CTrydll2()
    {
     return;
    }

    void CTrydll2::diplay()
    {
     cout<<"CTrydll2::diplay"<<endl;
    }

    int CTrydll2::fnp(int i)
    {
     cout<<"CTrydll2::fnp:" <<i<<endl;
     i++;
     return i;
    }

    、、、、客户端。。。。。。。。。。。。

    #include "stdafx.h"
     
    class __declspec(dllimport) CTrydll2
    {
    public:
     CTrydll2(void);
     virtual int fnp(int i);//请注意这里的声明顺序和dll不一样
     virtual void diplay();
     //virtual int fnp(int i);
     // TODO: add your methods here.
    };
     
    //__declspec(dllimport) int nTrydll2;
    //end¡£¡£¡£..
    #include "windows.h"
    #include <iostream>
    using namespace  std;

    int main(int argc, char* argv[])
    {
     HINSTANCE hi=::LoadLibrary("E:\\lianxi\\DLL_LIB_DCOM_COM\\trydll2\\Debug\\trydll2.dll");
     if( hi == NULL )
     {
      cout<<"LoadLibrary error\n";
      return 0;
     }
     //.......fn.................
     FARPROC  lpfn= ::GetProcAddress( hi , "fnTrydll2" );
     if( lpfn == NULL )
     {
      cout<<"GetProcAddress fnTrydll2 error\n";
      return 0;
     }
     lpfn();
     //FARPROC == int (*lpfn)(void )
     typedef int (*LPFNP)(int );
     LPFNP  lpfnP=(LPFNP)::GetProcAddress( hi , "fnp" );
     if ( lpfnP == NULL)
     {
      cout<<"GetProcAddress fnp error\n";
     }
     else
     {
      lpfnP(15);
     }
     //......variable................
     int *pi = (int *)::GetProcAddress(hi,"nTrydll2");
     if( pi==NULL)
     {
      cout<<"cann't find  nTrydll2 error\n";
      //return 0;
     }
     //.............class........
     CTrydll2 *pt = NULL;
     FARPROC lpfn2 = ::GetProcAddress(hi ,"GetInstance");
     if ( lpfn2 == NULL )
     {
      cout<<"GetProcAddress  GetInstance error\n";
      return 0;
     }

    //请注意:编译链接没有错误的
     pt =  (CTrydll2 *)lpfn2();
     pt->diplay(); //执行时这里实际上调用的是dll中的fnp(int )但由于参数不正确所以出现运行时错误
     pt->fnp(10);  //执行时这里实际上调用的是dll中的display()但由于参数不正确所以出现运行时错误
    /*之所以会出现上面的错误是由于编译main时它确定的vtable和dll的vatble不同。编译main时使用的是

    main中声明的 vtable,但是在运行时pt所指向的是dll的vtable所以出现了调用的错误

    */
     ::FreeLibrary( hi );
     return 0;
    }
    //关于这一点。。。

  • DLL入门 -- 转载

    2008-11-18 13:36:34

      由于自己对DLL不是很熟,准备转载几篇别人的DLL系列文章。

    四种dll:

    none_mfc dll;

    mfc dll with static link mfc

    mfc dll shared mfc

    extend mfx dll

    ///

    dll  在编译后会生成dll  和lib文件。 如果我们是显示地使用dll的话则只要用dll文件就可以了。。

    如果要隐士的使用dll文件则需要 dll , lib .还有头文件。

    //。。。。。。。。。

    //dll 部分。

    // trydll2.cpp : Defines the entry point for the DLL application.
    //

    #include "stdafx.h"
    #include "trydll2.h"
    //..............................
    #include <iostream>
    using namespace std;
    ///...............................
    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;
    }


    // This is an example of an exported variable
    TRYDLL2_API int nTrydll2=0;

    // This is an example of an exported function.
    TRYDLL2_API int fnTrydll2(void)
    {
     cout<<"fnTrydll2 \n";
     return 42;
    }

    // This is the constructor of a class that has been exported.
    // see trydll2.h for the class definition
    CTrydll2::CTrydll2()
    {
     return;
    }

    //。。trydll2.h。。。。。。。。。。。。。。。。。。


    // The following ifdef block is the standard way of creating macros which make exporting
    // from a DLL simpler. All files within this DLL are compiled with the TRYDLL2_EXPORTS
    // symbol defined on the command line. this symbol should not be defined on any project
    // that uses this DLL. This way any other project whose source files include this file see
    // TRYDLL2_API functions as being imported from a DLL, wheras this DLL sees symbols
    // defined with this macro as being exported.
    #ifdef TRYDLL2_EXPORTS
    #define TRYDLL2_API __declspec(dllexport)
    #else
    #define TRYDLL2_API __declspec(dllimport)
    #endif

    // This class is exported from the trydll2.dll
    class TRYDLL2_API CTrydll2 {
    public:
     CTrydll2(void);
     // TODO: add your methods here.
    };

    extern TRYDLL2_API int nTrydll2;

    /*
    extern "C"
    {
    TRYDLL2_API int fnTrydll2(void);
    };
    */
    TRYDLL2_API int fnTrydll2(void);//在c++文件中使用会出错
    //c++编译器对函数进行改名 修改后的名字依赖于 未修改的函数名,函数的参数类型,函数的参数个数
    //函数所在的类(如果有),函数所在的文件。
    //我在用隐士方法利用dll时,由于dll中的函数(设为void fn() )没有声明为extern "C",所以按照c++规则修改
    //然后再客户端(dll为服务器)直接用函数fn(),编译能够进行但是在运行时出错,
    //系统提示:无法找到函数的入口点。因为客户端的fn也进行了改名,结果修改后的名字和服务器修改后的名字不一样
    //所以 我才猜测 c++对函数名的修改也依赖于文件名..
    //事实我的猜测是错误的,但是记住,不要把希望寄托在 系统改名会让连个函数名字一样

    //。。。。。。。。。。

    上面的东西便以后生成 trydll2.dll,trydll2.lib

    //..................................................

    //显式利用dll

    #include "stdafx.h"

    //#include "trydll2.h"
    #include "windows.h"
    #include <iostream>
    using namespace  std;

    int main(int argc, char* argv[])
    {
     HINSTANCE hi=::LoadLibrary("E:\\lianxi\\DLL_LIB_DCOM_COM\\trydll2\\Debug\\trydll2.dll");
     if( hi == NULL )
     {
      cout<<"LoadLibrary error\n";
      return 0;
     }
     //fnTrydll2:别忘了C++默认编译器会对函数进行改名
     //所以你应该强制什么声明不应该改名
     //但是这样的话 重载就有麻烦了。。因为重载就是应用了改名的方法
     FARPROC  lpfn= ::GetProcAddress( hi , "fnTrydll2" );
     if( lpfn == NULL )
     {
      cout<<"GetProcAddress error\n";
      return 0;
     }
     lpfn();
     //fnTrydll2();
     /*
     怎么使用导出的类或者变量呢??
     */
     ::FreeLibrary( hi );
     return 0;
    }
    //所谓的显示 就是因为你是用了 loadlibrary getproaddress,freelibrary

    //.隐士利用dll


    #include "stdafx.h"

    /*
    隐士调用的方法:假如头文件 eg:#include "..\trydll2.h"
    假如生成dll时生成的lib文件。
    直接调用函数。eg:fnTrydll2();
    */
    #include "..\trydll2.h" //必须
    #include "windows.h"
    #include <iostream>
    using namespace  std;

    //此外还有在 工程的连接库里加入 trydll2.lib

    int main(int argc, char* argv[])
    {
     cout<<"used dll with hide way:";
     //fnTrydll2:别忘了C++默认编译器会对函数进行改名
     //所以你应该强制什么声明不应该改名
     //但是这样的话 重载就有麻烦了。。因为重载就是应用了改名的方法
     fnTrydll2();
     /*
     怎么使用导出的类或者变量呢??
     */
    // ::FreeLibrary( hi );
     return 0;

    如果采用隐士的话 每一次dll修改,则需要重新编译客户端。此外 这种方法在程序启动时就会夹在所有需要的dll

Open Toolbar