How to Pinvoke C++ DLL from C#

EDN Admin

Well-known member
Joined
Aug 7, 2010
Messages
12,794
Location
In the Machine
Im a C++ noobie.<br/>
<br/>
I created a Win32 app that works fine (there is no form, its basically a console app). Im trying to transform it to a DLL to be pinvoked from C#. There is only one function (called splitTiff) in the DLL that I need to call from C#. Oddly
enough, I can get it to work in C# by hard-coding a call to the splitTiff function in the C++ DLLmain.<br/>
<br/>
<pre>int WINAPI _stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
LPCWSTR sourceImage = TEXT("C:\z.tif");
LPCWSTR destDirectory = TEXT("C:\zout\z0000%d.tif");
splitTiff(sourceImage, destDirectory);
return 0;
}[/code]
<br/>
<br/>
Odd that this "works", as I am not invoking WinMain from C#: Rather I am invoking the splitTiff function:<br/>
<br/>
<pre> [DllImport("C:\Tiff-DLLs\TiffSplitter.dll")]
public static extern void splitTiff(string sourceFile, string dir);
[/code]
<br/>
The call to splitTiff (from C#) DOESNT work (my log indicates that splitTiff fails in its effort to create one of the objects, for reasons I dont understand). Oddly, though, the call from C# inadvertently invokes the Main function (which also
has a call to splitTiff). This call DOES work (and thus if I remove the Main function, the result is total failure). And theres (at least) two problems with relying on the Main function. (1) This is a hard coded call that doesnt allow my C# code to
pass in my parameters-values. (2) Even though the desired task is executed, an exception throws after completion.<br/>
<br/>
What I dont understand is this. When calling the DLL from C#, why does it succeed only if a Main function makes the call to splitTiff? Why does the object fail to instanciate if splitTiff is called from C# without help from a Main function? Heres the code:<br/>
<pre> // TiffSplitter.cpp : Defines the entry point for the console application.
//




#ifndef WINVER // Allow use of features specific to Windows XP or later.
#define WINVER 0x0501 // Change this to the appropriate value to target other versions of Windows.
#endif

#ifndef _WIN32_WINNT // Allow use of features specific to Windows XP or later.
#define _WIN32_WINNT 0x0501 // Change this to the appropriate value to target other versions of Windows.
#endif

#ifndef _WIN32_WINDOWS // Allow use of features specific to Windows 98 or later.
#define _WIN32_WINDOWS 0x0410 // Change this to the appropriate value to target Windows Me or later.
#endif

#ifndef _WIN32_IE // Allow use of features specific to IE 6.0 or later.
#define _WIN32_IE 0x0600 // Change this to the appropriate value to target other versions of IE.
#endif

#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers


#include <windows.h>
#include <wincodec.h>
#include <commdlg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string>
#include <string.h>
#include <winuser.h>
#include <wchar.h>
#include "stdafx.h"
#include <objbase.h>
#include <gdiplus.h>
#include <wincodec.h>
#include <wincodecsdk.h>
#include<iostream>
#include <fstream> //filestreams.

using namespace std;

template <typename T>
inline void SafeRelease(T *&p)
{
if (NULL != p)
{
p->Release();
p = NULL;
}
}





__declspec(dllexport) public void _stdcall splitTiff(LPCWSTR sourceFile, LPCWSTR outputDirectoryPlusPrefixWithPercentD);
std::string ToString(LPCWSTR original);

BYTE *m_pbBuffer;
IWICBitmapSource *m_pOriginalBitmapSource;
__declspec(dllexport) HRESULT _stdcall ConvertBitmapSource(IWICBitmapSource **ppToRenderBitmapSource);
__declspec(dllexport) HRESULT _stdcall CreateDIBSectionFromBitmapSource(IWICBitmapSource *pToRenderBitmapSource);
__declspec(dllexport) void _stdcall DeleteBufferAndBmp();
__declspec(dllexport) void _stdcall subGetCLSID(WCHAR* format, CLSID* pClsid);
Gdiplus::Bitmap *m_pGdiPlusBitmap;
IWICImagingFactory *m_pIWICFactory;




__declspec(dllexport) void _stdcall splitTiff(LPCWSTR sourceFile, LPCWSTR outputDirectoryPlusPrefixWithPercentD)

{

ofstream streamWriter("C:\zDebug.txt");
streamWriter << "Version 5" << endl;


streamWriter << "Arrived in splitTiff" << endl;
int numPagesProcessed =0;
streamWriter << "Next step is coinitialize...." << endl;
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
streamWriter << "Next step is declare the IWICbitmapDecoder as NULL...." << endl;
IWICBitmapFrameDecode *pFrame = NULL;
IWICBitmapDecoder *pDecoder = NULL; //pass in this decoder-pointer byRef as to create the decoder.
if (!SUCCEEDED(hr)) goto ExitSub;
streamWriter << "Next step is to initialize GDI...." << endl;
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
Gdiplus::Status status = Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
streamWriter << "Next step is to get the classID for Tiffs...." << endl;
if(status != Gdiplus::Ok) goto ExitSub;
CLSID *pClsidForTiffFormat = new CLSID;
subGetCLSID(L"TIFF", pClsidForTiffFormat);
streamWriter << "Finished getting the classID for Tiffs:"<< endl;
streamWriter << "Now doing co-Create instance to create teh IWICfactory." << endl;
hr = CoCreateInstance( CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&m_pIWICFactory));
if (!SUCCEEDED(hr)) goto ExitSub;
streamWriter << "Finished CoCreate instance" << endl;
// Step 2: Decode the source image to IWICBitmapSource
// Create a decoder specific to the image file on disk.
streamWriter << "Now creating decoder for this file: " << endl;
streamWriter << *sourceFile<< endl;
hr = m_pIWICFactory->CreateDecoderFromFilename(
sourceFile, // The file on disk to be decoded.
NULL, // Do not prefer a particular vendor
GENERIC_READ, // Desired read access to the file
WICDecodeMetadataCacheOnDemand, // Cache metadata when needed
&pDecoder // Pointer to the decoder
);

if (!SUCCEEDED(hr)) goto ExitSub;
// Retrieve the first frame of the image from the decoder
streamWriter << "Finished creating decoder. Now getting frame count..." << endl;
UINT* pNumPages = new UINT;
hr = (*pDecoder).GetFrameCount(pNumPages);
if (SUCCEEDED(hr)) streamWriter << "Successfully got framecount: " << *pNumPages << endl;
else
{
*pNumPages =1;
streamWriter << "Failed to get frame count!!! " << endl;
}
for (UINT intFrameIndex = 0; intFrameIndex < *pNumPages; intFrameIndex++)
{
streamWriter << "Selecting active frame number " << intFrameIndex + 1 << endl;
hr = pDecoder->GetFrame(intFrameIndex, &pFrame); //<------ loads the frame into pFrame.
if (SUCCEEDED(hr)) streamWriter << "Succesfully got frame number " << intFrameIndex + 1 << endl;
else streamWriter << "Failed to get frame number " << intFrameIndex + 1 << endl;
// Retrieve IWICBitmapSource from the frame
// m_pOriginalBitmapSource contains the original bitmap and acts as an intermediate
SafeRelease(m_pOriginalBitmapSource);
streamWriter << "Loading this frame into the OriginalBitmap object..." << endl;
hr = pFrame->QueryInterface( IID_IWICBitmapSource, reinterpret_cast<void **>(&m_pOriginalBitmapSource));
if (SUCCEEDED(hr)) streamWriter << "Success loading frame into OriginalBitmap object.." << endl;
else streamWriter << "Failed to load frame into the OriginalBitmap object..." << endl;
// Step 3: Convert the pixel format to 32bppBGR because it is is compatible with GDIplus
IWICBitmapSource *pToRenderBitmapSource = NULL;
streamWriter << "Calling sub to convert Bitmap into 32bppBGR." << endl;
hr = ConvertBitmapSource(&pToRenderBitmapSource);
if (SUCCEEDED(hr)) streamWriter << "Conversion sucessful." << endl;
else streamWriter << "Conversion failed." << endl;
// Step 4: Create a DIB from the converted IWICBitmapSource
streamWriter << "Calling sub to create DIB section." << endl;
hr = CreateDIBSectionFromBitmapSource(pToRenderBitmapSource);
if (SUCCEEDED(hr)) streamWriter << "Success creating DIB." << endl;
else streamWriter << "Failed to create DIB." << endl;
SafeRelease(pToRenderBitmapSource); //todo - release memory here?
TCHAR pathToDestFile[1000];
wsprintf(pathToDestFile, outputDirectoryPlusPrefixWithPercentD , intFrameIndex +1);
streamWriter << "Saving to this filename: " << pathToDestFile << endl;
status = m_pGdiPlusBitmap->Save(pathToDestFile, pClsidForTiffFormat, 0);
if(status == Gdiplus::Ok) streamWriter << "Saved it to: " << pathToDestFile << endl;
else streamWriter << "Failed to save. " << endl;
numPagesProcessed++;
}

ExitSub:;
SafeRelease(pDecoder);
SafeRelease(pFrame);
SafeRelease(m_pIWICFactory);
SafeRelease(m_pOriginalBitmapSource);
DeleteBufferAndBmp();
Gdiplus::GdiplusShutdown(gdiplusToken);
CoUninitialize();
}


// Converts to 32bppBGR
__declspec(dllexport) HRESULT _stdcall ConvertBitmapSource(IWICBitmapSource **ppToRenderBitmapSource)
{
HRESULT hr = S_OK;
*ppToRenderBitmapSource = NULL;
IWICFormatConverter *pConverter = NULL;
hr = m_pIWICFactory->CreateFormatConverter(&pConverter);
if (!SUCCEEDED(hr)) return hr;

hr = pConverter->Initialize(
m_pOriginalBitmapSource, // Input bitmap to convert
GUID_WICPixelFormat32bppBGR, // Destination pixel format
WICBitmapDitherTypeNone, // Specified dither patterm
NULL, // Specify a particular palette
0.f, // Alpha threshold
WICBitmapPaletteTypeCustom // Palette translation type
);
if (!SUCCEEDED(hr)) goto ExitSub;
// Store the converted bitmap as ppToRenderBitmapSource
hr = pConverter->QueryInterface( IID_PPV_ARGS(ppToRenderBitmapSource));
ExitSub:
SafeRelease(pConverter);
return hr;
}


// Creates a DIB Section from the converted IWICBitmapSource *
__declspec(dllexport) HRESULT _stdcall CreateDIBSectionFromBitmapSource(IWICBitmapSource *pToRenderBitmapSource)
{
HRESULT hr = S_OK;
UINT width = 0;
UINT height = 0;
// Check BitmapSource format
WICPixelFormatGUID pixelFormat;
hr = pToRenderBitmapSource->GetPixelFormat(&pixelFormat);
if (SUCCEEDED(hr)) hr = (pixelFormat == GUID_WICPixelFormat32bppBGR) ? S_OK : E_FAIL;
if (SUCCEEDED(hr)) hr = pToRenderBitmapSource->GetSize(&width, &height);
UINT cbStride = 0;
// Size of a scan line represented in bytes: 4 bytes each pixel
if (SUCCEEDED(hr)) hr = UIntMult(width, sizeof(Gdiplus::ARGB), &cbStride);
UINT cbBufferSize = 0;
if (SUCCEEDED(hr)) hr = UIntMult(cbStride, height, &cbBufferSize); // Size of the image, represented in bytes
if (!SUCCEEDED(hr)) return hr;
DeleteBufferAndBmp(); // Make sure to free previously allocated buffer and bitmap
m_pbBuffer = new BYTE[cbBufferSize];
hr = (m_pbBuffer) ? S_OK : E_FAIL;
if (!SUCCEEDED(hr)) return hr;
WICRect rc = { 0, 0, width, height };
// Extract the image into the GDI+ Bitmap
hr = pToRenderBitmapSource->CopyPixels(&rc, cbStride, cbBufferSize, m_pbBuffer);
if (!SUCCEEDED(hr)) return hr;
m_pGdiPlusBitmap = new Gdiplus::Bitmap( width, height, cbStride, PixelFormat32bppRGB, m_pbBuffer);
hr = m_pGdiPlusBitmap ? S_OK : E_FAIL;
if (!SUCCEEDED(hr)) return hr;
if (m_pGdiPlusBitmap->GetLastStatus() != Gdiplus::Ok) hr = E_FAIL;
if (FAILED(hr)) DeleteBufferAndBmp();
return hr;
}

__declspec(dllexport) void _stdcall DeleteBufferAndBmp()
{
delete m_pGdiPlusBitmap;
m_pGdiPlusBitmap = NULL;

delete[] m_pbBuffer;
m_pbBuffer = NULL;


}
//todo return numpages to caller.

__declspec(dllexport) void _stdcall subGetCLSID(WCHAR* format, CLSID* pClsid)
{
CLSID classID;
UINT num = 0; // number of image encoders
UINT size = 0; // size of the image encoder array in bytes
Gdiplus::ImageCodecInfo* pImageCodecInfo = NULL;
Gdiplus::GetImageEncodersSize(&num, &size);
pImageCodecInfo = (Gdiplus::ImageCodecInfo*)(malloc(size));
GetImageEncoders(num, size, pImageCodecInfo);
for(UINT j = 0; j < num; ++j)
if( wcscmp(pImageCodecInfo[j].FormatDescription, format) == 0 )
{
classID = pImageCodecInfo[j].Clsid;
*pClsid = classID;
free(pImageCodecInfo);
return;
}
}

std::string ToString(LPCWSTR original)
{
string result;
wstring orig = original;
result.resize(orig.size() ); //make enough room in copy for the string
std::copy(orig.begin(),orig.end(), result.begin()); //copy it
return result;
}

//todo - try to get the percent d moved.
// int _stdcall _tmain(int argc, _TCHAR* argv[])

int WINAPI _stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
LPCWSTR sourceImage = TEXT("C:\z.tif");
LPCWSTR destDirectory = TEXT("C:\zout\z0000%d.tif");
splitTiff(sourceImage, destDirectory);
return 0;
}
[/code]
<br/>

View the full article
 
Back
Top