Avatar billede stringbuffer Nybegynder
25. februar 2003 - 01:46 Der er 16 kommentarer og
1 løsning

DLL

Jeg søger eksempel på hvordan man laver en Win32 DLL i C, der kan bruges fra C# gennem DLLImport.

Jeg vil gerne vide alt lige fra hvordan man erklærer exports til hvordan man oversætter med Borlands C/C++ compiler eller MS Visual C++
Avatar billede tam Nybegynder
25. februar 2003 - 03:01 #1
Avatar billede tam Nybegynder
25. februar 2003 - 03:02 #2
Eller køb en bog ;)
Avatar billede soreno Praktikant
25. februar 2003 - 11:32 #3
Eksempel med MinGW:

------------dll.cpp
#include <windows.h>

extern "C" int getNumber();

int WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
  return false;
}

__declspec ( dllexport) int getNumber()
{
  return 12345;
}

------------main.cpp
#include <windows.h>
#include <stdio.h>

int getNumber();
typedef int (* DLLFunc)();

int main(int argc, char **argv)
{
  for(int i=0;i<10;i++)
  {
    printf("%i", getNumber());
  }

  return 0;
}

int getNumber()
{
  HINSTANCE dll;
  DLLFunc dllFunc;
    int number;
   
  dll=LoadLibrary("mindll.dll");
  if(dll != NULL)
  {
      dllFunc = (DLLFunc)GetProcAddress(dll, "getNumber");
      if(dllFunc != NULL)
      {
        number = dllFunc();
        if(!FreeLibrary(dll))
        {
            printf("*error* FreeLibrary\n");
        }
      }
      else
      {
        printf("*error* GetProcAddress\n");
      }
  }
  else
  {
      printf("*error* LoadLibrary\n");
  }
  return number;
}

------------makefile
CC=g++

EXEDLL= "mindll.dll"
EXEMAIN= "main.exe"

# targets

all:    dll main

dll:
    $(CC) -c "dll.cpp" -s -mwindows -DBUILDING_DLL=1 -I.
    dllwrap --export-all --output-def "dll.def" --implib "dll.a" "dll.o" -o $(EXEDLL)

main:
    $(CC) -c main.cpp -s
    $(CC) -o $(EXEMAIN) "main.o" -s
Avatar billede stringbuffer Nybegynder
25. februar 2003 - 14:20 #4
soreno <- kan du forklare mig hvad denne linie bruges til:

extern "C" int getNumber();

?
Avatar billede soreno Praktikant
25. februar 2003 - 15:51 #5
Fra "Standard C++ bible" af Stevens & Walnum (side 89-90):
Languages other than C and C++ can be supported by linkage specifications, in which case their string value are implementation-dependent. The C++ compiler must know how to encode the names for those languages. The other language must support C++ compatible conventions for passing arguments and return values. If you link with assembly language or some other language that employs no name mangling, you usually can use extern "C" as the linkage specification for those languages.

Jeg ved ikke lige hvordan det kan forklares på dansk ?
Avatar billede stringbuffer Nybegynder
25. februar 2003 - 16:42 #6
Skal der så ikke være

extern "C" {

// Om ALLE exportfunktioner

}

?
Avatar billede soreno Praktikant
25. februar 2003 - 16:52 #7
Ja, det vil jeg gå ud fra.
Jeg har ikke selv megen erfaring med dll, men et "hello world" har jeg da lavet (det som jeg postede).
Avatar billede hsloth Novice
25. februar 2003 - 19:47 #8
"Skal der så ikke være . . . ?" <- Ja det skal det.

extern "C" erklæringen på en funktion er en instruks til kompileren om at den skal :
  1) Bruge en bestemt (veldefineret) måde til at parse argumenter og returværdier
  2) Navnet på funktionen skal ikke "mangles".

Når man bruger en C++ compiler vil alle navne normalt blive mangled - dvs compileren tilføjer information til det symbolske navn for en funtion, dette er nødvendigt i C++ hvor f.eks. :

int Cpp_fct(int x)
{
  return x
}

er en anden funktion end :
float Cpp_fct(float x)
{
  return x;
}

Visual C++ compileren generer f.eks. de manglede navne således:
  int Cpp_fct(int x) : Cpp_fct@@YAHH@Z
 
  og

  float Cpp_fct(float x) : Cpp_fct@@YAMM@Z

Hvis jeg derimod angiver :

extern "C" int Cpp_fct(int x);

Vil compileren generere det symbolske navn Cpp_fct - hvilket kan linkes sammen med kode genereret af f.eks. en ren C compiler.
Avatar billede hsloth Novice
25. februar 2003 - 19:49 #9
Nu gik dit oprindelige spørgsmål jo faktisk på hvordan man _laver_ et .dll i C.

Soreno's løsning er jo egentligt C++, derfor er extern "C" nødvendigt, hvis du bruger en C compiler (f.eks. ved at give .c filer til MSDEV CL.exe) er det ikke nødvendigt.
Avatar billede stringbuffer Nybegynder
25. februar 2003 - 21:29 #10
OK, så har jeg bare et enkelt spg. til:

Hvis man vælger at lave sin DLL i ren C, kan jeg forstå at den vil have flg. udseende:

#include <windows.h>

int WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
  return false;
}

/* Evt. funktionsprototyper, f.eks. */
int getNumber(void);
int getAnswer(void);


/* Funktionsimplementeringer, f.eks. */
__declspec ( dllexport) int getNumber(void)
{
  return 12345;
}
__declspec ( dllexport) int getAnswer(void)
{
  return 42;
}


Er det korrekt?
Så vil jeg bare høre hvordan jeg oversætter den med cl.exe
Avatar billede hsloth Novice
25. februar 2003 - 22:34 #11
Følgende dur for mig (i MS DEV )

// dlltest.c
#include <windows.h>

int WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
  return TRUE;
}

/* Funktionsimplementeringer, f.eks. */
__declspec ( dllexport) int getNumber(void)
{
  return 12345;
}
__declspec ( dllexport) int getAnswer(void)
{
  return 42;
}

Og tilsidst - en genereret makefile du kan køre med NMAKE : (nmake /f makefile.mak)

#makefile.mak
# Microsoft Developer Studio Generated NMAKE File, Based on testdll.dsp
!IF "$(CFG)" == ""
CFG=testdll - Win32 Debug
!MESSAGE No configuration specified. Defaulting to testdll - Win32 Debug.
!ENDIF

!IF "$(CFG)" != "testdll - Win32 Release" && "$(CFG)" != "testdll - Win32 Debug"
!MESSAGE Invalid configuration "$(CFG)" specified.
!MESSAGE You can specify a configuration when running NMAKE
!MESSAGE by defining the macro CFG on the command line. For example:
!MESSAGE
!MESSAGE NMAKE /f "testdll.mak" CFG="testdll - Win32 Debug"
!MESSAGE
!MESSAGE Possible choices for configuration are:
!MESSAGE
!MESSAGE "testdll - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
!MESSAGE "testdll - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
!MESSAGE
!ERROR An invalid configuration is specified.
!ENDIF

!IF "$(OS)" == "Windows_NT"
NULL=
!ELSE
NULL=nul
!ENDIF

CPP=cl.exe
MTL=midl.exe
RSC=rc.exe

!IF  "$(CFG)" == "testdll - Win32 Release"

OUTDIR=.\Release
INTDIR=.\Release
# Begin Custom Macros
OutDir=.\Release
# End Custom Macros

ALL : "$(OUTDIR)\testdll.dll"


CLEAN :
    -@erase "$(INTDIR)\testdll.obj"
    -@erase "$(INTDIR)\vc60.idb"
    -@erase "$(OUTDIR)\testdll.dll"
    -@erase "$(OUTDIR)\testdll.exp"
    -@erase "$(OUTDIR)\testdll.lib"

"$(OUTDIR)" :
    if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"

CPP_PROJ=/nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "TESTDLL_EXPORTS" /Fp"$(INTDIR)\testdll.pch" /Yu"stdafx.h" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c
MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32
BSC32=bscmake.exe
BSC32_FLAGS=/nologo /o"$(OUTDIR)\testdll.bsc"
BSC32_SBRS= \
   
LINK32=link.exe
LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /incremental:no /pdb:"$(OUTDIR)\testdll.pdb" /machine:I386 /out:"$(OUTDIR)\testdll.dll" /implib:"$(OUTDIR)\testdll.lib"
LINK32_OBJS= \
    "$(INTDIR)\testdll.obj"

"$(OUTDIR)\testdll.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
    $(LINK32) @<<
  $(LINK32_FLAGS) $(LINK32_OBJS)
<<

!ELSEIF  "$(CFG)" == "testdll - Win32 Debug"

OUTDIR=.\Debug
INTDIR=.\Debug
# Begin Custom Macros
OutDir=.\Debug
# End Custom Macros

ALL : "$(OUTDIR)\testdll.dll" "$(OUTDIR)\testdll.bsc"


CLEAN :
    -@erase "$(INTDIR)\testdll.obj"
    -@erase "$(INTDIR)\testdll.sbr"
    -@erase "$(INTDIR)\vc60.idb"
    -@erase "$(INTDIR)\vc60.pdb"
    -@erase "$(OUTDIR)\testdll.bsc"
    -@erase "$(OUTDIR)\testdll.dll"
    -@erase "$(OUTDIR)\testdll.exp"
    -@erase "$(OUTDIR)\testdll.ilk"
    -@erase "$(OUTDIR)\testdll.lib"
    -@erase "$(OUTDIR)\testdll.pdb"

"$(OUTDIR)" :
    if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"

CPP_PROJ=/nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "TESTDLL_EXPORTS" /FR"$(INTDIR)\\" /Fp"$(INTDIR)\testdll.pch" /Yu"stdafx.h" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ  /c
MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32
BSC32=bscmake.exe
BSC32_FLAGS=/nologo /o"$(OUTDIR)\testdll.bsc"
BSC32_SBRS= \
    "$(INTDIR)\testdll.sbr"

"$(OUTDIR)\testdll.bsc" : "$(OUTDIR)" $(BSC32_SBRS)
    $(BSC32) @<<
  $(BSC32_FLAGS) $(BSC32_SBRS)
<<

LINK32=link.exe
LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /incremental:yes /pdb:"$(OUTDIR)\testdll.pdb" /debug /machine:I386 /out:"$(OUTDIR)\testdll.dll" /implib:"$(OUTDIR)\testdll.lib" /pdbtype:sept
LINK32_OBJS= \
    "$(INTDIR)\testdll.obj"

"$(OUTDIR)\testdll.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
    $(LINK32) @<<
  $(LINK32_FLAGS) $(LINK32_OBJS)
<<

!ENDIF

.c{$(INTDIR)}.obj::
  $(CPP) @<<
  $(CPP_PROJ) $<
<<

.cpp{$(INTDIR)}.obj::
  $(CPP) @<<
  $(CPP_PROJ) $<
<<

.cxx{$(INTDIR)}.obj::
  $(CPP) @<<
  $(CPP_PROJ) $<
<<

.c{$(INTDIR)}.sbr::
  $(CPP) @<<
  $(CPP_PROJ) $<
<<

.cpp{$(INTDIR)}.sbr::
  $(CPP) @<<
  $(CPP_PROJ) $<
<<

.cxx{$(INTDIR)}.sbr::
  $(CPP) @<<
  $(CPP_PROJ) $<
<<


!IF "$(NO_EXTERNAL_DEPS)" != "1"
!IF EXISTS("testdll.dep")
!INCLUDE "testdll.dep"
!ELSE
!MESSAGE Warning: cannot find "testdll.dep"
!ENDIF
!ENDIF


!IF "$(CFG)" == "testdll - Win32 Release" || "$(CFG)" == "testdll - Win32 Debug"
SOURCE=.\testdll.c

!IF  "$(CFG)" == "testdll - Win32 Release"

CPP_SWITCHES=/nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "TESTDLL_EXPORTS" /Fp"$(INTDIR)\testdll.pch" /Yu"stdafx.h" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c

"$(INTDIR)\testdll.obj" : $(SOURCE) "$(INTDIR)" "$(INTDIR)\testdll.pch"
    $(CPP) @<<
  $(CPP_SWITCHES) $(SOURCE)
<<


!ELSEIF  "$(CFG)" == "testdll - Win32 Debug"

CPP_SWITCHES=/nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "TESTDLL_EXPORTS" /FR"$(INTDIR)\\" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ  /c

"$(INTDIR)\testdll.obj"    "$(INTDIR)\testdll.sbr" : $(SOURCE) "$(INTDIR)"
    $(CPP) @<<
  $(CPP_SWITCHES) $(SOURCE)
<<


!ENDIF


!ENDIF

// Test program dlltester.c
// husk at linke med testdll.lib
#include "stdio.h"

int getNumber(int);

int main()
{
    printf("GetNumber returned : %d\n", getNumber(42) );
    return 0;
}
Avatar billede doctorseb Nybegynder
26. februar 2003 - 21:14 #12
Husk at der også er en reel chance for at navne bliver mangled af C-compileren. Eksporterede stdcall C-funktioner (også med extern "C") bliver eksempelvis mangled i Visual C++.
Avatar billede stringbuffer Nybegynder
26. februar 2003 - 23:07 #13
Min kode er ren og skær C, så hvis jeg kunne slippe helt for C++ for at generere min .dll, så ville det være bedst...

Men jeg aner ikke hvordan man gør.
Avatar billede hsloth Novice
26. februar 2003 - 23:12 #14
Den kode jeg har postet er ren C . . .
Avatar billede stringbuffer Nybegynder
26. februar 2003 - 23:17 #15
hsloth <- jeg har gemt din kode i filer efter rækkefølge, du postede dem:
(folder: dll\)
testdll.c
makefile.mak
dlltester.c

når jeg kører nmake makefile.mak sår får jeg følgende vrøvl:
c:\Documents and Settings\stringbuffer\Desktop\dll\testdll.c(2) : fatal error C1083: C
nnot open include file: 'windows.h': No such file or directory
NMAKE : fatal error U1077: 'cl.exe' : return code '0x2'
Stop.
Avatar billede hsloth Novice
26. februar 2003 - 23:24 #16
Da du installered Visual Studio fik du en fil der hedder VCVARS32.bat, den ligger f.eks. i : C:\Program Files\Microsoft Visual Studio\VC98\Bin.

Kør den inden du kører med make, den sætter bl.a. include pathen op - det skulle gerne hjælpe.
Avatar billede stringbuffer Nybegynder
27. februar 2003 - 00:42 #17
Den virker!

Men for at kunne oversætte mere end en gang måtte jeg ændre en del i den der makefile....


Men nu er jeg da godt igang :)
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester