cefsharp 잘 아시는 분들 "Uncaught TypeError: window.cefQuery is not a function" 이런 에러가 나면서 콜백이 실행이 안됩니다.

먼저 말씀 드릴것은 제가 cef를 써본적이 없습니다.

현재 기존 소스가 C++에 cef로 만들어져 있습니다.
헷갈리는 부분은 JS에서 C++을 호출하는 부분이 있습니다.

function cefQuery_(cefQuery__){
    window.cefQuery({request: cefQuery__,
    persistent: false,
    onSuccess: function(response) {},
    onFailure: function(error_code, error_message) {} });
}

위에서 실제 콜하는 부분은 window.cefQuery 요 부분일거고요.

// class 선언
class CMessageHandler : public CefMessageRouterBrowserSide::Handler
{
public:
	virtual bool OnQuery(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, int64 query_id, const CefString& request, bool persistent, CefRefPtr<Callback> callback) OVERRIDE;
};

// class 구현부
bool CMessageHandler::OnQuery(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, int64 query_id, const CefString& request, bool persistent, CefRefPtr<Callback> callback)
{
	const std::string& url = frame->GetURL();
	const std::string& message_name = request;
	callback->Success("ok");
	
	if (message_name.length() > 0) {
		//// code
	}
	
	return false;
}

// 콜백 등록 분 
CefMessageRouterConfig config;|
CefRefPtr<CefMessageRouterBrowserSide> message_router = CefMessageRouterBrowserSide::Create(config);|
message_router->AddHandler(new CMessageHandler() , false);

c++에서는 위의 코드만 적용하면 콜백이 등록이 됩니다.

그런데 아무리 검색을 해도 CefSharp은 저러한 코드가 나오지를 않습니다.
콜백 등록을 하는 방법은 나오는데.
cefQuery를 등록을 해도 실제 동작을 하지 않습니다.
아무래도 CMessageHandler와 같은 형태를 취해야 할 것 같은데.
CefMessageRouterBrowserSide 이것에 해당하는 클래스나 인터페이스가 검색되지 않습니다.

C++쪽 코드는 아래의 링크처럼 기본적으로 사용되는 방법으로 보입니다.

글 읽어 주셔서 감사합니다.

1개의 좋아요

자바스크립트 ↔ c# 사이 호출을 말씀하시는거라면 Javascript Binding을 찾아 보시면 되시는데

Javascript Binding v2 · Issue #2246 · cefsharp/CefSharp · GitHub

이 글을 읽어 보시거나

CefSharp/CefSharp.Example/JavascriptBinding at 00f5db9159415ec537682efb48ac3b112285c582 · cefsharp/CefSharp · GitHub

예제 샘플 코드를 참조해보시면 되세요.
코드들 보시고, 함수 등록해서 호출해보시면 답을 얻으실 것 같네요.

2개의 좋아요

답변 감사합니다.
말씀하신것은 해 보았습니다 만 안되는 듯 합니다.

몇가지 테스트를 해 본 바 이름이 조금이라도 다르면 호출이 안됩니다.
두번째로 인자가 다르면 호출이 안됩니다.
문제는 C++에는 기본으로 포함되어 있는 클래스가 C#버전인 cefsharp에는 포함이 안되어 있거나?
아니면 엉뚱한 곳에 가 있는 것 같습니다.
cef c++소스도 보고 있는데. CefMessageRouterBrowserSide 이게 wrapping 클래스로 확장 기능인 것 같습니다. 기능까지 본건 아니라 추측이고요.
기능하나 때문에 이렇게까지 봐야하나? 자괴감이 들고 있네요. ㅎㅎ

1개의 좋아요

제가 경험했을땐 c++쪽 코드까지 볼 필요 없이 함수 등록하고 쓰기만 하면 되는거였는데 뭔가 다른 이슈가 있나봐요 ㅠㅠ

1개의 좋아요
window.cefQuery({request: cefQuery__,
    persistent: false,
    onSuccess: function(response) {},
    onFailure: function(error_code, error_message) {} });

요기서

{request: cefQuery__,
    persistent: false,
    onSuccess: function(response) {},
    onFailure: function(error_code, error_message) {} }

요게 CefMessageRouterBrowserSide::Handler 요 클래스 인 듯 합니다.
즉 자바스크립트측에서 클래스를 인자로 넣어서 호출하는 형태인데요.

테스트코드 돌려보니 argument 의 type이 바뀌면 호출이 안되는 것 같드라고요.
저도 이것저것 해 보는 중이라 100%는 아닌데. 일단은 안되고 있네요. ㅎㅎ

1개의 좋아요

우선 binding v2 방식으로 함수 등록하는 코드가 안보이는데요.

예전에 샘플로 만들었던 코드 공유 드려요.
여전히 잘 동작합니다.

function setPropertyAsync(key, subject) {
    return new Promise(async function(resolve, reject) {
        await CefSharp.BindObjectAsync("nativeStorage", "nativeStorage");
        return nativeStorage.setPropertyAsync(key, subject, resolve, reject);
    });
    
}
function getPropertyAsync(key) {
    return new Promise(async function(resolve, reject) {
        await CefSharp.BindObjectAsync("nativeStorage", "nativeStorage");
        return nativeStorage.getPropertyAsync(key, resolve, reject);
    });
}
$(document).ready(async function() {
    await CefSharp.BindObjectAsync("nativeStorage", "nativeStorage");
    if(nativeStorage === undefined) {
        $.toast('nativeStorage is undefined');
    }
});
public class NativeStorage
{
    /// <inheritdoc />
    public NativeStorage(string applicationName) => BlobCache.ApplicationName = applicationName;

    [DebuggerHidden]
    public async void SetPropertyAsync(string key, string subject, IJavascriptCallback resolve, IJavascriptCallback reject)
    {
        if (key == null)
        {
            throw new ArgumentNullException(nameof(key));
        }

        if (subject == null)
        {
            throw new ArgumentNullException(nameof(subject));
        }

        if (resolve == null)
        {
            throw new ArgumentNullException(nameof(resolve));
        }

        if (reject == null)
        {
            throw new ArgumentNullException(nameof(reject));
        }

        try
        {
            await BlobCache.UserAccount.InsertObject(key, subject);

            if (!resolve.IsDisposed)
            {
                await resolve.ExecuteAsync(true);
            }
        }
        catch (Exception e)
        {
            if (!reject.IsDisposed)
            {
                await reject.ExecuteAsync(e.ToString());
            }
        }
    }

    [DebuggerHidden]
    public async void GetPropertyAsync(string key, IJavascriptCallback resolve, IJavascriptCallback reject)
    {
        if (key == null)
        {
            throw new ArgumentNullException(nameof(key));
        }

        if (resolve == null)
        {
            throw new ArgumentNullException(nameof(resolve));
        }

        if (reject == null)
        {
            throw new ArgumentNullException(nameof(reject));
        }

        try
        {
            var result = await BlobCache.UserAccount.GetObject<string>(key);

            if (!resolve.IsDisposed)
            {
                await resolve.ExecuteAsync(result);
            }
        }
        catch (Exception e)
        {
            if (!reject.IsDisposed)
            {
                await reject.ExecuteAsync(e.ToString());
            }
        }
    }
public class MainViewModel : INotifyPropertyChanged
{
    private bool _enable = true;
    private ChromiumWebBrowser _webBrowser;

    /// <inheritdoc />
    public MainViewModel()
    {
        _webBrowser = new ChromiumWebBrowser
        {
            BrowserSettings = new BrowserSettings
            {
                DefaultEncoding = "utf-8"
            }
        };

        WebBrowser.JavascriptObjectRepository.Register("nativeStorage", new NativeStorage("CefSharpDemo"), true, new BindingOptions
        {
            CamelCaseJavascriptNames = true
        });

        WebBrowser.JavascriptObjectRepository.Register("nativeWindow", new NativeWindow(this), true, new BindingOptions
        {
            CamelCaseJavascriptNames = true
        });
    }

    public ChromiumWebBrowser WebBrowser
    {
        get => _webBrowser;
        set
        {
            _webBrowser = value;
            OnPropertyChanged(nameof(WebBrowser));
        }
    }
}
3개의 좋아요

감사합니다.
참고하겠습니다.
즐거운 저녁 되시고요~

2개의 좋아요

좋은 저녁 되세요 :slight_smile:

CefSharp 공식 JSB 문서를 찾은 것 같아서 공유드려요~

2개의 좋아요

브라우저에 c# 클래스 등록하는 예제 코드를 빼먹어서 보강했습니다~

2개의 좋아요

일단 적어주신 부분이 해결책은 아니나
이용할 수 있는 방법을 찾아냈습니다.
C++버전과 결정적인 차이는 자바스크립트 최상위 객체인 window에 function을 추가 가능 하냐의 차이인 것 같습니다.
cefsharp은 바인딩만으로는 불가능 한것으로 보이고요.
저는 ExecuteScriptAsync 이게 이름에서 보이듯이 실행만 된다고 생각을 했는데.
function추가가 가능하더군요.
browser.ExecuteScriptAsync("function cefQuery(arg) { alert(arg.request + arg.persistent ); arg.onFailure(1, 'test'); }");
요리 하니 일단 c++버전과 같은 window.cefQuery가 추가가 됐습니다.
이제 내용을 cefsharp의 binding으로 바꿔 치기 하면 원하는 목표가 달성 될 것으로 보입니다.

3개의 좋아요