We help IT Professionals succeed at work.

Check out our new AWS podcast with Certified Expert, Phil Phillips! Listen to "How to Execute a Seamless AWS Migration" on EE or on your favorite podcast platform. Listen Now

x

C# translation

rossryan
rossryan asked
on
Medium Priority
1,054 Views
Last Modified: 2007-12-19
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;
}
Comment
Watch Question

Commented:
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.

Author

Commented:
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...

Commented:
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.

Author

Commented:
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.

Author

Commented:
Oh yeah, all code is in a .NET Managed class library (VS.NET 2003).

Commented:
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.

Author

Commented:
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));
}


Author

Commented:
Any thoughts on why a C# project would not be able to reference this project?

Author

Commented:
I guess what I do not understand is how an unmanaged dll can be accessed by managed code...
Commented:
Unlock this solution with a free trial preview.
(No credit card required)
Get Preview

Author

Commented:
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

Commented:
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.
Unlock the solution to this question.
Thanks for using Experts Exchange.

Please provide your email to receive a free trial preview!

*This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

OR

Please enter a first name

Please enter a last name

8+ characters (letters, numbers, and a symbol)

By clicking, you agree to the Terms of Use and Privacy Policy.