Link to home
Start Free TrialLog in
Avatar of rossryan
rossryan

asked on

C# translation

What would the C# equiv be of this code?:

bool Hook(const TCHAR * module, const TCHAR * proc, unsigned & syscall_id, BYTE * & pProc, const void * pNewProc)
{
    HINSTANCE hMod = GetModuleHandle(module);

    pProc = (BYTE *) GetProcAddress(hMod, proc);

    if ( pProc[0] == 0xB8 )
    {
        syscall_id = * (unsigned *) (pProc + 1);

        DWORD flOldProtect;

        VirtualProtect(pProc, 5, PAGE_EXECUTE_READWRITE, & flOldProtect);

        pProc[0] = 0xE9;
        * (unsigned *) (pProc+1) = (unsigned)pNewProc - (unsigned) (pProc+5);

        pProc += 5;

        return true;
    }
    else
        return false;
}
Avatar of AlexFM
AlexFM

rossryan, after reading all stuff you want to translate I think you need to implement it in mixed C++ project.
First, it's impossible to translate C++ function to C#. It is possible to call API in C#, but translating the whole C++ function...

bool Hook(const TCHAR * module, const TCHAR * proc, unsigned & syscall_id, BYTE * & pProc, const void * pNewProc)

It's impossible to translate this to C#. Possibly pNewProc is C# delegate. TCHAR* is String. But converting all this stuff to C# is not equal to converting of single API calls.

I think you have wotking C++ project or class which makes some paint hook. The best thing you can do this is to write managed C++ wrapper for it and use this wrapper in C# code.
Avatar of rossryan

ASKER

Any ideas how to do that? I've been thinking about stripping out the class, compiling it as a dll, and using static extern to grab the procedures...
Generally, there are 2 ways:
1) Make unmanaged Dll which internally uses C++ class and exposes C-style interface (API) available in C#
2) Make mixed managed/unmanaged C++ Dll which uses internally C++ class and exposes pure managed interface for C# client.

I think first you should think about desired interface for your C# client: set of API or managed class. What information do you need from server?

Suppose you have the following C++ class:

class A
{
public:
     A(int n){ m_n = n};
     ~A();
     int Get(return m_n);
private:
     int m_n;
};

API interface for C# client may look like this:

void* CreateA(int n)
{
    A* pA = new A(n);
    return (void*) pA;
}

void DeleteA(void* p)
{
    A* pA = (A*)p;
    delete p;
}

int GetA(void* p)
{
    A* pA = (A*)p;
    return pA->n;
}

API wrapper contains Create and Delete function and function for each public class member. Member wrapper always gets pointer to class instance as it's first parameter. In C# this pointer is kepr in IntPtr variable.
Do you have a minute to help me get this working (wrappers are new to me). For ease of use, I'll give you all the files below.


// PaintHookWrapper.h
//This is the wrapper class.
#pragma once
#include "PaintHook.h"
#pragma managed
#using <mscorlib.dll>
using namespace System;


namespace PaintHookWrapper
{
      public __gc class PaintHookEx : public ICloneable
      {
      public:
            PaintHookEx();

            virtual Object* Clone()
            {            
            PaintHookEx* mHook = new PaintHookEx();      
            *(mHook->m_pCPaintHook) = *m_pCPaintHook;  
            return mHook;
            }

            void SubClass(HWND hWnd);
            bool Within_WM_PRINT();

      private:
            CPaintHook __nogc* m_pCPaintHook;
      };

      PaintHookEx::PaintHookEx()
      {
            m_pCPaintHook = new CPaintHook();
      }
      void PaintHookEx::SubClass(HWND hWnd)
      {
            m_pCPaintHook->SubClass(hWnd);

      }
      bool PaintHookEx::Within_WM_PRINT()
      {
            return m_pCPaintHook->Within_WM_PRINT();
      }

}

// Copyright (C) 2000 by Feng Yuan (www.fengyuan.com)
// Feng Yuan's class (Paint Hook) which I am trying to access from c#.
//PaintHook.h

// Windows Header Files:
#include <windows.h>

// C RunTime Header Files
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <tchar.h>
class CPaintHook
{
    BYTE      m_thunk[9];
    WNDPROC   m_OldWndProc;
    HDC       m_hDC;

    static HDC  WINAPI MyBeginPaint(HWND hWnd, LPPAINTSTRUCT lpPaint);
    static BOOL WINAPI MyEndPaint(HWND hWnd, LPPAINTSTRUCT lpPaint);

    virtual LRESULT WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
      
public:
   
    bool Within_WM_PRINT(void) const
    {  
        if ( IsBadWritePtr((void *) this, sizeof(CPaintHook)) )
            return false;

        if ( (m_thunk[0]==0xB9) && ((* (unsigned *) (m_thunk+5))==0x20FF018B) )
            return m_hDC !=0;
        else
            return false;
    }    
   
     CPaintHook(void);

    void SubClass(HWND hWnd);
};

// Copyright (C) 2000 by Feng Yuan (www.fengyuan.com)
//PaintHook.cpp file
#include "stdafx.h"
#include <assert.h>
// Windows Header Files:


#include "PaintHook.h"

bool Hook(const TCHAR * module, const TCHAR * proc, unsigned & syscall_id, BYTE * & pProc, const void * pNewProc)
{
    HINSTANCE hMod = GetModuleHandle(module);

    pProc = (BYTE *) GetProcAddress(hMod, proc);

    if ( pProc[0] == 0xB8 )
    {
        syscall_id = * (unsigned *) (pProc + 1);

        DWORD flOldProtect;

        VirtualProtect(pProc, 5, PAGE_EXECUTE_READWRITE, & flOldProtect);

        pProc[0] = 0xE9;
        * (unsigned *) (pProc+1) = (unsigned)pNewProc - (unsigned) (pProc+5);

        pProc += 5;

        return true;
    }
    else
        return false;
}


static unsigned syscall_BeginPaint = 0;
static BYTE *   pBeginPaint        = NULL;

static unsigned syscall_EndPaint   = 0;
static BYTE *   pEndPaint          = NULL;


LRESULT CPaintHook::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    //assert(m_OldWndProc);

    if ( uMsg==WM_PRINTCLIENT )
    {
        m_hDC = (HDC) wParam;
        uMsg  = WM_PAINT;
    }
       
    LRESULT hRslt = CallWindowProc(m_OldWndProc, hWnd, uMsg, wParam, lParam);

    m_hDC = NULL;

    return hRslt;
}
      

HDC WINAPI CPaintHook::MyBeginPaint(HWND hWnd, LPPAINTSTRUCT lpPaint)
{
    const CPaintHook * pThis = (CPaintHook *) GetWindowLong(hWnd, GWL_WNDPROC);
   
    pThis = (const CPaintHook *) ( (unsigned) pThis - (unsigned) & pThis->m_thunk[0] + (unsigned) pThis );
   
    if ( pThis->Within_WM_PRINT() )
    {
        memset(lpPaint, 0, sizeof(PAINTSTRUCT));

        lpPaint->hdc = pThis->m_hDC;
       
        GetClientRect(hWnd, & lpPaint->rcPaint);
       
        return pThis->m_hDC;
    }
    else
    {
        __asm   mov     eax, syscall_BeginPaint
        __asm   push    lpPaint
        __asm   push    hWnd
        __asm   call    pBeginPaint
    }
}


BOOL WINAPI CPaintHook::MyEndPaint(HWND hWnd, LPPAINTSTRUCT lpPaint)
{
    const CPaintHook * pThis = (CPaintHook *) GetWindowLong(hWnd, GWL_WNDPROC);
   
    pThis = (const CPaintHook *) ( (unsigned) pThis - (unsigned) & pThis->m_thunk[0] + (unsigned) pThis );
   
    if ( pThis->Within_WM_PRINT() )
        return TRUE;
    else
    {
        __asm   mov     eax, syscall_EndPaint
        __asm   push    lpPaint
        __asm   push    hWnd
        __asm   call    pEndPaint
    }
}

CPaintHook::CPaintHook()
{
    static bool s_hooked = false;

    if ( ! s_hooked )
    {
        Hook("USER32.DLL", "BeginPaint", syscall_BeginPaint, pBeginPaint, MyBeginPaint);
        Hook("USER32.DLL", "EndPaint",   syscall_EndPaint,   pEndPaint,   MyEndPaint);

        s_hooked = true;
    }

    m_thunk[0]              = 0xB9;               // mov ecx,
    *((DWORD *)(m_thunk+1)) = (DWORD) this;  //          this
      *((DWORD *)(m_thunk+5)) = 0x20FF018B;    // mov eax, [ecx]

    m_OldWndProc = NULL;
    m_hDC        = NULL;
}


void CPaintHook::SubClass(HWND hWnd)
{            
    m_OldWndProc = (WNDPROC) GetWindowLong(hWnd, GWL_WNDPROC);
    SetWindowLong(hWnd, GWL_WNDPROC, (LONG) ((void *) m_thunk));
}



Basically, I need the functionality provided by Feng's code, but I do not know how to properly wrap it. I have attempted above, but two things: 1.) I keep getting a linker error, and 2.) it looks like he hasn't implemented a destructor (no worries, I'll get around to it later). Any help would be greatly appreciated.
Oh yeah, all code is in a .NET Managed class library (VS.NET 2003).
If you want API wrapper, create unmanaged Dll (in my case it is called Sample):
File - New - Project - Visual C++ Projects - Win32 - Win32 Project. In Application Settings dialog select Dll, check Export Symbols ( to have some sample code). Press Finish.
Open Sample.h and Sample.cpp files. Remove CSample class and nSample variable from both files. fnSample function should be removed also, but instead of it you need to add API wrapper functions to Dll. These functions should be used by C# client using PInvoke.
Add PaintHook.h and PaintHook.cpp files to the project. Include PaintHook.h to Sample.cpp.

Add the following functions prototypes to Sample.h:

SAMPLE_API void* CreatePaintHook();
SAMPLE_API void DeletePaintHook(void* p);
SAMPLE_API void SubClass(void* p, HWND hWnd);
SAMPLE_API bool Within_WM_PRINT(void* p);

Add function implementations to Sample.cpp:

SAMPLE_API void* CreatePaintHook()
{
    CPaintHook* p = new CPaintHook();
    return (void*)p;
}

SAMPLE_API void DeletePaintHook(void* p)   // you still need this even if CPaintHook doesn't have destructor
{
    CPaintHook* pHook = (CPaintHook*)p;
    delete pHook;
}

SAMPLE_API void SubClass(void* p, HWND hWnd)
{
    CPaintHook* pHook = (CPaintHook*)p;
    pHook->SubClass(hWnd);
}

SAMPLE_API bool Within_WM_PRINT(void* p)
{
    CPaintHook* pHook = (CPaintHook*)p;
    return pHook->Within_WM_PRINT();
}

In C# client call CreatePaintHook and keep result in IntPtr variable. After this call other function always passing as first parameter this variable. To release C++ instance call DeletePaintHook.

Your problem is similar to the problem of C programmer who wants to use C++ class. It is solved by the same way.
Hmm. Created the project with the files below, compiles ok. I copied the dll to my other project folder, but when I go to add reference, I get a not valid assembly or COM component. Here are the files below:

//Sample.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 SAMPLE_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
// SAMPLE_API functions as being imported from a DLL, whereas this DLL sees symbols
// defined with this macro as being exported.
#ifdef SAMPLE_EXPORTS
#define SAMPLE_API __declspec(dllexport)
#else
#define SAMPLE_API __declspec(dllimport)
#endif

// This class is exported from the Sample.dll


SAMPLE_API void* CreatePaintHook();
SAMPLE_API void DeletePaintHook(void* p);
SAMPLE_API void SubClass(void* p, HWND hWnd);
SAMPLE_API bool Within_WM_PRINT(void* p);


//SAMPLE_API int fnSample(void);





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

#include "stdafx.h"
#include "Sample.h"
#include "PaintHook.h"
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


/*
// This is an example of an exported function.
SAMPLE_API int fnSample(void)
{
      return 42;
}
*/

// This is the constructor of a class that has been exported.
// see Sample.h for the class definition


SAMPLE_API void* CreatePaintHook()
{
    CPaintHook* p = new CPaintHook();
    return (void*)p;
}

SAMPLE_API void DeletePaintHook(void* p)   // you still need this even if CPaintHook doesn't have destructor
{
    CPaintHook* pHook = (CPaintHook*)p;
    delete pHook;
}

SAMPLE_API void SubClass(void* p, HWND hWnd)
{
    CPaintHook* pHook = (CPaintHook*)p;
    pHook->SubClass(hWnd);
}

SAMPLE_API bool Within_WM_PRINT(void* p)
{
    CPaintHook* pHook = (CPaintHook*)p;
    return pHook->Within_WM_PRINT();
}



//PaintHook.h
// Copyright (C) 2000 by Feng Yuan (www.fengyuan.com)


//#include "SwitchClass.h"
class CPaintHook
{
    BYTE      m_thunk[9];
    WNDPROC   m_OldWndProc;
    HDC       m_hDC;

    static HDC  WINAPI MyBeginPaint(HWND hWnd, LPPAINTSTRUCT lpPaint);
    static BOOL WINAPI MyEndPaint(HWND hWnd, LPPAINTSTRUCT lpPaint);

    virtual LRESULT WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
      
public:
   
    bool Within_WM_PRINT(void) const
    {  
        if ( IsBadWritePtr((void *) this, sizeof(CPaintHook)) )
            return false;

        if ( (m_thunk[0]==0xB9) && ((* (unsigned *) (m_thunk+5))==0x20FF018B) )
            return m_hDC !=0;
        else
            return false;
    }    
   
    CPaintHook(void);

    void SubClass(HWND hWnd);
};


//PaintHook.cpp
// Copyright (C) 2000 by Feng Yuan (www.fengyuan.com)

#include "stdafx.h"
#include <assert.h>

#include "PaintHook.h"

bool Hook(const TCHAR * module, const TCHAR * proc, unsigned & syscall_id, BYTE * & pProc, const void * pNewProc)
{
    HINSTANCE hMod = GetModuleHandle(module);

    pProc = (BYTE *) GetProcAddress(hMod, proc);

    if ( pProc[0] == 0xB8 )
    {
        syscall_id = * (unsigned *) (pProc + 1);

        DWORD flOldProtect;

        VirtualProtect(pProc, 5, PAGE_EXECUTE_READWRITE, & flOldProtect);

        pProc[0] = 0xE9;
        * (unsigned *) (pProc+1) = (unsigned)pNewProc - (unsigned) (pProc+5);

        pProc += 5;

        return true;
    }
    else
        return false;
}


static unsigned syscall_BeginPaint = 0;
static BYTE *   pBeginPaint        = NULL;

static unsigned syscall_EndPaint   = 0;
static BYTE *   pEndPaint          = NULL;


LRESULT CPaintHook::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    assert(m_OldWndProc);

    if ( uMsg==WM_PRINTCLIENT )
    {
        m_hDC = (HDC) wParam;
        uMsg  = WM_PAINT;
    }
       
    LRESULT hRslt = CallWindowProc(m_OldWndProc, hWnd, uMsg, wParam, lParam);

    m_hDC = NULL;

    return hRslt;
}
      

HDC WINAPI CPaintHook::MyBeginPaint(HWND hWnd, LPPAINTSTRUCT lpPaint)
{
    const CPaintHook * pThis = (CPaintHook *) GetWindowLong(hWnd, GWL_WNDPROC);
   
    pThis = (const CPaintHook *) ( (unsigned) pThis - (unsigned) & pThis->m_thunk[0] + (unsigned) pThis );
   
    if ( pThis->Within_WM_PRINT() )
    {
        memset(lpPaint, 0, sizeof(PAINTSTRUCT));

        lpPaint->hdc = pThis->m_hDC;
       
        GetClientRect(hWnd, & lpPaint->rcPaint);
       
        return pThis->m_hDC;
    }
    else
    {
        __asm   mov     eax, syscall_BeginPaint
        __asm   push    lpPaint
        __asm   push    hWnd
        __asm   call    pBeginPaint
    }
}


BOOL WINAPI CPaintHook::MyEndPaint(HWND hWnd, LPPAINTSTRUCT lpPaint)
{
    const CPaintHook * pThis = (CPaintHook *) GetWindowLong(hWnd, GWL_WNDPROC);
   
    pThis = (const CPaintHook *) ( (unsigned) pThis - (unsigned) & pThis->m_thunk[0] + (unsigned) pThis );
   
    if ( pThis->Within_WM_PRINT() )
        return TRUE;
    else
    {
        __asm   mov     eax, syscall_EndPaint
        __asm   push    lpPaint
        __asm   push    hWnd
        __asm   call    pEndPaint
    }
}

CPaintHook::CPaintHook()
{
    static bool s_hooked = false;

    if ( ! s_hooked )
    {
        Hook("USER32.DLL", "BeginPaint", syscall_BeginPaint, pBeginPaint, MyBeginPaint);
        Hook("USER32.DLL", "EndPaint",   syscall_EndPaint,   pEndPaint,   MyEndPaint);

        s_hooked = true;
    }

    m_thunk[0]              = 0xB9;               // mov ecx,
    *((DWORD *)(m_thunk+1)) = (DWORD) this;  //          this
      *((DWORD *)(m_thunk+5)) = 0x20FF018B;    // mov eax, [ecx]

    m_OldWndProc = NULL;
    m_hDC        = NULL;
}


void CPaintHook::SubClass(HWND hWnd)
{            
    m_OldWndProc = (WNDPROC) GetWindowLong(hWnd, GWL_WNDPROC);
    SetWindowLong(hWnd, GWL_WNDPROC, (LONG) ((void *) m_thunk));
}


Any thoughts on why a C# project would not be able to reference this project?
I guess what I do not understand is how an unmanaged dll can be accessed by managed code...
ASKER CERTIFIED SOLUTION
Avatar of AlexFM
AlexFM

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Very nice, and please kill me for forgetting PInvoke. I literally have lines of it:

[DllImport("GDI32.dll")]
            public static extern bool BitBlt(int hdcDest,int nXDest,int nYDest,
                  int nWidth,int nHeight,int hdcSrc,
                  int nXSrc,int nYSrc,int dwRop);
            [DllImport("GDI32.dll")]
            public static extern bool BitBlt(IntPtr hdcDest,int nXDest,int nYDest,
                  int nWidth,int nHeight,IntPtr hdcSrc,
                  int nXSrc,int nYSrc,int dwRop);
            [DllImport("GDI32.dll")]
            public static extern int CreateCompatibleBitmap(int hdc,int nWidth,
                  int nHeight);
            [DllImport("GDI32.dll")]
            public static extern IntPtr CreateCompatibleBitmap(IntPtr hdc,int nWidth,
                  int nHeight);
            [DllImport("GDI32.dll")]
            public static extern int CreateCompatibleDC(int hdc);
            [DllImport("GDI32.dll")]
            public static extern IntPtr CreateCompatibleDC(IntPtr hdc);
            [DllImport("GDI32.dll")]
            public static extern bool DeleteDC(int hdc);

Anyways, you got the points, and if you make it to the other questions (C# Translations[i]) before I finish stress testing some features, I'll give you those points as well. This class is immensely useful for some operations I need.

Thanks,
Ryan
Hi rossryan, nice to see that I can help.
EE rules doesn't allow to experts to get more than 500 points for one question. However, in some extraordinary cases it is possible to get additional 500 points. Since I provided two different solutions to your "C# translation" questions, I allow to myself to add notes to two more questions from this series. You can accept them as an answer if you think I deserve this. Thanks.