Create MFC DLL from C/C++ (VS 2010) code to be use

2019-08-26 19:26发布

问题:

I have a third party component written in C/C++ (on VS 2010) which can be downloaded here.

This component accepts 3 parameters as input (a filename and two numbers) and outputs a result in the console, and then outputs a file.

I've used Process and ProcessStartInfo in a C# WinForm project to consume this component which works fine. However, now I want to consume this in a WCF C# RESTful service, in which case the solution I thought with WinForm will not work. It was suggested that I instead convert this to a MFC DLL and then use InterOp to call the unmanaged DLL through my C# web service (other suggestions are welcome).

Unfortunately, I have no idea on how to do that and my knowledge on C/C++ is fairly average. So my question is: How do I create a DLL from that component which accepts these 3 parameters (taken from main()):

    cin >> fname;
    cin  >> minA;
    cin  >> minO;

then does whatever calculations it's supposed to do and return this (again taken from main()):

cout << "\nNumber is: " << num;

(and obviously still output the file it's supposed to output) ?

Any help would be HIGHLY appreciated. Thanks in advance!

UPDATE: As a point of reference, here is my WinForm implementation mentioned above.

    ProcessStartInfo cmdStartInfo = new ProcessStartInfo();
    Process cmdProcess = new Process();
    BackgroundWorker BWorker = new BackgroundWorker();

//is able to report progress
    BWorker.WorkerReportsProgress = true;
    //is able to be cancelled
    BWorker.WorkerSupportsCancellation = true;
    //attach events
    BWorker.DoWork += worker_DoWork;
    BWorker.RunWorkerCompleted += worker_RunWorkerCompleted;
    BWorker.RunWorkerAsync();

  private void worker_DoWork(object sender, DoWorkEventArgs e)
    {
        if (firstTimeLoaded)
        {
            cmdStartInfo.FileName = Path.GetFullPath("../../Resources/thirdparty.exe");
            cmdStartInfo.WorkingDirectory = Path.GetFullPath("../../Resources/");
            cmdStartInfo.RedirectStandardOutput = true;
            cmdStartInfo.RedirectStandardError = true;
            cmdStartInfo.RedirectStandardInput = true;
            cmdStartInfo.UseShellExecute = false;
            cmdStartInfo.CreateNoWindow = true;
            cmdProcess.StartInfo = cmdStartInfo;
            cmdProcess.SynchronizingObject = this;
            cmdProcess.ErrorDataReceived += cmd_Error;
            cmdProcess.Exited += cmd_Exited;
            cmdProcess.EnableRaisingEvents = true;
            cmdProcess.Start();
            cmdProcess.BeginErrorReadLine();
            firstTimeLoaded = false;
        }
        while (!cmdProcess.HasExited)
        {
            if (use)
            {
                if (BWorker.CancellationPending)
                {
                    e.Cancel = true;
                }
                StringBuilder builder = new StringBuilder();
                //read unbuffered output
                while (cmdProcess.StandardOutput.Peek() != -1)
                {
                    char inputChar = (char)cmdProcess.StandardOutput.Read();
                    if (inputChar != '\r' && inputChar != '\n')
                    {
                        builder.Append(inputChar);
                    }
                    if (inputChar == '\n')
                    {
                        break;
                    }
                }
                if (cmdProcess.StandardOutput.Peek() == -1)
                {
                    cmdProcess.StandardOutput.DiscardBufferedData();
                }
                //process the output
                string output = builder.ToString();
                //determine appropriate action
                switch (output)
                {
                    case "Enter file name: ":
                        cmdProcess.StandardInput.WriteLine(textBox1.Text);
                        break;
                    case "Enter minimum size of A: ":
                        cmdProcess.StandardInput.WriteLine(textBox2.Text);
                        break;
                    case "Enter minimum size of O: ":
                        cmdProcess.StandardInput.WriteLine(textBox3.Text);
                        break;
                }
                if (output.Contains("Number: "))
                {
                    MessageBox.Show("Number is: " + output.Substring(output.LastIndexOf(" ") + 1));
                    use = false;
                }         

            }

        }
    }

回答1:

Let's give this a try.

  1. In VS2010, create a Win32 Project under Visual C++/Win32. For this purpose, call it MyWin32Lib.
  2. Add the thirdparty.cpp file to the project and compile. You should get some warnings, but it's ok.
  3. Create a new header file called thirdparty.h so we can export the function signature.
  4. In the thirdparty.h file, do:

    #pragma once
    // This will be the interface for third party file
    int concepts(char* szFileName, int nMinIntent, int nMinExtent);
    
  5. In the thirdparty.cpp file, add #include "stdafx.h" right before #include

  6. Change the main function signature to match the one in the header:

    //int main()
    // Instead of getting input from console, we're passing it the values
    int concepts(char* szFileName, int nMinIntent, int nMinExtent)
    
  7. Comment out all input requests, and just copy the args to the local vars:

    //cout << "\n\n***** In-Close 3.0 Concept Miner *****";
    //cout << "\n\nEnter cxt file name including extension: ";
    //cin >> fname;
    //cout << "\nEnter minimum size of intent (no. attributes): ";
    //cin  >> minIn;
    //cout << "\nEnter minimum size of extent (no. objects): ";
    //cin  >> minEx;
    
    strcpy_s(fname, _countof(fname), szFileName);
    minIn = nMinIntent;
    minEx = nMinExtent;
    
  8. Comment out cout << "\nNumber... (this is no longer needed)

  9. At the end of the function, do:

    break;
    }
    
    //cout << "\n\nHit <enter> to finish";
    //while ( !_kbhit());
    
      return numcons;
    }
    

I don't know why there's a while(1) since there's no way to get out of it, but assume we'll doing it only once.

  1. Make sure you compile ok.
  2. Create a new CPP file, call it "Concepts.cpp"
  3. In Concepts.cpp, enter:

    #include "stdafx.h"
    #include "thirdparty.h"
    
    extern "C"
    {
        __declspec(dllexport) int GetConcepts(char* szFileName, int nMinIntent, int nMinExtent)
        {
          return concepts(szFileName, nMinIntent, nMinExtent);
        }
    }
    

*You should now have a Win32 DLL that performs the work using arguments instead.

  1. Create a C# Class Library project.
  2. Create a C# class called "Concepts.cs"
  3. In this class, enter:

    public class Concepts
    {
      // Link to the Win32 library through InterOp
      [DllImport("MyWin32Lib.dll")]
      public static extern int GetConcepts(
        [MarshalAs( UnmanagedType.LPStr )] string fileName, int minIntent, int minExtent );
    }
    

*You have to marshal the filename input as ANSI since that's what thirdparty.cpp uses.

I think I got all of it. You can now reference your C# library from a web service.