I'm writing a test DLL for an IDC service provider. I also written an mfc testing application that loads the XFS Manager.
I'm encountering some problems calling the function WFSOpen.
The manager correctly loads the SPI dll and calls its WFPOpen function. The WFPOpen executes correctly, reaching the return statement and returning WFS_SUCCESS to the caller. Then in the XFS Manager dll something bad happens, an exception is thrown and the WFSOpen function returns WFS_ERR_INTERNAL_ERROR to my testing application.
I always get this message:
First-chance exception at 0x1000C285 (msxfs.dll) in TestXFSIDC.exe: 0xC0000005: Access violation writing location 0x011E3987.
(Date)01.07.2015 (Time)09:29.39,570
Exception occurred
The XFS log file reports this:
09:29:39.554 APICALL: WFSAsyncOpen
actLogicalName: <MyCardReader>
hApp: 398257252 0X17BCEC64
actszAppId: <TestXFSIDC>
actTraceLevel: 10 0X0000000A
actTimeOut: 0 0X00000000
actpHService: 8321216 0X007EF8C0
actHWnd: 461622 0X00070B36
dwSrvcVersionsRequired: 201731 0X00031403
lpSrvcVersion: 8315892 0X007EE3F4
lpSPIVersion: 8315364 0X007EE1E4
actpRequestId: 8315352 0X007EE1D8
09:29:39.554 OpenKey( HKEY_CURRENT_USER,WOSA_CURRENT_USER_KEY_NAME,0,KEY_READ | KEY_WRITE,&hKeyWosaRoot) failed (reason=2,file=d:\buffington\development\cen xfs manager\source\xfs_conf.cpp,line=2168)
09:29:39.570 WFPCALL: WFPOpen
tmpServiceHandle: 1 0X00000001
actLogicalName: <MyCardReader>
hApp: 398257252 0X17BCEC64
actszAppId: <TestXFSIDC>
actTraceLevel: 10 0X0000000A
actTimeOut: 0 0X00000000
actHWnd: 461622 0X00070B36
tmpRqId: 1 0X00000001
pUser->getServiceProviderHandle(): 1807786008 0X6BC0A018
dwSPIVersionsRequired: 16908035 0X0101FF03
lpSPIVersion: 8315364 0X007EE1E4
dwSrvcVersionsRequired: 201731 0X00031403
lpSrvcVersion: 8315892 0X007EE3F4
09:29:39.570
(Date)01.07.2015 (Time)09:29.39,570
Called WFPOpen
09:29:39.570
(Date)01.07.2015 (Time)09:29.39,570
Exiting WFPOpen
09:29:39.554 RegOpenKeyEx( x80000003, .DEFAULT\XFS )) failed (reason=5,file=d:\buffington\development\cen xfs manager\source\xfs_conf.cpp,line=384)
09:29:39.570
(Date)01.07.2015 (Time)09:29.39,570
Exception occurred
I'm using Visual Studio 2012 in Windows 8.1 64bit
I've managed to solve the exception in this way:
#pragma comment(linker, "/EXPORT:WFPCancelAsyncRequest=_WFPCancelAsyncRequest#8")
#pragma comment(linker, "/EXPORT:WFPClose=_WFPClose#12")
#pragma comment(linker, "/EXPORT:WFPDeregister=_WFPDeregister#20")
#pragma comment(linker, "/EXPORT:WFPExecute=_WFPExecute#24")
#pragma comment(linker, "/EXPORT:WFPGetInfo=_WFPGetInfo#24")
#pragma comment(linker, "/EXPORT:WFPLock=_WFPLock#16")
#pragma comment(linker, "/EXPORT:WFPOpen=_WFPOpen#52")
#pragma comment(linker, "/EXPORT:WFPRegister=_WFPRegister#20")
#pragma comment(linker, "/EXPORT:WFPSetTraceLevel=_WFPSetTraceLevel#8")
#pragma comment(linker, "/EXPORT:WFPUnloadService=_WFPUnloadService#0")
#pragma comment(linker, "/EXPORT:WFPUnlock=_WFPUnlock#12")
extern "C"{
DllExport HRESULT WINAPI WFPCancelAsyncRequest(HSERVICE hService, REQUESTID RequestID);
DllExport HRESULT WINAPI WFPClose(HSERVICE hService, HWND hWnd, REQUESTID ReqID);
DllExport HRESULT WINAPI WFPDeregister(HSERVICE hService, DWORD dwEventClass, HWND hWndReg, HWND hWnd, REQUESTID ReqID);
DllExport HRESULT WINAPI WFPExecute(HSERVICE hService, DWORD dwCommand, LPVOID lpCmdData, DWORD dwTimeOut, HWND hWnd, REQUESTID ReqID);
DllExport HRESULT WINAPI WFPGetInfo(HSERVICE hService, DWORD dwCategory, LPVOID lpQueryDetails, DWORD dwTimeOut, HWND hWnd, REQUESTID ReqID);
DllExport HRESULT WINAPI WFPLock(HSERVICE hService, DWORD dwTimeOut, HWND hWnd, REQUESTID ReqID);
DllExport HRESULT WINAPI WFPOpen(HSERVICE hService, LPSTR lpszLogicalName, HAPP hApp, LPSTR lpszAppID, DWORD dwTraceLevel, DWORD dwTimeOut, HWND hWnd, REQUESTID ReqID, HPROVIDER hProvider, DWORD dwSPIVersionsRequired, LPWFSVERSION lpSPIVersion, DWORD dwSrvcVersionsRequired, LPWFSVERSION lpSrvcVersion);
DllExport HRESULT WINAPI WFPRegister(HSERVICE hService, DWORD dwEventClass, HWND hWndReg, HWND hWnd, REQUESTID ReqID);
DllExport HRESULT WINAPI WFPSetTraceLevel(HSERVICE hService, DWORD dwTraceLevel);
DllExport HRESULT WINAPI WFPUnloadService();
DllExport HRESULT WINAPI WFPUnlock(HSERVICE hService, HWND hWnd, REQUESTID ReqID);
};
When exporting the WFPxxx function using WINAPI the dll produced has the _WFPxxx#yyy names, that are not readable by the msxfs.dll, which gives the error WFS_ERR_INVALID_SERVPROV.
The solution was to specify the name recognizable by the msxfs.dll using the
#pragma comment(linker, "/EXPORT:WFPxxx=_WFPxxx#yyy")
Related
So I have a tls callback implemented in a separate dll and I want to retrieve the thread information on thread attach like the entry point,
parameters etc
void NTAPI on_tls_callback(LPVOID dll, DWORD reason, LPVOID reserved)
{
if (reason == DLL_THREAD_ATTACH)
{
//obtain thread info
}
}
#pragma comment (linker, "/INCLUDE:__tls_used")
#pragma comment (linker, "/INCLUDE:__xl_b")
#pragma data_seg(".CRT$XLB")
EXTERN_C
PIMAGE_TLS_CALLBACK _xl_b = on_tls_callback;
#pragma data_seg()
I read here that it's possible
How can DLL injection be detected?
Okay after a bit of further research I found a way here
Thread EntryPoint in TLS callback as AntiDebug technique
typedef NTSTATUS
(NTAPI*NtQueryInformationThread_t)(
IN HANDLE ThreadHandle,
IN THREADINFOCLASS ThreadInformationClass,
OUT PVOID ThreadInformation,
IN ULONG ThreadInformationLength,
OUT PULONG ReturnLength OPTIONAL
);
void NTAPI on_tls_callback(LPVOID dll, DWORD reason, LPVOID reserved)
{
if (reason == DLL_THREAD_ATTACH)
{
DWORD startAddress = 0;
NtQueryInformationThread_t pNtQueryInformationThread =
reinterpret_cast<NtQueryInformationThread_t>(GetProcAddress(
GetModuleHandle("ntdll.dll"), "NtQueryInformationThread"));
pNtQueryInformationThread(GetCurrentThread(), THREADINFOCLASS(9),
&startAddress, sizeof(startAddress), nullptr);
}
}
#pragma comment (linker, "/INCLUDE:__tls_used")
#pragma comment (linker, "/INCLUDE:__xl_b")
#pragma data_seg(".CRT$XLB")
EXTERN_C
PIMAGE_TLS_CALLBACK _xl_b = on_tls_callback;
#pragma data_seg()
all
I have source below:
in my .rc file
IDR_XML1 XML "LoginQuery.xml"
in my resource.h file
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
//
#define IDR_XML1 106
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 107
#define _APS_NEXT_COMMAND_VALUE 40002
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
and in my .cpp file.
HMODULE handle = ::GetModuleHandle(NULL);
HRSRC rc = ::FindResource(handle, MAKEINTRESOURCE(IDR_XML1), MAKEINTRESOURCE("XML"));
HGLOBAL rcData = ::LoadResource(handle, rc);
DWORD size = ::SizeofResource(handle, rc);
const char* data = static_cast<const char*>(::LockResource(rcData));
But data returns null.
What am I doing wrong?
EDIT
My C++ project is dll project, and I am reading the file inside of that project.
Your dll entry is something like:
BOOL WINAPI DllMain(_In_ HINSTANCE hinstDLL, _In_ DWORD fdwReason, _In_ LPVOID lpvReserved)
hinstDLL is instance of your dll, I recommend to have global variable to keep this instance and assign it instantly after dll is loaded.
HINSTANCE g_hInstance;
BOOL WINAPI DllMain(_In_ HINSTANCE hinstDLL, _In_ DWORD fdwReason, _In_ LPVOID lpvReserved)
{
g_hInstance = hinstDLL;
/*code*/
}
And you resource load should look something like:
HRSRC rc = ::FindResource(g_hInstance, MAKEINTRESOURCE(IDR_XML1), MAKEINTRESOURCE(XML));
HGLOBAL rcData = ::LoadResource(g_hInstance, rc);
DWORD size = ::SizeofResource(g_hInstance, rc);
const char* data = static_cast<const char*>(::LockResource(rcData));
BTW. nothing about your question but variable named rc usually is used for RECT type.
My ultimate goal is to track file operations done by explorer.exe via hooking file api in kernel32.dll, however I have yet to get that working (either explorer.exe is not calling the APIs, or something is wrong on my end). In order to figure out what is going on, I've set a goal to track whenever notepad.exe creates a file, however this has failed for some reason as well!
I have 3 Visual Studio 2012 C++ projects: my DLL, a DLL injector that will force any executable to load my dll (although it may fail if Unicode/Multibyte and 32/64bit settings don't match up), and a test program that calls API. I have a batch file that will start my test program, and then use the injector program to load my DLL into the test program. The weird part is that the outputs do show that the APIs are being tracked on the test program (spawns the console and prints everything!) but nothing happens for notepad.exe (no console = no operations caught).
Here is the DLL that hooks the API (uses the mhook library). The format and concepts are taken from this guide. The important things to note are that I hook CreateFile(A/W), and spawn a console to print file I/O logs when the first operation occurs.
#include "stdafx.h"
#include "mhook/mhook-lib/mhook.h"
//////////////////////////////////////////////////////////////////////////
// Defines and typedefs
typedef HANDLE (WINAPI *CreateFileWFP)(
_In_ LPCWSTR lpFileName,
_In_ DWORD dwDesiredAccess,
_In_ DWORD dwShareMode,
_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
_In_ DWORD dwCreationDisposition,
_In_ DWORD dwFlagsAndAttributes,
_In_opt_ HANDLE hTemplateFile
);
typedef HANDLE (WINAPI *CreateFileAFP)(
_In_ LPCSTR lpFileName,
_In_ DWORD dwDesiredAccess,
_In_ DWORD dwShareMode,
_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
_In_ DWORD dwCreationDisposition,
_In_ DWORD dwFlagsAndAttributes,
_In_opt_ HANDLE hTemplateFile
);
//////////////////////////////////////////////////////////////////////////
// Original function
CreateFileWFP OriginalCreateFileW = (CreateFileWFP)::GetProcAddress(::GetModuleHandle(TEXT("kernel32")), "CreateFileW");
CreateFileAFP OriginalCreateFileA = (CreateFileAFP)::GetProcAddress(::GetModuleHandle(TEXT("kernel32")), "CreateFileA");
//////////////////////////////////////////////////////////////////////////
// Some Helper Stuff
struct Console{
HANDLE handle;
Console(){ handle = INVALID_HANDLE_VALUE; }
void write(LPCWSTR text){
if(handle==INVALID_HANDLE_VALUE){
AllocConsole();
handle = GetStdHandle(STD_OUTPUT_HANDLE);
}
DWORD numCharsWritten = 0;
WriteConsoleW(handle, text, (DWORD)wcslen(text), &numCharsWritten,NULL);
}
void write(LPCSTR text){
if(handle==INVALID_HANDLE_VALUE){
AllocConsole();
handle = GetStdHandle(STD_OUTPUT_HANDLE);
}
DWORD numCharsWritten = 0;
WriteConsoleA(handle, text, (DWORD)strlen(text), &numCharsWritten,NULL);
}
} console;
void operationPrint(LPCWSTR left, LPCWSTR middle, LPCWSTR right){
console.write(left);
console.write(middle);
console.write(right);
console.write(L"\n");
}
void operationPrint(LPCSTR left, LPCSTR middle, LPCSTR right){
console.write(left);
console.write(middle);
console.write(right);
console.write(L"\n");
}
//////////////////////////////////////////////////////////////////////////
// Hooked function
HANDLE HookedCreateFileW(
_In_ LPCWSTR lpFileName,
_In_ DWORD dwDesiredAccess,
_In_ DWORD dwShareMode,
_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
_In_ DWORD dwCreationDisposition,
_In_ DWORD dwFlagsAndAttributes,
_In_opt_ HANDLE hTemplateFile
){
HANDLE out = OriginalCreateFileW(
lpFileName,
dwDesiredAccess,
dwShareMode,
lpSecurityAttributes,
dwCreationDisposition,
dwFlagsAndAttributes,
hTemplateFile);
if(out == INVALID_HANDLE_VALUE) return out; //ignore failiures
operationPrint(L"CreatedW file",L" at ",lpFileName);
return out;
}
HANDLE HookedCreateFileA(
_In_ LPCSTR lpFileName,
_In_ DWORD dwDesiredAccess,
_In_ DWORD dwShareMode,
_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
_In_ DWORD dwCreationDisposition,
_In_ DWORD dwFlagsAndAttributes,
_In_opt_ HANDLE hTemplateFile
){
HANDLE out = OriginalCreateFileA(
lpFileName,
dwDesiredAccess,
dwShareMode,
lpSecurityAttributes,
dwCreationDisposition,
dwFlagsAndAttributes,
hTemplateFile);
if(out == INVALID_HANDLE_VALUE) return out; //ignore failiures
operationPrint("CreatedA file"," at ",lpFileName);
return out;
}
//////////////////////////////////////////////////////////////////////////
// Entry point
BOOL WINAPI DllMain(
__in HINSTANCE hInstance,
__in DWORD Reason,
__in LPVOID Reserved
)
{
switch (Reason)
{
case DLL_PROCESS_ATTACH:
Mhook_SetHook((PVOID*)&OriginalCreateFileW, HookedCreateFileW);
Mhook_SetHook((PVOID*)&OriginalCreateFileA, HookedCreateFileA);
break;
case DLL_PROCESS_DETACH:
FreeConsole();
Mhook_Unhook((PVOID*)&OriginalCreateFileW);
Mhook_Unhook((PVOID*)&OriginalCreateFileA);
break;
}
return TRUE;
}
I can't find exactly where I found the injector program, but it is nearly identical to this guide. Some of the comments are even the same, so I'm pretty sure one took from the other (not sure which from whom though). In any case, I just made minor alterations so that it works when compiled either under Unicode or Multibyte. I won't post the entire code here unless requested because I think it's a waste of space, but here is the important part.
#include "Injector.h"
#include <windows.h>
#include <tlhelp32.h>
#include <shlwapi.h>
#include <conio.h>
#include <stdio.h>
#include "DebugPrint.h"
#include <atlbase.h>
using namespace std;
Injector::Injector(void)
{
}
Injector::~Injector(void)
{
}
#define CREATE_THREAD_ACCESS (PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ)
bool Injector::Inject(string procName, string dllName){
DWORD pID = GetTargetThreadIDFromProcName(procName.c_str());
return Inject(pID,dllName);
}
bool Injector::Inject(DWORD pID, string dllName){
const char* DLL_NAME = dllName.c_str();
HANDLE Proc = 0;
HMODULE hLib = 0;
LPVOID RemoteString, LoadLibAddy;
if(!pID)
return false;
Proc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pID);
if(!Proc)
{
DEBUG_PRINT("OpenProcess() failed: %d", GetLastError());
return false;
}
LoadLibAddy = (LPVOID)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "LoadLibraryA");
// Allocate space in the process for our <strong class="highlight">DLL</strong>
RemoteString = (LPVOID)VirtualAllocEx(Proc, NULL, strlen(DLL_NAME), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
// Write the string name of our <strong class="highlight">DLL</strong> in the memory allocated
WriteProcessMemory(Proc, (LPVOID)RemoteString, DLL_NAME, strlen(DLL_NAME), NULL);
// Load our <strong class="highlight">DLL</strong>
CreateRemoteThread(Proc, NULL, NULL, (LPTHREAD_START_ROUTINE)LoadLibAddy, (LPVOID)RemoteString, NULL, NULL);
CloseHandle(Proc);
return true;
}
DWORD Injector::GetTargetThreadIDFromProcName(const char* ProcName)
{
PROCESSENTRY32 pe;
HANDLE thSnapShot;
BOOL retval, ProcFound = false;
thSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if(thSnapShot == INVALID_HANDLE_VALUE)
{
//MessageBox(NULL, "Error: Unable to create toolhelp snapshot!", "2MLoader", MB_OK);
DEBUG_PRINT("Error: Unable to create toolhelp snapshot!");
return false;
}
pe.dwSize = sizeof(PROCESSENTRY32);
retval = Process32First(thSnapShot, &pe);
while(retval)
{
#ifdef _UNICODE
char peSzExeFile[MAX_PATH];
wcstombs_s(NULL,peSzExeFile,MAX_PATH,pe.szExeFile,MAX_PATH);
#else
const char* peSzExeFile = pe.szExeFile;
#endif
DEBUG_PRINT("\nSearching for process: %s ",peSzExeFile);
if(!strcmp(peSzExeFile, ProcName))
{
DEBUG_PRINT(" Found!\n\n");
return pe.th32ProcessID;
}
retval = Process32Next(thSnapShot, &pe);
}
return 0;
}
Finally, my test program just calls some file APIs to see if the injected DLL can catch them. As mentioned before, it does catch the calls quite successfully.
#include <windows.h>
#include <tchar.h>
using namespace std;
#ifdef _UNICODE
#define printf(X,...) wprintf(TEXT(X),__VA_ARGS__); //I don't want to have to keep replacing printf whenever I switch to Unicode or Multibyte
#endif
#ifdef _DEBUG
int _tmain(int argc, TCHAR* argv[]){ //makes a console. printf() will have a place to go in this case
#else
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow){ //no console
#endif
Sleep(2000); //let DLL finish loading
LPCTSTR fileSrc = TEXT("C:\\Users\\jajoach\\Desktop\\hi.txt");
LPCTSTR fileDst = TEXT("C:\\Users\\jajoach\\Desktop\\hi\\hi.txt");
printf("Moving file from %s to %s\n",fileSrc,fileDst);
MoveFile(fileSrc,fileDst);
Sleep(1000);
printf("Moving file from %s to %s\n",fileSrc,fileDst);
MoveFile(fileDst,fileSrc);
Sleep(1000);
printf("Copying file from %s to %s\n",fileSrc,fileDst);
CopyFile(fileSrc,fileDst,true);
Sleep(1000);
printf("Deleting file %s\n",fileDst);
DeleteFile(fileDst);
Sleep(1000);
printf("Creating file %s\n",fileDst);
HANDLE h=CreateFile(fileDst,0,0,NULL,CREATE_NEW,FILE_ATTRIBUTE_NORMAL,NULL);
Sleep(1000);
printf("Deleting file %s\n",fileDst);
CloseHandle(h);
DeleteFile(fileDst);
Sleep(5000);
return 0;
}
Here is the confirmed output for Unicode (as noted by the 'W's) and Release mode, and what I was expecting (but didn't get) from both notepad.exe and explorer.exe. For the record, the test also works for Multibyte, but instead gives 'A's as expected.
I had thought that maybe explorer.exe and notepad.exe don't use these API for their file I/O, but my research says otherwise. This post hooks CreateFile in notepad.exe using Detours and reports success for that application. Additionally, ProgramMonitor clearly shows notepad.exe calling CreateFile during a Saveas operation (after many failed requests with different parameters...):
Nevermind about explorer.exe for now; why isn't my hook working for notepad.exe when I do Saveas?
EDIT: I forgot to mention that I also hook MoveFile(A/W) and CopyFile(A/W) with the test program, but I removed that code from the DLL on this post for brevity.
As noted in the comments of the OP, it seems that that notepad.exe uses ASLR and your test program does not. With that the address of LoadLibraryA would be different in each process, and your injection code fails.
The situation is that you are getting the addres of LoadLibraryA in the injector address space and assume that it is the same that in the target process. That would be usually right, but ASLR is designed specifically to make that assumption fail. And so it does... the thread you create get a -most likely- invalid address, and fails.
A couple of reasons:
Bitness of your hook code must match the target.
CreateFileA/W is quite low, but not low enough to catch em all!
To solve 2. you must hook Zw/NtCreateFile from Ntdll.dll which is what you're seeing in procmon. There is nothing lower than these API's in user land.
i need to create small dll to intercept simple mapi calls and send files via:
file->Send to as attachment (in excel, word, acrobat reader...)
or via explorer->rightclickmenu->Send to->Mail recipient
to attach to gmail.
after reading this:
Mapi32.dll Stub Registry Settings (Windows)
Mapi32 Stub Library (Windows)
and searching here on stackoverflow and other webs i have created one very small dll using code::blocks that shows me information for investigating the process.
I have installed it on registry correctly like indicated in previous links on HKEY_LOCAL_MACHINE\SOFTWARE\Clients\Mail\SimpMapi.
I have selected on my win xp pro in control panel->Internet options my SimpMapi client.
when i open excel write some cells and go to File->Send like attachment dll shows me that first calls MapiLogon, second calls MapiSendMail and third when i close excel it calls MapiLogoff, but then it throws an error and closes excel.exe.
If i try it from explorer window right clicking one file and SendTo->Mail recipient it calls directly MapiSendMail and when i click on ok in MsgBox it throws an error and closes explorer.exe
Could you help me to find out what is wrong.
This is the mail.h file:
#ifndef __MAIN_H__
#define __MAIN_H__
#include <windows.h>
// Todo lo necesario a incluir por mapi.h
#define SUCCESS_SUCCESS 0
#define MAPI_E_USER_ABORT 1
#define MAPI_E_LOGIN_FAILURE 3
typedef unsigned long FLAGS;
typedef unsigned long LHANDLE;
typedef unsigned long FAR *LPLHANDLE, FAR *LPULONG;
typedef struct {
ULONG ulReserved;
ULONG ulRecipClass;
LPSTR lpszName;
LPSTR lpszAddress;
ULONG ulEIDSize;
LPVOID lpEntryID;
} MapiRecipDesc, *lpMapiRecipDesc;
typedef struct {
ULONG ulReserved;
ULONG flFlags;
ULONG nPosition;
LPSTR lpszPathName;
LPSTR lpszFileName;
LPVOID lpFileType;
} MapiFileDesc, *lpMapiFileDesc;
typedef struct {
ULONG ulReserved;
LPSTR lpszSubject;
LPSTR lpszNoteText;
LPSTR lpszMessageType;
LPSTR lpszDateReceived;
LPSTR lpszConversationID;
FLAGS flFlags;
lpMapiRecipDesc lpOriginator;
ULONG nRecipCount;
lpMapiRecipDesc lpRecips;
ULONG nFileCount;
lpMapiFileDesc lpFiles;
} MapiMessage, *lpMapiMessage;
/* To use this exported function of dll, include this header
* in your project.
*/
#ifdef BUILD_DLL
#define DLL_EXPORT __declspec(dllexport)
#else
#define DLL_EXPORT __declspec(dllimport)
#endif
extern "C" void DLL_EXPORT SomeFunction(const LPCSTR sometext);
extern "C" ULONG DLL_EXPORT MAPILogon(ULONG_PTR ulUIParam,LPSTR lpszProfileName,LPSTR lpszPassword,FLAGS flFlags,ULONG ulReserved,LPLHANDLE lplhSession);
extern "C" ULONG DLL_EXPORT MAPILogoff(LHANDLE lhSession,ULONG_PTR ulUIParam,FLAGS flFlags,ULONG ulReserved);
extern "C" ULONG DLL_EXPORT MAPISendDocuments(ULONG_PTR ulUIParam,LPSTR lpszDelimChar,LPSTR lpszFilePaths,LPSTR lpszFileNames,ULONG ulReserved);
extern "C" ULONG DLL_EXPORT MAPISendMail(LHANDLE lhSession,ULONG_PTR ulUIParam,lpMapiMessage lpMessage,FLAGS flFlags,ULONG ulReserved);
#endif
This is the main.cpp file:
#include "main.h"
// a sample exported function
void DLL_EXPORT SomeFunction(const LPCSTR sometext)
{
MessageBoxA(0, sometext, "DLL Message", MB_OK | MB_ICONINFORMATION);
}
ULONG DLL_EXPORT MAPILogon(ULONG_PTR ulUIParam,LPSTR lpszProfileName,LPSTR lpszPassword,FLAGS flFlags,ULONG ulReserved,LPLHANDLE lplhSession)
{
MessageBoxA(0, "MAPILogon", "MAPILogon", MB_OK | MB_ICONINFORMATION);
return SUCCESS_SUCCESS;
}
ULONG DLL_EXPORT MAPILogoff(LHANDLE lhSession,ULONG_PTR ulUIParam,FLAGS flFlags,ULONG ulReserved)
{
MessageBoxA(0, "MAPILogoff", "MAPILogoff", MB_OK | MB_ICONINFORMATION);
return SUCCESS_SUCCESS;
}
ULONG DLL_EXPORT MAPISendDocuments(ULONG_PTR ulUIParam,LPSTR lpszDelimChar,LPSTR lpszFilePaths,LPSTR lpszFileNames,ULONG ulReserved)
{
MessageBoxA(0, "MAPISendDocuments", "MAPISendDocuments", MB_OK | MB_ICONINFORMATION);
return SUCCESS_SUCCESS;
}
ULONG DLL_EXPORT MAPISendMail(LHANDLE lhSession,ULONG_PTR ulUIParam,lpMapiMessage lpMessage,FLAGS flFlags,ULONG ulReserved)
{
MessageBoxA(0, "MAPISendMail", "MAPISendMail", MB_OK | MB_ICONINFORMATION);
return SUCCESS_SUCCESS;
}
extern "C" DLL_EXPORT BOOL APIENTRY DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
// attach to process
// return FALSE to fail DLL load
break;
case DLL_PROCESS_DETACH:
// detach from process
break;
case DLL_THREAD_ATTACH:
// attach to thread
break;
case DLL_THREAD_DETACH:
// detach from thread
break;
}
return TRUE; // succesful
}
Thanks in advance.
Jorge
In link: Creating Custom Simple Mapi DLL, throws error when executing
Andy helped me to solve the problem that was incorrect calling convention.
Adding WINAPI to all my Simple MAPI function declarations and definitions it worked, example:
extern "C" ULONG DLL_EXPORT WINAPI MAPILogon( // etc
Then the problem was that my exported functions were exported with declarations.
To correct this in code::blocks you have to add in:
Main menu: Project -> Build options -> GNU GCC Compiler -> Linker settings -> Other linker options: -Wl,--kill-at
Thanks!
Let's say I have very simple little code sample which uses async WinInet:
#include "stdafx.h"
#include "WinInet.h"
#pragma comment(lib, "wininet.lib")
DWORD LatestResult = 0;
HANDLE MayContinue = 0;
VOID CALLBACK
CallBack(
__in HINTERNET hInternet,
__in DWORD_PTR dwContext,
__in DWORD dwInternetStatus,
__in_bcount(dwStatusInformationLength) LPVOID lpvStatusInformation,
__in DWORD dwStatusInformationLength
)
{
if (dwInternetStatus == INTERNET_STATUS_REQUEST_COMPLETE)
{
LatestResult = ((LPINTERNET_ASYNC_RESULT)lpvStatusInformation)->dwResult;
SetEvent (MayContinue);
}
}
int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPTSTR lpCmdLine,
_In_ int nCmdShow)
{
MayContinue = ::CreateEvent (NULL, FALSE, FALSE, NULL);
HINTERNET Session = InternetOpen (NULL, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, INTERNET_FLAG_ASYNC);
INTERNET_STATUS_CALLBACK CallbackPointer = InternetSetStatusCallback (Session, (INTERNET_STATUS_CALLBACK) CallBack);
MayContinue = ::CreateEvent (NULL, FALSE, FALSE, NULL);
InternetConnect (Session, _T ("ftp.secureftp-test.com"), INTERNET_INVALID_PORT_NUMBER, _T ("test"), _T ("test"), INTERNET_SERVICE_FTP, 0, 1);
WaitForSingleObject (MayContinue, INFINITE);
HINTERNET Internet = (HINTERNET) LatestResult;
WIN32_FIND_DATA *FindData = new WIN32_FIND_DATA;
FtpFindFirstFileW (Internet, _T ("*.*"), FindData, 0, 1);
WaitForSingleObject (MayContinue, INFINITE);
delete FindData;
return 0;
}
What I've got after execution:
Unhandled exception at 0xBAADF00D in WinInetTest.exe:
0xC0000005: Access violation executing location 0xBAADF00D.
It happens somewhere around final WaitForSingleObject and callstack is rather confusing.
But if I change
WIN32_FIND_DATA *FindData = new WIN32_FIND_DATA;
FtpFindFirstFileW (Internet, _T ("*.*"), FindData, 0, 1);
to
WIN32_FIND_DATAA *FindData = new WIN32_FIND_DATAA;
FtpFindFirstFileA (Internet, "*.*", FindData, 0, 1);
It executes and works as it should.
So my question is - am I really not doing something correctly or it just failure on WinInet side?
I'm testing it on Windows 7 using Visual Studio 2012 btw.
I also had difficulty with FtpFindFirstFileW. When I converted my project from MBCS to Unicode FtpFindFirstFileW resulted in an access violation that appeared to be a pointer dereference of 0xbaadf00d somewhere after the call, possibly when the async result was being prepared. I worked around this by using FtpFindFirstFileA, InternetFindNextFileA and the WIN32_FIND_DATAA struct in both MBCS and Unicode builds. I then convert the output cFileName field to a TCHAR string.
I'd recommend changing compiler setting Character Set from Unicode to Multi-Byte Character Set. The same thing was happening to me.