গতিশীলভাবে একটি ডিএলএল থেকে কোনও ফাংশন লোড করুন


88

আমি .dll ফাইলগুলিতে একটু নজর রাখছি, আমি তাদের ব্যবহারগুলি বুঝতে পারি এবং সেগুলি কীভাবে ব্যবহার করতে হয় তা বোঝার চেষ্টা করছি।

আমি একটি .dll ফাইল তৈরি করেছি যাতে একটি ফাংশন থাকে যা ফ্যানসি () নামে একটি পূর্ণসংখ্যা ফেরত দেয়

এই কোডটি ব্যবহার করে, আমি (মনে করি) আমি .dll ফাইলটি প্রকল্পে আমদানি করেছি (কোনও অভিযোগ নেই):

#include <windows.h>
#include <iostream>

int main() {
  HINSTANCE hGetProcIDDLL = LoadLibrary("C:\\Documents and Settings\\User\\Desktop  \\fgfdg\\dgdg\\test.dll");

  if (hGetProcIDDLL == NULL) {
    std::cout << "cannot locate the .dll file" << std::endl;
  } else {
    std::cout << "it has been called" << std::endl;
    return -1;
  }

  int a = funci();

  return a;
}

# funci function 

int funci() {
  return 40;
}

যাইহোক আমি যখন এই .cpp ফাইলটি সংকলন করার চেষ্টা করি যা আমার মনে হয় .dll আমদানি করেছে আমার নিম্নলিখিত ত্রুটি আছে:

C:\Documents and Settings\User\Desktop\fgfdg\onemore.cpp||In function 'int main()':|
C:\Documents and Settings\User\Desktop\fgfdg\onemore.cpp|16|error: 'funci' was not     declared in this scope|
||=== Build finished: 1 errors, 0 warnings ===|

আমি জানি একটি .dll একটি শিরোলেখ ফাইলের থেকে আলাদা তাই আমি জানি যে আমি এর মতো কোনও ফাংশন আমদানি করতে পারি না তবে আমি চেষ্টা করেছি তা দেখাতে এটিই সেরা।

আমার প্রশ্ন, আমি কীভাবে hGetProcIDDLL.dll এর মধ্যে ফাংশনটি অ্যাক্সেস করতে পয়েন্টারটি ব্যবহার করতে পারি ।

আমি আশা করি এই প্রশ্নটি বোধগম্য হয়ে উঠেছে এবং আমি আবার কোনও ভুল গাছের ঝাঁকুনি দিচ্ছি না।


স্থির / গতিশীল লিঙ্কিংয়ের সন্ধান করা।
মিচ গম

আপনাকে ধন্যবাদ, আমি এটি পর্যালোচনা করব

আমি আমার কোডটি ইনডেন্ট করি তবে যখন আমি এটি এখানে সরিয়ে ফর্ম্যাটটি মিস হয়ে যায় তাই আমি

উত্তর:


153

LoadLibraryআপনি যা ভাবেন তা করে না। এটি বর্তমান প্রক্রিয়ার স্মৃতিতে ডিএলএল লোড করে, তবে এটি যাদুকরীভাবে এটিতে নির্ধারিত ফাংশনগুলি আমদানি করে না ! এটি সম্ভব হবে না, যেহেতু ফাংশন কলগুলি সংযোগের সময় লিঙ্কারের দ্বারা সমাধান করা হয়েছিল যখন LoadLibraryরানটাইম সময়ে ডাকা হয় (মনে রাখবেন যে সি ++ একটি স্থিতিযুক্ত টাইপযুক্ত ভাষা)।

আপনি পরিবর্তনশীল লোড ফাংশন ঠিকানা পেতে একটি পৃথক WinAPI ফাংশন প্রয়োজন: GetProcAddress

উদাহরণ

#include <windows.h>
#include <iostream>

/* Define a function pointer for our imported
 * function.
 * This reads as "introduce the new type f_funci as the type: 
 *                pointer to a function returning an int and 
 *                taking no arguments.
 *
 * Make sure to use matching calling convention (__cdecl, __stdcall, ...)
 * with the exported function. __stdcall is the convention used by the WinAPI
 */
typedef int (__stdcall *f_funci)();

int main()
{
  HINSTANCE hGetProcIDDLL = LoadLibrary("C:\\Documents and Settings\\User\\Desktop\\test.dll");

  if (!hGetProcIDDLL) {
    std::cout << "could not load the dynamic library" << std::endl;
    return EXIT_FAILURE;
  }

  // resolve function address here
  f_funci funci = (f_funci)GetProcAddress(hGetProcIDDLL, "funci");
  if (!funci) {
    std::cout << "could not locate the function" << std::endl;
    return EXIT_FAILURE;
  }

  std::cout << "funci() returned " << funci() << std::endl;

  return EXIT_SUCCESS;
}

এছাড়াও, আপনার ডিএলএল থেকে আপনার ফাংশনটি সঠিকভাবে রফতানি করা উচিত । এটি এইভাবে করা যেতে পারে:

int __declspec(dllexport) __stdcall funci() {
   // ...
}

লুন্ডিন নোট হিসাবে, হ্যান্ডেলটি লাইব্রেরিতে মুক্ত করার ভাল অনুশীলন যদি আপনার আর এটির প্রয়োজন না হয়। যদি অন্য কোনও প্রক্রিয়া এখনও একই ডিএলএলে একটি হ্যান্ডেল না ধরে থাকে তবে এটি আনলোড হতে পারে।


একটি নির্বোধ প্রশ্নের মতো শব্দ হতে পারে তবে এফ_ফ্যান্সির ধরণটি কী / হওয়া উচিত?

8
এটি

6
মনে রাখবেন যে, f_funciআসলে এক প্রকার (বদলে গেছে একটি টাইপ)। প্রকারটি f_funci"কোনও ফাংশনটির দিকে নির্দেশককে প্রত্যাবর্তন করে intএবং কোনও আর্গুমেন্ট না নেওয়ার জন্য নির্দেশক" হিসাবে পড়ে । সিতে ফাংশন পয়েন্টার সম্পর্কিত আরও তথ্য newty.de/fpt/index.html পাওয়া যাবে
নিক্লাস বি।

উত্তরের জন্য আপনাকে আবার ধন্যবাদ, ফ্যানসি কোন যুক্তি নেয় না এবং পূর্ণসংখ্যা ফেরত দেয়; আমি প্রশ্নটি সম্পাদনা করে ফাংশনটি সংকলিতটি দেখানোর জন্য করেছি? .dll মধ্যে। যখন আমি " টাইপিড ইনট ( এফ_ফুন্সি) () অন্তর্ভুক্ত করে চালানোর চেষ্টা করেছি ;" আমি এই ত্রুটিটি পেয়েছি: সি: \ ডকুমেন্টস এবং সেটিংস \ ব্যবহারকারীর \ ডেস্কটপ \ fgfdg \ onemore.cpp || ফাংশনটিতে 'int প্রধান ()': | সি: \ ডকুমেন্টস এবং সেটিংস \ ব্যবহারকারী \ ডেস্কটপ \ fgfdg \ onemore.cpp | 18 | ত্রুটি: আর্গুমেন্ট '2' তে 'আন্ত' (* গেটপ্রোকএড্রেস (HINSTANCE__ , HINSTANCE__), হিসাবে 'int ( ) ()' কে 'কনস্ট CHAR *' তে রূপান্তর করতে পারে না , কনস্ট CHAR )) () '| || === বিল্ড সমাপ্ত: 1 ত্রুটি, 0 সতর্কতা === |

ঠিক আছে আমি সেখানে একটি .ালাই ভুলে গেছি (এটি সম্পাদিত)। ত্রুটিটি অন্যটি বলে মনে হচ্ছে, আপনি কি নিশ্চিত যে সঠিক কোডটি ব্যবহার করেছেন? যদি হ্যাঁ, আপনি দয়া করে আপনার ব্যর্থ কোড এবং pastie.org এ সম্পূর্ণ সংকলক আউটপুট আটকান ? এছাড়াও, আপনার মন্তব্যে যে টাইপেডেফ লিখেছেন তা ভুল (একটি *নিখোঁজ রয়েছে যা ত্রুটির কারণ হতে পারে)
নিক্লাস বি।

34

ইতিমধ্যে পোস্ট করা উত্তর ছাড়াও, আমি ভেবেছিলাম যে আমি প্রতিটি ডিএনএল ফাংশন লোড করার জন্য একটি কার্যকরী কৌশল ভাগ করে নেব যা ফাংশন পয়েন্টারগুলির মাধ্যমে প্রতিটি ফাংশনের জন্য পৃথক গেটপ্রোকএড্রেস কল না লিখেই করা উচিত। ওপিতে চেষ্টা করার সাথে সাথে আমি ফাংশনগুলিকে সরাসরি কল করতে চাই।

জেনেরিক ফাংশন পয়েন্টার ধরণের সংজ্ঞা দিয়ে শুরু করুন:

typedef int (__stdcall* func_ptr_t)();

কি ধরণের ব্যবহৃত হয় তা গুরুত্বপূর্ণ নয়। এবার সেই ধরণের একটি অ্যারে তৈরি করুন, যা আপনার ডিএলএল-এর কার্যকারিতার পরিমাণের সাথে মিলে যায়:

func_ptr_t func_ptr [DLL_FUNCTIONS_N];

এই অ্যারেতে আমরা আসল ফাংশন পয়েন্টারগুলি সঞ্চয় করতে পারি যা ডিএলএল মেমরি স্পেসে নির্দেশ করে।

পরবর্তী সমস্যাটি হ'ল GetProcAddressস্ট্রিং হিসাবে ফাংশনটির নাম প্রত্যাশা করে। সুতরাং ডিএলএলে ফাংশনের নামগুলি সমন্বিত একটি অনুরূপ অ্যারে তৈরি করুন:

const char* DLL_FUNCTION_NAMES [DLL_FUNCTIONS_N] = 
{
  "dll_add",
  "dll_subtract",
  "dll_do_stuff",
  ...
};

এখন আমরা সহজেই একটি লুপে গেটপ্রোকএড্রেস () কল করতে পারি এবং প্রতিটি ফাংশন সেই অ্যারের ভিতরে সঞ্চয় করতে পারি:

for(int i=0; i<DLL_FUNCTIONS_N; i++)
{
  func_ptr[i] = GetProcAddress(hinst_mydll, DLL_FUNCTION_NAMES[i]);

  if(func_ptr[i] == NULL)
  {
    // error handling, most likely you have to terminate the program here
  }
}

যদি লুপটি সফল হয় তবে আমাদের এখন কেবলমাত্র সমস্যাটি হ'ল ফাংশনগুলি কল করা। পূর্ব থেকে ফাংশন পয়েন্টার টাইপএফ সহায়ক নয়, কারণ প্রতিটি ফাংশনের নিজস্ব স্বাক্ষর থাকবে। সমস্ত কার্যকারিতা সহ একটি স্ট্রাক তৈরি করে এটি সমাধান করা যেতে পারে:

typedef struct
{
  int  (__stdcall* dll_add_ptr)(int, int);
  int  (__stdcall* dll_subtract_ptr)(int, int);
  void (__stdcall* dll_do_stuff_ptr)(something);
  ...
} functions_struct;

এবং শেষ অবধি, এটিকে আগে থেকে অ্যারেতে সংযুক্ত করতে একটি ইউনিয়ন তৈরি করুন:

typedef union
{
  functions_struct  by_type;
  func_ptr_t        func_ptr [DLL_FUNCTIONS_N];
} functions_union;

এখন আপনি ডিএলএল থেকে সমস্ত ফাংশন সুবিধাজনক লুপের সাথে লোড করতে পারেন তবে by_typeইউনিয়ন সদস্যের মাধ্যমে তাদের কল করতে পারেন ।

তবে অবশ্যই এর মতো কিছু টাইপ করা কিছুটা বোঝা

functions.by_type.dll_add_ptr(1, 1); আপনি যখনই কোনও ফাংশন কল করতে চান।

দেখা যাচ্ছে, এই কারণেই আমি নামগুলিতে "পিটিআর" পোস্টফিক্স যুক্ত করেছি: আমি এগুলিকে প্রকৃত ফাংশন নাম থেকে আলাদা রাখতে চেয়েছিলাম। আমরা এখন আইকি স্ট্রাক্ট সিনট্যাক্সটি মসৃণ করতে এবং কিছু ম্যাক্রো ব্যবহার করে পছন্দসই নামগুলি পেতে পারি:

#define dll_add (functions.by_type.dll_add_ptr)
#define dll_subtract (functions.by_type.dll_subtract_ptr)
#define dll_do_stuff (functions.by_type.dll_do_stuff_ptr)

এবং যাইহোক, আপনি এখন সঠিক প্রকার এবং পরামিতিগুলি সহ ফাংশনটির নামগুলি ব্যবহার করতে পারেন, যেন এগুলি আপনার প্রকল্পের সাথে স্থিরভাবে সংযুক্ত ছিল:

int result = dll_add(1, 1);

দাবি অস্বীকার: কঠোরভাবে বলতে গেলে, বিভিন্ন ফাংশন পয়েন্টারগুলির মধ্যে রূপান্তরগুলি সি স্ট্যান্ডার্ড দ্বারা নির্ধারিত হয় না এবং নিরাপদ নয় not তাই আনুষ্ঠানিকভাবে, আমি এখানে যা করছি তা অপরিজ্ঞাত আচরণ। যাইহোক, উইন্ডোজ বিশ্বে, ফাংশন পয়েন্টারগুলি সর্বদা একই আকারের হয় সে যাই হোক না কেন তাদের ধরন এবং তাদের মধ্যে রূপান্তরগুলি আমি ব্যবহার করা উইন্ডোজের যে কোনও সংস্করণে অনুমানযোগ্য।

এছাড়াও, তত্ত্বে ইউনিয়ন / কাঠামোর মধ্যে প্যাডিং sertedোকানো হতে পারে, যা সবকিছুকে ব্যর্থ করে দেবে। যাইহোক, পয়েন্টারগুলি উইন্ডোতে প্রান্তিককরণের প্রয়োজনীয়তার মতো একই আকারের হয়। static_assertকাঠামো / ইউনিয়নের কোনও প্যাডিং নেই এমন নিশ্চিত হওয়ার জন্য একটি ।


4
এই সি স্টাইল পদ্ধতির কাজ করবে। তবে এটিগুলি এড়াতে C ++ কনস্ট্রাক্ট ব্যবহার করা কি উপযুক্ত হবে না #define?
হার্পার

@ হ্যাপার ওয়েল সি ++ 11 এ আপনি ব্যবহার করতে পারেন auto dll_add = ...তবে সি ++ 03 তে এমন কোনও নির্মাণ নেই যা আমি ভাবতে পারি যে এটি কাজটি সহজ করবে (আমি #defineএখানে
এসগুলির

যেহেতু এটি সমস্ত উইনাপি-নির্দিষ্ট, আপনার নিজের নিজস্ব টাইপডেফ করার দরকার নেই func_ptr_t। পরিবর্তে আপনি ব্যবহার করতে পারেন FARPROC, যা এর রিটার্ন টাইপ GetProcAddress। এটি আপনাকে GetProcAddressকলটিতে কোনও কাস্ট যোগ না করে উচ্চতর সতর্কতা স্তরের সাথে সংকলনের অনুমতি দিতে পারে ।
অ্যাড্রিয়ান ম্যাকার্থি

@ নিক্লাসবি। আপনি autoএকবারে কেবল একটি ফাংশনের জন্য ব্যবহার করতে পারেন , এটি একবারে লুপের জন্য একবার করার ধারণাটিকে পরাস্ত করে। তবে অ্যারে স্ট্যান্ড ::
ফাংশনটিতে কী হয়েছে

4
@ ফ্রেঞ্চসকো স্ট্যান্ড :: ফাংশন প্রকারগুলি ফানক্ট্রিটার প্রকারের মতোই পৃথক হবে। আমার ধারণা, বৈকল্পিক টেম্পলেটগুলি সাহায্য করবে
নিক্লাস বি।

1

এটি ঠিক একটি উত্তপ্ত বিষয় নয়, তবে আমার একটি কারখানার ক্লাস রয়েছে যা একটি ডেলকে একটি উদাহরণ তৈরি করতে এবং এটি একটি ডিএলএল হিসাবে ফেরত দেওয়ার অনুমতি দেয়। এটি আমি যা খুঁজতে এসেছি কিন্তু সঠিকভাবে খুঁজে পাইনি।

এটিকে বলা হয়,

IHTTP_Server *server = SN::SN_Factory<IHTTP_Server>::CreateObject();
IHTTP_Server *server2 =
      SN::SN_Factory<IHTTP_Server>::CreateObject(IHTTP_Server_special_entry);

যেখানে IHTTP_Server হয় অন্য কোনও ডিএলএল বা একই জাতীয় কোনও শ্রেণীর জন্য তৈরি খাঁটি ভার্চুয়াল ইন্টারফেস।

DEFINE_INTERFACE ক্লাস আইডিকে একটি ইন্টারফেস দেওয়ার জন্য ব্যবহৃত হয়। ইন্টারফেসের ভিতরে রাখুন;

একটি ইন্টারফেস শ্রেণীর মত দেখাচ্ছে,

class IMyInterface
{
    DEFINE_INTERFACE(IMyInterface);

public:
    virtual ~IMyInterface() {};

    virtual void MyMethod1() = 0;
    ...
};

শিরোনাম ফাইলটি এরকম

#if !defined(SN_FACTORY_H_INCLUDED)
#define SN_FACTORY_H_INCLUDED

#pragma once

গ্রন্থাগারগুলি এই ম্যাক্রো সংজ্ঞাতে তালিকাভুক্ত রয়েছে। লাইব্রেরি / এক্সিকিউটেবলের জন্য এক লাইন। আমরা যদি অন্য কোন এক্সিকিউটেবলকে কল করতে পারি তবে এটি দুর্দান্ত।

#define SN_APPLY_LIBRARIES(L, A)                          \
    L(A, sn, "sn.dll")                                    \
    L(A, http_server_lib, "http_server_lib.dll")          \
    L(A, http_server, "")

তারপরে প্রতিটি ডিএল / এক্সের জন্য আপনি একটি ম্যাক্রো সংজ্ঞায়িত করেন এবং এর বাস্তবায়নগুলি তালিকাভুক্ত করেন। Def এর অর্থ এটি ইন্টারফেসের জন্য ডিফল্ট বাস্তবায়ন। যদি এটি ডিফল্ট না হয় তবে আপনি এটি সনাক্ত করতে ব্যবহৃত ইন্টারফেসের জন্য একটি নাম দিন। অর্থাত্, বিশেষ এবং নামটি হবে IHTTP_Server_sp خصوصی_entry।

#define SN_APPLY_ENTRYPOINTS_sn(M)                                     \
    M(IHTTP_Handler, SNI::SNI_HTTP_Handler, sn, def)                   \
    M(IHTTP_Handler, SNI::SNI_HTTP_Handler, sn, special)

#define SN_APPLY_ENTRYPOINTS_http_server_lib(M)                        \
    M(IHTTP_Server, HTTP::server::server, http_server_lib, def)

#define SN_APPLY_ENTRYPOINTS_http_server(M)

সমস্ত সেটআপ লাইব্রেরি সহ, শিরোনাম ফাইলটি প্রয়োজনীয়কে সংজ্ঞায়িত করতে ম্যাক্রো সংজ্ঞা ব্যবহার করে।

#define APPLY_ENTRY(A, N, L) \
    SN_APPLY_ENTRYPOINTS_##N(A)

#define DEFINE_INTERFACE(I) \
    public: \
        static const long Id = SN::I##_def_entry; \
    private:

namespace SN
{
    #define DEFINE_LIBRARY_ENUM(A, N, L) \
        N##_library,

এটি গ্রন্থাগারগুলির জন্য একটি এনাম তৈরি করে।

    enum LibraryValues
    {
        SN_APPLY_LIBRARIES(DEFINE_LIBRARY_ENUM, "")
        LastLibrary
    };

    #define DEFINE_ENTRY_ENUM(I, C, L, D) \
        I##_##D##_entry,

এটি ইন্টারফেস বাস্তবায়নের জন্য একটি এনাম তৈরি করে।

    enum EntryValues
    {
        SN_APPLY_LIBRARIES(APPLY_ENTRY, DEFINE_ENTRY_ENUM)
        LastEntry
    };

    long CallEntryPoint(long id, long interfaceId);

এটি কারখানার শ্রেণিকে সংজ্ঞায়িত করে। এখানে এটি খুব বেশি না।

    template <class I>
    class SN_Factory
    {
    public:
        SN_Factory()
        {
        }

        static I *CreateObject(long id = I::Id )
        {
            return (I *)CallEntryPoint(id, I::Id);
        }
    };
}

#endif //SN_FACTORY_H_INCLUDED

তারপরে সিপিপি হ'ল,

#include "sn_factory.h"

#include <windows.h>

বাহ্যিক এন্ট্রি পয়েন্ট তৈরি করুন। আপনি depend.exe ব্যবহার করে এটি পরীক্ষা করতে পারেন।

extern "C"
{
    __declspec(dllexport) long entrypoint(long id)
    {
        #define CREATE_OBJECT(I, C, L, D) \
            case SN::I##_##D##_entry: return (int) new C();

        switch (id)
        {
            SN_APPLY_CURRENT_LIBRARY(APPLY_ENTRY, CREATE_OBJECT)
        case -1:
        default:
            return 0;
        }
    }
}

ম্যাক্রোগুলি প্রয়োজনীয় সমস্ত ডেটা সেট আপ করে।

namespace SN
{
    bool loaded = false;

    char * libraryPathArray[SN::LastLibrary];
    #define DEFINE_LIBRARY_PATH(A, N, L) \
        libraryPathArray[N##_library] = L;

    static void LoadLibraryPaths()
    {
        SN_APPLY_LIBRARIES(DEFINE_LIBRARY_PATH, "")
    }

    typedef long(*f_entrypoint)(long id);

    f_entrypoint libraryFunctionArray[LastLibrary - 1];
    void InitlibraryFunctionArray()
    {
        for (long j = 0; j < LastLibrary; j++)
        {
            libraryFunctionArray[j] = 0;
        }

        #define DEFAULT_LIBRARY_ENTRY(A, N, L) \
            libraryFunctionArray[N##_library] = &entrypoint;

        SN_APPLY_CURRENT_LIBRARY(DEFAULT_LIBRARY_ENTRY, "")
    }

    enum SN::LibraryValues libraryForEntryPointArray[SN::LastEntry];
    #define DEFINE_ENTRY_POINT_LIBRARY(I, C, L, D) \
            libraryForEntryPointArray[I##_##D##_entry] = L##_library;
    void LoadLibraryForEntryPointArray()
    {
        SN_APPLY_LIBRARIES(APPLY_ENTRY, DEFINE_ENTRY_POINT_LIBRARY)
    }

    enum SN::EntryValues defaultEntryArray[SN::LastEntry];
        #define DEFINE_ENTRY_DEFAULT(I, C, L, D) \
            defaultEntryArray[I##_##D##_entry] = I##_def_entry;

    void LoadDefaultEntries()
    {
        SN_APPLY_LIBRARIES(APPLY_ENTRY, DEFINE_ENTRY_DEFAULT)
    }

    void Initialize()
    {
        if (!loaded)
        {
            loaded = true;
            LoadLibraryPaths();
            InitlibraryFunctionArray();
            LoadLibraryForEntryPointArray();
            LoadDefaultEntries();
        }
    }

    long CallEntryPoint(long id, long interfaceId)
    {
        Initialize();

        // assert(defaultEntryArray[id] == interfaceId, "Request to create an object for the wrong interface.")
        enum SN::LibraryValues l = libraryForEntryPointArray[id];

        f_entrypoint f = libraryFunctionArray[l];
        if (!f)
        {
            HINSTANCE hGetProcIDDLL = LoadLibraryA(libraryPathArray[l]);

            if (!hGetProcIDDLL) {
                return NULL;
            }

            // resolve function address here
            f = (f_entrypoint)GetProcAddress(hGetProcIDDLL, "entrypoint");
            if (!f) {
                return NULL;
            }
            libraryFunctionArray[l] = f;
        }
        return f(id);
    }
}

প্রতিটি লাইব্রেরিতে প্রতিটি লাইব্রেরি / এক্সিকিউটেবলের জন্য একটি স্টাব সিপিপি সহ এই "সিপিপি" অন্তর্ভুক্ত থাকে। যে কোনও নির্দিষ্ট সংকলিত শিরোনাম স্টাফ।

#include "sn_pch.h"

এই লাইব্রেরিটি সেটআপ করুন।

#define SN_APPLY_CURRENT_LIBRARY(L, A) \
    L(A, sn, "sn.dll")

একটি প্রধান সিপিসি জন্য অন্তর্ভুক্ত। আমার ধারণা এই সিপিপি একটি .h হতে পারে। তবে আপনি এটি করতে পারেন এমন বিভিন্ন উপায় রয়েছে। এই পদ্ধতির আমার জন্য কাজ করে।

#include "../inc/sn_factory.cpp"
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.