1

I'm trying to get the FPS data from "Fraps". There is a project called LCDHost on github which has done this. Apparently it's done by hooking to "fraps.dll"

I need to port this code to a C# project i'm working on. Problem is I'm not good at C++ and haven't got any experience in accessing unmanaged dlls from C#.

If someone can give me pointers on how to convert this code to C#, I'd appreciate that.

#include <stdio.h>
#include <windows.h>

#include "LH_Text.h"

struct FRAPS_SHARED_DATA {
   DWORD sizeOfStruct;
   DWORD currentFPS;
   DWORD totalFrames;
   DWORD timeOfLastFrame;
   char gameName[32];
};

FRAPS_SHARED_DATA *(WINAPI *FrapsSharedData) ();    
int notify(int n,void* p)
{
    if( !n || n&LH_NOTE_SECOND )
    {
        HMODULE frapsDLL;
        FRAPS_SHARED_DATA *fsd;
        frapsDLL = GetModuleHandleA("FRAPS32.DLL");
        if (!frapsDLL) {
            if( setText("N/A") ) callback(lh_cb_render,NULL);
        } else {
            FrapsSharedData = (typeof(FrapsSharedData)) GetProcAddress(frapsDLL, "FrapsSharedData");
            if (!FrapsSharedData) {
                if( setText("Needs Fraps 1.9C or later!") ) callback(lh_cb_render,NULL);
            } else {
                if( setText( "Fraps is running & is the right version." ) ) callback(lh_cb_render,NULL);
                fsd = FrapsSharedData();
                if( setText(QString::number(fsd->currentFPS) ) ) callback(lh_cb_render,NULL);
            }
        }
    }
    return LH_Text::notify(n,p) | LH_NOTE_SECOND;
}

The line that really stumped me is this one

FrapsSharedData = (typeof(FrapsSharedData)) GetProcAddress(frapsDLL, "FrapsSharedData");

I don't know what's the equivalent of it in C#

The full code can be found here

AGB
  • 2,230
  • 1
  • 14
  • 21
flanker
  • 101
  • 6
  • The C++ code is loading the DLL at runtime. All GetProcAddress does is get a pointer to the DLL function it wants to call later on. This is all C++ stuff that you shouldn't worry about In C# -- just read up on how to interface to a third-party DLL in C# -- that is the very first step. – PaulMcKenzie May 13 '16 at 20:25
  • 2
    Usually can achieve this with the `DllImport` Attribute Class , https://msdn.microsoft.com/en-us/library/aa984739(v=vs.71).aspx – MDK May 13 '16 at 20:25
  • Is this a duplicate of http://stackoverflow.com/questions/1202744/dynamically-p-invoking-a-dll? There are a couple of useful links there to articles on how to do LoadLibrary/GetProcAddress in C# – Martin Bonner supports Monica May 13 '16 at 20:26
  • @PaulMcKenzie: I think he very much does have to worry about this in C# - the call must not be made if the current installed DLL is an old version. MDK: I don't think `DllImport` will help (much) for the same reason. – Martin Bonner supports Monica May 13 '16 at 20:29
  • 1
    @MartinBonner I disagree. The OP's Question seems to indicate trying to understand dynamic binding to DLL. Although it was used in version qualification I dont believe thats what the OP needs to understand – MDK May 13 '16 at 20:36
  • @MartinBonner: If you check the actual code behavior, not the strings, it simply says that the function pointer should not be used if it is null. P/invoke will catch that case (missing export) as well and throw it as an exception which can be handled in C#. – Ben Voigt May 13 '16 at 21:37
  • FrapsSharedData is a function pointer declaration, the exact equivalent in C# is a delegate. The cast on the GetProcAddress() return value needs to be Marshal.GetDelegateForFunctionPointer() and a cast to the delegate type. – Hans Passant May 13 '16 at 22:19
  • There seems to be an awful lot of pointers in C++, is it just my untrained eye or do they make code very hard to read? 'FRAPS_SHARED_DATA *(WINAPI *FrapsSharedData) (); ' What does that even mean? – flanker May 14 '16 at 06:48
  • No, they don't make things hard to read (unless you have too many at once). FrapsSharedData is a pointer to a function which takes no arguments, uses the WINAPI calling convention, and returns a pointer to a FRAPS_SHARED_DATA structure. – Martin Bonner supports Monica May 14 '16 at 07:06
  • @MartinBonner I tried to make this work as standalone C++ application. Figured that I could output the value from it to C#._ FrapsSharedData = (typeid(FrapsSharedData).name()) GetProcAddress(frapsDLL, "FrapsSharedData");_ This line still gives error. The error message is " 3 IntelliSense: a value of type "const char *" cannot be assigned to an entity of type "FRAPS_SHARED_DATA *(__stdcall *)()"ConsoleApplication1.cpp 33 19 ConsoleApplication1 " – flanker May 14 '16 at 10:08

2 Answers2

3

Use DllImport Attribute and it will call an unmanaged dlls. Just find the necessary C library call an appropriate function from it. Very useful example is there: https://msdn.microsoft.com/en-us/library/aa984739(v=vs.71).aspx

Nikolai Arsenov
  • 464
  • 2
  • 10
  • Thanks, but the link you provided seems be be about C++. I've found [this](http://stackoverflow.com/questions/19450783/how-to-use-dllimport-in-c) post. Might be related to my case – flanker May 14 '16 at 06:44
  • That what I was talking about. It doesn't matter. It works for C++ and Sharp. But in DllImport attribute put DLL needed for you. – Nikolai Arsenov May 14 '16 at 09:01
  • Managed to get the pointer to the struct from the DLL. Can't figure out how to put the returned data into struct. This is what I currently have http://pastebin.com/Vwzbeacv – flanker May 14 '16 at 14:21
0

Figured it out. Here is how I managed.

class fraps
{
    [StructLayout(LayoutKind.Sequential)]
    public struct sFraps
    {
        public int SizeOfStruct;
        public int CurrentFPS;
        public uint TotalFrames;
        public uint TimeOfLastFrame;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
        public char[] GameName;

    };

    [DllImport("FRAPS32.dll")]
    public static extern IntPtr FrapsSharedData();

    public int fpsGet() {
        try
        {
            sFraps fps = (sFraps)Marshal.PtrToStructure(FrapsSharedData(), typeof(sFraps));
            return fps.CurrentFPS;
        }
        catch
        {
            return -1;
        }


    }
}

And at the top you need to

using System.Runtime.InteropServices;

Using unmanaged DLLs in C# code is far more complicated then I initially thought. One thing I don't understand, how do we figure out the stuct's structure in the first place?

flanker
  • 101
  • 6
  • The structure itself you can find in documentation and write it in C# way. Then set the function from unmanaged library it will check the struct's structure and fill it. – Nikolai Arsenov May 17 '16 at 22:12
  • @NikolaiArsenov So there is no way to "discover" the structure or the dll's struct? I imagined there would be some kind of reverse engineering tool that'd show such a thing. – flanker May 18 '16 at 12:52
  • There is such tools, i am sure. Because most of all functionality for C# libraries are generated by tools. E.g. SharpDX project or SharpGL - generated wraps equal to DirectX and OpenGL libraries for C++. See these articles: http://stackoverflow.com/questions/11668932/automatically-generate-c-sharp-wrapper-class-from-dll-in-visual-studio-2010-expr and http://stackoverflow.com/questions/2120690/tool-for-creating-net-wrappers-for-a-com-dll – Nikolai Arsenov May 18 '16 at 21:59